Compare commits

..

3 Commits

Author SHA1 Message Date
lizzie
4da9c6303b better script opts
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-11-10 01:31:01 +01:00
lizzie
5478ddde87 better
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-11-10 01:31:01 +01:00
lizzie
8f1d895932 [cpm]: Ability to override and set custom hosts (like a self hosted instance or whatever)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-11-10 01:31:01 +01:00
127 changed files with 1780 additions and 3380 deletions

View File

@@ -1,19 +0,0 @@
name: tx-src
on:
push:
branches: [ master ]
jobs:
sources:
runs-on: source
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Push New Sources
run: |
export PATH=/usr/lib/qt6/bin:$PATH
./tools/translations/qt-source.sh
tx-cli push -s

View File

@@ -1,61 +0,0 @@
name: tx-pull
on:
# monday, wednesday, saturday at 2pm
schedule:
cron:
- '0 14 * * 1,3,6'
jobs:
tx-update:
runs-on: source
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get New Translations
run: tx-cli pull -t -f
- name: Push branch
run: |
git config --local user.name "Eden CI"
git config --local user.email "ci@eden-emu.dev"
git config --local user.signingkey "D57652791BB25D2A"
git config --local push.autoSetupRemote true
git remote set-url origin ci:eden-emu/eden.git
TIMESTAMP=$(date +"%s")
echo "TIMESTAMP=$TIMESTAMP" >> "$GITHUB_ENV"
git switch -c update-translations-$TIMESTAMP
git add dist src/android/app/src/main/res
git commit -sS -m "[dist, android] Update translations from Transifex"
git push
- name: Create PR
run: |
DATE=$(date +"%b %d")
TITLE="[dist, android] Update translations from Transifex for $DATE"
BODY="Automatic translation update for $DATE"
BASE=master
HEAD=update-translations-$TIMESTAMP
cat << EOF > data.json
{
"base": "$BASE",
"body": "$BODY",
"head": "$HEAD",
"title": "$TITLE"
}
EOF
curl -X 'POST' \
'https://git.eden-emu.dev/api/v1/repos/eden-emu/eden/pulls' \
-H 'accept: application/json' \
-H 'Authorization: Bearer ${{ secrets.CI_FJ_TOKEN }}' \
-H 'Content-Type: application/json' \
-d "@data.json" --fail

View File

@@ -21,11 +21,6 @@ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(PLATFORM_LINUX ON)
endif()
# dumb heuristic to detect msys2
if (CMAKE_COMMAND MATCHES "msys64")
set(PLATFORM_MSYS ON)
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(CXX_CLANG ON)
if (MSVC)
@@ -90,89 +85,6 @@ if (PLATFORM_NETBSD)
set(ENV{PKG_CONFIG_PATH} "${PKG_CONFIG_PATH}:${CMAKE_SYSROOT}/usr/pkg/lib/ffmpeg7/pkgconfig")
endif()
# MSYS2 utilities
if (PLATFORM_MSYS)
include(FixMsysPaths)
# really, really dumb heuristic to detect what environment we are in
macro(system var)
if (CMAKE_COMMAND MATCHES ${var})
set(MSYSTEM ${var})
endif()
endmacro()
system(mingw64)
system(clang64)
system(clangarm64)
system(ucrt64)
if (NOT DEFINED MSYSTEM)
set(MSYSTEM msys2)
endif()
# we (generally) want to prioritize environment-specific binaries if possible
# some, like autoconf, are not present on environments besides msys2 though
set(CMAKE_PROGRAM_PATH C:/msys64/${MSYSTEM}/bin C:/msys64/usr/bin)
set(ENV{PKG_CONFIG_PATH} C:/msys64/${MSYSTEM}/lib/pkgconfig)
endif()
# static stuff
option(YUZU_STATIC_BUILD "Use static libraries and executables if available" OFF)
if (YUZU_STATIC_BUILD)
# lol
set(Boost_USE_STATIC_LIBS ON)
set(BUILD_SHARED_LIBS OFF)
## find .a libs first (static, usually)
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
## some libraries define a Library::Name_static alternative ##
set(YUZU_STATIC_SUFFIX _static)
## some libraries use CMAKE_IMPORT_LIBRARY_SUFFIX e.g. Harfbuzz ##
set(CMAKE_IMPORT_LIBRARY_SUFFIX ".a")
if (MINGW)
# simple hook to reject dynamic libs
function(find_library var)
# also skip previously-found libraries cuz... yaknow
if (${var})
return()
endif()
_find_library(${var} ${ARGN})
if (${var})
get_filename_component(lib_name "${${var}}" NAME)
if (lib_name MATCHES "dll\\.a$")
unset(${var} CACHE)
set(${var} "${var}-NOTFOUND" CACHE INTERNAL "" FORCE)
endif()
endif()
endfunction()
# msys2 quazip does not build a static lib
set(QuaZip-Qt6_FORCE_BUNDLED ON)
set(YUZU_USE_BUNDLED_FFMPEG ON)
set(YUZU_USE_BUNDLED_SDL2 ON)
set(HTTPLIB_USE_BROTLI_IF_AVAILABLE OFF)
elseif(APPLE)
# these libs do not properly provide static libs/let you do it with cmake
set(YUZU_USE_CPM ON)
# IMPORTED_IMPLIB not set for imported target
# TODO(crueter): wtf
set(YUZU_USE_BUNDLED_FFMPEG ON)
set(YUZU_USE_EXTERNAL_SDL2 ON)
set(fmt_FORCE_BUNDLED ON)
set(SPIRV-Tools_FORCE_BUNDLED ON)
set(SPIRV-Headers_FORCE_BUNDLED ON)
set(zstd_FORCE_BUNDLED ON)
endif()
endif()
# Detect current compilation architecture and create standard definitions
# =======================================================================
@@ -307,9 +219,11 @@ endif()
# On Linux system SDL2 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion
cmake_dependent_option(ENABLE_SDL2 "Enable the SDL2 frontend" ON "NOT ANDROID" 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 "Build SDL2 from external source" OFF "ENABLE_SDL2;NOT MSVC" OFF)
cmake_dependent_option(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 build" "${MSVC}" "ENABLE_SDL2" OFF)
if (ENABLE_SDL2)
# 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()
# qt stuff
option(ENABLE_QT "Enable the Qt frontend" ON)
@@ -330,14 +244,10 @@ option(YUZU_USE_CPM "Use CPM to fetch system dependencies (fmt, boost, etc) if n
# ffmpeg
option(YUZU_USE_BUNDLED_FFMPEG "Download bundled FFmpeg" ${EXT_DEFAULT})
cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from external source" "${PLATFORM_SUN}" "NOT WIN32 AND NOT ANDROID" OFF)
cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from source" "${PLATFORM_SUN}" "NOT WIN32 AND NOT ANDROID" OFF)
# sirit
set(BUNDLED_SIRIT_DEFAULT OFF)
if ((MSVC AND NOT (CMAKE_BUILD_TYPE MATCHES "Debug|RelWithDebInfo") OR ANDROID))
set(BUNDLED_SIRIT_DEFAULT ON)
endif()
option(YUZU_USE_BUNDLED_SIRIT "Download bundled sirit" ${BUNDLED_SIRIT_DEFAULT})
option(YUZU_USE_BUNDLED_SIRIT "Download bundled sirit" ${EXT_DEFAULT})
# Re-allow on FreeBSD once its on mainline ports
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "WIN32 OR PLATFORM_LINUX OR APPLE" OFF)
@@ -374,17 +284,6 @@ if(USE_CCACHE)
else()
message(FATAL_ERROR "USE_CCACHE enabled, but no executable found at: ${CCACHE_PATH}")
endif()
# Follow SCCache recommendations:
# <https://github.com/mozilla/sccache/blob/main/README.md?plain=1#L144>
if(WIN32)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
endif()
endif()
endif()
# TODO(crueter): CI this?
@@ -456,6 +355,13 @@ if (ANDROID)
set(CMAKE_POLICY_VERSION_MINIMUM 3.5) # Workaround for Oboe
endif()
# We need to downgrade debug info (/Zi -> /Z7) to use an older but more cacheable format
# See https://github.com/nanoant/CMakePCHCompiler/issues/21
if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
endif()
# Default to a Release build
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE)
@@ -676,7 +582,6 @@ else()
find_package(stb MODULE)
find_package(Opus 1.3 MODULE REQUIRED)
find_package(ZLIB 1.2 REQUIRED)
find_package(zstd 1.5 REQUIRED MODULE)
@@ -729,15 +634,15 @@ if (APPLE)
# Umbrella framework for everything GUI-related
find_library(COCOA_LIBRARY Cocoa)
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY})
# find_library(ICONV_LIBRARY iconv REQUIRED)
# list(APPEND PLATFORM_LIBRARIES ${ICONV_LIBRARY})
find_library(ICONV_LIBRARY iconv REQUIRED)
list(APPEND PLATFORM_LIBRARIES ${ICONV_LIBRARY})
elseif (WIN32)
# Target Windows 10
add_compile_definitions(_WIN32_WINNT=0x0A00 WINVER=0x0A00)
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} winmm iphlpapi ws2_32 wlanapi)
set(PLATFORM_LIBRARIES winmm ws2_32 iphlpapi)
if (MINGW)
# PSAPI is the Process Status API
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version crypt32 rpcrt4 gdi32 wldap32 mswsock)
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version)
endif()
elseif (PLATFORM_HAIKU)
# Haiku is so special :)
@@ -750,8 +655,6 @@ elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
set(PLATFORM_LIBRARIES rt)
endif()
message(STATUS "Platform Libraries: ${PLATFORM_LIBRARIES}")
add_subdirectory(externals)
# pass targets from externals

View File

@@ -13,6 +13,11 @@ option(CPMUTIL_FORCE_BUNDLED
option(CPMUTIL_FORCE_SYSTEM
"Force system packages for all CPM dependencies (NOT RECOMMENDED)" OFF)
set(CPMUTIL_DEFAULT_HOST github.com CACHE STRING "Sets the default host when 'git_host' isn't defined")
option(CPMUTIL_FORCE_HOST
"Force host CPMUTIL_DEFAULT_HOST to be used for all CPM dependencies even when 'git_host' is defined" OFF)
cmake_minimum_required(VERSION 3.22)
include(CPM)
@@ -293,8 +298,8 @@ function(AddPackage)
option(${PKG_ARGS_NAME}_FORCE_SYSTEM "Force the system package for ${PKG_ARGS_NAME}")
option(${PKG_ARGS_NAME}_FORCE_BUNDLED "Force the bundled package for ${PKG_ARGS_NAME}")
if (NOT DEFINED PKG_ARGS_GIT_HOST)
set(git_host github.com)
if (CPMUTIL_FORCE_HOST OR NOT DEFINED PKG_ARGS_GIT_HOST)
set(git_host ${CPMUTIL_DEFAULT_HOST})
else()
set(git_host ${PKG_ARGS_GIT_HOST})
endif()
@@ -542,11 +547,10 @@ function(AddCIPackage)
PACKAGE
EXTENSION
MIN_VERSION
DISABLED_PLATFORMS
)
set(multiValueArgs DISABLED_PLATFORMS)
cmake_parse_arguments(PKG_ARGS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
cmake_parse_arguments(PKG_ARGS "" "${oneValueArgs}" "" ${ARGN})
if(NOT DEFINED PKG_ARGS_VERSION)
message(FATAL_ERROR "[CPMUtil] VERSION is required")

View File

@@ -1,6 +1,3 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
@@ -13,10 +10,6 @@ find_package_handle_standard_args(Opus
VERSION_VAR OPUS_VERSION
)
if (PLATFORM_MSYS)
FixMsysPath(PkgConfig::OPUS)
endif()
if (Opus_FOUND AND NOT TARGET Opus::opus)
add_library(Opus::opus ALIAS PkgConfig::OPUS)
endif()

View File

@@ -1,6 +1,3 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
@@ -13,10 +10,6 @@ find_package_handle_standard_args(SPIRV-Tools
VERSION_VAR SPIRV-Tools_VERSION
)
if (PLATFORM_MSYS)
FixMsysPath(PkgConfig::SPIRV-Tools)
endif()
if (SPIRV-Tools_FOUND AND NOT TARGET SPIRV-Tools::SPIRV-Tools)
if (TARGET SPIRV-Tools)
add_library(SPIRV-Tools::SPIRV-Tools ALIAS SPIRV-Tools)

View File

@@ -1,6 +1,3 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2022 Alexandre Bouvier <contact@amb.tf>
#
# SPDX-License-Identifier: GPL-3.0-or-later
@@ -14,10 +11,6 @@ find_package_handle_standard_args(enet
VERSION_VAR ENET_VERSION
)
if (PLATFORM_MSYS)
FixMsysPath(PkgConfig::ENET)
endif()
if (enet_FOUND AND NOT TARGET enet::enet)
add_library(enet::enet ALIAS PkgConfig::ENET)
endif()

View File

@@ -1,6 +1,3 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2022 Alexandre Bouvier <contact@amb.tf>
#
# SPDX-License-Identifier: GPL-3.0-or-later
@@ -14,10 +11,6 @@ find_package_handle_standard_args(libusb
VERSION_VAR LIBUSB_VERSION
)
if (PLATFORM_MSYS)
FixMsysPath(PkgConfig::LIBUSB)
endif()
if (libusb_FOUND AND NOT TARGET libusb::usb)
add_library(libusb::usb ALIAS PkgConfig::LIBUSB)
endif()

View File

@@ -1,6 +1,3 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
@@ -12,11 +9,6 @@ if (lz4_CONSIDERED_CONFIGS)
else()
find_package(PkgConfig QUIET)
pkg_search_module(LZ4 QUIET IMPORTED_TARGET liblz4)
if (PLATFORM_MSYS)
FixMsysPath(PkgConfig::LZ4)
endif()
find_package_handle_standard_args(lz4
REQUIRED_VARS LZ4_LINK_LIBRARIES
VERSION_VAR LZ4_VERSION

View File

@@ -6,34 +6,22 @@
include(FindPackageHandleStandardArgs)
find_package(zstd QUIET CONFIG)
if (zstd_CONSIDERED_CONFIGS)
find_package_handle_standard_args(zstd CONFIG_MODE)
else()
find_package(PkgConfig QUIET)
pkg_search_module(ZSTD QUIET IMPORTED_TARGET libzstd)
find_package_handle_standard_args(zstd
REQUIRED_VARS ZSTD_LINK_LIBRARIES
VERSION_VAR ZSTD_VERSION
)
endif()
find_package(PkgConfig QUIET)
pkg_search_module(ZSTD QUIET IMPORTED_TARGET libzstd)
find_package_handle_standard_args(zstd
REQUIRED_VARS ZSTD_LINK_LIBRARIES
VERSION_VAR ZSTD_VERSION
)
if (zstd_FOUND AND NOT TARGET zstd::zstd)
if (TARGET zstd::libzstd_shared AND NOT YUZU_STATIC_BUILD)
if (TARGET zstd::libzstd_shared)
add_library(zstd::zstd ALIAS zstd::libzstd_shared)
add_library(zstd::libzstd ALIAS zstd::libzstd_shared)
elseif (TARGET zstd::libzstd_static)
add_library(zstd::zstd ALIAS zstd::libzstd_static)
add_library(zstd::libzstd ALIAS zstd::libzstd_static)
else()
add_library(zstd::zstd ALIAS PkgConfig::ZSTD)
endif()
endif()
get_target_property(ZSTD_TARGET zstd::zstd ALIASED_TARGET)
if (NOT TARGET zstd::libzstd)
if (ZSTD_TARGET)
add_library(zstd::libzstd ALIAS ${ZSTD_TARGET})
else()
add_library(zstd::libzstd ALIAS zstd::zstd)
add_library(zstd::libzstd ALIAS PkgConfig::ZSTD)
endif()
endif()

View File

@@ -1,21 +0,0 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
function(FixMsysPath target)
get_target_property(include_dir ${target} INTERFACE_INCLUDE_DIRECTORIES)
if (NOT (include_dir MATCHES "^/"))
return()
endif()
set(root_default $ENV{MSYS2_LOCATION})
if (root_default STREQUAL "")
set(root_default "C:/msys64")
endif()
set(MSYS_ROOT_PATH ${root_default} CACHE STRING "Location of the MSYS2 root")
set(include_dir "C:/msys64${include_dir}")
set_target_properties(${target} PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${include_dir})
endfunction()

View File

@@ -0,0 +1,58 @@
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
set(MINGW_PREFIX /usr/x86_64-w64-mingw32/)
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
set(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX})
set(SDL2_PATH ${MINGW_PREFIX})
set(MINGW_TOOL_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-)
# Specify the cross compiler
set(CMAKE_C_COMPILER ${MINGW_TOOL_PREFIX}clang)
set(CMAKE_CXX_COMPILER ${MINGW_TOOL_PREFIX}clang++)
set(CMAKE_RC_COMPILER ${MINGW_TOOL_PREFIX}windres)
set(CMAKE_C_COMPILER_AR ${MINGW_TOOL_PREFIX}ar)
set(CMAKE_CXX_COMPILER_AR ${MINGW_TOOL_PREFIX}ar)
set(CMAKE_C_COMPILER_RANLIB ${MINGW_TOOL_PREFIX}ranlib)
set(CMAKE_CXX_COMPILER_RANLIB ${MINGW_TOOL_PREFIX}ranlib)
# Mingw tools
set(STRIP ${MINGW_TOOL_PREFIX}strip)
set(WINDRES ${MINGW_TOOL_PREFIX}windres)
set(ENV{PKG_CONFIG} ${MINGW_TOOL_PREFIX}pkg-config)
# ccache wrapper
option(USE_CCACHE "Use ccache for compilation" OFF)
if(USE_CCACHE)
find_program(CCACHE ccache)
if(CCACHE)
message(STATUS "Using ccache found in PATH")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE})
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE})
else(CCACHE)
message(WARNING "USE_CCACHE enabled, but no ccache found")
endif(CCACHE)
endif(USE_CCACHE)
# Search for programs in the build host directories
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Echo modified cmake vars to screen for debugging purposes
if(NOT DEFINED ENV{MINGW_DEBUG_INFO})
message("")
message("Custom cmake vars: (blank = system default)")
message("-----------------------------------------")
message("* CMAKE_C_COMPILER : ${CMAKE_C_COMPILER}")
message("* CMAKE_CXX_COMPILER : ${CMAKE_CXX_COMPILER}")
message("* CMAKE_RC_COMPILER : ${CMAKE_RC_COMPILER}")
message("* WINDRES : ${WINDRES}")
message("* ENV{PKG_CONFIG} : $ENV{PKG_CONFIG}")
message("* STRIP : ${STRIP}")
message("* USE_CCACHE : ${USE_CCACHE}")
message("")
# So that the debug info only appears once
set(ENV{MINGW_DEBUG_INFO} SHOWN)
endif()

View File

@@ -0,0 +1,57 @@
# SPDX-FileCopyrightText: 2018 tech4me <guiwanglong@gmail.com>
# SPDX-License-Identifier: GPL-2.0-or-later
set(MINGW_PREFIX /usr/x86_64-w64-mingw32/)
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
# Actually a hack, w/o this will cause some strange errors
set(CMAKE_HOST_WIN32 TRUE)
set(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX})
set(SDL2_PATH ${MINGW_PREFIX})
set(MINGW_TOOL_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-)
# Specify the cross compiler
set(CMAKE_C_COMPILER ${MINGW_TOOL_PREFIX}gcc)
set(CMAKE_CXX_COMPILER ${MINGW_TOOL_PREFIX}g++)
set(CMAKE_RC_COMPILER ${MINGW_TOOL_PREFIX}windres)
# Mingw tools
set(STRIP ${MINGW_TOOL_PREFIX}strip)
set(WINDRES ${MINGW_TOOL_PREFIX}windres)
set(ENV{PKG_CONFIG} ${MINGW_TOOL_PREFIX}pkg-config)
# ccache wrapper
option(USE_CCACHE "Use ccache for compilation" OFF)
if(USE_CCACHE)
find_program(CCACHE ccache)
if(CCACHE)
message(STATUS "Using ccache found in PATH")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE})
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE})
else(CCACHE)
message(WARNING "USE_CCACHE enabled, but no ccache found")
endif(CCACHE)
endif(USE_CCACHE)
# Search for programs in the build host directories
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Echo modified cmake vars to screen for debugging purposes
if(NOT DEFINED ENV{MINGW_DEBUG_INFO})
message("")
message("Custom cmake vars: (blank = system default)")
message("-----------------------------------------")
message("* CMAKE_C_COMPILER : ${CMAKE_C_COMPILER}")
message("* CMAKE_CXX_COMPILER : ${CMAKE_CXX_COMPILER}")
message("* CMAKE_RC_COMPILER : ${CMAKE_RC_COMPILER}")
message("* WINDRES : ${WINDRES}")
message("* ENV{PKG_CONFIG} : $ENV{PKG_CONFIG}")
message("* STRIP : ${STRIP}")
message("* USE_CCACHE : ${USE_CCACHE}")
message("")
# So that the debug info only appears once
set(ENV{MINGW_DEBUG_INFO} SHOWN)
endif()

BIN
dist/eden.ico vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 386 KiB

After

Width:  |  Height:  |  Size: 315 KiB

View File

@@ -1,9 +0,0 @@
# Icon variations
These icons are licensed under GPLv3. Please see the [script for generating icons](../../tools/README.md) and appropriatedly redirect for seasonal icons.
- `base_named.svg` - Named variant.
- `base_small.svg`: Variant used for tiny icons (16x16, 64x64, etc).
- `base.svg`: Variant without branding/naming.
Try to keep the icons simple. And preferably automatically be able to be generated (to reduce maintanaince burden). Additionally keep the files small (use `svgo` and `optipng`) to not clutter the git.

View File

@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="512"
height="512"
fill="none"
viewBox="0 0 512 512"
version="1.1"
id="svg7"
sodipodi:docname="base_small.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs7">
<linearGradient
id="linearGradient10"
inkscape:collect="always">
<stop
style="stop-color:#f977d9;stop-opacity:1;"
offset="0"
id="stop10" />
<stop
style="stop-color:#a655d5;stop-opacity:1;"
offset="0.54051435"
id="stop13" />
<stop
style="stop-color:#984fd5;stop-opacity:1;"
offset="0.5714342"
id="stop12" />
<stop
style="stop-color:#8c4ad4;stop-opacity:1;"
offset="0.59666741"
id="stop14" />
<stop
style="stop-color:#3928d2;stop-opacity:1;"
offset="1"
id="stop11" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient10"
id="linearGradient11"
x1="264.17508"
y1="28.385544"
x2="264.17508"
y2="488.65109"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.97815818,0,0,0.97880258,4.570042,5.8799159)" />
</defs>
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="0.88388348"
inkscape:cx="153.30075"
inkscape:cy="243.24473"
inkscape:window-width="1600"
inkscape:window-height="849"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg7"
showguides="false" />
<path
id="path9"
style="fill:url(#linearGradient11);fill-opacity:1;stroke:none;stroke-width:13.3314;stroke-opacity:1;paint-order:stroke fill markers"
d="M 262.97461 33.6875 A 218.44267 225.23091 0 0 0 138.83789 73.589844 L 141.13867 72.265625 L 161.2207 65.195312 L 181.01953 61.517578 L 204.35352 64.205078 L 228.96094 76.650391 L 265.80078 115 L 283 99.400391 L 304.59961 85.800781 L 331 76.400391 L 360.59961 69.599609 L 379.95508 69.208984 A 218.44267 225.23091 0 0 0 262.97461 33.6875 z M 380.07617 69.291016 L 350.19922 77.800781 L 329.19922 89.199219 L 307.40039 108 L 288.80078 129.80078 L 287.40039 135 L 302.40039 129.59961 L 319 127.80078 L 348.80078 131.80078 L 370.19922 141.40039 L 393 161.40039 L 399.59961 171.59961 L 374.80078 160.80078 L 338.40039 150.80078 L 309.19922 150 L 288.80078 154.40039 L 293.19922 155.19922 L 319.19922 168.80078 L 338 187.19922 L 350.80078 224 L 349.19922 260 L 326 222.80078 L 302.80078 194 L 277.59961 172.40039 L 269.19922 187.59961 L 256.80078 281.59961 L 258 364 L 278.40039 452.80078 L 297.19531 481.36914 A 218.44267 225.23091 0 0 0 481.41797 258.91797 A 218.44267 225.23091 0 0 0 380.07617 69.291016 z M 133.07422 77.839844 A 218.44267 225.23091 0 0 0 44.533203 258.91797 A 218.44267 225.23091 0 0 0 257.04102 484.06641 L 247.3457 458.62891 L 237.87109 418.18359 L 233.0625 380.42383 L 230.375 354.9668 L 229.95117 321.30859 L 232.35547 291.32812 L 237.44727 254.98242 L 254.55859 191.9082 L 261.62891 172.5332 L 255.54883 174.08984 L 240.98242 180.87695 L 229.59961 190.19922 L 210.59961 208.19922 L 197.40039 229.40039 L 186.40039 252.59961 L 173.40039 269 L 171.40039 253.19922 L 173.59961 229.40039 L 183 202.40039 L 199.40039 178 L 221.04102 153.44141 L 209.02148 155.70508 L 177.05859 162.77539 L 148.06836 171.40234 L 119.92578 183 L 120.63281 180.16992 L 129.82422 165.88672 L 151.17969 147.50195 L 172.95898 135.48242 L 190.07031 129.54102 L 209.02148 127.98633 L 227.6875 128.69336 L 247.76953 128.41016 L 246.49805 125.1582 L 226.69922 105.92383 L 219.62695 97.439453 L 221.4668 90.085938 L 206.75781 94.044922 L 195.86914 95.458984 L 179.46289 88.671875 L 156.12891 81.458984 L 133.07422 77.839844 z " />
<path
style="fill:#1b1b1b;fill-opacity:0.12492698;stroke:none;stroke-width:13.374;stroke-opacity:0.415999;paint-order:stroke fill markers"
d="m 259.36665,490.16617 39.03323,-6.96642 -20,-30.4 -20.4,-88.8 -1.2,-82.4 12.4,-94 8.4,-15.2 25.2,21.6 23.2,28.8 23.2,37.2 1.6,-36 -12.8,-36.8 -18.8,-18.4 -26,-13.6 -4.4,-0.8 20.4,-4.4 29.2,0.8 36.4,10 24.8,10.8 -6.6,-10.2 -22.8,-20 -21.4,-9.6 -29.8,-4 -16.6,1.8 -15,5.4 1.4,-5.2 18.6,-21.8 21.8,-18.800003 21,-11.4 30.2,-8.6 -19.8,0.4 -29.6,6.8 -26.4,9.4 -21.6,13.6 -17.2,15.600003 -36.83882,-38.349628 -24.60732,-12.445079 -23.33452,-2.687006 -19.79899,3.676955 -20.08184,7.071068 -9.33381,5.374012 24.32448,3.818376 23.33452,7.212489 16.40488,6.788226 10.88944,-1.414214 14.70782,-3.959798 -1.83847,7.353911 7.07106,8.485288 19.79899,19.2333 1.2728,3.25269 -20.08184,0.28284 -18.66762,-0.7071 -18.95046,1.55563 -17.11198,5.9397 -21.77889,12.02081 -21.35462,18.38478 -9.19239,14.28356 -0.70711,2.82843 28.14285,-11.59656 28.99138,-8.6267 31.96122,-7.07107 12.02082,-2.26274 -21.64158,24.55783 -16.4,24.4 -9.4,27 -2.2,23.8 2,15.8 13,-16.4 11,-23.2 13.2,-21.2 19,-18 11.38199,-9.32209 14.5664,-6.78822 6.08112,-1.55564 -7.07107,19.37473 -17.11198,63.07393 -5.09117,36.34528 -2.40416,29.98133 0.42426,33.65828 2.68701,25.45585 4.80832,37.7595 9.47523,40.44651 z"
id="path8-4" />
</svg>

Before

Width:  |  Height:  |  Size: 5.5 KiB

BIN
dist/yuzu.bmp vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 256 KiB

View File

@@ -7,6 +7,8 @@ Global Options:
- `CPMUTIL_FORCE_SYSTEM` (default `OFF`): Require all CPM dependencies to use system packages. NOT RECOMMENDED!
* You may optionally override this (section)
- `CPMUTIL_FORCE_BUNDLED` (default `ON` on MSVC and Android, `OFF` elsewhere): Require all CPM dependencies to use bundled packages.
- `CPMUTIL_DEFAULT_HOST`: (default `github.com`): Sets the default `git_host`.
- `CPMUTIL_FORCE_HOST`: (default `OFF`): Forces all CPM packages to use `CPMUTIL_DEFAULT_HOST` instead, even if they have `git_host` defined.
You are highly encouraged to read AddPackage first, even if you plan to only interact with CPMUtil via `AddJsonPackage`.

View File

@@ -160,6 +160,13 @@ if (YUZU_USE_BUNDLED_SIRIT)
AddJsonPackage(sirit-ci)
else()
AddJsonPackage(sirit)
# Change to old-but-more-cacheable debug info on Windows
if (WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
get_target_property(sirit_opts sirit COMPILE_OPTIONS)
list(FILTER sirit_opts EXCLUDE REGEX "/Zi")
list(APPEND sirit_opts "/Z7")
set_target_properties(sirit PROPERTIES COMPILE_OPTIONS "${sirit_opts}")
endif()
if(MSVC AND CXX_CLANG)
target_compile_options(siritobj PRIVATE -Wno-error=unused-command-line-argument)
endif()

View File

@@ -25,26 +25,18 @@ if (UNIX AND NOT ANDROID)
if (NOT APPLE)
# In Solaris needs explicit linking for ffmpeg which links to /lib/amd64/libX11.so
if(PLATFORM_SUN)
find_library(LIBDRM_LIB libdrm PATHS /usr/lib/64 /usr/lib/amd64 /usr/lib)
if(LIBDRM_LIB)
list(APPEND FFmpeg_HWACCEL_LIBRARIES
X11
"${LIBDRM_LIB}")
message(STATUS "Found libdrm at: ${LIBDRM_LIB}")
else()
message(WARNING "libdrm not found, disabling libdrm support")
list(APPEND FFmpeg_HWACCEL_FLAGS
--disable-libdrm)
endif()
list(APPEND FFmpeg_HWACCEL_LIBRARIES
X11
"/usr/lib/xorg/amd64/libdrm.so")
else()
pkg_check_modules(LIBDRM libdrm REQUIRED)
list(APPEND FFmpeg_HWACCEL_LIBRARIES
${LIBDRM_LIBRARIES})
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
${LIBDRM_INCLUDE_DIRS})
list(APPEND FFmpeg_HWACCEL_FLAGS
--enable-libdrm)
endif()
list(APPEND FFmpeg_HWACCEL_FLAGS
--enable-libdrm)
endif()
if(LIBVA_FOUND)
@@ -97,7 +89,7 @@ if (UNIX AND NOT ANDROID)
endif(CUDA_FOUND)
endif()
if (VDPAU_FOUND AND NOT APPLE)
if (VDPAU_FOUND)
list(APPEND FFmpeg_HWACCEL_FLAGS
--enable-vdpau
--enable-hwaccel=h264_vdpau
@@ -255,19 +247,11 @@ else()
SYSTEM_THREADS)
set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
# BSD make or Solaris make don't support ffmpeg make-j8
if (PLATFORM_LINUX OR ANDROID OR APPLE OR WIN32 OR PLATFORM_FREEBSD)
set(FFmpeg_MAKE_ARGS -j${SYSTEM_THREADS})
else()
set(FFmpeg_MAKE_ARGS "")
endif()
add_custom_command(
OUTPUT
${FFmpeg_BUILD_LIBRARIES}
COMMAND
make ${FFmpeg_MAKE_ARGS}
make -j${SYSTEM_THREADS}
WORKING_DIRECTORY
${FFmpeg_BUILD_DIR}
)

View File

@@ -15,7 +15,8 @@
"disabled_platforms": [
"freebsd-amd64",
"solaris-amd64",
"openbsd-amd64"
"openbsd-amd64",
"macos-universal"
]
}
}

View File

@@ -5,7 +5,7 @@
"git_host": "git.crueter.xyz",
"artifact": "%VERSION%.tar.gz",
"tag": "%VERSION%",
"hash": "dc37a189a44ce8b5c988ca550582431a6c7eadfd3c6e709bee6277116ee803e714333e85c9e6cbb5c69346a14d6f2cc7ed96e8aa09cc5fb8a89f945059651db6",
"version": "121125"
"hash": "87abb2aeca716d5d77b05317086dbc2f8acfc2f3f76ce4778345ee3df19973e6cd8ecbf16cfab5ad94c9636a6c44fd3588f9aadd3cba89403cfd56c8bec645c5",
"version": "091025"
}
}

View File

@@ -35,6 +35,7 @@ if (MSVC AND NOT CXX_CLANG)
# /W4 - Level 4 warnings
# /MP - Multi-threaded compilation
# /Zi - Output debugging information
# /Zm - Specifies the precompiled header memory allocation limit
# /Zo - Enhanced debug info for optimized builds
# /permissive- - Enables stricter C++ standards conformance checks
@@ -97,6 +98,11 @@ if (MSVC AND NOT CXX_CLANG)
)
endif()
if (WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
endif()
if (ARCHITECTURE_x86_64)
add_compile_options(/QIntel-jcc-erratum)
endif()
@@ -121,7 +127,7 @@ else()
-Werror=unused
-Wno-attributes
$<$<COMPILE_LANGUAGE:CXX>:-Wno-invalid-offsetof>
-Wno-invalid-offsetof
-Wno-unused-parameter
-Wno-missing-field-initializers
)
@@ -170,28 +176,16 @@ else()
add_compile_definitions(_FILE_OFFSET_BITS=64)
endif()
if (YUZU_STATIC_BUILD)
add_compile_definitions(QT_STATICPLUGIN)
# macos doesn't even let you make static executables... wtf?
if (NOT APPLE)
add_compile_options(-static)
if (YUZU_STATIC_BUILD)
# yuzu-cmd requires us to explicitly link libpthread, libgcc, and libstdc++ as static
# At a guess, it's probably because Qt handles the Qt executable for us, whereas this does not
add_link_options(-static -lpthread)
add_link_options(-static-libgcc -static-libstdc++)
endif()
endif()
endif()
if (MINGW)
add_compile_definitions(MINGW_HAS_SECURE_API)
# Only windows has this requirement, thanks windows
if (WIN32 AND ARCHITECTURE_x86_64)
if (WIN32)
add_compile_options("-msse4.1")
endif()
if (MINGW_STATIC_BUILD)
add_compile_definitions(QT_STATICPLUGIN)
add_compile_options("-static")
endif()
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW)

View File

@@ -16,15 +16,11 @@ import java.io.FileOutputStream
import java.security.KeyStore
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager
import android.content.res.Configuration
import android.os.LocaleList
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.DocumentsTree
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.PowerStateUpdater
import java.util.Locale
fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
@@ -77,38 +73,5 @@ class YuzuApplication : Application() {
val appContext: Context
get() = application.applicationContext
private val LANGUAGE_CODES = arrayOf(
"system", "en", "es", "fr", "de", "it", "pt", "pt-BR", "ru", "ja", "ko",
"zh-CN", "zh-TW", "pl", "cs", "nb", "hu", "uk", "vi", "id", "ar", "ckb", "fa", "he", "sr"
)
fun applyLanguage(context: Context): Context {
val languageIndex = IntSetting.APP_LANGUAGE.getInt()
val langCode = if (languageIndex in LANGUAGE_CODES.indices) {
LANGUAGE_CODES[languageIndex]
} else {
"system"
}
if (langCode == "system") {
return context
}
val locale = when {
langCode.contains("-") -> {
val parts = langCode.split("-")
Locale.Builder().setLanguage(parts[0]).setRegion(parts[1]).build()
}
else -> Locale.Builder().setLanguage(langCode).build()
}
Locale.setDefault(locale)
val config = Configuration(context.resources.configuration)
config.setLocales(LocaleList(locale))
return context.createConfigurationContext(config)
}
}
}

View File

@@ -86,10 +86,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
private var foregroundService: Intent? = null
override fun attachBaseContext(base: Context) {
super.attachBaseContext(YuzuApplication.applyLanguage(base))
}
override fun onCreate(savedInstanceState: Bundle?) {
Log.gameLaunched = true
ThemeHelper.setTheme(this)

View File

@@ -32,7 +32,6 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
MAX_ANISOTROPY("max_anisotropy"),
THEME("theme"),
THEME_MODE("theme_mode"),
APP_LANGUAGE("app_language"),
OVERLAY_SCALE("control_scale"),
OVERLAY_OPACITY("control_opacity"),
LOCK_DRAWER("lock_drawer"),

View File

@@ -667,11 +667,10 @@ abstract class SettingsItem(
)
)
put(
SpinBoxSetting(
SliderSetting(
IntSetting.CPU_TICKS,
titleId = R.string.cpu_ticks,
descriptionId = 0,
valueHint = R.string.cpu_ticks,
min = 77,
max = 65535
)
@@ -757,15 +756,6 @@ abstract class SettingsItem(
titleId = R.string.enable_update_checks,
)
)
put(
SingleChoiceSetting(
IntSetting.APP_LANGUAGE,
titleId = R.string.app_language,
descriptionId = R.string.app_language_description,
choicesId = R.array.appLanguageNames,
valuesId = R.array.appLanguageValues
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_DEBUG,

View File

@@ -1,13 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui
import android.content.Context
import org.yuzu.yuzu_emu.YuzuApplication
import android.os.Bundle
import android.view.View
import android.view.ViewGroup.MarginLayoutParams
@@ -29,7 +24,6 @@ import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment
import org.yuzu.yuzu_emu.utils.*
import org.yuzu.yuzu_emu.utils.collect
class SettingsActivity : AppCompatActivity() {
private lateinit var binding: ActivitySettingsBinding
@@ -38,10 +32,6 @@ class SettingsActivity : AppCompatActivity() {
private val settingsViewModel: SettingsViewModel by viewModels()
override fun attachBaseContext(base: Context) {
super.attachBaseContext(YuzuApplication.applyLanguage(base))
}
override fun onCreate(savedInstanceState: Bundle?) {
ThemeHelper.setTheme(this)
@@ -135,16 +125,6 @@ class SettingsActivity : AppCompatActivity() {
NativeConfig.savePerGameConfig()
NativeConfig.unloadPerGameConfig()
}
if (settingsViewModel.shouldRecreateForLanguageChange.value) {
settingsViewModel.setShouldRecreateForLanguageChange(false)
val relaunchIntent = packageManager?.getLaunchIntentForPackage(packageName)
if (relaunchIntent != null) {
relaunchIntent.addFlags(android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK or android.content.Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(relaunchIntent)
android.os.Process.killProcess(android.os.Process.myPid())
}
}
}
}

View File

@@ -425,14 +425,6 @@ class SettingsAdapter(
position
).show(fragment.childFragmentManager, SettingsDialogFragment.TAG)
// reset language if detected
if (item.setting.key == "app_language") {
// recreate page apply language change instantly
fragment.requireActivity().recreate()
settingsViewModel.setShouldRecreateForLanguageChange(true)
}
return true
}

View File

@@ -14,9 +14,6 @@ import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.MotionEvent
import android.os.Handler
import android.os.Looper
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import androidx.fragment.app.DialogFragment
@@ -200,54 +197,6 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
updateValidity(newValue)
}
fun attachRepeat(button: View, delta: Int) {
val handler = Handler(Looper.getMainLooper())
var runnable: Runnable? = null
val initialDelay = 400L
val minDelay = 40L
val accelerationFactor = 0.75f
button.setOnTouchListener { v, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
val current = spinboxBinding.editValue.text.toString().toIntOrNull() ?: currentValue
val newValue = (current + delta)
spinboxBinding.editValue.setText(newValue.toString())
updateValidity(newValue)
var delay = initialDelay
runnable = object : Runnable {
override fun run() {
val curr = spinboxBinding.editValue.text.toString().toIntOrNull() ?: currentValue
val next = curr + delta
spinboxBinding.editValue.setText(next.toString())
updateValidity(next)
// accelerate
delay = (delay * accelerationFactor).toLong().coerceAtLeast(minDelay)
handler.postDelayed(this, delay)
}
}
handler.postDelayed(runnable!!, initialDelay)
true
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
if (runnable != null) {
handler.removeCallbacks(runnable!!)
runnable = null
}
v.performClick()
true
}
else -> false
}
}
}
attachRepeat(spinboxBinding.buttonDecrement, -1)
attachRepeat(spinboxBinding.buttonIncrement, 1)
spinboxBinding.editValue.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
@@ -387,12 +336,6 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
.show()
}
scSetting.setSelectedValue(value)
if (scSetting.setting.key == "app_language") {
settingsViewModel.setShouldRecreateForLanguageChange(true)
// recreate page apply language change instantly
requireActivity().recreate()
}
}
is StringSingleChoiceSetting -> {

View File

@@ -1030,10 +1030,8 @@ class SettingsFragmentPresenter(
override fun reset() = IntSetting.THEME.setInt(defaultValue)
}
add(HeaderSetting(R.string.app_settings))
add(IntSetting.APP_LANGUAGE.key)
if (NativeLibrary.isUpdateCheckerEnabled()) {
add(HeaderSetting(R.string.app_settings))
add(BooleanSetting.ENABLE_UPDATE_CHECKS.key)
}

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -57,8 +54,6 @@ class SettingsViewModel : ViewModel() {
private val _shouldShowResetInputDialog = MutableStateFlow(false)
val shouldShowResetInputDialog = _shouldShowResetInputDialog.asStateFlow()
private val _shouldRecreateForLanguageChange = MutableStateFlow(false)
val shouldRecreateForLanguageChange = _shouldRecreateForLanguageChange.asStateFlow()
fun setShouldRecreate(value: Boolean) {
_shouldRecreate.value = value
}
@@ -108,10 +103,6 @@ class SettingsViewModel : ViewModel() {
_shouldShowResetInputDialog.value = value
}
fun setShouldRecreateForLanguageChange(value: Boolean) {
_shouldRecreateForLanguageChange.value = value
}
fun getCurrentDeviceParams(defaultParams: ParamPackage): ParamPackage =
try {
InputHandler.registeredControllers[currentDevice]

View File

@@ -4,7 +4,6 @@
package org.yuzu.yuzu_emu.ui.main
import android.content.Intent
import android.content.Context
import android.net.Uri
import android.os.Bundle
import android.view.View
@@ -54,7 +53,6 @@ import org.yuzu.yuzu_emu.activities.EmulationActivity
import kotlin.text.compareTo
import androidx.core.net.toUri
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.YuzuApplication
class MainActivity : AppCompatActivity(), ThemeProvider {
private lateinit var binding: ActivityMainBinding
@@ -70,10 +68,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
private val CHECKED_DECRYPTION = "CheckedDecryption"
private var checkedDecryption = false
override fun attachBaseContext(base: Context) {
super.attachBaseContext(YuzuApplication.applyLanguage(base))
}
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }

View File

@@ -62,7 +62,6 @@ namespace AndroidSettings {
Settings::Setting<s32> theme_mode{linkage, -1, "theme_mode", Settings::Category::Android};
Settings::Setting<bool> black_backgrounds{linkage, false, "black_backgrounds",
Settings::Category::Android};
Settings::Setting<s32> app_language{linkage, 0, "app_language", Settings::Category::Android};
Settings::Setting<bool> enable_update_checks{linkage, true, "enable_update_checks",
Settings::Category::Android};

View File

@@ -4,8 +4,6 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#define VMA_IMPLEMENTATION
#include "video_core/vulkan_common/vma.h"
#include <codecvt>
#include <locale>

View File

@@ -391,61 +391,6 @@
<item>2</item>
</integer-array>
<string-array name="appLanguageNames">
<item>@string/app_language_system</item>
<item>@string/app_language_english</item>
<item>@string/app_language_spanish</item>
<item>@string/app_language_french</item>
<item>@string/app_language_german</item>
<item>@string/app_language_italian</item>
<item>@string/app_language_portuguese</item>
<item>@string/app_language_brazilian_portuguese</item>
<item>@string/app_language_russian</item>
<item>@string/app_language_japanese</item>
<item>@string/app_language_korean</item>
<item>@string/app_language_simplified_chinese</item>
<item>@string/app_language_traditional_chinese</item>
<item>@string/app_language_polish</item>
<item>@string/app_language_czech</item>
<item>@string/app_language_norwegian</item>
<item>@string/app_language_hungarian</item>
<item>@string/app_language_ukrainian</item>
<item>@string/app_language_vietnamese</item>
<item>@string/app_language_indonesian</item>
<item>@string/app_language_arabic</item>
<item>@string/app_language_central_kurdish</item>
<item>@string/app_language_persian</item>
<item>@string/app_language_hebrew</item>
<item>@string/app_language_serbian</item>
</string-array>
<integer-array name="appLanguageValues">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
<item>7</item>
<item>8</item>
<item>9</item>
<item>10</item>
<item>11</item>
<item>12</item>
<item>13</item>
<item>14</item>
<item>15</item>
<item>16</item>
<item>17</item>
<item>18</item>
<item>19</item>
<item>20</item>
<item>21</item>
<item>22</item>
<item>23</item>
<item>24</item>
</integer-array>
<string-array name="outputEngineEntries">
<item>@string/auto</item>
<item>@string/oboe</item>

View File

@@ -1028,35 +1028,6 @@
<string name="use_black_backgrounds">Black backgrounds</string>
<string name="use_black_backgrounds_description">When using the dark theme, apply black backgrounds.</string>
<!-- App Language -->
<string name="app_language">App Language</string>
<string name="app_language_description">Change the language of the app interface</string>
<string name="app_language_system">Follow System</string>
<string name="app_language_english">English</string>
<string name="app_language_spanish">Español</string>
<string name="app_language_french">Français</string>
<string name="app_language_german">Deutsch</string>
<string name="app_language_italian">Italiano</string>
<string name="app_language_portuguese">Português</string>
<string name="app_language_brazilian_portuguese">Português do Brasil</string>
<string name="app_language_russian">Русский</string>
<string name="app_language_japanese">日本語</string>
<string name="app_language_korean">한국어</string>
<string name="app_language_simplified_chinese">简体中文</string>
<string name="app_language_traditional_chinese">繁體中文</string>
<string name="app_language_polish">Polski</string>
<string name="app_language_czech">Čeština</string>
<string name="app_language_norwegian">Norsk bokmål</string>
<string name="app_language_hungarian">Magyar</string>
<string name="app_language_ukrainian">Українська</string>
<string name="app_language_vietnamese">Tiếng Việt</string>
<string name="app_language_indonesian">Bahasa Indonesia</string>
<string name="app_language_arabic">العربية</string>
<string name="app_language_central_kurdish">کوردیی ناوەندی</string>
<string name="app_language_persian">فارسی</string>
<string name="app_language_hebrew">עברית</string>
<string name="app_language_serbian">Српски</string>
<!-- Static Themes -->
<string name="static_theme_color">Theme Color</string>
<string name="eden_theme">Eden (Default)</string>

View File

@@ -252,7 +252,7 @@ if(CXX_CLANG)
endif()
if (BOOST_NO_HEADERS)
target_link_libraries(common PUBLIC Boost::algorithm Boost::icl Boost::pool)
target_link_libraries(common PUBLIC Boost::algorithm Boost::icl Boost::pool Boost::filesystem)
else()
target_link_libraries(common PUBLIC Boost::headers)
endif()

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -106,60 +103,46 @@ template <>
#else
// Some architectures lack u128, there is no definitive way to check them all without even more macro magic
// so let's just... do this; add your favourite arches once they get u128 support :)
#if (defined(__clang__) || defined(__GNUC__)) && (defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64))
using RealU128 = unsigned __int128;
# define SYNC_VAL_COMPARE_AND_SWAP(p, e, v) __sync_val_compare_and_swap(p, e, v)
# define SYNC_BOOL_COMPARE_AND_SWAP(p, e, v) __sync_bool_compare_and_swap(p, e, v)
# define U128_ZERO_INIT 0
#else
using RealU128 = u128;
# define SYNC_VAL_COMPARE_AND_SWAP(p, e, v) ((*p == e) ? *p = v : *p)
# define SYNC_BOOL_COMPARE_AND_SWAP(p, e, v) ((*p == e) ? (void)(*p = v) : (void)0), true
# define U128_ZERO_INIT {}
#endif
template <typename T>
[[nodiscard]] inline bool AtomicCompareAndSwap(T* pointer, T value, T expected) {
return SYNC_BOOL_COMPARE_AND_SWAP(pointer, expected, value);
return __sync_bool_compare_and_swap(pointer, expected, value);
}
[[nodiscard]] inline bool AtomicCompareAndSwap(u64* pointer, u128 value, u128 expected) {
RealU128 value_a;
RealU128 expected_a;
unsigned __int128 value_a;
unsigned __int128 expected_a;
std::memcpy(&value_a, value.data(), sizeof(u128));
std::memcpy(&expected_a, expected.data(), sizeof(u128));
return SYNC_BOOL_COMPARE_AND_SWAP((RealU128*)pointer, expected_a, value_a);
return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
}
template <typename T>
[[nodiscard]] inline bool AtomicCompareAndSwap(T* pointer, T value, T expected, T& actual) {
actual = SYNC_VAL_COMPARE_AND_SWAP(pointer, expected, value);
actual = __sync_val_compare_and_swap(pointer, expected, value);
return actual == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(u64* pointer, u128 value, u128 expected, u128& actual) {
RealU128 value_a;
RealU128 expected_a;
RealU128 actual_a;
[[nodiscard]] inline bool AtomicCompareAndSwap(u64* pointer, u128 value, u128 expected,
u128& actual) {
unsigned __int128 value_a;
unsigned __int128 expected_a;
unsigned __int128 actual_a;
std::memcpy(&value_a, value.data(), sizeof(u128));
std::memcpy(&expected_a, expected.data(), sizeof(u128));
actual_a = SYNC_VAL_COMPARE_AND_SWAP((RealU128*)pointer, expected_a, value_a);
actual_a = __sync_val_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
std::memcpy(actual.data(), &actual_a, sizeof(u128));
return actual_a == expected_a;
}
[[nodiscard]] inline u128 AtomicLoad128(u64* pointer) {
RealU128 zeros_a = U128_ZERO_INIT;
RealU128 result_a = SYNC_VAL_COMPARE_AND_SWAP((RealU128*)pointer, zeros_a, zeros_a);
unsigned __int128 zeros_a = 0;
unsigned __int128 result_a =
__sync_val_compare_and_swap((unsigned __int128*)pointer, zeros_a, zeros_a);
u128 result;
std::memcpy(result.data(), &result_a, sizeof(u128));
return result;
}
#undef U128_ZERO_INIT
#undef SYNC_VAL_COMPARE_AND_SWAP
#undef SYNC_BOOL_COMPARE_AND_SWAP
#endif

View File

@@ -730,9 +730,7 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(host_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0);
if (impl && virtual_base) {
ASSERT(virtual_offset + length <= virtual_size);
}
ASSERT(virtual_offset + length <= virtual_size);
ASSERT(host_offset + length <= backing_size);
if (length == 0 || !virtual_base || !impl) {
return;
@@ -743,9 +741,7 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap) {
ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0);
if (impl && virtual_base) {
ASSERT(virtual_offset + length <= virtual_size);
}
ASSERT(virtual_offset + length <= virtual_size);
if (length == 0 || !virtual_base || !impl) {
return;
}
@@ -755,9 +751,7 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap)
void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission perm) {
ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0);
if (impl && virtual_base) {
ASSERT(virtual_offset + length <= virtual_size);
}
ASSERT(virtual_offset + length <= virtual_size);
if (length == 0 || !virtual_base || !impl) {
return;
}

View File

@@ -1,14 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <type_traits>
#include <fmt/ranges.h>
#include "common/swap.h"
// adapted from https://github.com/fmtlib/fmt/issues/2704
// a generic formatter for enum classes
@@ -23,14 +20,3 @@ struct fmt::formatter<T, std::enable_if_t<std::is_enum_v<T>, char>>
}
};
#endif
template <typename T, typename U>
struct fmt::formatter<SwapStructT<T, U>> {
constexpr auto parse(format_parse_context& ctx) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const SwapStructT<T, U>& reg, FormatContext& ctx) const {
return fmt::format_to(ctx.out(), "{}", T(reg));
}
};

View File

@@ -359,9 +359,6 @@ void RestoreGlobalState(bool is_powered_on) {
for (const auto& reset : values.linkage.restore_functions) {
reset();
}
// Reset per-game flags
values.use_squashed_iterated_blend = false;
}
static bool configuring_global = true;

View File

@@ -546,15 +546,6 @@ struct Values {
Category::RendererExtensions,
Specialization::Scalar};
SwitchableSetting<bool> vertex_input_dynamic_state{linkage,
#if defined (ANDROID)
false, // Disabled by default on Android (some drivers have issues)
#else
false, // Disabled by default on desktop (some drivers have issues)
#endif
"vertex_input_dynamic_state",
Category::RendererExtensions};
SwitchableSetting<bool> provoking_vertex{linkage, false, "provoking_vertex", Category::RendererExtensions};
SwitchableSetting<bool> descriptor_indexing{linkage, false, "descriptor_indexing", Category::RendererExtensions};
SwitchableSetting<bool> sample_shading{linkage, false, "sample_shading", Category::RendererExtensions, Specialization::Paired};
@@ -569,60 +560,6 @@ struct Values {
false,
&sample_shading};
#ifdef ANDROID
// Shader Float Controls (Android only) - Eden Veil / Extensions
// Force enable VK_KHR_shader_float_controls even if driver has known issues
// Allows fine-tuning float behavior to match Switch/Maxwell or optimize performance
SwitchableSetting<bool> shader_float_controls_force_enable{linkage,
false,
"shader_float_controls_force_enable",
Category::RendererExtensions,
Specialization::Paired};
// Individual float behavior controls (visible only when force_enable is true)
// Multiple can be active simultaneously EXCEPT FTZ and DenormPreserve (mutually exclusive)
//
// Recommended configurations:
// Switch-native: FTZ=ON, RTE=ON, SignedZero=ON (matches Maxwell behavior)
// Performance: FTZ=ON only (fastest)
// Accuracy: DenormPreserve=ON, RTE=ON, SignedZero=ON (slowest, highest precision)
SwitchableSetting<bool> shader_float_ftz{linkage,
false,
"shader_float_ftz",
Category::RendererExtensions,
Specialization::Default,
true,
false,
&shader_float_controls_force_enable};
SwitchableSetting<bool> shader_float_denorm_preserve{linkage,
false,
"shader_float_denorm_preserve",
Category::RendererExtensions,
Specialization::Default,
true,
false,
&shader_float_controls_force_enable};
SwitchableSetting<bool> shader_float_rte{linkage,
false,
"shader_float_rte",
Category::RendererExtensions,
Specialization::Default,
true,
false,
&shader_float_controls_force_enable};
SwitchableSetting<bool> shader_float_signed_zero_inf_nan{linkage,
false,
"shader_float_signed_zero_inf_nan",
Category::RendererExtensions,
Specialization::Default,
true,
false,
&shader_float_controls_force_enable};
#endif
Setting<bool> renderer_debug{linkage, false, "debug", Category::RendererDebug};
Setting<bool> renderer_shader_feedback{linkage, false, "shader_feedback",
Category::RendererDebug};
@@ -822,9 +759,6 @@ struct Values {
// Add-Ons
std::map<u64, std::vector<std::string>> disabled_addons;
// Per-game overrides
bool use_squashed_iterated_blend;
};
extern Values values;

View File

@@ -152,16 +152,6 @@ ENUM(SpirvOptimizeMode, Never, OnLoad, Always);
ENUM(GpuOverclock, Low, Medium, High)
ENUM(TemperatureUnits, Celsius, Fahrenheit)
// Shader Float Controls behavior modes
// These control how floating-point denormals and special values are handled in shaders
ENUM(ShaderFloatBehavior,
DriverDefault, // Let driver choose (safest, may not match Switch behavior)
SwitchNative, // Emulate Switch/Maxwell behavior (FTZ + RTE + SignedZero)
FlushToZero, // FTZ only - flush denorms to zero (fastest, some precision loss)
PreserveDenorms, // Preserve denorms (slowest, highest precision)
RoundToEven, // RTE rounding mode (IEEE 754 compliant)
SignedZeroInfNan); // Preserve signed zero, inf, nan (accuracy for edge cases)
template <typename Type>
inline std::string_view CanonicalizeEnum(Type id) {
const auto group = EnumMetadata<Type>::Canonicalizations();

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2012 PPSSPP Project
// SPDX-FileCopyrightText: 2012 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -20,17 +17,7 @@
namespace Common {
#if defined(__Bitrig__) || defined(__OpenBSD__)
// We'll redefine swap16, swap32, swap64 as inline functions
// but OpenBSD is like "wow I bring my own stuff"
// It would be nice if we could use them without C++ namespace shenanigans
// But alas :)
#undef swap16
#undef swap32
#undef swap64
#endif
#if defined(_MSC_VER)
#ifdef _MSC_VER
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
return _byteswap_ushort(data);
}
@@ -41,6 +28,12 @@ namespace Common {
return _byteswap_uint64(data);
}
#elif defined(__clang__) || defined(__GNUC__)
#if defined(__Bitrig__) || defined(__OpenBSD__)
// redefine swap16, swap32, swap64 as inline functions
#undef swap16
#undef swap32
#undef swap64
#endif
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
return __builtin_bswap16(data);
}
@@ -51,9 +44,7 @@ namespace Common {
return __builtin_bswap64(data);
}
#else
// Generic implementation - compiler will optimise these into their respective
// __builtin_byteswapXX() and such; if not, the compiler is stupid and we probably
// have bigger problems to worry about :)
// Generic implementation.
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
return (data >> 8) | (data << 8);
}
@@ -71,27 +62,33 @@ namespace Common {
[[nodiscard]] inline float swapf(float f) noexcept {
static_assert(sizeof(u32) == sizeof(float), "float must be the same size as uint32_t.");
u32 value;
std::memcpy(&value, &f, sizeof(u32));
value = swap32(value);
std::memcpy(&f, &value, sizeof(u32));
return f;
}
[[nodiscard]] inline double swapd(double f) noexcept {
static_assert(sizeof(u64) == sizeof(double), "double must be the same size as uint64_t.");
u64 value;
std::memcpy(&value, &f, sizeof(u64));
value = swap64(value);
std::memcpy(&f, &value, sizeof(u64));
return f;
}
} // Namespace Common
template <typename T, typename F>
struct SwapStructT {
using SwappedT = SwapStructT;
struct swap_struct_t {
using swapped_t = swap_struct_t;
protected:
T value;
@@ -104,137 +101,137 @@ public:
T swap() const {
return swap(value);
}
SwapStructT() = default;
SwapStructT(const T& v) : value(swap(v)) {}
swap_struct_t() = default;
swap_struct_t(const T& v) : value(swap(v)) {}
template <typename S>
SwappedT& operator=(const S& source) {
value = swap(T(source));
swapped_t& operator=(const S& source) {
value = swap(static_cast<T>(source));
return *this;
}
operator s8() const {
return s8(swap());
return static_cast<s8>(swap());
}
operator u8() const {
return u8(swap());
return static_cast<u8>(swap());
}
operator s16() const {
return s16(swap());
return static_cast<s16>(swap());
}
operator u16() const {
return u16(swap());
return static_cast<u16>(swap());
}
operator s32() const {
return s32(swap());
return static_cast<s32>(swap());
}
operator u32() const {
return u32(swap());
return static_cast<u32>(swap());
}
operator s64() const {
return s64(swap());
return static_cast<s64>(swap());
}
operator u64() const {
return u64(swap());
return static_cast<u64>(swap());
}
operator float() const {
return float(swap());
return static_cast<float>(swap());
}
operator double() const {
return double(swap());
return static_cast<double>(swap());
}
// +v
SwappedT operator+() const {
swapped_t operator+() const {
return +swap();
}
// -v
SwappedT operator-() const {
swapped_t operator-() const {
return -swap();
}
// v / 5
SwappedT operator/(const SwappedT& i) const {
swapped_t operator/(const swapped_t& i) const {
return swap() / i.swap();
}
template <typename S>
SwappedT operator/(const S& i) const {
swapped_t operator/(const S& i) const {
return swap() / i;
}
// v * 5
SwappedT operator*(const SwappedT& i) const {
swapped_t operator*(const swapped_t& i) const {
return swap() * i.swap();
}
template <typename S>
SwappedT operator*(const S& i) const {
swapped_t operator*(const S& i) const {
return swap() * i;
}
// v + 5
SwappedT operator+(const SwappedT& i) const {
swapped_t operator+(const swapped_t& i) const {
return swap() + i.swap();
}
template <typename S>
SwappedT operator+(const S& i) const {
return swap() + T(i);
swapped_t operator+(const S& i) const {
return swap() + static_cast<T>(i);
}
// v - 5
SwappedT operator-(const SwappedT& i) const {
swapped_t operator-(const swapped_t& i) const {
return swap() - i.swap();
}
template <typename S>
SwappedT operator-(const S& i) const {
return swap() - T(i);
swapped_t operator-(const S& i) const {
return swap() - static_cast<T>(i);
}
// v += 5
SwappedT& operator+=(const SwappedT& i) {
swapped_t& operator+=(const swapped_t& i) {
value = swap(swap() + i.swap());
return *this;
}
template <typename S>
SwappedT& operator+=(const S& i) {
value = swap(swap() + T(i));
swapped_t& operator+=(const S& i) {
value = swap(swap() + static_cast<T>(i));
return *this;
}
// v -= 5
SwappedT& operator-=(const SwappedT& i) {
swapped_t& operator-=(const swapped_t& i) {
value = swap(swap() - i.swap());
return *this;
}
template <typename S>
SwappedT& operator-=(const S& i) {
value = swap(swap() - T(i));
swapped_t& operator-=(const S& i) {
value = swap(swap() - static_cast<T>(i));
return *this;
}
// ++v
SwappedT& operator++() {
swapped_t& operator++() {
value = swap(swap() + 1);
return *this;
}
// --v
SwappedT& operator--() {
swapped_t& operator--() {
value = swap(swap() - 1);
return *this;
}
// v++
SwappedT operator++(int) {
SwappedT old = *this;
swapped_t operator++(int) {
swapped_t old = *this;
value = swap(swap() + 1);
return old;
}
// v--
SwappedT operator--(int) {
SwappedT old = *this;
swapped_t operator--(int) {
swapped_t old = *this;
value = swap(swap() - 1);
return old;
}
// Comparison
// v == i
bool operator==(const SwappedT& i) const {
bool operator==(const swapped_t& i) const {
return swap() == i.swap();
}
template <typename S>
@@ -243,7 +240,7 @@ public:
}
// v != i
bool operator!=(const SwappedT& i) const {
bool operator!=(const swapped_t& i) const {
return swap() != i.swap();
}
template <typename S>
@@ -252,7 +249,7 @@ public:
}
// v > i
bool operator>(const SwappedT& i) const {
bool operator>(const swapped_t& i) const {
return swap() > i.swap();
}
template <typename S>
@@ -261,7 +258,7 @@ public:
}
// v < i
bool operator<(const SwappedT& i) const {
bool operator<(const swapped_t& i) const {
return swap() < i.swap();
}
template <typename S>
@@ -270,7 +267,7 @@ public:
}
// v >= i
bool operator>=(const SwappedT& i) const {
bool operator>=(const swapped_t& i) const {
return swap() >= i.swap();
}
template <typename S>
@@ -279,7 +276,7 @@ public:
}
// v <= i
bool operator<=(const SwappedT& i) const {
bool operator<=(const swapped_t& i) const {
return swap() <= i.swap();
}
template <typename S>
@@ -288,82 +285,82 @@ public:
}
// logical
SwappedT operator!() const {
swapped_t operator!() const {
return !swap();
}
// bitmath
SwappedT operator~() const {
swapped_t operator~() const {
return ~swap();
}
SwappedT operator&(const SwappedT& b) const {
swapped_t operator&(const swapped_t& b) const {
return swap() & b.swap();
}
template <typename S>
SwappedT operator&(const S& b) const {
swapped_t operator&(const S& b) const {
return swap() & b;
}
SwappedT& operator&=(const SwappedT& b) {
swapped_t& operator&=(const swapped_t& b) {
value = swap(swap() & b.swap());
return *this;
}
template <typename S>
SwappedT& operator&=(const S b) {
swapped_t& operator&=(const S b) {
value = swap(swap() & b);
return *this;
}
SwappedT operator|(const SwappedT& b) const {
swapped_t operator|(const swapped_t& b) const {
return swap() | b.swap();
}
template <typename S>
SwappedT operator|(const S& b) const {
swapped_t operator|(const S& b) const {
return swap() | b;
}
SwappedT& operator|=(const SwappedT& b) {
swapped_t& operator|=(const swapped_t& b) {
value = swap(swap() | b.swap());
return *this;
}
template <typename S>
SwappedT& operator|=(const S& b) {
swapped_t& operator|=(const S& b) {
value = swap(swap() | b);
return *this;
}
SwappedT operator^(const SwappedT& b) const {
swapped_t operator^(const swapped_t& b) const {
return swap() ^ b.swap();
}
template <typename S>
SwappedT operator^(const S& b) const {
swapped_t operator^(const S& b) const {
return swap() ^ b;
}
SwappedT& operator^=(const SwappedT& b) {
swapped_t& operator^=(const swapped_t& b) {
value = swap(swap() ^ b.swap());
return *this;
}
template <typename S>
SwappedT& operator^=(const S& b) {
swapped_t& operator^=(const S& b) {
value = swap(swap() ^ b);
return *this;
}
template <typename S>
SwappedT operator<<(const S& b) const {
swapped_t operator<<(const S& b) const {
return swap() << b;
}
template <typename S>
SwappedT& operator<<=(const S& b) const {
swapped_t& operator<<=(const S& b) const {
value = swap(swap() << b);
return *this;
}
template <typename S>
SwappedT operator>>(const S& b) const {
swapped_t operator>>(const S& b) const {
return swap() >> b;
}
template <typename S>
SwappedT& operator>>=(const S& b) const {
swapped_t& operator>>=(const S& b) const {
value = swap(swap() >> b);
return *this;
}
@@ -373,167 +370,167 @@ public:
// Arithmetic
template <typename S, typename T2, typename F2>
friend S operator+(const S& p, const SwappedT v);
friend S operator+(const S& p, const swapped_t v);
template <typename S, typename T2, typename F2>
friend S operator-(const S& p, const SwappedT v);
friend S operator-(const S& p, const swapped_t v);
template <typename S, typename T2, typename F2>
friend S operator/(const S& p, const SwappedT v);
friend S operator/(const S& p, const swapped_t v);
template <typename S, typename T2, typename F2>
friend S operator*(const S& p, const SwappedT v);
friend S operator*(const S& p, const swapped_t v);
template <typename S, typename T2, typename F2>
friend S operator%(const S& p, const SwappedT v);
friend S operator%(const S& p, const swapped_t v);
// Arithmetic + assignments
template <typename S, typename T2, typename F2>
friend S operator+=(const S& p, const SwappedT v);
friend S operator+=(const S& p, const swapped_t v);
template <typename S, typename T2, typename F2>
friend S operator-=(const S& p, const SwappedT v);
friend S operator-=(const S& p, const swapped_t v);
// Bitmath
template <typename S, typename T2, typename F2>
friend S operator&(const S& p, const SwappedT v);
friend S operator&(const S& p, const swapped_t v);
// Comparison
template <typename S, typename T2, typename F2>
friend bool operator<(const S& p, const SwappedT v);
friend bool operator<(const S& p, const swapped_t v);
template <typename S, typename T2, typename F2>
friend bool operator>(const S& p, const SwappedT v);
friend bool operator>(const S& p, const swapped_t v);
template <typename S, typename T2, typename F2>
friend bool operator<=(const S& p, const SwappedT v);
friend bool operator<=(const S& p, const swapped_t v);
template <typename S, typename T2, typename F2>
friend bool operator>=(const S& p, const SwappedT v);
friend bool operator>=(const S& p, const swapped_t v);
template <typename S, typename T2, typename F2>
friend bool operator!=(const S& p, const SwappedT v);
friend bool operator!=(const S& p, const swapped_t v);
template <typename S, typename T2, typename F2>
friend bool operator==(const S& p, const SwappedT v);
friend bool operator==(const S& p, const swapped_t v);
};
// Arithmetic
template <typename S, typename T, typename F>
S operator+(const S& i, const SwapStructT<T, F> v) {
S operator+(const S& i, const swap_struct_t<T, F> v) {
return i + v.swap();
}
template <typename S, typename T, typename F>
S operator-(const S& i, const SwapStructT<T, F> v) {
S operator-(const S& i, const swap_struct_t<T, F> v) {
return i - v.swap();
}
template <typename S, typename T, typename F>
S operator/(const S& i, const SwapStructT<T, F> v) {
S operator/(const S& i, const swap_struct_t<T, F> v) {
return i / v.swap();
}
template <typename S, typename T, typename F>
S operator*(const S& i, const SwapStructT<T, F> v) {
S operator*(const S& i, const swap_struct_t<T, F> v) {
return i * v.swap();
}
template <typename S, typename T, typename F>
S operator%(const S& i, const SwapStructT<T, F> v) {
S operator%(const S& i, const swap_struct_t<T, F> v) {
return i % v.swap();
}
// Arithmetic + assignments
template <typename S, typename T, typename F>
S& operator+=(S& i, const SwapStructT<T, F> v) {
S& operator+=(S& i, const swap_struct_t<T, F> v) {
i += v.swap();
return i;
}
template <typename S, typename T, typename F>
S& operator-=(S& i, const SwapStructT<T, F> v) {
S& operator-=(S& i, const swap_struct_t<T, F> v) {
i -= v.swap();
return i;
}
// Logical
template <typename S, typename T, typename F>
S operator&(const S& i, const SwapStructT<T, F> v) {
S operator&(const S& i, const swap_struct_t<T, F> v) {
return i & v.swap();
}
// Comparison
template <typename S, typename T, typename F>
bool operator<(const S& p, const SwapStructT<T, F> v) {
bool operator<(const S& p, const swap_struct_t<T, F> v) {
return p < v.swap();
}
template <typename S, typename T, typename F>
bool operator>(const S& p, const SwapStructT<T, F> v) {
bool operator>(const S& p, const swap_struct_t<T, F> v) {
return p > v.swap();
}
template <typename S, typename T, typename F>
bool operator<=(const S& p, const SwapStructT<T, F> v) {
bool operator<=(const S& p, const swap_struct_t<T, F> v) {
return p <= v.swap();
}
template <typename S, typename T, typename F>
bool operator>=(const S& p, const SwapStructT<T, F> v) {
bool operator>=(const S& p, const swap_struct_t<T, F> v) {
return p >= v.swap();
}
template <typename S, typename T, typename F>
bool operator!=(const S& p, const SwapStructT<T, F> v) {
bool operator!=(const S& p, const swap_struct_t<T, F> v) {
return p != v.swap();
}
template <typename S, typename T, typename F>
bool operator==(const S& p, const SwapStructT<T, F> v) {
bool operator==(const S& p, const swap_struct_t<T, F> v) {
return p == v.swap();
}
template <typename T>
struct Swap64T {
struct swap_64_t {
static T swap(T x) {
return T(Common::swap64(x));
return static_cast<T>(Common::swap64(x));
}
};
template <typename T>
struct Swap32T {
struct swap_32_t {
static T swap(T x) {
return T(Common::swap32(x));
return static_cast<T>(Common::swap32(x));
}
};
template <typename T>
struct Swap16T {
struct swap_16_t {
static T swap(T x) {
return T(Common::swap16(x));
return static_cast<T>(Common::swap16(x));
}
};
template <typename T>
struct SwapFloatT {
struct swap_float_t {
static T swap(T x) {
return T(Common::swapf(x));
return static_cast<T>(Common::swapf(x));
}
};
template <typename T>
struct SwapDoubleT {
struct swap_double_t {
static T swap(T x) {
return T(Common::swapd(x));
return static_cast<T>(Common::swapd(x));
}
};
template <typename T>
struct SwapEnumT {
struct swap_enum_t {
static_assert(std::is_enum_v<T>);
using base = std::underlying_type_t<T>;
public:
SwapEnumT() = default;
SwapEnumT(const T& v) : value(swap(v)) {}
swap_enum_t() = default;
swap_enum_t(const T& v) : value(swap(v)) {}
SwapEnumT& operator=(const T& v) {
swap_enum_t& operator=(const T& v) {
value = swap(v);
return *this;
}
@@ -543,20 +540,22 @@ public:
}
explicit operator base() const {
return base(swap(value));
return static_cast<base>(swap(value));
}
protected:
T value{};
// clang-format off
using swap_t = std::conditional_t<
std::is_same_v<base, u16>, Swap16T<u16>, std::conditional_t<
std::is_same_v<base, s16>, Swap16T<s16>, std::conditional_t<
std::is_same_v<base, u32>, Swap32T<u32>, std::conditional_t<
std::is_same_v<base, s32>, Swap32T<s32>, std::conditional_t<
std::is_same_v<base, u64>, Swap64T<u64>, std::conditional_t<
std::is_same_v<base, s64>, Swap64T<s64>, void>>>>>>;
std::is_same_v<base, u16>, swap_16_t<u16>, std::conditional_t<
std::is_same_v<base, s16>, swap_16_t<s16>, std::conditional_t<
std::is_same_v<base, u32>, swap_32_t<u32>, std::conditional_t<
std::is_same_v<base, s32>, swap_32_t<s32>, std::conditional_t<
std::is_same_v<base, u64>, swap_64_t<u64>, std::conditional_t<
std::is_same_v<base, s64>, swap_64_t<s64>, void>>>>>>;
// clang-format on
static T swap(T x) {
return T(swap_t::swap(base(x)));
return static_cast<T>(swap_t::swap(static_cast<base>(x)));
}
};
@@ -582,17 +581,17 @@ struct AddEndian<u8, SwapTag> {
template <>
struct AddEndian<u16, SwapTag> {
using type = SwapStructT<u16, Swap16T<u16>>;
using type = swap_struct_t<u16, swap_16_t<u16>>;
};
template <>
struct AddEndian<u32, SwapTag> {
using type = SwapStructT<u32, Swap32T<u32>>;
using type = swap_struct_t<u32, swap_32_t<u32>>;
};
template <>
struct AddEndian<u64, SwapTag> {
using type = SwapStructT<u64, Swap64T<u64>>;
using type = swap_struct_t<u64, swap_64_t<u64>>;
};
template <>
@@ -602,33 +601,33 @@ struct AddEndian<s8, SwapTag> {
template <>
struct AddEndian<s16, SwapTag> {
using type = SwapStructT<s16, Swap16T<s16>>;
using type = swap_struct_t<s16, swap_16_t<s16>>;
};
template <>
struct AddEndian<s32, SwapTag> {
using type = SwapStructT<s32, Swap32T<s32>>;
using type = swap_struct_t<s32, swap_32_t<s32>>;
};
template <>
struct AddEndian<s64, SwapTag> {
using type = SwapStructT<s64, Swap64T<s64>>;
using type = swap_struct_t<s64, swap_64_t<s64>>;
};
template <>
struct AddEndian<float, SwapTag> {
using type = SwapStructT<float, SwapFloatT<float>>;
using type = swap_struct_t<float, swap_float_t<float>>;
};
template <>
struct AddEndian<double, SwapTag> {
using type = SwapStructT<double, SwapDoubleT<double>>;
using type = swap_struct_t<double, swap_double_t<double>>;
};
template <typename T>
struct AddEndian<T, SwapTag> {
static_assert(std::is_enum_v<T>);
using type = SwapEnumT<T>;
using type = swap_enum_t<T>;
};
// Alias LETag/BETag as KeepTag/SwapTag depending on the system

View File

@@ -1199,10 +1199,10 @@ else()
target_link_libraries(core PUBLIC Boost::headers)
endif()
target_link_libraries(core PRIVATE fmt::fmt nlohmann_json::nlohmann_json RenderDoc::API MbedTLS::mbedcrypto${YUZU_STATIC_SUFFIX} MbedTLS::mbedtls${YUZU_STATIC_SUFFIX})
# if (MINGW)
# target_link_libraries(core PRIVATE ws2_32 mswsock wlanapi)
# endif()
target_link_libraries(core PRIVATE fmt::fmt nlohmann_json::nlohmann_json RenderDoc::API MbedTLS::mbedcrypto MbedTLS::mbedtls)
if (MINGW)
target_link_libraries(core PRIVATE ws2_32 mswsock wlanapi)
endif()
if (ENABLE_WEB_SERVICE)
target_compile_definitions(core PUBLIC ENABLE_WEB_SERVICE)

View File

@@ -210,12 +210,9 @@ std::shared_ptr<Dynarmic::A32::Jit> ArmDynarmic32::MakeJit(Common::PageTable* pa
config.wall_clock_cntpct = m_uses_wall_clock;
config.enable_cycle_counting = !m_uses_wall_clock;
// Code cache size
#if defined(ARCHITECTURE_arm64) || defined(__sun__)
// Code cache size - max in ARM is 128MiB, max in x86_64 is 2GiB
// Solaris doesn't support kPageSize >= 512MiB
config.code_cache_size = std::uint32_t(128_MiB);
#else
config.code_cache_size = std::uint32_t(512_MiB);
#endif
// Allow memory fault handling to work
if (m_system.DebuggerEnabled()) {

View File

@@ -269,12 +269,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
config.wall_clock_cntpct = m_uses_wall_clock;
config.enable_cycle_counting = !m_uses_wall_clock;
// Code cache size
#if defined(ARCHITECTURE_arm64) || defined(__sun__)
// Code cache size - max in ARM is 128MiB, max in x86_64 is 2GiB
// Solaris doesn't support kPageSize >= 512MiB
config.code_cache_size = std::uint32_t(128_MiB);
#else
config.code_cache_size = std::uint32_t(512_MiB);
#endif
// Allow memory fault handling to work
if (m_system.DebuggerEnabled()) {

View File

@@ -297,9 +297,6 @@ struct System::Impl {
std::string vendor = gpu_core->Renderer().GetDeviceVendor();
LOG_INFO(Core, "GPU Vendor: {}", vendor);
// Reset all per-game flags
Settings::values.use_squashed_iterated_blend = false;
// Insert PC overrides here
#ifdef ANDROID
@@ -325,13 +322,6 @@ struct System::Impl {
#endif
// Ninja Gaiden Ragebound
constexpr u64 ngr = 0x0100781020710000ULL;
if (programId == ngr) {
LOG_INFO(Core, "Enabling game specifc override: use_squashed_iterated_blend");
Settings::values.use_squashed_iterated_blend = true;
}
}
SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
@@ -435,9 +425,6 @@ struct System::Impl {
void ShutdownMainProcess() {
SetShuttingDown(true);
// Reset per-game flags
Settings::values.use_squashed_iterated_blend = false;
is_powered_on = false;
exit_locked = false;
exit_requested = false;

View File

@@ -237,6 +237,7 @@ WebBrowser::~WebBrowser() = default;
void WebBrowser::Initialize() {
if (Settings::values.disable_web_applet) {
LOG_INFO(Service_AM, "Web Browser Applet disabled, skipping.");
return;
}
@@ -304,7 +305,6 @@ void WebBrowser::ExecuteInteractive() {
void WebBrowser::Execute() {
if (Settings::values.disable_web_applet) {
LOG_WARNING(Service_AM, "(STUBBED) called, Web Browser Applet is disabled");
WebBrowserExit(WebExitReason::EndButtonPressed);
return;
}

View File

@@ -229,7 +229,8 @@ std::shared_ptr<FrontendApplet> FrontendAppletHolder::GetApplet(std::shared_ptr<
case AppletId::ProfileSelect:
return std::make_shared<ProfileSelect>(system, applet, mode, *frontend.profile_select);
case AppletId::SoftwareKeyboard:
return std::make_shared<SoftwareKeyboard>(system, applet, mode, *frontend.software_keyboard);
return std::make_shared<SoftwareKeyboard>(system, applet, mode,
*frontend.software_keyboard);
case AppletId::MiiEdit:
return std::make_shared<MiiEdit>(system, applet, mode, *frontend.mii_edit);
case AppletId::Web:
@@ -243,7 +244,9 @@ std::shared_ptr<FrontendApplet> FrontendAppletHolder::GetApplet(std::shared_ptr<
case AppletId::NetConnect:
return std::make_shared<NetConnect>(system, applet, mode, *frontend.net_connect);
default:
LOG_ERROR(Service_AM, "No backend implementation exists for applet_id={:02X}. Falling back to stub applet", static_cast<u8>(id));
UNIMPLEMENTED_MSG(
"No backend implementation exists for applet_id={:02X}! Falling back to stub applet.",
static_cast<u8>(id));
return std::make_shared<StubApplet>(system, applet, id, mode);
}
}

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -396,24 +393,6 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
BufferDescriptorB()[buffer_index].Size()};
const std::size_t buffer_size{GetWriteBufferSize(buffer_index)};
// Defensive check: if client didn't provide output buffer, log detailed error but don't crash
if (buffer_size == 0) {
LOG_ERROR(Core,
"WriteBuffer called but client provided NO output buffer! "
"Requested size: 0x{:X}, buffer_index: {}, is_buffer_b: {}, "
"BufferB count: {}, BufferC count: {}",
size, buffer_index, is_buffer_b, BufferDescriptorB().size(),
BufferDescriptorC().size());
// Log command context for debugging
LOG_ERROR(Core, "IPC Command: 0x{:X}, Type: {}", GetCommand(),
static_cast<u32>(GetCommandType()));
// Return 0 instead of crashing - let service handle error
return 0;
}
if (size > buffer_size) {
LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
buffer_size);

View File

@@ -4,6 +4,9 @@
# SPDX-FileCopyrightText: 2017 Citra Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
add_library(yuzu-room STATIC EXCLUDE_FROM_ALL
yuzu_room.cpp
yuzu_room.h
@@ -16,7 +19,7 @@ if (ENABLE_WEB_SERVICE)
target_link_libraries(yuzu-room PRIVATE web_service)
endif()
target_link_libraries(yuzu-room PRIVATE MbedTLS::mbedcrypto${YUZU_STATIC_SUFFIX} MbedTLS::mbedtls${YUZU_STATIC_SUFFIX})
target_link_libraries(yuzu-room PRIVATE MbedTLS::mbedcrypto MbedTLS::mbedtls)
if (MSVC)
target_link_libraries(yuzu-room PRIVATE getopt)
endif()

View File

@@ -81,6 +81,11 @@ if (MSVC)
/bigobj # Increase number of sections in .obj files
/DNOMINMAX)
if (WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
endif()
if (CXX_CLANG)
list(APPEND DYNARMIC_CXX_FLAGS
-Qunused-arguments

View File

@@ -80,15 +80,16 @@ public:
};
// TODO: Check code alignment
const CodePtr current_code_ptr = [this] {
const CodePtr aligned_code_ptr = CodePtr((uintptr_t(GetCurrentBlock()) + 15) & ~uintptr_t(15));
const CodePtr current_code_ptr = [this, aligned_code_ptr] {
// RSB optimization
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A64JitState::RSBPtrMask;
if (jit_state.GetUniqueHash() == jit_state.rsb_location_descriptors[new_rsb_ptr]) {
jit_state.rsb_ptr = new_rsb_ptr;
return CodePtr(jit_state.rsb_codeptrs[new_rsb_ptr]);
}
return CodePtr((uintptr_t(GetCurrentBlock()) + 15) & ~uintptr_t(15));
return aligned_code_ptr;
//return GetCurrentBlock();
}();
const HaltReason hr = block_of_code.RunCode(&jit_state, current_code_ptr);

View File

@@ -81,12 +81,14 @@ add_library(shader_recompiler STATIC
environment.h
exception.h
frontend/ir/abstract_syntax_list.h
frontend/ir/attribute.cpp
frontend/ir/attribute.h
frontend/ir/basic_block.cpp
frontend/ir/basic_block.h
frontend/ir/breadth_first_search.h
frontend/ir/condition.cpp
frontend/ir/condition.h
frontend/ir/flow_test.cpp
frontend/ir/flow_test.h
frontend/ir/ir_emitter.cpp
frontend/ir/ir_emitter.h

View File

@@ -380,14 +380,13 @@ void EmitContext::SetupExtensions() {
if (info.uses_int64 && profile.support_int64) {
header += "#extension GL_ARB_gpu_shader_int64 : enable\n";
}
if (info.uses_int64_bit_atomics && profile.support_gl_shader_atomic_int64) {
if (info.uses_int64_bit_atomics) {
header += "#extension GL_NV_shader_atomic_int64 : enable\n";
}
if (info.uses_atomic_f32_add && profile.support_gl_shader_atomic_float) {
if (info.uses_atomic_f32_add) {
header += "#extension GL_NV_shader_atomic_float : enable\n";
}
if ((info.uses_atomic_f16x2_add || info.uses_atomic_f16x2_min || info.uses_atomic_f16x2_max) &&
profile.support_gl_shader_atomic_fp16_vector) {
if (info.uses_atomic_f16x2_add || info.uses_atomic_f16x2_min || info.uses_atomic_f16x2_max) {
header += "#extension GL_NV_shader_atomic_fp16_vector : enable\n";
}
if (info.uses_fp16) {

View File

@@ -341,35 +341,19 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
void SetupDenormControl(const Profile& profile, const IR::Program& program, EmitContext& ctx,
Id main_func) {
const Info& info{program.info};
// User-forced behavior overrides (Android Eden Veil/Extensions)
// When force flags are active, they take precedence over shader-declared behavior
const bool force_flush = profile.force_fp32_denorm_flush;
const bool force_preserve = profile.force_fp32_denorm_preserve;
if (force_flush && force_preserve) {
LOG_WARNING(Shader_SPIRV, "Both FTZ and Preserve forced simultaneously - FTZ takes precedence");
}
if (info.uses_fp32_denorms_flush && info.uses_fp32_denorms_preserve) {
LOG_DEBUG(Shader_SPIRV, "Fp32 denorm flush and preserve on the same shader");
} else if (force_flush || info.uses_fp32_denorms_flush) {
} else if (info.uses_fp32_denorms_flush) {
if (profile.support_fp32_denorm_flush) {
ctx.AddCapability(spv::Capability::DenormFlushToZero);
ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormFlushToZero, 32U);
if (force_flush) {
LOG_DEBUG(Shader_SPIRV, "Fp32 DenormFlushToZero FORCED by user setting");
}
} else {
// Drivers will most likely flush denorms by default, no need to warn
}
} else if (force_preserve || info.uses_fp32_denorms_preserve) {
} else if (info.uses_fp32_denorms_preserve) {
if (profile.support_fp32_denorm_preserve) {
ctx.AddCapability(spv::Capability::DenormPreserve);
ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormPreserve, 32U);
if (force_preserve) {
LOG_DEBUG(Shader_SPIRV, "Fp32 DenormPreserve FORCED by user setting");
}
} else {
LOG_DEBUG(Shader_SPIRV, "Fp32 denorm preserve used in shader without host support");
}
@@ -402,24 +386,13 @@ void SetupSignedNanCapabilities(const Profile& profile, const IR::Program& progr
if (profile.has_broken_fp16_float_controls && program.info.uses_fp16) {
return;
}
// User-forced behavior (Android Eden Veil/Extensions)
const bool force_signed_zero_inf_nan = profile.force_fp32_signed_zero_inf_nan;
if (program.info.uses_fp16 && profile.support_fp16_signed_zero_nan_preserve) {
ctx.AddCapability(spv::Capability::SignedZeroInfNanPreserve);
ctx.AddExecutionMode(main_func, spv::ExecutionMode::SignedZeroInfNanPreserve, 16U);
}
if (force_signed_zero_inf_nan || profile.support_fp32_signed_zero_nan_preserve) {
if (profile.support_fp32_signed_zero_nan_preserve) {
ctx.AddCapability(spv::Capability::SignedZeroInfNanPreserve);
ctx.AddExecutionMode(main_func, spv::ExecutionMode::SignedZeroInfNanPreserve, 32U);
if (force_signed_zero_inf_nan) {
LOG_DEBUG(Shader_SPIRV, "Fp32 SignedZeroInfNanPreserve FORCED by user setting");
}
} else if (force_signed_zero_inf_nan) {
LOG_WARNING(Shader_SPIRV, "SignedZeroInfNanPreserve forced but driver doesn't support it");
}
if (profile.support_fp32_signed_zero_nan_preserve) {
ctx.AddCapability(spv::Capability::SignedZeroInfNanPreserve);
ctx.AddExecutionMode(main_func, spv::ExecutionMode::SignedZeroInfNanPreserve, 32U);
}
if (program.info.uses_fp64 && profile.support_fp64_signed_zero_nan_preserve) {
ctx.AddCapability(spv::Capability::SignedZeroInfNanPreserve);

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -321,23 +318,13 @@ void AddOffsetToCoordinates(EmitContext& ctx, const IR::TextureInstInfo& info, I
return;
}
// Mobile GPUs: 1D textures emulated as 2D with height=1
const bool emulate_1d = ctx.profile.needs_1d_texture_emulation;
Id result_type{};
switch (info.type) {
case TextureType::Buffer:
case TextureType::Color1D: {
result_type = ctx.U32[1];
break;
case TextureType::Color1D:
if (emulate_1d) {
// Treat as 2D: offset needs Y component
offset = ctx.OpCompositeConstruct(ctx.U32[2], offset, ctx.u32_zero_value);
result_type = ctx.U32[2];
} else {
result_type = ctx.U32[1];
}
break;
}
case TextureType::ColorArray1D:
offset = ctx.OpCompositeConstruct(ctx.U32[2], offset, ctx.u32_zero_value);
[[fallthrough]];
@@ -361,40 +348,6 @@ void AddOffsetToCoordinates(EmitContext& ctx, const IR::TextureInstInfo& info, I
}
coords = ctx.OpIAdd(result_type, coords, offset);
}
// Helper: Convert 1D coordinates to 2D when emulating 1D textures on mobile GPUs
[[nodiscard]] Id AdjustCoordinatesForEmulation(EmitContext& ctx, const IR::TextureInstInfo& info,
Id coords) {
if (!ctx.profile.needs_1d_texture_emulation) {
return coords;
}
switch (info.type) {
case TextureType::Color1D: {
// Convert scalar → vec2(x, 0.0)
return ctx.OpCompositeConstruct(ctx.F32[2], coords, ctx.f32_zero_value);
}
case TextureType::ColorArray1D: {
// Convert vec2(x, layer) → vec3(x, 0.0, layer)
// ColorArray1D coords are always vec2 in IR
const Id x = ctx.OpCompositeExtract(ctx.F32[1], coords, 0);
const Id layer = ctx.OpCompositeExtract(ctx.F32[1], coords, 1);
return ctx.OpCompositeConstruct(ctx.F32[3], x, ctx.f32_zero_value, layer);
}
case TextureType::Color2D:
case TextureType::ColorArray2D:
case TextureType::Color3D:
case TextureType::ColorCube:
case TextureType::ColorArrayCube:
case TextureType::Buffer:
case TextureType::Color2DRect:
// No adjustment needed for non-1D textures
return coords;
}
return coords; // Unreachable, but silences -Werror=return-type
}
} // Anonymous namespace
Id EmitBindlessImageSampleImplicitLod(EmitContext&) {
@@ -496,7 +449,6 @@ Id EmitBoundImageWrite(EmitContext&) {
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id bias_lc, const IR::Value& offset) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
if (ctx.stage == Stage::Fragment) {
const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
bias_lc, offset);
@@ -518,7 +470,6 @@ Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value&
Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id lod, const IR::Value& offset) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
const ImageOperands operands(ctx, false, true, false, lod, offset);
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
@@ -528,7 +479,6 @@ Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value&
Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
Id coords, Id dref, Id bias_lc, const IR::Value& offset) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
if (ctx.stage == Stage::Fragment) {
const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
bias_lc, offset);
@@ -550,7 +500,6 @@ Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va
Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
Id coords, Id dref, Id lod, const IR::Value& offset) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
const ImageOperands operands(ctx, false, true, false, lod, offset);
return Emit(&EmitContext::OpImageSparseSampleDrefExplicitLod,
&EmitContext::OpImageSampleDrefExplicitLod, ctx, inst, ctx.F32[1],
@@ -560,7 +509,6 @@ Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va
Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
const IR::Value& offset, const IR::Value& offset2) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
const ImageOperands operands(ctx, offset, offset2);
if (ctx.profile.need_gather_subpixel_offset) {
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
@@ -573,7 +521,6 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
const IR::Value& offset, const IR::Value& offset2, Id dref) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
const ImageOperands operands(ctx, offset, offset2);
if (ctx.profile.need_gather_subpixel_offset) {
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
@@ -586,7 +533,6 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
Id lod, Id ms) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
AddOffsetToCoordinates(ctx, info, coords, offset);
if (info.type == TextureType::Buffer) {
lod = Id{};
@@ -613,20 +559,9 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& i
return uses_lod ? ctx.OpImageQuerySizeLod(type, image, lod)
: ctx.OpImageQuerySize(type, image);
}};
// Mobile GPUs: 1D textures emulated as 2D, query returns vec2 instead of scalar
const bool emulate_1d = ctx.profile.needs_1d_texture_emulation;
switch (info.type) {
case TextureType::Color1D:
if (emulate_1d) {
// Query as 2D, extract only X component for 1D size
const Id size_2d = query(ctx.U32[2]);
const Id width = ctx.OpCompositeExtract(ctx.U32[1], size_2d, 0);
return ctx.OpCompositeConstruct(ctx.U32[4], width, zero, zero, mips());
} else {
return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips());
}
return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips());
case TextureType::ColorArray1D:
case TextureType::Color2D:
case TextureType::ColorCube:
@@ -644,7 +579,6 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& i
Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
const Id zero{ctx.f32_zero_value};
const Id sampler{Texture(ctx, info, index)};
return ctx.OpCompositeConstruct(ctx.F32[4], ctx.OpImageQueryLod(ctx.F32[2], sampler, coords),
@@ -654,7 +588,6 @@ 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 derivatives, const IR::Value& offset, Id lod_clamp) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
const auto operands = info.num_derivatives == 3
? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
ctx.Def(offset), {}, lod_clamp)
@@ -667,7 +600,6 @@ Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
if (info.image_format == ImageFormat::Typeless && !ctx.profile.support_typeless_image_loads) {
LOG_WARNING(Shader_SPIRV, "Typeless image read not supported by host");
return ctx.ConstantNull(ctx.U32[4]);
@@ -684,7 +616,6 @@ Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id co
void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
const auto [image, is_integer] = Image(ctx, index, info);
if (!is_integer) {
color = ctx.OpBitcast(ctx.F32[4], color);

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -95,7 +92,7 @@ void EmitLoadGlobalS16(EmitContext&) {
}
Id EmitLoadGlobal32(EmitContext& ctx, Id address) {
if (ctx.SupportsNativeInt64() || ctx.UsesInt64Emulation()) {
if (ctx.profile.support_int64) {
return ctx.OpFunctionCall(ctx.U32[1], ctx.load_global_func_u32, address);
}
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
@@ -103,7 +100,7 @@ Id EmitLoadGlobal32(EmitContext& ctx, Id address) {
}
Id EmitLoadGlobal64(EmitContext& ctx, Id address) {
if (ctx.SupportsNativeInt64() || ctx.UsesInt64Emulation()) {
if (ctx.profile.support_int64) {
return ctx.OpFunctionCall(ctx.U32[2], ctx.load_global_func_u32x2, address);
}
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
@@ -111,7 +108,7 @@ Id EmitLoadGlobal64(EmitContext& ctx, Id address) {
}
Id EmitLoadGlobal128(EmitContext& ctx, Id address) {
if (ctx.SupportsNativeInt64() || ctx.UsesInt64Emulation()) {
if (ctx.profile.support_int64) {
return ctx.OpFunctionCall(ctx.U32[4], ctx.load_global_func_u32x4, address);
}
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
@@ -135,7 +132,7 @@ void EmitWriteGlobalS16(EmitContext&) {
}
void EmitWriteGlobal32(EmitContext& ctx, Id address, Id value) {
if (ctx.SupportsNativeInt64() || ctx.UsesInt64Emulation()) {
if (ctx.profile.support_int64) {
ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32, address, value);
return;
}
@@ -143,7 +140,7 @@ void EmitWriteGlobal32(EmitContext& ctx, Id address, Id value) {
}
void EmitWriteGlobal64(EmitContext& ctx, Id address, Id value) {
if (ctx.SupportsNativeInt64() || ctx.UsesInt64Emulation()) {
if (ctx.profile.support_int64) {
ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32x2, address, value);
return;
}
@@ -151,7 +148,7 @@ void EmitWriteGlobal64(EmitContext& ctx, Id address, Id value) {
}
void EmitWriteGlobal128(EmitContext& ctx, Id address, Id value) {
if (ctx.SupportsNativeInt64() || ctx.UsesInt64Emulation()) {
if (ctx.profile.support_int64) {
ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32x4, address, value);
return;
}

View File

@@ -33,24 +33,11 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
const Id type{ctx.F32[1]};
const bool depth{desc.is_depth};
const bool ms{desc.is_multisample};
// Mobile GPUs lack Sampled1D SPIR-V capability - emulate 1D as 2D with array layer
const bool emulate_1d = ctx.profile.needs_1d_texture_emulation;
// Debug log for 1D emulation
if (desc.type == TextureType::Color1D || desc.type == TextureType::ColorArray1D) {
LOG_WARNING(Shader_SPIRV, "ImageType(texture): Creating {} texture, emulate_1d={}",
desc.type == TextureType::Color1D ? "Color1D" : "ColorArray1D",
emulate_1d);
}
switch (desc.type) {
case TextureType::Color1D:
return emulate_1d ? ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, false, 1, format)
: ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format);
return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format);
case TextureType::ColorArray1D:
return emulate_1d ? ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, false, 1, format)
: ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format);
return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format);
case TextureType::Color2D:
case TextureType::Color2DRect:
return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, ms, 1, format);
@@ -92,22 +79,11 @@ spv::ImageFormat GetImageFormat(ImageFormat format) {
Id ImageType(EmitContext& ctx, const ImageDescriptor& desc, Id sampled_type) {
const spv::ImageFormat format{GetImageFormat(desc.format)};
const bool emulate_1d = ctx.profile.needs_1d_texture_emulation;
// Debug log for 1D emulation
if (desc.type == TextureType::Color1D || desc.type == TextureType::ColorArray1D) {
LOG_WARNING(Shader_SPIRV, "ImageType: Creating {} image, emulate_1d={}",
desc.type == TextureType::Color1D ? "Color1D" : "ColorArray1D",
emulate_1d);
}
switch (desc.type) {
case TextureType::Color1D:
return emulate_1d ? ctx.TypeImage(sampled_type, spv::Dim::Dim2D, false, false, false, 2, format)
: ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, false, false, 2, format);
return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, false, false, 2, format);
case TextureType::ColorArray1D:
return emulate_1d ? ctx.TypeImage(sampled_type, spv::Dim::Dim2D, false, true, false, 2, format)
: ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, true, false, 2, format);
return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, true, false, 2, format);
case TextureType::Color2D:
return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, false, false, false, 2, format);
case TextureType::ColorArray2D:
@@ -484,14 +460,9 @@ void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_vie
EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_info_,
IR::Program& program, Bindings& bindings)
: Sirit::Module(profile_.supported_spirv), profile{profile_}, runtime_info{runtime_info_},
stage{program.stage},
// Enable int64 emulation if host lacks int64 but we either use int64 ops
// or we need 64-bit addressing for global memory operations.
emulate_int64{!profile.support_int64 &&
(program.info.uses_int64 || program.info.uses_global_memory)},
texture_rescaling_index{bindings.texture_scaling_index},
image_rescaling_index{bindings.image_scaling_index} {
: Sirit::Module(profile_.supported_spirv), profile{profile_}, runtime_info{runtime_info_},
stage{program.stage}, texture_rescaling_index{bindings.texture_scaling_index},
image_rescaling_index{bindings.image_scaling_index} {
const bool is_unified{profile.unified_descriptor_binding};
u32& uniform_binding{is_unified ? bindings.unified : bindings.uniform_buffer};
u32& storage_binding{is_unified ? bindings.unified : bindings.storage_buffer};
@@ -961,163 +932,11 @@ void EmitContext::DefineWriteStorageCasLoopFunction(const Info& info) {
}
void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
if (!info.uses_global_memory) {
if (!info.uses_global_memory || !profile.support_int64) {
return;
}
using DefPtr = Id StorageDefinitions::*;
const Id zero{u32_zero_value};
if (SupportsNativeInt64()) {
const auto define_body{[&](DefPtr ssbo_member, Id addr, Id element_pointer, u32 shift,
auto&& callback) {
AddLabel();
const size_t num_buffers{info.storage_buffers_descriptors.size()};
for (size_t index = 0; index < num_buffers; ++index) {
if (!info.nvn_buffer_used[index]) {
continue;
}
const auto& ssbo{info.storage_buffers_descriptors[index]};
const Id ssbo_addr_cbuf_offset{Const(ssbo.cbuf_offset / 8)};
const Id ssbo_size_cbuf_offset{Const(ssbo.cbuf_offset / 4 + 2)};
const Id ssbo_addr_pointer{OpAccessChain(
uniform_types.U32x2, cbufs[ssbo.cbuf_index].U32x2, zero,
ssbo_addr_cbuf_offset)};
const Id ssbo_size_pointer{OpAccessChain(
uniform_types.U32, cbufs[ssbo.cbuf_index].U32, zero, ssbo_size_cbuf_offset)};
const u64 ssbo_align_mask{~(profile.min_ssbo_alignment - 1U)};
const Id unaligned_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))};
const Id ssbo_addr{OpBitwiseAnd(U64, unaligned_addr, Constant(U64, ssbo_align_mask))};
const Id ssbo_size{OpUConvert(U64, OpLoad(U32[1], ssbo_size_pointer))};
const Id ssbo_end{OpIAdd(U64, ssbo_addr, ssbo_size)};
const Id cond{OpLogicalAnd(U1, OpUGreaterThanEqual(U1, addr, ssbo_addr),
OpULessThan(U1, addr, ssbo_end))};
const Id then_label{OpLabel()};
const Id else_label{OpLabel()};
OpSelectionMerge(else_label, spv::SelectionControlMask::MaskNone);
OpBranchConditional(cond, then_label, else_label);
AddLabel(then_label);
const Id ssbo_id{ssbos[index].*ssbo_member};
const Id ssbo_offset{OpUConvert(U32[1], OpISub(U64, addr, ssbo_addr))};
const Id ssbo_index{OpShiftRightLogical(U32[1], ssbo_offset, Const(shift))};
const Id ssbo_pointer{OpAccessChain(element_pointer, ssbo_id, zero, ssbo_index)};
callback(ssbo_pointer);
AddLabel(else_label);
}
}};
const auto define_load{[&](DefPtr ssbo_member, Id element_pointer, Id type, u32 shift) {
const Id function_type{TypeFunction(type, U64)};
const Id func_id{OpFunction(type, spv::FunctionControlMask::MaskNone, function_type)};
const Id addr{OpFunctionParameter(U64)};
define_body(ssbo_member, addr, element_pointer, shift,
[&](Id ssbo_pointer) { OpReturnValue(OpLoad(type, ssbo_pointer)); });
OpReturnValue(ConstantNull(type));
OpFunctionEnd();
return func_id;
}};
const auto define_write{[&](DefPtr ssbo_member, Id element_pointer, Id type, u32 shift) {
const Id function_type{TypeFunction(void_id, U64, type)};
const Id func_id{
OpFunction(void_id, spv::FunctionControlMask::MaskNone, function_type)};
const Id addr{OpFunctionParameter(U64)};
const Id data{OpFunctionParameter(type)};
define_body(ssbo_member, addr, element_pointer, shift, [&](Id ssbo_pointer) {
OpStore(ssbo_pointer, data);
OpReturn();
});
OpReturn();
OpFunctionEnd();
return func_id;
}};
const auto define{
[&](DefPtr ssbo_member, const StorageTypeDefinition& type_def, Id type, size_t size) {
const Id element_type{type_def.element};
const u32 shift{static_cast<u32>(std::countr_zero(size))};
const Id load_func{define_load(ssbo_member, element_type, type, shift)};
const Id write_func{define_write(ssbo_member, element_type, type, shift)};
return std::make_pair(load_func, write_func);
}};
std::tie(load_global_func_u32, write_global_func_u32) =
define(&StorageDefinitions::U32, storage_types.U32, U32[1], sizeof(u32));
std::tie(load_global_func_u32x2, write_global_func_u32x2) =
define(&StorageDefinitions::U32x2, storage_types.U32x2, U32[2], sizeof(u32[2]));
std::tie(load_global_func_u32x4, write_global_func_u32x4) =
define(&StorageDefinitions::U32x4, storage_types.U32x4, U32[4], sizeof(u32[4]));
return;
}
if (!UsesInt64Emulation()) {
return;
}
const auto make_pair = [&](Id lo, Id hi) {
return OpCompositeConstruct(U32[2], lo, hi);
};
const auto split_pair = [&](Id value) {
return std::array<Id, 2>{OpCompositeExtract(U32[1], value, 0U),
OpCompositeExtract(U32[1], value, 1U)};
};
const auto bool_to_u32 = [&](Id predicate) {
return OpSelect(U32[1], predicate, Const(1u), zero);
};
const auto and_pair = [&](Id value, Id mask) {
const auto value_parts{split_pair(value)};
const auto mask_parts{split_pair(mask)};
return make_pair(OpBitwiseAnd(U32[1], value_parts[0], mask_parts[0]),
OpBitwiseAnd(U32[1], value_parts[1], mask_parts[1]));
};
const auto add_pair = [&](Id lhs, Id rhs) {
const auto lhs_parts{split_pair(lhs)};
const auto rhs_parts{split_pair(rhs)};
const Id sum_lo{OpIAdd(U32[1], lhs_parts[0], rhs_parts[0])};
const Id carry{OpULessThan(U1, sum_lo, lhs_parts[0])};
Id sum_hi{OpIAdd(U32[1], lhs_parts[1], rhs_parts[1])};
sum_hi = OpIAdd(U32[1], sum_hi, bool_to_u32(carry));
return make_pair(sum_lo, sum_hi);
};
const auto sub_pair = [&](Id lhs, Id rhs) {
const auto lhs_parts{split_pair(lhs)};
const auto rhs_parts{split_pair(rhs)};
const Id borrow{OpULessThan(U1, lhs_parts[0], rhs_parts[0])};
const Id diff_lo{OpISub(U32[1], lhs_parts[0], rhs_parts[0])};
Id diff_hi{OpISub(U32[1], lhs_parts[1], rhs_parts[1])};
diff_hi = OpISub(U32[1], diff_hi, bool_to_u32(borrow));
return make_pair(diff_lo, diff_hi);
};
const auto shift_right_pair = [&](Id value, u32 shift) {
if (shift == 0) {
return value;
}
const auto parts{split_pair(value)};
const Id shift_id{Const(shift)};
const Id high_shifted{OpShiftRightLogical(U32[1], parts[1], shift_id)};
Id low_shifted{OpShiftRightLogical(U32[1], parts[0], shift_id)};
const Id carry_bits{OpShiftLeftLogical(U32[1], parts[1], Const(32u - shift))};
low_shifted = OpBitwiseOr(U32[1], low_shifted, carry_bits);
return make_pair(low_shifted, high_shifted);
};
const auto greater_equal_pair = [&](Id lhs, Id rhs) {
const auto lhs_parts{split_pair(lhs)};
const auto rhs_parts{split_pair(rhs)};
const Id hi_gt{OpUGreaterThan(U1, lhs_parts[1], rhs_parts[1])};
const Id hi_eq{OpIEqual(U1, lhs_parts[1], rhs_parts[1])};
const Id lo_ge{OpUGreaterThanEqual(U1, lhs_parts[0], rhs_parts[0])};
return OpLogicalOr(U1, hi_gt, OpLogicalAnd(U1, hi_eq, lo_ge));
};
const auto less_than_pair = [&](Id lhs, Id rhs) {
const auto lhs_parts{split_pair(lhs)};
const auto rhs_parts{split_pair(rhs)};
const Id hi_lt{OpULessThan(U1, lhs_parts[1], rhs_parts[1])};
const Id hi_eq{OpIEqual(U1, lhs_parts[1], rhs_parts[1])};
const Id lo_lt{OpULessThan(U1, lhs_parts[0], rhs_parts[0])};
return OpLogicalOr(U1, hi_lt, OpLogicalAnd(U1, hi_eq, lo_lt));
};
const u64 ssbo_align_mask_value{~(profile.min_ssbo_alignment - 1U)};
const Id ssbo_align_mask{
Const(static_cast<u32>(ssbo_align_mask_value & 0xFFFFFFFFu),
static_cast<u32>(ssbo_align_mask_value >> 32))};
const auto define_body{[&](DefPtr ssbo_member, Id addr, Id element_pointer, u32 shift,
auto&& callback) {
AddLabel();
@@ -1134,44 +953,40 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
const Id ssbo_size_pointer{OpAccessChain(uniform_types.U32, cbufs[ssbo.cbuf_index].U32,
zero, ssbo_size_cbuf_offset)};
const Id unaligned_addr_pair{OpLoad(U32[2], ssbo_addr_pointer)};
const Id ssbo_addr_pair{and_pair(unaligned_addr_pair, ssbo_align_mask)};
const Id ssbo_size_value{OpLoad(U32[1], ssbo_size_pointer)};
const Id ssbo_size_pair{make_pair(ssbo_size_value, zero)};
const Id ssbo_end_pair{add_pair(ssbo_addr_pair, ssbo_size_pair)};
const Id cond{OpLogicalAnd(U1, greater_equal_pair(addr, ssbo_addr_pair),
less_than_pair(addr, ssbo_end_pair))};
const u64 ssbo_align_mask{~(profile.min_ssbo_alignment - 1U)};
const Id unaligned_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))};
const Id ssbo_addr{OpBitwiseAnd(U64, unaligned_addr, Constant(U64, ssbo_align_mask))};
const Id ssbo_size{OpUConvert(U64, OpLoad(U32[1], ssbo_size_pointer))};
const Id ssbo_end{OpIAdd(U64, ssbo_addr, ssbo_size)};
const Id cond{OpLogicalAnd(U1, OpUGreaterThanEqual(U1, addr, ssbo_addr),
OpULessThan(U1, addr, ssbo_end))};
const Id then_label{OpLabel()};
const Id else_label{OpLabel()};
OpSelectionMerge(else_label, spv::SelectionControlMask::MaskNone);
OpBranchConditional(cond, then_label, else_label);
AddLabel(then_label);
const Id ssbo_id{ssbos[index].*ssbo_member};
const Id ssbo_offset_pair{sub_pair(addr, ssbo_addr_pair)};
const Id ssbo_index_pair{shift_right_pair(ssbo_offset_pair, shift)};
const Id ssbo_index{OpCompositeExtract(U32[1], ssbo_index_pair, 0U)};
const Id ssbo_offset{OpUConvert(U32[1], OpISub(U64, addr, ssbo_addr))};
const Id ssbo_index{OpShiftRightLogical(U32[1], ssbo_offset, Const(shift))};
const Id ssbo_pointer{OpAccessChain(element_pointer, ssbo_id, zero, ssbo_index)};
callback(ssbo_pointer);
AddLabel(else_label);
}
}};
const auto define_load{[&](DefPtr ssbo_member, Id element_pointer, Id type, u32 shift) {
const Id function_type{TypeFunction(type, U32[2])};
const Id function_type{TypeFunction(type, U64)};
const Id func_id{OpFunction(type, spv::FunctionControlMask::MaskNone, function_type)};
const Id addr{OpFunctionParameter(U32[2])};
const Id addr{OpFunctionParameter(U64)};
define_body(ssbo_member, addr, element_pointer, shift,
[&](Id ssbo_pointer) { OpReturnValue(OpLoad(type, ssbo_pointer)); });
OpReturnValue(ConstantNull(type));
OpFunctionEnd();
return func_id;
}};
const auto define_write{[&](DefPtr ssbo_member, Id element_pointer, Id type, u32 shift) {
const Id function_type{TypeFunction(void_id, U32[2], type)};
const Id func_id{
OpFunction(void_id, spv::FunctionControlMask::MaskNone, function_type)};
const Id addr{OpFunctionParameter(U32[2])};
const Id function_type{TypeFunction(void_id, U64, type)};
const Id func_id{OpFunction(void_id, spv::FunctionControlMask::MaskNone, function_type)};
const Id addr{OpFunctionParameter(U64)};
const Id data{OpFunctionParameter(type)};
define_body(ssbo_member, addr, element_pointer, shift, [&](Id ssbo_pointer) {
OpStore(ssbo_pointer, data);
@@ -1181,7 +996,6 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
OpFunctionEnd();
return func_id;
}};
const auto define{
[&](DefPtr ssbo_member, const StorageTypeDefinition& type_def, Id type, size_t size) {
const Id element_type{type_def.element};
@@ -1190,7 +1004,6 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
const Id write_func{define_write(ssbo_member, element_type, type, shift)};
return std::make_pair(load_func, write_func);
}};
std::tie(load_global_func_u32, write_global_func_u32) =
define(&StorageDefinitions::U32, storage_types.U32, U32[1], sizeof(u32));
std::tie(load_global_func_u32x2, write_global_func_u32x2) =
@@ -1629,15 +1442,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR);
subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR);
subgroup_mask_ge = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGeMaskKHR);
// Vulkan spec: Fragment shader Input variables with integer/float type must have Flat decoration
if (stage == Stage::Fragment) {
Decorate(subgroup_mask_eq, spv::Decoration::Flat);
Decorate(subgroup_mask_lt, spv::Decoration::Flat);
Decorate(subgroup_mask_le, spv::Decoration::Flat);
Decorate(subgroup_mask_gt, spv::Decoration::Flat);
Decorate(subgroup_mask_ge, spv::Decoration::Flat);
}
}
if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
(profile.warp_size_potentially_larger_than_guest &&

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -210,15 +207,6 @@ public:
const Profile& profile;
const RuntimeInfo& runtime_info;
Stage stage{};
const bool emulate_int64{};
bool SupportsNativeInt64() const {
return profile.support_int64;
}
bool UsesInt64Emulation() const {
return emulate_int64;
}
Id void_id{};
Id U1{};

View File

@@ -0,0 +1,459 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <fmt/ranges.h>
#include "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/ir/attribute.h"
namespace Shader::IR {
bool IsGeneric(Attribute attribute) noexcept {
return attribute >= Attribute::Generic0X && attribute <= Attribute::Generic31X;
}
u32 GenericAttributeIndex(Attribute attribute) {
if (!IsGeneric(attribute)) {
throw InvalidArgument("Attribute is not generic {}", attribute);
}
return (static_cast<u32>(attribute) - static_cast<u32>(Attribute::Generic0X)) / 4u;
}
u32 GenericAttributeElement(Attribute attribute) {
if (!IsGeneric(attribute)) {
throw InvalidArgument("Attribute is not generic {}", attribute);
}
return static_cast<u32>(attribute) % 4;
}
std::string NameOf(Attribute attribute) {
switch (attribute) {
case Attribute::PrimitiveId:
return "PrimitiveId";
case Attribute::Layer:
return "Layer";
case Attribute::ViewportIndex:
return "ViewportIndex";
case Attribute::PointSize:
return "PointSize";
case Attribute::PositionX:
return "Position.X";
case Attribute::PositionY:
return "Position.Y";
case Attribute::PositionZ:
return "Position.Z";
case Attribute::PositionW:
return "Position.W";
case Attribute::Generic0X:
return "Generic[0].X";
case Attribute::Generic0Y:
return "Generic[0].Y";
case Attribute::Generic0Z:
return "Generic[0].Z";
case Attribute::Generic0W:
return "Generic[0].W";
case Attribute::Generic1X:
return "Generic[1].X";
case Attribute::Generic1Y:
return "Generic[1].Y";
case Attribute::Generic1Z:
return "Generic[1].Z";
case Attribute::Generic1W:
return "Generic[1].W";
case Attribute::Generic2X:
return "Generic[2].X";
case Attribute::Generic2Y:
return "Generic[2].Y";
case Attribute::Generic2Z:
return "Generic[2].Z";
case Attribute::Generic2W:
return "Generic[2].W";
case Attribute::Generic3X:
return "Generic[3].X";
case Attribute::Generic3Y:
return "Generic[3].Y";
case Attribute::Generic3Z:
return "Generic[3].Z";
case Attribute::Generic3W:
return "Generic[3].W";
case Attribute::Generic4X:
return "Generic[4].X";
case Attribute::Generic4Y:
return "Generic[4].Y";
case Attribute::Generic4Z:
return "Generic[4].Z";
case Attribute::Generic4W:
return "Generic[4].W";
case Attribute::Generic5X:
return "Generic[5].X";
case Attribute::Generic5Y:
return "Generic[5].Y";
case Attribute::Generic5Z:
return "Generic[5].Z";
case Attribute::Generic5W:
return "Generic[5].W";
case Attribute::Generic6X:
return "Generic[6].X";
case Attribute::Generic6Y:
return "Generic[6].Y";
case Attribute::Generic6Z:
return "Generic[6].Z";
case Attribute::Generic6W:
return "Generic[6].W";
case Attribute::Generic7X:
return "Generic[7].X";
case Attribute::Generic7Y:
return "Generic[7].Y";
case Attribute::Generic7Z:
return "Generic[7].Z";
case Attribute::Generic7W:
return "Generic[7].W";
case Attribute::Generic8X:
return "Generic[8].X";
case Attribute::Generic8Y:
return "Generic[8].Y";
case Attribute::Generic8Z:
return "Generic[8].Z";
case Attribute::Generic8W:
return "Generic[8].W";
case Attribute::Generic9X:
return "Generic[9].X";
case Attribute::Generic9Y:
return "Generic[9].Y";
case Attribute::Generic9Z:
return "Generic[9].Z";
case Attribute::Generic9W:
return "Generic[9].W";
case Attribute::Generic10X:
return "Generic[10].X";
case Attribute::Generic10Y:
return "Generic[10].Y";
case Attribute::Generic10Z:
return "Generic[10].Z";
case Attribute::Generic10W:
return "Generic[10].W";
case Attribute::Generic11X:
return "Generic[11].X";
case Attribute::Generic11Y:
return "Generic[11].Y";
case Attribute::Generic11Z:
return "Generic[11].Z";
case Attribute::Generic11W:
return "Generic[11].W";
case Attribute::Generic12X:
return "Generic[12].X";
case Attribute::Generic12Y:
return "Generic[12].Y";
case Attribute::Generic12Z:
return "Generic[12].Z";
case Attribute::Generic12W:
return "Generic[12].W";
case Attribute::Generic13X:
return "Generic[13].X";
case Attribute::Generic13Y:
return "Generic[13].Y";
case Attribute::Generic13Z:
return "Generic[13].Z";
case Attribute::Generic13W:
return "Generic[13].W";
case Attribute::Generic14X:
return "Generic[14].X";
case Attribute::Generic14Y:
return "Generic[14].Y";
case Attribute::Generic14Z:
return "Generic[14].Z";
case Attribute::Generic14W:
return "Generic[14].W";
case Attribute::Generic15X:
return "Generic[15].X";
case Attribute::Generic15Y:
return "Generic[15].Y";
case Attribute::Generic15Z:
return "Generic[15].Z";
case Attribute::Generic15W:
return "Generic[15].W";
case Attribute::Generic16X:
return "Generic[16].X";
case Attribute::Generic16Y:
return "Generic[16].Y";
case Attribute::Generic16Z:
return "Generic[16].Z";
case Attribute::Generic16W:
return "Generic[16].W";
case Attribute::Generic17X:
return "Generic[17].X";
case Attribute::Generic17Y:
return "Generic[17].Y";
case Attribute::Generic17Z:
return "Generic[17].Z";
case Attribute::Generic17W:
return "Generic[17].W";
case Attribute::Generic18X:
return "Generic[18].X";
case Attribute::Generic18Y:
return "Generic[18].Y";
case Attribute::Generic18Z:
return "Generic[18].Z";
case Attribute::Generic18W:
return "Generic[18].W";
case Attribute::Generic19X:
return "Generic[19].X";
case Attribute::Generic19Y:
return "Generic[19].Y";
case Attribute::Generic19Z:
return "Generic[19].Z";
case Attribute::Generic19W:
return "Generic[19].W";
case Attribute::Generic20X:
return "Generic[20].X";
case Attribute::Generic20Y:
return "Generic[20].Y";
case Attribute::Generic20Z:
return "Generic[20].Z";
case Attribute::Generic20W:
return "Generic[20].W";
case Attribute::Generic21X:
return "Generic[21].X";
case Attribute::Generic21Y:
return "Generic[21].Y";
case Attribute::Generic21Z:
return "Generic[21].Z";
case Attribute::Generic21W:
return "Generic[21].W";
case Attribute::Generic22X:
return "Generic[22].X";
case Attribute::Generic22Y:
return "Generic[22].Y";
case Attribute::Generic22Z:
return "Generic[22].Z";
case Attribute::Generic22W:
return "Generic[22].W";
case Attribute::Generic23X:
return "Generic[23].X";
case Attribute::Generic23Y:
return "Generic[23].Y";
case Attribute::Generic23Z:
return "Generic[23].Z";
case Attribute::Generic23W:
return "Generic[23].W";
case Attribute::Generic24X:
return "Generic[24].X";
case Attribute::Generic24Y:
return "Generic[24].Y";
case Attribute::Generic24Z:
return "Generic[24].Z";
case Attribute::Generic24W:
return "Generic[24].W";
case Attribute::Generic25X:
return "Generic[25].X";
case Attribute::Generic25Y:
return "Generic[25].Y";
case Attribute::Generic25Z:
return "Generic[25].Z";
case Attribute::Generic25W:
return "Generic[25].W";
case Attribute::Generic26X:
return "Generic[26].X";
case Attribute::Generic26Y:
return "Generic[26].Y";
case Attribute::Generic26Z:
return "Generic[26].Z";
case Attribute::Generic26W:
return "Generic[26].W";
case Attribute::Generic27X:
return "Generic[27].X";
case Attribute::Generic27Y:
return "Generic[27].Y";
case Attribute::Generic27Z:
return "Generic[27].Z";
case Attribute::Generic27W:
return "Generic[27].W";
case Attribute::Generic28X:
return "Generic[28].X";
case Attribute::Generic28Y:
return "Generic[28].Y";
case Attribute::Generic28Z:
return "Generic[28].Z";
case Attribute::Generic28W:
return "Generic[28].W";
case Attribute::Generic29X:
return "Generic[29].X";
case Attribute::Generic29Y:
return "Generic[29].Y";
case Attribute::Generic29Z:
return "Generic[29].Z";
case Attribute::Generic29W:
return "Generic[29].W";
case Attribute::Generic30X:
return "Generic[30].X";
case Attribute::Generic30Y:
return "Generic[30].Y";
case Attribute::Generic30Z:
return "Generic[30].Z";
case Attribute::Generic30W:
return "Generic[30].W";
case Attribute::Generic31X:
return "Generic[31].X";
case Attribute::Generic31Y:
return "Generic[31].Y";
case Attribute::Generic31Z:
return "Generic[31].Z";
case Attribute::Generic31W:
return "Generic[31].W";
case Attribute::ColorFrontDiffuseR:
return "ColorFrontDiffuse.R";
case Attribute::ColorFrontDiffuseG:
return "ColorFrontDiffuse.G";
case Attribute::ColorFrontDiffuseB:
return "ColorFrontDiffuse.B";
case Attribute::ColorFrontDiffuseA:
return "ColorFrontDiffuse.A";
case Attribute::ColorFrontSpecularR:
return "ColorFrontSpecular.R";
case Attribute::ColorFrontSpecularG:
return "ColorFrontSpecular.G";
case Attribute::ColorFrontSpecularB:
return "ColorFrontSpecular.B";
case Attribute::ColorFrontSpecularA:
return "ColorFrontSpecular.A";
case Attribute::ColorBackDiffuseR:
return "ColorBackDiffuse.R";
case Attribute::ColorBackDiffuseG:
return "ColorBackDiffuse.G";
case Attribute::ColorBackDiffuseB:
return "ColorBackDiffuse.B";
case Attribute::ColorBackDiffuseA:
return "ColorBackDiffuse.A";
case Attribute::ColorBackSpecularR:
return "ColorBackSpecular.R";
case Attribute::ColorBackSpecularG:
return "ColorBackSpecular.G";
case Attribute::ColorBackSpecularB:
return "ColorBackSpecular.B";
case Attribute::ColorBackSpecularA:
return "ColorBackSpecular.A";
case Attribute::ClipDistance0:
return "ClipDistance[0]";
case Attribute::ClipDistance1:
return "ClipDistance[1]";
case Attribute::ClipDistance2:
return "ClipDistance[2]";
case Attribute::ClipDistance3:
return "ClipDistance[3]";
case Attribute::ClipDistance4:
return "ClipDistance[4]";
case Attribute::ClipDistance5:
return "ClipDistance[5]";
case Attribute::ClipDistance6:
return "ClipDistance[6]";
case Attribute::ClipDistance7:
return "ClipDistance[7]";
case Attribute::PointSpriteS:
return "PointSprite.S";
case Attribute::PointSpriteT:
return "PointSprite.T";
case Attribute::FogCoordinate:
return "FogCoordinate";
case Attribute::TessellationEvaluationPointU:
return "TessellationEvaluationPoint.U";
case Attribute::TessellationEvaluationPointV:
return "TessellationEvaluationPoint.V";
case Attribute::InstanceId:
return "InstanceId";
case Attribute::VertexId:
return "VertexId";
case Attribute::FixedFncTexture0S:
return "FixedFncTexture[0].S";
case Attribute::FixedFncTexture0T:
return "FixedFncTexture[0].T";
case Attribute::FixedFncTexture0R:
return "FixedFncTexture[0].R";
case Attribute::FixedFncTexture0Q:
return "FixedFncTexture[0].Q";
case Attribute::FixedFncTexture1S:
return "FixedFncTexture[1].S";
case Attribute::FixedFncTexture1T:
return "FixedFncTexture[1].T";
case Attribute::FixedFncTexture1R:
return "FixedFncTexture[1].R";
case Attribute::FixedFncTexture1Q:
return "FixedFncTexture[1].Q";
case Attribute::FixedFncTexture2S:
return "FixedFncTexture[2].S";
case Attribute::FixedFncTexture2T:
return "FixedFncTexture[2].T";
case Attribute::FixedFncTexture2R:
return "FixedFncTexture[2].R";
case Attribute::FixedFncTexture2Q:
return "FixedFncTexture[2].Q";
case Attribute::FixedFncTexture3S:
return "FixedFncTexture[3].S";
case Attribute::FixedFncTexture3T:
return "FixedFncTexture[3].T";
case Attribute::FixedFncTexture3R:
return "FixedFncTexture[3].R";
case Attribute::FixedFncTexture3Q:
return "FixedFncTexture[3].Q";
case Attribute::FixedFncTexture4S:
return "FixedFncTexture[4].S";
case Attribute::FixedFncTexture4T:
return "FixedFncTexture[4].T";
case Attribute::FixedFncTexture4R:
return "FixedFncTexture[4].R";
case Attribute::FixedFncTexture4Q:
return "FixedFncTexture[4].Q";
case Attribute::FixedFncTexture5S:
return "FixedFncTexture[5].S";
case Attribute::FixedFncTexture5T:
return "FixedFncTexture[5].T";
case Attribute::FixedFncTexture5R:
return "FixedFncTexture[5].R";
case Attribute::FixedFncTexture5Q:
return "FixedFncTexture[5].Q";
case Attribute::FixedFncTexture6S:
return "FixedFncTexture[6].S";
case Attribute::FixedFncTexture6T:
return "FixedFncTexture[6].T";
case Attribute::FixedFncTexture6R:
return "FixedFncTexture[6].R";
case Attribute::FixedFncTexture6Q:
return "FixedFncTexture[6].Q";
case Attribute::FixedFncTexture7S:
return "FixedFncTexture[7].S";
case Attribute::FixedFncTexture7T:
return "FixedFncTexture[7].T";
case Attribute::FixedFncTexture7R:
return "FixedFncTexture[7].R";
case Attribute::FixedFncTexture7Q:
return "FixedFncTexture[7].Q";
case Attribute::FixedFncTexture8S:
return "FixedFncTexture[8].S";
case Attribute::FixedFncTexture8T:
return "FixedFncTexture[8].T";
case Attribute::FixedFncTexture8R:
return "FixedFncTexture[8].R";
case Attribute::FixedFncTexture8Q:
return "FixedFncTexture[8].Q";
case Attribute::FixedFncTexture9S:
return "FixedFncTexture[9].S";
case Attribute::FixedFncTexture9T:
return "FixedFncTexture[9].T";
case Attribute::FixedFncTexture9R:
return "FixedFncTexture[9].R";
case Attribute::FixedFncTexture9Q:
return "FixedFncTexture[9].Q";
case Attribute::ViewportMask:
return "ViewportMask";
case Attribute::FrontFace:
return "FrontFace";
case Attribute::BaseInstance:
return "BaseInstance";
case Attribute::BaseVertex:
return "BaseVertex";
case Attribute::DrawID:
return "DrawID";
}
return fmt::format("<reserved attribute {}>", static_cast<int>(attribute));
}
} // namespace Shader::IR

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -9,261 +6,240 @@
#include <fmt/ranges.h>
#include "common/common_types.h"
#include "shader_recompiler/exception.h"
namespace Shader::IR {
enum class Attribute : u64 {
#define SRIR_ATTRIBUTE_LIST \
SRIR_ATTRIBUTE_ELEM(PrimitiveId, 24) \
SRIR_ATTRIBUTE_ELEM(Layer, 25) \
SRIR_ATTRIBUTE_ELEM(ViewportIndex, 26) \
SRIR_ATTRIBUTE_ELEM(PointSize, 27) \
SRIR_ATTRIBUTE_ELEM(PositionX, 28) \
SRIR_ATTRIBUTE_ELEM(PositionY, 29) \
SRIR_ATTRIBUTE_ELEM(PositionZ, 30) \
SRIR_ATTRIBUTE_ELEM(PositionW, 31) \
SRIR_ATTRIBUTE_ELEM(Generic0X, 32) \
SRIR_ATTRIBUTE_ELEM(Generic0Y, 33) \
SRIR_ATTRIBUTE_ELEM(Generic0Z, 34) \
SRIR_ATTRIBUTE_ELEM(Generic0W, 35) \
SRIR_ATTRIBUTE_ELEM(Generic1X, 36) \
SRIR_ATTRIBUTE_ELEM(Generic1Y, 37) \
SRIR_ATTRIBUTE_ELEM(Generic1Z, 38) \
SRIR_ATTRIBUTE_ELEM(Generic1W, 39) \
SRIR_ATTRIBUTE_ELEM(Generic2X, 40) \
SRIR_ATTRIBUTE_ELEM(Generic2Y, 41) \
SRIR_ATTRIBUTE_ELEM(Generic2Z, 42) \
SRIR_ATTRIBUTE_ELEM(Generic2W, 43) \
SRIR_ATTRIBUTE_ELEM(Generic3X, 44) \
SRIR_ATTRIBUTE_ELEM(Generic3Y, 45) \
SRIR_ATTRIBUTE_ELEM(Generic3Z, 46) \
SRIR_ATTRIBUTE_ELEM(Generic3W, 47) \
SRIR_ATTRIBUTE_ELEM(Generic4X, 48) \
SRIR_ATTRIBUTE_ELEM(Generic4Y, 49) \
SRIR_ATTRIBUTE_ELEM(Generic4Z, 50) \
SRIR_ATTRIBUTE_ELEM(Generic4W, 51) \
SRIR_ATTRIBUTE_ELEM(Generic5X, 52) \
SRIR_ATTRIBUTE_ELEM(Generic5Y, 53) \
SRIR_ATTRIBUTE_ELEM(Generic5Z, 54) \
SRIR_ATTRIBUTE_ELEM(Generic5W, 55) \
SRIR_ATTRIBUTE_ELEM(Generic6X, 56) \
SRIR_ATTRIBUTE_ELEM(Generic6Y, 57) \
SRIR_ATTRIBUTE_ELEM(Generic6Z, 58) \
SRIR_ATTRIBUTE_ELEM(Generic6W, 59) \
SRIR_ATTRIBUTE_ELEM(Generic7X, 60) \
SRIR_ATTRIBUTE_ELEM(Generic7Y, 61) \
SRIR_ATTRIBUTE_ELEM(Generic7Z, 62) \
SRIR_ATTRIBUTE_ELEM(Generic7W, 63) \
SRIR_ATTRIBUTE_ELEM(Generic8X, 64) \
SRIR_ATTRIBUTE_ELEM(Generic8Y, 65) \
SRIR_ATTRIBUTE_ELEM(Generic8Z, 66) \
SRIR_ATTRIBUTE_ELEM(Generic8W, 67) \
SRIR_ATTRIBUTE_ELEM(Generic9X, 68) \
SRIR_ATTRIBUTE_ELEM(Generic9Y, 69) \
SRIR_ATTRIBUTE_ELEM(Generic9Z, 70) \
SRIR_ATTRIBUTE_ELEM(Generic9W, 71) \
SRIR_ATTRIBUTE_ELEM(Generic10X, 72) \
SRIR_ATTRIBUTE_ELEM(Generic10Y, 73) \
SRIR_ATTRIBUTE_ELEM(Generic10Z, 74) \
SRIR_ATTRIBUTE_ELEM(Generic10W, 75) \
SRIR_ATTRIBUTE_ELEM(Generic11X, 76) \
SRIR_ATTRIBUTE_ELEM(Generic11Y, 77) \
SRIR_ATTRIBUTE_ELEM(Generic11Z, 78) \
SRIR_ATTRIBUTE_ELEM(Generic11W, 79) \
SRIR_ATTRIBUTE_ELEM(Generic12X, 80) \
SRIR_ATTRIBUTE_ELEM(Generic12Y, 81) \
SRIR_ATTRIBUTE_ELEM(Generic12Z, 82) \
SRIR_ATTRIBUTE_ELEM(Generic12W, 83) \
SRIR_ATTRIBUTE_ELEM(Generic13X, 84) \
SRIR_ATTRIBUTE_ELEM(Generic13Y, 85) \
SRIR_ATTRIBUTE_ELEM(Generic13Z, 86) \
SRIR_ATTRIBUTE_ELEM(Generic13W, 87) \
SRIR_ATTRIBUTE_ELEM(Generic14X, 88) \
SRIR_ATTRIBUTE_ELEM(Generic14Y, 89) \
SRIR_ATTRIBUTE_ELEM(Generic14Z, 90) \
SRIR_ATTRIBUTE_ELEM(Generic14W, 91) \
SRIR_ATTRIBUTE_ELEM(Generic15X, 92) \
SRIR_ATTRIBUTE_ELEM(Generic15Y, 93) \
SRIR_ATTRIBUTE_ELEM(Generic15Z, 94) \
SRIR_ATTRIBUTE_ELEM(Generic15W, 95) \
SRIR_ATTRIBUTE_ELEM(Generic16X, 96) \
SRIR_ATTRIBUTE_ELEM(Generic16Y, 97) \
SRIR_ATTRIBUTE_ELEM(Generic16Z, 98) \
SRIR_ATTRIBUTE_ELEM(Generic16W, 99) \
SRIR_ATTRIBUTE_ELEM(Generic17X, 100) \
SRIR_ATTRIBUTE_ELEM(Generic17Y, 101) \
SRIR_ATTRIBUTE_ELEM(Generic17Z, 102) \
SRIR_ATTRIBUTE_ELEM(Generic17W, 103) \
SRIR_ATTRIBUTE_ELEM(Generic18X, 104) \
SRIR_ATTRIBUTE_ELEM(Generic18Y, 105) \
SRIR_ATTRIBUTE_ELEM(Generic18Z, 106) \
SRIR_ATTRIBUTE_ELEM(Generic18W, 107) \
SRIR_ATTRIBUTE_ELEM(Generic19X, 108) \
SRIR_ATTRIBUTE_ELEM(Generic19Y, 109) \
SRIR_ATTRIBUTE_ELEM(Generic19Z, 110) \
SRIR_ATTRIBUTE_ELEM(Generic19W, 111) \
SRIR_ATTRIBUTE_ELEM(Generic20X, 112) \
SRIR_ATTRIBUTE_ELEM(Generic20Y, 113) \
SRIR_ATTRIBUTE_ELEM(Generic20Z, 114) \
SRIR_ATTRIBUTE_ELEM(Generic20W, 115) \
SRIR_ATTRIBUTE_ELEM(Generic21X, 116) \
SRIR_ATTRIBUTE_ELEM(Generic21Y, 117) \
SRIR_ATTRIBUTE_ELEM(Generic21Z, 118) \
SRIR_ATTRIBUTE_ELEM(Generic21W, 119) \
SRIR_ATTRIBUTE_ELEM(Generic22X, 120) \
SRIR_ATTRIBUTE_ELEM(Generic22Y, 121) \
SRIR_ATTRIBUTE_ELEM(Generic22Z, 122) \
SRIR_ATTRIBUTE_ELEM(Generic22W, 123) \
SRIR_ATTRIBUTE_ELEM(Generic23X, 124) \
SRIR_ATTRIBUTE_ELEM(Generic23Y, 125) \
SRIR_ATTRIBUTE_ELEM(Generic23Z, 126) \
SRIR_ATTRIBUTE_ELEM(Generic23W, 127) \
SRIR_ATTRIBUTE_ELEM(Generic24X, 128) \
SRIR_ATTRIBUTE_ELEM(Generic24Y, 129) \
SRIR_ATTRIBUTE_ELEM(Generic24Z, 130) \
SRIR_ATTRIBUTE_ELEM(Generic24W, 131) \
SRIR_ATTRIBUTE_ELEM(Generic25X, 132) \
SRIR_ATTRIBUTE_ELEM(Generic25Y, 133) \
SRIR_ATTRIBUTE_ELEM(Generic25Z, 134) \
SRIR_ATTRIBUTE_ELEM(Generic25W, 135) \
SRIR_ATTRIBUTE_ELEM(Generic26X, 136) \
SRIR_ATTRIBUTE_ELEM(Generic26Y, 137) \
SRIR_ATTRIBUTE_ELEM(Generic26Z, 138) \
SRIR_ATTRIBUTE_ELEM(Generic26W, 139) \
SRIR_ATTRIBUTE_ELEM(Generic27X, 140) \
SRIR_ATTRIBUTE_ELEM(Generic27Y, 141) \
SRIR_ATTRIBUTE_ELEM(Generic27Z, 142) \
SRIR_ATTRIBUTE_ELEM(Generic27W, 143) \
SRIR_ATTRIBUTE_ELEM(Generic28X, 144) \
SRIR_ATTRIBUTE_ELEM(Generic28Y, 145) \
SRIR_ATTRIBUTE_ELEM(Generic28Z, 146) \
SRIR_ATTRIBUTE_ELEM(Generic28W, 147) \
SRIR_ATTRIBUTE_ELEM(Generic29X, 148) \
SRIR_ATTRIBUTE_ELEM(Generic29Y, 149) \
SRIR_ATTRIBUTE_ELEM(Generic29Z, 150) \
SRIR_ATTRIBUTE_ELEM(Generic29W, 151) \
SRIR_ATTRIBUTE_ELEM(Generic30X, 152) \
SRIR_ATTRIBUTE_ELEM(Generic30Y, 153) \
SRIR_ATTRIBUTE_ELEM(Generic30Z, 154) \
SRIR_ATTRIBUTE_ELEM(Generic30W, 155) \
SRIR_ATTRIBUTE_ELEM(Generic31X, 156) \
SRIR_ATTRIBUTE_ELEM(Generic31Y, 157) \
SRIR_ATTRIBUTE_ELEM(Generic31Z, 158) \
SRIR_ATTRIBUTE_ELEM(Generic31W, 159) \
SRIR_ATTRIBUTE_ELEM(ColorFrontDiffuseR, 160) \
SRIR_ATTRIBUTE_ELEM(ColorFrontDiffuseG, 161) \
SRIR_ATTRIBUTE_ELEM(ColorFrontDiffuseB, 162) \
SRIR_ATTRIBUTE_ELEM(ColorFrontDiffuseA, 163) \
SRIR_ATTRIBUTE_ELEM(ColorFrontSpecularR, 164) \
SRIR_ATTRIBUTE_ELEM(ColorFrontSpecularG, 165) \
SRIR_ATTRIBUTE_ELEM(ColorFrontSpecularB, 166) \
SRIR_ATTRIBUTE_ELEM(ColorFrontSpecularA, 167) \
SRIR_ATTRIBUTE_ELEM(ColorBackDiffuseR, 168) \
SRIR_ATTRIBUTE_ELEM(ColorBackDiffuseG, 169) \
SRIR_ATTRIBUTE_ELEM(ColorBackDiffuseB, 170) \
SRIR_ATTRIBUTE_ELEM(ColorBackDiffuseA, 171) \
SRIR_ATTRIBUTE_ELEM(ColorBackSpecularR, 172) \
SRIR_ATTRIBUTE_ELEM(ColorBackSpecularG, 173) \
SRIR_ATTRIBUTE_ELEM(ColorBackSpecularB, 174) \
SRIR_ATTRIBUTE_ELEM(ColorBackSpecularA, 175) \
SRIR_ATTRIBUTE_ELEM(ClipDistance0, 176) \
SRIR_ATTRIBUTE_ELEM(ClipDistance1, 177) \
SRIR_ATTRIBUTE_ELEM(ClipDistance2, 178) \
SRIR_ATTRIBUTE_ELEM(ClipDistance3, 179) \
SRIR_ATTRIBUTE_ELEM(ClipDistance4, 180) \
SRIR_ATTRIBUTE_ELEM(ClipDistance5, 181) \
SRIR_ATTRIBUTE_ELEM(ClipDistance6, 182) \
SRIR_ATTRIBUTE_ELEM(ClipDistance7, 183) \
SRIR_ATTRIBUTE_ELEM(PointSpriteS, 184) \
SRIR_ATTRIBUTE_ELEM(PointSpriteT, 185) \
SRIR_ATTRIBUTE_ELEM(FogCoordinate, 186) \
SRIR_ATTRIBUTE_ELEM(TessellationEvaluationPointU, 188) \
SRIR_ATTRIBUTE_ELEM(TessellationEvaluationPointV, 189) \
SRIR_ATTRIBUTE_ELEM(InstanceId, 190) \
SRIR_ATTRIBUTE_ELEM(VertexId, 191) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture0S, 192) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture0T, 193) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture0R, 194) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture0Q, 195) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture1S, 196) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture1T, 197) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture1R, 198) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture1Q, 199) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture2S, 200) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture2T, 201) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture2R, 202) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture2Q, 203) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture3S, 204) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture3T, 205) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture3R, 206) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture3Q, 207) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture4S, 208) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture4T, 209) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture4R, 210) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture4Q, 211) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture5S, 212) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture5T, 213) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture5R, 214) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture5Q, 215) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture6S, 216) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture6T, 217) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture6R, 218) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture6Q, 219) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture7S, 220) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture7T, 221) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture7R, 222) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture7Q, 223) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture8S, 224) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture8T, 225) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture8R, 226) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture8Q, 227) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture9S, 228) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture9T, 229) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture9R, 230) \
SRIR_ATTRIBUTE_ELEM(FixedFncTexture9Q, 231) \
SRIR_ATTRIBUTE_ELEM(ViewportMask, 232) \
SRIR_ATTRIBUTE_ELEM(FrontFace, 255) \
/* Implementation attributes */ \
SRIR_ATTRIBUTE_ELEM(BaseInstance, 256) \
SRIR_ATTRIBUTE_ELEM(BaseVertex, 257) \
SRIR_ATTRIBUTE_ELEM(DrawID, 258)
#define SRIR_ATTRIBUTE_ELEM(n, v) n = v,
SRIR_ATTRIBUTE_LIST
#undef SRIR_ATTRIBUTE_ELEM
PrimitiveId = 24,
Layer = 25,
ViewportIndex = 26,
PointSize = 27,
PositionX = 28,
PositionY = 29,
PositionZ = 30,
PositionW = 31,
Generic0X = 32,
Generic0Y = 33,
Generic0Z = 34,
Generic0W = 35,
Generic1X = 36,
Generic1Y = 37,
Generic1Z = 38,
Generic1W = 39,
Generic2X = 40,
Generic2Y = 41,
Generic2Z = 42,
Generic2W = 43,
Generic3X = 44,
Generic3Y = 45,
Generic3Z = 46,
Generic3W = 47,
Generic4X = 48,
Generic4Y = 49,
Generic4Z = 50,
Generic4W = 51,
Generic5X = 52,
Generic5Y = 53,
Generic5Z = 54,
Generic5W = 55,
Generic6X = 56,
Generic6Y = 57,
Generic6Z = 58,
Generic6W = 59,
Generic7X = 60,
Generic7Y = 61,
Generic7Z = 62,
Generic7W = 63,
Generic8X = 64,
Generic8Y = 65,
Generic8Z = 66,
Generic8W = 67,
Generic9X = 68,
Generic9Y = 69,
Generic9Z = 70,
Generic9W = 71,
Generic10X = 72,
Generic10Y = 73,
Generic10Z = 74,
Generic10W = 75,
Generic11X = 76,
Generic11Y = 77,
Generic11Z = 78,
Generic11W = 79,
Generic12X = 80,
Generic12Y = 81,
Generic12Z = 82,
Generic12W = 83,
Generic13X = 84,
Generic13Y = 85,
Generic13Z = 86,
Generic13W = 87,
Generic14X = 88,
Generic14Y = 89,
Generic14Z = 90,
Generic14W = 91,
Generic15X = 92,
Generic15Y = 93,
Generic15Z = 94,
Generic15W = 95,
Generic16X = 96,
Generic16Y = 97,
Generic16Z = 98,
Generic16W = 99,
Generic17X = 100,
Generic17Y = 101,
Generic17Z = 102,
Generic17W = 103,
Generic18X = 104,
Generic18Y = 105,
Generic18Z = 106,
Generic18W = 107,
Generic19X = 108,
Generic19Y = 109,
Generic19Z = 110,
Generic19W = 111,
Generic20X = 112,
Generic20Y = 113,
Generic20Z = 114,
Generic20W = 115,
Generic21X = 116,
Generic21Y = 117,
Generic21Z = 118,
Generic21W = 119,
Generic22X = 120,
Generic22Y = 121,
Generic22Z = 122,
Generic22W = 123,
Generic23X = 124,
Generic23Y = 125,
Generic23Z = 126,
Generic23W = 127,
Generic24X = 128,
Generic24Y = 129,
Generic24Z = 130,
Generic24W = 131,
Generic25X = 132,
Generic25Y = 133,
Generic25Z = 134,
Generic25W = 135,
Generic26X = 136,
Generic26Y = 137,
Generic26Z = 138,
Generic26W = 139,
Generic27X = 140,
Generic27Y = 141,
Generic27Z = 142,
Generic27W = 143,
Generic28X = 144,
Generic28Y = 145,
Generic28Z = 146,
Generic28W = 147,
Generic29X = 148,
Generic29Y = 149,
Generic29Z = 150,
Generic29W = 151,
Generic30X = 152,
Generic30Y = 153,
Generic30Z = 154,
Generic30W = 155,
Generic31X = 156,
Generic31Y = 157,
Generic31Z = 158,
Generic31W = 159,
ColorFrontDiffuseR = 160,
ColorFrontDiffuseG = 161,
ColorFrontDiffuseB = 162,
ColorFrontDiffuseA = 163,
ColorFrontSpecularR = 164,
ColorFrontSpecularG = 165,
ColorFrontSpecularB = 166,
ColorFrontSpecularA = 167,
ColorBackDiffuseR = 168,
ColorBackDiffuseG = 169,
ColorBackDiffuseB = 170,
ColorBackDiffuseA = 171,
ColorBackSpecularR = 172,
ColorBackSpecularG = 173,
ColorBackSpecularB = 174,
ColorBackSpecularA = 175,
ClipDistance0 = 176,
ClipDistance1 = 177,
ClipDistance2 = 178,
ClipDistance3 = 179,
ClipDistance4 = 180,
ClipDistance5 = 181,
ClipDistance6 = 182,
ClipDistance7 = 183,
PointSpriteS = 184,
PointSpriteT = 185,
FogCoordinate = 186,
TessellationEvaluationPointU = 188,
TessellationEvaluationPointV = 189,
InstanceId = 190,
VertexId = 191,
FixedFncTexture0S = 192,
FixedFncTexture0T = 193,
FixedFncTexture0R = 194,
FixedFncTexture0Q = 195,
FixedFncTexture1S = 196,
FixedFncTexture1T = 197,
FixedFncTexture1R = 198,
FixedFncTexture1Q = 199,
FixedFncTexture2S = 200,
FixedFncTexture2T = 201,
FixedFncTexture2R = 202,
FixedFncTexture2Q = 203,
FixedFncTexture3S = 204,
FixedFncTexture3T = 205,
FixedFncTexture3R = 206,
FixedFncTexture3Q = 207,
FixedFncTexture4S = 208,
FixedFncTexture4T = 209,
FixedFncTexture4R = 210,
FixedFncTexture4Q = 211,
FixedFncTexture5S = 212,
FixedFncTexture5T = 213,
FixedFncTexture5R = 214,
FixedFncTexture5Q = 215,
FixedFncTexture6S = 216,
FixedFncTexture6T = 217,
FixedFncTexture6R = 218,
FixedFncTexture6Q = 219,
FixedFncTexture7S = 220,
FixedFncTexture7T = 221,
FixedFncTexture7R = 222,
FixedFncTexture7Q = 223,
FixedFncTexture8S = 224,
FixedFncTexture8T = 225,
FixedFncTexture8R = 226,
FixedFncTexture8Q = 227,
FixedFncTexture9S = 228,
FixedFncTexture9T = 229,
FixedFncTexture9R = 230,
FixedFncTexture9Q = 231,
ViewportMask = 232,
FrontFace = 255,
// Implementation attributes
BaseInstance = 256,
BaseVertex = 257,
DrawID = 258,
};
constexpr size_t NUM_GENERICS = 32;
constexpr size_t NUM_FIXEDFNCTEXTURE = 10;
[[nodiscard]] inline bool IsGeneric(Attribute attribute) noexcept {
return attribute >= Attribute::Generic0X && attribute <= Attribute::Generic31X;
}
[[nodiscard]] bool IsGeneric(Attribute attribute) noexcept;
[[nodiscard]] inline u32 GenericAttributeIndex(Attribute attribute) {
if (!IsGeneric(attribute))
throw InvalidArgument("Attribute is not generic {}", attribute);
return (u32(attribute) - u32(Attribute::Generic0X)) / 4u;
}
[[nodiscard]] u32 GenericAttributeIndex(Attribute attribute);
[[nodiscard]] inline u32 GenericAttributeElement(Attribute attribute) {
if (!IsGeneric(attribute))
throw InvalidArgument("Attribute is not generic {}", attribute);
return u32(attribute) % 4;
}
[[nodiscard]] u32 GenericAttributeElement(Attribute attribute);
[[nodiscard]] inline std::string NameOf(Attribute attribute) {
switch (attribute) {
#define SRIR_ATTRIBUTE_ELEM(n, v) case Attribute::n: return #n;
SRIR_ATTRIBUTE_LIST
#undef SRIR_ATTRIBUTE_ELEM
default:
return fmt::format("<reserved attribute {}>", int(attribute));
}
}
[[nodiscard]] std::string NameOf(Attribute attribute);
[[nodiscard]] constexpr IR::Attribute operator+(IR::Attribute attribute, size_t value) noexcept {
return IR::Attribute(size_t(attribute) + value);
return static_cast<IR::Attribute>(static_cast<size_t>(attribute) + value);
}
} // namespace Shader::IR

View File

@@ -0,0 +1,82 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <string>
#include <fmt/ranges.h>
#include "shader_recompiler/frontend/ir/flow_test.h"
namespace Shader::IR {
std::string NameOf(FlowTest flow_test) {
switch (flow_test) {
case FlowTest::F:
return "F";
case FlowTest::LT:
return "LT";
case FlowTest::EQ:
return "EQ";
case FlowTest::LE:
return "LE";
case FlowTest::GT:
return "GT";
case FlowTest::NE:
return "NE";
case FlowTest::GE:
return "GE";
case FlowTest::NUM:
return "NUM";
case FlowTest::NaN:
return "NAN";
case FlowTest::LTU:
return "LTU";
case FlowTest::EQU:
return "EQU";
case FlowTest::LEU:
return "LEU";
case FlowTest::GTU:
return "GTU";
case FlowTest::NEU:
return "NEU";
case FlowTest::GEU:
return "GEU";
case FlowTest::T:
return "T";
case FlowTest::OFF:
return "OFF";
case FlowTest::LO:
return "LO";
case FlowTest::SFF:
return "SFF";
case FlowTest::LS:
return "LS";
case FlowTest::HI:
return "HI";
case FlowTest::SFT:
return "SFT";
case FlowTest::HS:
return "HS";
case FlowTest::OFT:
return "OFT";
case FlowTest::CSM_TA:
return "CSM_TA";
case FlowTest::CSM_TR:
return "CSM_TR";
case FlowTest::CSM_MX:
return "CSM_MX";
case FlowTest::FCSM_TA:
return "FCSM_TA";
case FlowTest::FCSM_TR:
return "FCSM_TR";
case FlowTest::FCSM_MX:
return "FCSM_MX";
case FlowTest::RLE:
return "RLE";
case FlowTest::RGT:
return "RGT";
}
return fmt::format("<invalid flow test {}>", static_cast<int>(flow_test));
}
} // namespace Shader::IR

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -10,58 +7,45 @@
#include <fmt/ranges.h>
#include "common/common_types.h"
#include "shader_recompiler/exception.h"
namespace Shader::IR {
enum class FlowTest : u64 {
#define SRIR_FLOW_TEST_LIST \
SRIR_FLOW_TEST_ELEM(F) \
SRIR_FLOW_TEST_ELEM(LT) \
SRIR_FLOW_TEST_ELEM(EQ) \
SRIR_FLOW_TEST_ELEM(LE) \
SRIR_FLOW_TEST_ELEM(GT) \
SRIR_FLOW_TEST_ELEM(NE) \
SRIR_FLOW_TEST_ELEM(GE) \
SRIR_FLOW_TEST_ELEM(NUM) \
SRIR_FLOW_TEST_ELEM(NaN) \
SRIR_FLOW_TEST_ELEM(LTU) \
SRIR_FLOW_TEST_ELEM(EQU) \
SRIR_FLOW_TEST_ELEM(LEU) \
SRIR_FLOW_TEST_ELEM(GTU) \
SRIR_FLOW_TEST_ELEM(NEU) \
SRIR_FLOW_TEST_ELEM(GEU) \
SRIR_FLOW_TEST_ELEM(T) \
SRIR_FLOW_TEST_ELEM(OFF) \
SRIR_FLOW_TEST_ELEM(LO) \
SRIR_FLOW_TEST_ELEM(SFF) \
SRIR_FLOW_TEST_ELEM(LS) \
SRIR_FLOW_TEST_ELEM(HI) \
SRIR_FLOW_TEST_ELEM(SFT) \
SRIR_FLOW_TEST_ELEM(HS) \
SRIR_FLOW_TEST_ELEM(OFT) \
SRIR_FLOW_TEST_ELEM(CSM_TA) \
SRIR_FLOW_TEST_ELEM(CSM_TR) \
SRIR_FLOW_TEST_ELEM(CSM_MX) \
SRIR_FLOW_TEST_ELEM(FCSM_TA) \
SRIR_FLOW_TEST_ELEM(FCSM_TR) \
SRIR_FLOW_TEST_ELEM(FCSM_MX) \
SRIR_FLOW_TEST_ELEM(RLE) \
SRIR_FLOW_TEST_ELEM(RGT)
#define SRIR_FLOW_TEST_ELEM(n) n,
SRIR_FLOW_TEST_LIST
#undef SRIR_FLOW_TEST_ELEM
F,
LT,
EQ,
LE,
GT,
NE,
GE,
NUM,
NaN,
LTU,
EQU,
LEU,
GTU,
NEU,
GEU,
T,
OFF,
LO,
SFF,
LS,
HI,
SFT,
HS,
OFT,
CSM_TA,
CSM_TR,
CSM_MX,
FCSM_TA,
FCSM_TR,
FCSM_MX,
RLE,
RGT,
};
[[nodiscard]] inline std::string NameOf(FlowTest flow_test) {
switch (flow_test) {
#define SRIR_FLOW_TEST_ELEM(n) case FlowTest::n: return #n;
SRIR_FLOW_TEST_LIST
#undef SRIR_FLOW_TEST_ELEM
default:
return fmt::format("<invalid flow test {}>", int(flow_test));
}
}
[[nodiscard]] std::string NameOf(FlowTest flow_test);
} // namespace Shader::IR

View File

@@ -145,7 +145,7 @@ bool IsSizeInt32(Size size) {
}
void ImageAtomOp(TranslatorVisitor& v, IR::Reg dest_reg, IR::Reg operand_reg, IR::Reg coord_reg,
std::optional<IR::Reg> bindless_reg, AtomicOp op, Clamp clamp, Size size, Type type,
IR::Reg bindless_reg, AtomicOp op, Clamp clamp, Size size, Type type,
u64 bound_offset, bool is_bindless, bool write_result) {
if (clamp != Clamp::IGN) {
throw NotImplementedException("Clamp {}", clamp);
@@ -158,7 +158,8 @@ void ImageAtomOp(TranslatorVisitor& v, IR::Reg dest_reg, IR::Reg operand_reg, IR
const TextureType tex_type{GetType(type)};
const IR::Value coords{MakeCoords(v, coord_reg, type)};
const IR::U32 handle = is_bindless ? v.X(*bindless_reg) : v.ir.Imm32(u32(bound_offset * 4));
const IR::U32 handle{is_bindless != 0 ? v.X(bindless_reg)
: v.ir.Imm32(static_cast<u32>(bound_offset * 4))};
IR::TextureInstInfo info{};
info.type.Assign(tex_type);
info.image_format.Assign(format);
@@ -184,7 +185,7 @@ void TranslatorVisitor::SUATOM(u64 insn) {
BitField<0, 8, IR::Reg> dest_reg;
BitField<8, 8, IR::Reg> coord_reg;
BitField<20, 8, IR::Reg> operand_reg;
BitField<36, 13, u64> bound_offset; // !is_bindless
BitField<36, 13, u64> bound_offset; // !is_bindless
BitField<39, 8, IR::Reg> bindless_reg; // is_bindless
} const suatom{insn};
@@ -195,20 +196,21 @@ void TranslatorVisitor::SUATOM(u64 insn) {
void TranslatorVisitor::SURED(u64 insn) {
// TODO: confirm offsets
// SURED unlike SUATOM does NOT have a binded register
union {
u64 raw;
BitField<24, 3, AtomicOp> op; //OK - 24 (SURedOp)
BitField<33, 3, Type> type; //OK? - 33 (Dim)
BitField<20, 3, Size> size; //?
BitField<49, 2, Clamp> clamp; //OK - 49 (Clamp4)
BitField<0, 8, IR::Reg> operand_reg; //RA?
BitField<8, 8, IR::Reg> coord_reg; //RB?
BitField<36, 13, u64> bound_offset; //OK 33 (TidB)
BitField<51, 1, u64> is_bound;
BitField<21, 3, AtomicOp> op;
BitField<33, 3, Type> type;
BitField<20, 3, Size> size;
BitField<49, 2, Clamp> clamp;
BitField<0, 8, IR::Reg> operand_reg;
BitField<8, 8, IR::Reg> coord_reg;
BitField<36, 13, u64> bound_offset; // is_bound
BitField<39, 8, IR::Reg> bindless_reg; // !is_bound
} const sured{insn};
ImageAtomOp(*this, IR::Reg::RZ, sured.operand_reg, sured.coord_reg, std::nullopt,
ImageAtomOp(*this, IR::Reg::RZ, sured.operand_reg, sured.coord_reg, sured.bindless_reg,
sured.op, sured.clamp, sured.size, sured.type, sured.bound_offset,
false, false);
sured.is_bound == 0, false);
}
} // namespace Shader::Maxwell

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -296,14 +293,6 @@ std::optional<LowAddrInfo> TrackLowAddress(IR::Inst* inst) {
}
// This address is expected to either be a PackUint2x32, a IAdd64, or a CompositeConstructU32x2
IR::Inst* addr_inst{addr.InstRecursive()};
// Unwrap Identity ops introduced by lowerings (e.g., PackUint2x32 -> Identity)
while (addr_inst->GetOpcode() == IR::Opcode::Identity) {
const IR::Value id_arg{addr_inst->Arg(0)};
if (id_arg.IsImmediate()) {
return std::nullopt;
}
addr_inst = id_arg.InstRecursive();
}
s32 imm_offset{0};
if (addr_inst->GetOpcode() == IR::Opcode::IAdd64) {
// If it's an IAdd64, get the immediate offset it is applying and grab the address
@@ -319,14 +308,6 @@ std::optional<LowAddrInfo> TrackLowAddress(IR::Inst* inst) {
return std::nullopt;
}
addr_inst = iadd_addr.InstRecursive();
// Unwrap Identity again if present after folding IAdd64
while (addr_inst->GetOpcode() == IR::Opcode::Identity) {
const IR::Value id_arg{addr_inst->Arg(0)};
if (id_arg.IsImmediate()) {
return std::nullopt;
}
addr_inst = id_arg.InstRecursive();
}
}
// With IAdd64 handled, now PackUint2x32 is expected
if (addr_inst->GetOpcode() == IR::Opcode::PackUint2x32) {
@@ -336,14 +317,6 @@ std::optional<LowAddrInfo> TrackLowAddress(IR::Inst* inst) {
return std::nullopt;
}
addr_inst = vector.InstRecursive();
// Unwrap Identity that may replace PackUint2x32
while (addr_inst->GetOpcode() == IR::Opcode::Identity) {
const IR::Value id_arg{addr_inst->Arg(0)};
if (id_arg.IsImmediate()) {
return std::nullopt;
}
addr_inst = id_arg.InstRecursive();
}
}
// The vector is expected to be a CompositeConstructU32x2
if (addr_inst->GetOpcode() != IR::Opcode::CompositeConstructU32x2) {

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -28,14 +25,6 @@ struct Profile {
bool support_fp16_signed_zero_nan_preserve{};
bool support_fp32_signed_zero_nan_preserve{};
bool support_fp64_signed_zero_nan_preserve{};
// User-forced float behavior overrides (Android Eden Veil/Extensions)
// When shader_float_controls_force_enable is true, these override shader-declared behavior
bool force_fp32_denorm_flush{}; // Force FTZ for all FP32 ops
bool force_fp32_denorm_preserve{}; // Force denorm preservation for all FP32 ops
bool force_fp32_rte_rounding{}; // Force Round-To-Even for all FP32 ops
bool force_fp32_signed_zero_inf_nan{}; // Force signed zero/inf/nan preservation
bool support_explicit_workgroup_layout{};
bool support_vote{};
bool support_viewport_index_layer_non_geometry{};
@@ -49,9 +38,6 @@ struct Profile {
bool support_gl_nv_gpu_shader_5{};
bool support_gl_amd_gpu_shader_half_float{};
bool support_gl_texture_shadow_lod{};
bool support_gl_shader_atomic_float{};
bool support_gl_shader_atomic_fp16_vector{};
bool support_gl_shader_atomic_int64{};
bool support_gl_warp_intrinsics{};
bool support_gl_variable_aoffi{};
bool support_gl_sparse_textures{};
@@ -95,8 +81,6 @@ struct Profile {
bool ignore_nan_fp_comparisons{};
/// Some drivers have broken support for OpVectorExtractDynamic on subgroup mask inputs
bool has_broken_spirv_subgroup_mask_vector_extract_dynamic{};
/// Mobile GPUs lack Sampled1D capability - need to emulate 1D textures as 2D with height=1
bool needs_1d_texture_emulation{};
u32 gl_max_compute_smem_size{};

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -133,7 +130,7 @@ enum class TexturePixelFormat {
ASTC_2D_8X6_SRGB,
ASTC_2D_6X5_UNORM,
ASTC_2D_6X5_SRGB,
E5B9G9R9_FLOAT,
D32_FLOAT,
D16_UNORM,
X8_D24_UNORM,

View File

@@ -27,12 +27,3 @@ target_link_libraries(tests PRIVATE common core input_common video_core)
target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2WithMain Threads::Threads)
add_test(NAME tests COMMAND tests)
# needed for vma
if (NOT MSVC)
target_compile_options(tests PRIVATE
-Wno-conversion
-Wno-unused-variable
-Wno-unused-parameter
-Wno-missing-field-initializers)
endif()

View File

@@ -1,12 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2019 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#define VMA_IMPLEMENTATION
#include "video_core/vulkan_common/vma.h"
#include <array>
#include <cstring>
#include <type_traits>

View File

@@ -312,6 +312,7 @@ add_library(video_core STATIC
vulkan_common/vulkan_wrapper.h
vulkan_common/nsight_aftermath_tracker.cpp
vulkan_common/nsight_aftermath_tracker.h
vulkan_common/vma.cpp
vulkan_common/vma.h
vulkan_common/vulkan.h
)
@@ -368,6 +369,9 @@ else()
# xbyak
set_source_files_properties(macro/macro_jit_x64.cpp PROPERTIES COMPILE_OPTIONS "-Wno-conversion;-Wno-shadow")
# VMA
set_source_files_properties(vulkan_common/vma.cpp PROPERTIES COMPILE_OPTIONS "-Wno-conversion;-Wno-unused-variable;-Wno-unused-parameter;-Wno-missing-field-initializers")
# Get around GCC failing with intrinsics in Debug
if (CXX_GCC AND CMAKE_BUILD_TYPE MATCHES "Debug")
set_source_files_properties(host1x/vic.cpp PROPERTIES COMPILE_OPTIONS "-O2")

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -45,7 +42,7 @@ constexpr std::array VIEW_CLASS_32_BITS{
PixelFormat::A2B10G10R10_UNORM, PixelFormat::R16G16_UINT, PixelFormat::R32_UINT,
PixelFormat::R16G16_SINT, PixelFormat::R32_SINT, PixelFormat::A8B8G8R8_UNORM,
PixelFormat::R16G16_UNORM, PixelFormat::A8B8G8R8_SNORM, PixelFormat::R16G16_SNORM,
PixelFormat::A8B8G8R8_SRGB, PixelFormat::B8G8R8A8_UNORM,
PixelFormat::A8B8G8R8_SRGB, PixelFormat::E5B9G9R9_FLOAT, PixelFormat::B8G8R8A8_UNORM,
PixelFormat::B8G8R8A8_SRGB, PixelFormat::A8B8G8R8_UINT, PixelFormat::A8B8G8R8_SINT,
PixelFormat::A2B10G10R10_UINT,
};
@@ -55,7 +52,7 @@ constexpr std::array VIEW_CLASS_32_BITS_NO_BGR{
PixelFormat::A2B10G10R10_UNORM, PixelFormat::R16G16_UINT, PixelFormat::R32_UINT,
PixelFormat::R16G16_SINT, PixelFormat::R32_SINT, PixelFormat::A8B8G8R8_UNORM,
PixelFormat::R16G16_UNORM, PixelFormat::A8B8G8R8_SNORM, PixelFormat::R16G16_SNORM,
PixelFormat::A8B8G8R8_SRGB, PixelFormat::A8B8G8R8_UINT,
PixelFormat::A8B8G8R8_SRGB, PixelFormat::E5B9G9R9_FLOAT, PixelFormat::A8B8G8R8_UINT,
PixelFormat::A8B8G8R8_SINT, PixelFormat::A2B10G10R10_UINT,
};

View File

@@ -76,7 +76,6 @@ set(SHADER_FILES
vulkan_quad_indexed.comp
vulkan_turbo_mode.comp
vulkan_uint8.comp
vulkan_qcom_msaa_resolve.frag
convert_rgba8_to_bgra8.frag
convert_yuv420_to_rgb.comp
convert_rgb_to_yuv420.comp

View File

@@ -1,39 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#version 450
// VK_QCOM_render_pass_shader_resolve fragment shader
// Resolves MSAA attachment to single-sample within render pass
// Requires VK_SUBPASS_DESCRIPTION_SHADER_RESOLVE_BIT_QCOM in subpass flags
// Use combined image sampler for MSAA texture instead of input attachment
// This allows us to sample MSAA textures from previous rendering
layout(set = 0, binding = 0) uniform sampler2DMS msaa_texture;
layout(location = 0) out vec4 color_output;
layout(push_constant) uniform PushConstants {
vec2 tex_scale;
vec2 tex_offset;
} push_constants;
// Custom MSAA resolve using box filter (simple average)
// Assumes 4x MSAA (can be extended with push constant for dynamic sample count)
void main() {
ivec2 coord = ivec2(gl_FragCoord.xy);
ivec2 tex_size = textureSize(msaa_texture);
// Clamp coordinates to texture bounds
coord = clamp(coord, ivec2(0), tex_size - ivec2(1));
vec4 accumulated_color = vec4(0.0);
int sample_count = 4; // Adreno typically uses 4x MSAA max
// Box filter: simple average of all MSAA samples
for (int i = 0; i < sample_count; i++) {
accumulated_color += texelFetch(msaa_texture, coord, i);
}
color_output = accumulated_color / float(sample_count);
}

View File

@@ -225,9 +225,6 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) {
has_amd_shader_half_float = GLAD_GL_AMD_gpu_shader_half_float;
has_sparse_texture_2 = GLAD_GL_ARB_sparse_texture2;
has_draw_texture = GLAD_GL_NV_draw_texture;
has_shader_atomic_float = GLAD_GL_NV_shader_atomic_float;
has_shader_atomic_fp16_vector = GLAD_GL_NV_shader_atomic_fp16_vector;
has_shader_atomic_int64 = GLAD_GL_NV_shader_atomic_int64;
warp_size_potentially_larger_than_guest = !is_nvidia && !is_intel;
need_fastmath_off = is_nvidia;
can_report_memory = GLAD_GL_NVX_gpu_memory_info;

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -155,18 +152,6 @@ public:
return has_draw_texture;
}
bool HasShaderAtomicFloat() const {
return has_shader_atomic_float;
}
bool HasShaderAtomicFp16Vector() const {
return has_shader_atomic_fp16_vector;
}
bool HasShaderAtomicInt64() const {
return has_shader_atomic_int64;
}
bool IsWarpSizePotentiallyLargerThanGuest() const {
return warp_size_potentially_larger_than_guest;
}
@@ -250,9 +235,6 @@ private:
bool has_amd_shader_half_float{};
bool has_sparse_texture_2{};
bool has_draw_texture{};
bool has_shader_atomic_float{};
bool has_shader_atomic_fp16_vector{};
bool has_shader_atomic_int64{};
bool warp_size_potentially_larger_than_guest{};
bool need_fastmath_off{};
bool has_cbuf_ftou_bug{};

View File

@@ -1142,14 +1142,6 @@ void RasterizerOpenGL::SyncBlendState() {
glDisable(GL_BLEND);
return;
}
// Temporary workaround for games that use iterated blending
if (regs.iterated_blend.enable && Settings::values.use_squashed_iterated_blend) {
glEnable(GL_BLEND);
glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE_MINUS_SRC_COLOR, GL_ZERO);
glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
return;
}
glEnable(GL_BLEND);
glBlendFuncSeparate(MaxwellToGL::BlendFunc(regs.blend.color_source),
MaxwellToGL::BlendFunc(regs.blend.color_dest),

View File

@@ -215,9 +215,6 @@ ShaderCache::ShaderCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.support_gl_nv_gpu_shader_5 = device.HasNvGpuShader5(),
.support_gl_amd_gpu_shader_half_float = device.HasAmdShaderHalfFloat(),
.support_gl_texture_shadow_lod = device.HasTextureShadowLod(),
.support_gl_shader_atomic_float = device.HasShaderAtomicFloat(),
.support_gl_shader_atomic_fp16_vector = device.HasShaderAtomicFp16Vector(),
.support_gl_shader_atomic_int64 = device.HasShaderAtomicInt64(),
.support_gl_warp_intrinsics = false,
.support_gl_variable_aoffi = device.HasVariableAoffi(),
.support_gl_sparse_textures = device.HasSparseTexture2(),

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -100,10 +97,6 @@ public:
return true;
}
bool CanDownloadMSAA() const noexcept {
return true;
}
void CopyImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
void CopyImageMSAA(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -116,6 +113,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR}, // ASTC_2D_8X6_SRGB
{GL_COMPRESSED_RGBA_ASTC_6x5_KHR}, // ASTC_2D_6X5_UNORM
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR}, // ASTC_2D_6X5_SRGB
{GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV}, // E5B9G9R9_FLOAT
{GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}, // D32_FLOAT
{GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16_UNORM
{GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT_24_8}, // X8_D24_UNORM

View File

@@ -40,7 +40,6 @@
#include "video_core/host_shaders/convert_rgba16f_to_rgba8_frag_spv.h"
#include "video_core/host_shaders/dither_temporal_frag_spv.h"
#include "video_core/host_shaders/dynamic_resolution_scale_comp_spv.h"
#include "video_core/host_shaders/vulkan_qcom_msaa_resolve_frag_spv.h"
namespace Vulkan {
@@ -546,7 +545,6 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
convert_rgba16f_to_rgba8_frag(BuildShader(device, CONVERT_RGBA16F_TO_RGBA8_FRAG_SPV)),
dither_temporal_frag(BuildShader(device, DITHER_TEMPORAL_FRAG_SPV)),
dynamic_resolution_scale_comp(BuildShader(device, DYNAMIC_RESOLUTION_SCALE_COMP_SPV)),
qcom_msaa_resolve_frag(BuildShader(device, VULKAN_QCOM_MSAA_RESOLVE_FRAG_SPV)),
linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)),
nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {}
@@ -1242,30 +1240,4 @@ void BlitImageHelper::ApplyDynamicResolutionScale(const Framebuffer* dst_framebu
Convert(*dynamic_resolution_scale_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ResolveMSAAQcom(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view) {
// VK_QCOM_render_pass_shader_resolve implementation
// This must be used within a render pass with VK_SUBPASS_DESCRIPTION_SHADER_RESOLVE_BIT_QCOM
ConvertPipeline(qcom_msaa_resolve_pipeline,
dst_framebuffer->RenderPass(),
false);
RecordShaderReadBarrier(scheduler, src_image_view);
scheduler.RequestRenderpass(dst_framebuffer);
const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
const VkPipelineLayout layout = *one_texture_pipeline_layout;
const VkPipeline pipeline = *qcom_msaa_resolve_pipeline;
scheduler.Record([this, src_view, layout, pipeline](vk::CommandBuffer cmdbuf) {
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
UpdateOneTextureDescriptorSet(device, descriptor_set, *nearest_sampler, src_view);
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, nullptr);
cmdbuf.Draw(3, 1, 0, 0);
});
scheduler.InvalidateState();
}
} // namespace Vulkan

View File

@@ -95,8 +95,6 @@ public:
void ConvertRGBA16FtoRGBA8(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
void ApplyDitherTemporal(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
void ApplyDynamicResolutionScale(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
void ResolveMSAAQcom(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
private:
void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
@@ -161,7 +159,6 @@ private:
vk::ShaderModule convert_rgba16f_to_rgba8_frag;
vk::ShaderModule dither_temporal_frag;
vk::ShaderModule dynamic_resolution_scale_comp;
vk::ShaderModule qcom_msaa_resolve_frag;
vk::Sampler linear_sampler;
vk::Sampler nearest_sampler;
@@ -191,7 +188,6 @@ private:
vk::Pipeline convert_rgba16f_to_rgba8_pipeline;
vk::Pipeline dither_temporal_pipeline;
vk::Pipeline dynamic_resolution_scale_pipeline;
vk::Pipeline qcom_msaa_resolve_pipeline;
};
} // namespace Vulkan

View File

@@ -11,7 +11,6 @@
#include <ranges>
#include "common/cityhash.h"
#include "common/common_types.h"
#include "common/settings.h"
#include "video_core/engines/draw_manager.h"
#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
#include "video_core/renderer_vulkan/vk_state_tracker.h"
@@ -58,42 +57,12 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
const auto topology_ = maxwell3d.draw_manager->GetDrawState().topology;
raw1 = 0;
raw1_eds3_extended = 0;
pad_align_u64 = 0;
// EDS1
extended_dynamic_state.Assign(features.has_extended_dynamic_state);
// EDS2
extended_dynamic_state_2.Assign(features.has_extended_dynamic_state_2);
extended_dynamic_state_2_logic_op.Assign(features.has_extended_dynamic_state_2_logic_op);
extended_dynamic_state_2_patch_control_points.Assign(features.has_extended_dynamic_state_2_patch_control_points);
// EDS3 - Blending/Enables
extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend);
extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables);
// EDS3 - Granular features
extended_dynamic_state_3_depth_clamp.Assign(features.has_extended_dynamic_state_3_depth_clamp);
extended_dynamic_state_3_logic_op_enable.Assign(features.has_extended_dynamic_state_3_logic_op_enable);
extended_dynamic_state_3_tessellation_domain_origin.Assign(features.has_extended_dynamic_state_3_tessellation_domain_origin);
extended_dynamic_state_3_polygon_mode.Assign(features.has_extended_dynamic_state_3_polygon_mode);
extended_dynamic_state_3_rasterization_samples.Assign(features.has_extended_dynamic_state_3_rasterization_samples);
extended_dynamic_state_3_sample_mask.Assign(features.has_extended_dynamic_state_3_sample_mask);
extended_dynamic_state_3_alpha_to_coverage_enable.Assign(features.has_extended_dynamic_state_3_alpha_to_coverage_enable);
extended_dynamic_state_3_alpha_to_one_enable.Assign(features.has_extended_dynamic_state_3_alpha_to_one_enable);
extended_dynamic_state_3_depth_clip_enable.Assign(features.has_extended_dynamic_state_3_depth_clip_enable);
extended_dynamic_state_3_depth_clip_negative_one_to_one.Assign(features.has_extended_dynamic_state_3_depth_clip_negative_one_to_one);
extended_dynamic_state_3_line_rasterization_mode.Assign(features.has_extended_dynamic_state_3_line_rasterization_mode);
extended_dynamic_state_3_line_stipple_enable.Assign(features.has_extended_dynamic_state_3_line_stipple_enable);
extended_dynamic_state_3_provoking_vertex_mode.Assign(features.has_extended_dynamic_state_3_provoking_vertex_mode);
extended_dynamic_state_3_conservative_rasterization_mode.Assign(features.has_extended_dynamic_state_3_conservative_rasterization_mode);
extended_dynamic_state_3_sample_locations_enable.Assign(features.has_extended_dynamic_state_3_sample_locations_enable);
extended_dynamic_state_3_rasterization_stream.Assign(features.has_extended_dynamic_state_3_rasterization_stream);
// Vertex Input
dynamic_vertex_input.Assign(features.has_dynamic_vertex_input);
extended_dynamic_state.Assign(features.has_extended_dynamic_state ? 1 : 0);
extended_dynamic_state_2.Assign(features.has_extended_dynamic_state_2 ? 1 : 0);
extended_dynamic_state_2_extra.Assign(features.has_extended_dynamic_state_2_extra ? 1 : 0);
extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend ? 1 : 0);
extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables ? 1 : 0);
dynamic_vertex_input.Assign(features.has_dynamic_vertex_input ? 1 : 0);
xfb_enabled.Assign(regs.transform_feedback_enabled != 0);
ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
polygon_mode.Assign(PackPolygonMode(VideoCore::EffectivePolygonMode(regs)));
@@ -188,7 +157,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
return static_cast<u16>(array.stride.Value());
});
}
if (!extended_dynamic_state_2_logic_op) {
if (!extended_dynamic_state_2_extra) {
dynamic_state.Refresh2(regs, topology_, extended_dynamic_state_2);
}
if (!extended_dynamic_state_3_blend) {
@@ -232,19 +201,6 @@ void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t
};
if (!regs.blend_per_target_enabled) {
// Temporary workaround for games that use iterated blending
// even when dynamic blending is off so overrides work with EDS = 0 as well
if (regs.iterated_blend.enable && Settings::values.use_squashed_iterated_blend) {
equation_rgb.Assign(PackBlendEquation(Maxwell::Blend::Equation::Add_GL));
equation_a.Assign(PackBlendEquation(Maxwell::Blend::Equation::Add_GL));
factor_source_rgb.Assign(PackBlendFactor(Maxwell::Blend::Factor::One_GL));
factor_dest_rgb.Assign(PackBlendFactor(Maxwell::Blend::Factor::One_GL));
factor_source_a.Assign(
PackBlendFactor(Maxwell::Blend::Factor::OneMinusSourceColor_GL));
factor_dest_a.Assign(PackBlendFactor(Maxwell::Blend::Factor::Zero_GL));
enable.Assign(1);
return;
}
setup_blend(regs.blend);
return;
}

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -21,45 +18,13 @@ namespace Vulkan {
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
struct DynamicFeatures {
union {
u32 raw0;
// VK_EXT_extended_dynamic_state (EDS1) - Bit 0
BitField<0, 1, u32> has_extended_dynamic_state;
// VK_EXT_extended_dynamic_state2 (EDS2) - Bits 1-3
BitField<1, 1, u32> has_extended_dynamic_state_2; // Core EDS2
BitField<2, 1, u32> has_extended_dynamic_state_2_logic_op; // LogicOp
BitField<3, 1, u32> has_extended_dynamic_state_2_patch_control_points; // Tessellation
// VK_EXT_extended_dynamic_state3 (EDS3) - Bits 4-5
BitField<4, 1, u32> has_extended_dynamic_state_3_blend; // Blending composite
BitField<5, 1, u32> has_extended_dynamic_state_3_enables; // Enables composite
// VK_EXT_vertex_input_dynamic_state - Bit 6
BitField<6, 1, u32> has_dynamic_vertex_input;
// EDS3 Granular Features - Bits 7-15
BitField<7, 1, u32> has_extended_dynamic_state_3_depth_clamp;
BitField<8, 1, u32> has_extended_dynamic_state_3_logic_op_enable;
BitField<9, 1, u32> has_extended_dynamic_state_3_tessellation_domain_origin;
BitField<10, 1, u32> has_extended_dynamic_state_3_polygon_mode;
BitField<11, 1, u32> has_extended_dynamic_state_3_rasterization_samples;
BitField<12, 1, u32> has_extended_dynamic_state_3_sample_mask;
BitField<13, 1, u32> has_extended_dynamic_state_3_alpha_to_coverage_enable;
BitField<14, 1, u32> has_extended_dynamic_state_3_alpha_to_one_enable;
BitField<15, 1, u32> has_extended_dynamic_state_3_depth_clip_enable;
// EDS3 Additional Features - Bits 16-22
BitField<16, 1, u32> has_extended_dynamic_state_3_depth_clip_negative_one_to_one;
BitField<17, 1, u32> has_extended_dynamic_state_3_line_rasterization_mode;
BitField<18, 1, u32> has_extended_dynamic_state_3_line_stipple_enable;
BitField<19, 1, u32> has_extended_dynamic_state_3_provoking_vertex_mode;
BitField<20, 1, u32> has_extended_dynamic_state_3_conservative_rasterization_mode;
BitField<21, 1, u32> has_extended_dynamic_state_3_sample_locations_enable;
BitField<22, 1, u32> has_extended_dynamic_state_3_rasterization_stream;
};
bool has_extended_dynamic_state;
bool has_extended_dynamic_state_2;
bool has_extended_dynamic_state_2_extra;
bool has_extended_dynamic_state_3_blend;
bool has_extended_dynamic_state_3_enables;
bool has_dynamic_vertex_input;
};
static_assert(std::has_unique_object_representations_v<DynamicFeatures>);
struct FixedPipelineState {
static u32 PackComparisonOp(Maxwell::ComparisonOp op) noexcept;
@@ -219,56 +184,23 @@ struct FixedPipelineState {
union {
u32 raw1;
// EDS1 - Bit 0
BitField<0, 1, u32> extended_dynamic_state;
// EDS2 - Bits 1-3
BitField<1, 1, u32> extended_dynamic_state_2;
BitField<2, 1, u32> extended_dynamic_state_2_logic_op;
BitField<3, 1, u32> extended_dynamic_state_2_patch_control_points;
// EDS3 Blending/Enables - Bits 4-5
BitField<4, 1, u32> extended_dynamic_state_3_blend;
BitField<5, 1, u32> extended_dynamic_state_3_enables;
// Vertex Input - Bit 6
BitField<6, 1, u32> dynamic_vertex_input;
// Other state - Bits 7-19
BitField<7, 1, u32> xfb_enabled;
BitField<8, 1, u32> ndc_minus_one_to_one;
BitField<9, 2, u32> polygon_mode;
BitField<11, 2, u32> tessellation_primitive;
BitField<13, 2, u32> tessellation_spacing;
BitField<15, 1, u32> tessellation_clockwise;
BitField<16, 5, u32> patch_control_points_minus_one;
BitField<2, 1, u32> extended_dynamic_state_2_extra;
BitField<3, 1, u32> extended_dynamic_state_3_blend;
BitField<4, 1, u32> extended_dynamic_state_3_enables;
BitField<5, 1, u32> dynamic_vertex_input;
BitField<6, 1, u32> xfb_enabled;
BitField<7, 1, u32> ndc_minus_one_to_one;
BitField<8, 2, u32> polygon_mode;
BitField<10, 2, u32> tessellation_primitive;
BitField<12, 2, u32> tessellation_spacing;
BitField<14, 1, u32> tessellation_clockwise;
BitField<15, 5, u32> patch_control_points_minus_one;
// Topology and MSAA - Bits 24-31
BitField<24, 4, Maxwell::PrimitiveTopology> topology;
BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode;
};
union {
u32 raw1_eds3_extended;
// EDS3 Additional Features - Bits 0-15
BitField<0, 1, u32> extended_dynamic_state_3_depth_clamp;
BitField<1, 1, u32> extended_dynamic_state_3_logic_op_enable;
BitField<2, 1, u32> extended_dynamic_state_3_tessellation_domain_origin;
BitField<3, 1, u32> extended_dynamic_state_3_polygon_mode;
BitField<4, 1, u32> extended_dynamic_state_3_rasterization_samples;
BitField<5, 1, u32> extended_dynamic_state_3_sample_mask;
BitField<6, 1, u32> extended_dynamic_state_3_alpha_to_coverage_enable;
BitField<7, 1, u32> extended_dynamic_state_3_alpha_to_one_enable;
BitField<8, 1, u32> extended_dynamic_state_3_depth_clip_enable;
BitField<9, 1, u32> extended_dynamic_state_3_depth_clip_negative_one_to_one;
BitField<10, 1, u32> extended_dynamic_state_3_line_rasterization_mode;
BitField<11, 1, u32> extended_dynamic_state_3_line_stipple_enable;
BitField<12, 1, u32> extended_dynamic_state_3_provoking_vertex_mode;
BitField<13, 1, u32> extended_dynamic_state_3_conservative_rasterization_mode;
BitField<14, 1, u32> extended_dynamic_state_3_sample_locations_enable;
BitField<15, 1, u32> extended_dynamic_state_3_rasterization_stream;
};
union {
u32 raw2;
BitField<1, 3, u32> alpha_test_func;
@@ -283,15 +215,12 @@ struct FixedPipelineState {
BitField<16, 1, u32> alpha_to_one_enabled;
BitField<17, 3, Tegra::Engines::Maxwell3D::EngineHint> app_stage;
};
std::array<u8, Maxwell::NumRenderTargets> color_formats;
u32 alpha_test_ref;
u32 point_size;
std::array<u8, Maxwell::NumRenderTargets> color_formats;
std::array<u16, Maxwell::NumViewports> viewport_swizzles;
u32 pad_align_u64;
union {
u64 attribute_types; // Used with VK_EXT_vertex_input_dynamic_state
u64 enabled_divisors;

View File

@@ -214,6 +214,7 @@ struct FormatTuple {
{VK_FORMAT_ASTC_8x6_SRGB_BLOCK}, // ASTC_2D_8X6_SRGB
{VK_FORMAT_ASTC_6x5_UNORM_BLOCK}, // ASTC_2D_6X5_UNORM
{VK_FORMAT_ASTC_6x5_SRGB_BLOCK}, // ASTC_2D_6X5_SRGB
{VK_FORMAT_E5B9G9R9_UFLOAT_PACK32}, // E5B9G9R9_FLOAT
// Depth formats
{VK_FORMAT_D32_SFLOAT, Attachable}, // D32_FLOAT

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -27,13 +24,8 @@ public:
DescriptorLayoutBuilder(const Device& device_) : device{&device_} {}
bool CanUsePushDescriptor() const noexcept {
if (!device->IsKhrPushDescriptorSupported()) {
return false;
}
if (num_descriptors > device->MaxPushDescriptors()) {
return false;
}
return true;
return device->IsKhrPushDescriptorSupported() &&
num_descriptors <= device->MaxPushDescriptors();
}
// TODO(crueter): utilize layout binding flags

View File

@@ -5,6 +5,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include <ranges>
#include "video_core/renderer_vulkan/present/util.h"
namespace Vulkan {

View File

@@ -837,38 +837,13 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.pAttachments = cb_attachments.data(),
.blendConstants = {}
};
// Base Vulkan Dynamic States - Always active (independent of EDS)
// Granular fallback: Each state added only if device supports it (protection against broken drivers)
static_vector<VkDynamicState, 34> dynamic_states;
if (device.SupportsDynamicViewport()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_VIEWPORT);
}
if (device.SupportsDynamicScissor()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_SCISSOR);
}
if (device.SupportsDynamicLineWidth()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_LINE_WIDTH);
}
if (device.SupportsDynamicDepthBias()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS);
}
if (device.SupportsDynamicBlendConstants()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_BLEND_CONSTANTS);
}
if (device.SupportsDynamicDepthBounds()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS);
}
if (device.SupportsDynamicStencilCompareMask()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK);
}
if (device.SupportsDynamicStencilWriteMask()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK);
}
if (device.SupportsDynamicStencilReference()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE);
}
// EDS1 - Extended Dynamic State (12 states)
static_vector<VkDynamicState, 34> dynamic_states{
VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS,
VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE,
VK_DYNAMIC_STATE_LINE_WIDTH,
};
if (key.state.extended_dynamic_state) {
static constexpr std::array extended{
VK_DYNAMIC_STATE_CULL_MODE_EXT,
@@ -881,59 +856,49 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT,
VK_DYNAMIC_STATE_STENCIL_OP_EXT,
};
if (key.state.dynamic_vertex_input) {
dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
}
dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end());
}
if (key.state.extended_dynamic_state_2) {
static constexpr std::array extended2{
VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT,
VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT,
VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended2.begin(), extended2.end());
}
if (key.state.extended_dynamic_state_2_extra) {
dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT);
}
if (key.state.extended_dynamic_state_3_blend) {
static constexpr std::array extended3{
VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT,
VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT,
VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT,
// Vertex Input Dynamic State (independent toggle)
if (key.state.dynamic_vertex_input) {
dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
}
// VK_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
}
if (key.state.extended_dynamic_state_3_enables) {
static constexpr std::array extended3{
VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT,
VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT,
// EDS2 - Extended Dynamic State 2 Core (3 states)
if (key.state.extended_dynamic_state_2) {
static constexpr std::array extended2{
VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT,
VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT,
VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended2.begin(), extended2.end());
}
// additional state3 extensions
VK_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT,
// EDS2 - Logic Op (granular feature)
if (key.state.extended_dynamic_state_2_logic_op) {
dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT);
}
VK_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT,
// EDS3 - Blending (composite: ColorBlendEnable + Equation + WriteMask)
if (key.state.extended_dynamic_state_3_blend) {
static constexpr std::array extended3{
VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT,
VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT,
VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT,
// VK_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
}
// EDS3 - Enables (granular: DepthClamp + LogicOpEnable + ...)
if (key.state.extended_dynamic_state_3_enables) {
static constexpr std::array extended3{
VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT,
VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT,
// additional state3 extensions
VK_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT,
VK_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT,
VK_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT,
VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT,
VK_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT,
VK_DYNAMIC_STATE_DEPTH_CLIP_ENABLE_EXT,
VK_DYNAMIC_STATE_PROVOKING_VERTEX_MODE_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
VK_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT,
VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT,
VK_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT,
VK_DYNAMIC_STATE_DEPTH_CLIP_ENABLE_EXT,
VK_DYNAMIC_STATE_PROVOKING_VERTEX_MODE_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
}
}
const VkPipelineDynamicStateCreateInfo dynamic_state_ci{

View File

@@ -80,7 +80,7 @@ public:
PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache,
const GraphicsPipelineCacheKey& key, std::array<vk::ShaderModule, NUM_STAGES> stages,
const std::array<const Shader::Info*, NUM_STAGES>& infos);
// True if this pipeline was created with VK_DYNAMIC_STATE_VERTEX_INPUT_EXT
bool HasDynamicVertexInput() const noexcept { return key.state.dynamic_vertex_input; }
GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete;
GraphicsPipeline(GraphicsPipeline&&) noexcept = delete;

View File

@@ -341,17 +341,6 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
float_control.shaderSignedZeroInfNanPreserveFloat32 != VK_FALSE,
.support_fp64_signed_zero_nan_preserve =
float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE,
// Switch/Maxwell native float behavior - ONLY for Turnip Mesa (Stock Qualcomm broken)
// Stock Adreno drivers have broken float controls disabled in vulkan_device.cpp
.force_fp32_denorm_flush = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY &&
device.IsKhrShaderFloatControlsSupported(), // false on Stock, true on Turnip
.force_fp32_denorm_preserve = false, // FTZ dominates
.force_fp32_rte_rounding = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY &&
device.IsKhrShaderFloatControlsSupported(), // false on Stock, true on Turnip
.force_fp32_signed_zero_inf_nan = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY &&
device.IsKhrShaderFloatControlsSupported(), // false on Stock, true on Turnip
.support_explicit_workgroup_layout = device.IsKhrWorkgroupMemoryExplicitLayoutSupported(),
.support_vote = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_VOTE_BIT),
.support_viewport_index_layer_non_geometry =
@@ -382,17 +371,10 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.has_broken_spirv_position_input = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
.has_broken_unsigned_image_offsets = false,
.has_broken_signed_operations = false,
.has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY ||
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
.has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY,
.ignore_nan_fp_comparisons = false,
.has_broken_spirv_subgroup_mask_vector_extract_dynamic =
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
.needs_1d_texture_emulation =
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_MESA_TURNIP ||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_BROADCOM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_IMAGINATION_PROPRIETARY,
.has_broken_robust =
device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal,
.min_ssbo_alignment = device.GetStorageBufferAlignment(),
@@ -426,39 +408,19 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
LOG_INFO(Render_Vulkan, "DynamicState value is set to {}", (u32) dynamic_state);
dynamic_features.raw0 = 0;
// EDS1 - All-or-nothing (enabled if driver supports AND setting > 0)
dynamic_features.has_extended_dynamic_state.Assign(device.IsExtExtendedDynamicStateSupported() && dynamic_state > 0);
// EDS2 - Core features (enabled if driver supports AND setting > 1)
dynamic_features.has_extended_dynamic_state_2.Assign(device.IsExtExtendedDynamicState2Supported() && dynamic_state > 1);
dynamic_features.has_extended_dynamic_state_2_logic_op.Assign(device.IsExtExtendedDynamicState2LogicOpSupported() && dynamic_state > 1);
dynamic_features.has_extended_dynamic_state_2_patch_control_points.Assign(device.IsExtExtendedDynamicState2PatchControlPointsSupported() && dynamic_state > 1);
// EDS3 - Granular features (enabled if driver supports AND setting > 2)
dynamic_features.has_extended_dynamic_state_3_blend.Assign(device.IsExtExtendedDynamicState3BlendingSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_enables.Assign(device.IsExtExtendedDynamicState3EnablesSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_depth_clamp.Assign(device.IsExtExtendedDynamicState3DepthClampEnableSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_logic_op_enable.Assign(device.IsExtExtendedDynamicState3LogicOpEnableSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_tessellation_domain_origin.Assign(device.IsExtExtendedDynamicState3TessellationDomainOriginSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_polygon_mode.Assign(device.IsExtExtendedDynamicState3PolygonModeSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_rasterization_samples.Assign(device.IsExtExtendedDynamicState3RasterizationSamplesSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_sample_mask.Assign(device.IsExtExtendedDynamicState3SampleMaskSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_alpha_to_coverage_enable.Assign(device.IsExtExtendedDynamicState3AlphaToCoverageEnableSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_alpha_to_one_enable.Assign(device.IsExtExtendedDynamicState3AlphaToOneEnableSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_depth_clip_enable.Assign(device.IsExtExtendedDynamicState3DepthClipEnableSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_depth_clip_negative_one_to_one.Assign(device.IsExtExtendedDynamicState3DepthClipNegativeOneToOneSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_line_rasterization_mode.Assign(device.IsExtExtendedDynamicState3LineRasterizationModeSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_line_stipple_enable.Assign(device.IsExtExtendedDynamicState3LineStippleEnableSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_provoking_vertex_mode.Assign(device.IsExtExtendedDynamicState3ProvokingVertexModeSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_conservative_rasterization_mode.Assign(device.IsExtExtendedDynamicState3ConservativeRasterizationModeSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_sample_locations_enable.Assign(device.IsExtExtendedDynamicState3SampleLocationsEnableSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_rasterization_stream.Assign(device.IsExtExtendedDynamicState3RasterizationStreamSupported() && dynamic_state > 2);
// Vertex input dynamic state (independent toggle)
dynamic_features.has_dynamic_vertex_input.Assign(device.IsExtVertexInputDynamicStateSupported() &&
Settings::values.vertex_input_dynamic_state.GetValue());
dynamic_features = DynamicFeatures{
.has_extended_dynamic_state = device.IsExtExtendedDynamicStateSupported() && dynamic_state > 0,
.has_extended_dynamic_state_2 = device.IsExtExtendedDynamicState2Supported() && dynamic_state > 1,
.has_extended_dynamic_state_2_extra = device.IsExtExtendedDynamicState2ExtrasSupported() && dynamic_state > 1,
.has_extended_dynamic_state_3_blend = device.IsExtExtendedDynamicState3BlendingSupported() && dynamic_state > 2,
.has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported() && dynamic_state > 2,
.has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported(),
};
LOG_INFO(Render_Vulkan, "DynamicState1: {}", dynamic_features.has_extended_dynamic_state);
LOG_INFO(Render_Vulkan, "DynamicState2: {}", dynamic_features.has_extended_dynamic_state_2);
LOG_INFO(Render_Vulkan, "DynamicState3: {}", dynamic_features.has_extended_dynamic_state_3_enables);
LOG_INFO(Render_Vulkan, "DynamicVertexInput: {}", dynamic_features.has_dynamic_vertex_input);
}
PipelineCache::~PipelineCache() {
@@ -559,29 +521,16 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
GraphicsPipelineCacheKey key;
file.read(reinterpret_cast<char*>(&key), sizeof(key));
// Validate dynamic features compatibility - granular per-feature check
if ((key.state.extended_dynamic_state != 0) != dynamic_features.has_extended_dynamic_state ||
(key.state.extended_dynamic_state_2 != 0) != dynamic_features.has_extended_dynamic_state_2 ||
(key.state.extended_dynamic_state_2_logic_op != 0) != dynamic_features.has_extended_dynamic_state_2_logic_op ||
(key.state.extended_dynamic_state_2_patch_control_points != 0) != dynamic_features.has_extended_dynamic_state_2_patch_control_points ||
(key.state.extended_dynamic_state_3_blend != 0) != dynamic_features.has_extended_dynamic_state_3_blend ||
(key.state.extended_dynamic_state_3_enables != 0) != dynamic_features.has_extended_dynamic_state_3_enables ||
(key.state.extended_dynamic_state_3_depth_clamp != 0) != dynamic_features.has_extended_dynamic_state_3_depth_clamp ||
(key.state.extended_dynamic_state_3_logic_op_enable != 0) != dynamic_features.has_extended_dynamic_state_3_logic_op_enable ||
(key.state.extended_dynamic_state_3_tessellation_domain_origin != 0) != dynamic_features.has_extended_dynamic_state_3_tessellation_domain_origin ||
(key.state.extended_dynamic_state_3_polygon_mode != 0) != dynamic_features.has_extended_dynamic_state_3_polygon_mode ||
(key.state.extended_dynamic_state_3_rasterization_samples != 0) != dynamic_features.has_extended_dynamic_state_3_rasterization_samples ||
(key.state.extended_dynamic_state_3_sample_mask != 0) != dynamic_features.has_extended_dynamic_state_3_sample_mask ||
(key.state.extended_dynamic_state_3_alpha_to_coverage_enable != 0) != dynamic_features.has_extended_dynamic_state_3_alpha_to_coverage_enable ||
(key.state.extended_dynamic_state_3_alpha_to_one_enable != 0) != dynamic_features.has_extended_dynamic_state_3_alpha_to_one_enable ||
(key.state.extended_dynamic_state_3_depth_clip_enable != 0) != dynamic_features.has_extended_dynamic_state_3_depth_clip_enable ||
(key.state.extended_dynamic_state_3_depth_clip_negative_one_to_one != 0) != dynamic_features.has_extended_dynamic_state_3_depth_clip_negative_one_to_one ||
(key.state.extended_dynamic_state_3_line_rasterization_mode != 0) != dynamic_features.has_extended_dynamic_state_3_line_rasterization_mode ||
(key.state.extended_dynamic_state_3_line_stipple_enable != 0) != dynamic_features.has_extended_dynamic_state_3_line_stipple_enable ||
(key.state.extended_dynamic_state_3_provoking_vertex_mode != 0) != dynamic_features.has_extended_dynamic_state_3_provoking_vertex_mode ||
(key.state.extended_dynamic_state_3_conservative_rasterization_mode != 0) != dynamic_features.has_extended_dynamic_state_3_conservative_rasterization_mode ||
(key.state.extended_dynamic_state_3_sample_locations_enable != 0) != dynamic_features.has_extended_dynamic_state_3_sample_locations_enable ||
(key.state.extended_dynamic_state_3_rasterization_stream != 0) != dynamic_features.has_extended_dynamic_state_3_rasterization_stream ||
if ((key.state.extended_dynamic_state != 0) !=
dynamic_features.has_extended_dynamic_state ||
(key.state.extended_dynamic_state_2 != 0) !=
dynamic_features.has_extended_dynamic_state_2 ||
(key.state.extended_dynamic_state_2_extra != 0) !=
dynamic_features.has_extended_dynamic_state_2_extra ||
(key.state.extended_dynamic_state_3_blend != 0) !=
dynamic_features.has_extended_dynamic_state_3_blend ||
(key.state.extended_dynamic_state_3_enables != 0) !=
dynamic_features.has_extended_dynamic_state_3_enables ||
(key.state.dynamic_vertex_input != 0) != dynamic_features.has_dynamic_vertex_input) {
return;
}
@@ -733,17 +682,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
const auto runtime_info{MakeRuntimeInfo(programs, key, program, previous_stage)};
ConvertLegacyToGeneric(program, runtime_info);
// Adreno don't support subgroup operations in vertex stages
// Disable subgroup features for vertex shaders if not supported by the device
Shader::Profile stage_profile = profile;
if (program.stage == Shader::Stage::VertexA || program.stage == Shader::Stage::VertexB) {
if (!device.IsSubgroupSupportedForStage(VK_SHADER_STAGE_VERTEX_BIT)) {
stage_profile.support_vote = false;
}
}
const std::vector<u32> code{EmitSPIRV(stage_profile, runtime_info, program, binding, this->optimize_spirv_output)};
const std::vector<u32> code{EmitSPIRV(profile, runtime_info, program, binding, this->optimize_spirv_output)};
device.SaveShader(code);
modules[stage_index] = BuildShader(device, code);
if (device.HasDebuggingToolAttached()) {
@@ -837,18 +776,6 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
}
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
// Adreno and mobile GPUs have lower shared memory limits (32KB vs Switch's 48KB)
// Skip shader compilation if it exceeds device limits to prevent GPU crashes
const u32 max_shared_memory = device.GetMaxComputeSharedMemorySize();
if (program.shared_memory_size > max_shared_memory) {
LOG_ERROR(Render_Vulkan,
"Compute shader 0x{:016x} requests {}KB shared memory but device max is {}KB - "
"SKIPPING compilation to prevent GPU crash. Visual effect will be missing.",
key.unique_hash, program.shared_memory_size / 1024, max_shared_memory / 1024);
return nullptr;
}
const std::vector<u32> code{EmitSPIRV(profile, program, this->optimize_spirv_output)};
device.SaveShader(code);
vk::ShaderModule spv_module{BuildShader(device, code)};

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -116,10 +113,6 @@ public:
void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
const VideoCore::DiskResourceLoadCallback& callback);
[[nodiscard]] const DynamicFeatures& GetDynamicFeatures() const noexcept {
return dynamic_features;
}
private:
[[nodiscard]] GraphicsPipeline* CurrentGraphicsPipelineSlowPath();

View File

@@ -954,92 +954,74 @@ void RasterizerVulkan::UpdateDynamicStates() {
UpdateStencilFaces(regs);
UpdateLineWidth(regs);
const auto& dynamic_features = pipeline_cache.GetDynamicFeatures();
const u8 dynamic_state = Settings::values.dyna_state.GetValue();
// EDS1 - Extended Dynamic State 1
if (dynamic_features.has_extended_dynamic_state) {
auto features = DynamicFeatures{
.has_extended_dynamic_state = device.IsExtExtendedDynamicStateSupported() && dynamic_state > 0,
.has_extended_dynamic_state_2 = device.IsExtExtendedDynamicState2Supported() && dynamic_state > 1,
.has_extended_dynamic_state_2_extra = device.IsExtExtendedDynamicState2ExtrasSupported() && dynamic_state > 1,
.has_extended_dynamic_state_3_blend = device.IsExtExtendedDynamicState3BlendingSupported() && dynamic_state > 2,
.has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported() && dynamic_state > 2,
.has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported(),
};
if (features.has_extended_dynamic_state) {
UpdateCullMode(regs);
UpdateDepthCompareOp(regs);
UpdateFrontFace(regs);
UpdateStencilOp(regs);
if (state_tracker.TouchStateEnable()) {
UpdateDepthBoundsTestEnable(regs);
UpdateDepthTestEnable(regs);
UpdateDepthWriteEnable(regs);
UpdateStencilTestEnable(regs);
}
}
// EDS2 - Extended Dynamic State 2 Core
if (dynamic_features.has_extended_dynamic_state_2) {
if (state_tracker.TouchStateEnable()) {
UpdatePrimitiveRestartEnable(regs);
UpdateRasterizerDiscardEnable(regs);
UpdateDepthBiasEnable(regs);
}
}
// EDS2 - LogicOp (granular feature)
if (dynamic_features.has_extended_dynamic_state_2_logic_op) {
UpdateLogicOp(regs);
}
// EDS3 - Depth Clamp Enable (granular)
if (dynamic_features.has_extended_dynamic_state_3_depth_clamp ||
dynamic_features.has_extended_dynamic_state_3_enables) {
if (state_tracker.TouchStateEnable()) {
UpdateDepthClampEnable(regs);
}
}
// EDS3 - Logic Op Enable (granular)
if (dynamic_features.has_extended_dynamic_state_3_logic_op_enable ||
dynamic_features.has_extended_dynamic_state_3_enables) {
if (state_tracker.TouchStateEnable()) {
using namespace Tegra::Engines;
// AMD workaround for logic op with float vertex attributes
if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE ||
device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) {
struct In {
const Maxwell3D::Regs::VertexAttribute::Type d;
In(Maxwell3D::Regs::VertexAttribute::Type n) : d(n) {}
bool operator()(Maxwell3D::Regs::VertexAttribute n) const {
return n.type == d;
}
};
auto has_float = std::any_of(regs.vertex_attrib_format.begin(),
regs.vertex_attrib_format.end(),
In(Maxwell3D::Regs::VertexAttribute::Type::Float));
if (regs.logic_op.enable) {
regs.logic_op.enable = static_cast<u32>(!has_float);
}
if (features.has_extended_dynamic_state_2) {
UpdatePrimitiveRestartEnable(regs);
UpdateRasterizerDiscardEnable(regs);
UpdateDepthBiasEnable(regs);
}
UpdateLogicOpEnable(regs);
}
}
// EDS3 - Line Stipple Enable (granular)
if (dynamic_features.has_extended_dynamic_state_3_line_stipple_enable) {
if (state_tracker.TouchStateEnable()) {
if (features.has_extended_dynamic_state_3_enables) {
using namespace Tegra::Engines;
if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE ||
device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) {
struct In {
const Maxwell3D::Regs::VertexAttribute::Type d;
In(Maxwell3D::Regs::VertexAttribute::Type n) : d(n) {}
bool operator()(Maxwell3D::Regs::VertexAttribute n) const {
return n.type == d;
}
};
auto has_float = std::any_of(regs.vertex_attrib_format.begin(),
regs.vertex_attrib_format.end(),
In(Maxwell3D::Regs::VertexAttribute::Type::Float));
if (regs.logic_op.enable)
regs.logic_op.enable = static_cast<u32>(!has_float);
UpdateLogicOpEnable(regs);
} else {
UpdateLogicOpEnable(regs);
}
UpdateDepthClampEnable(regs);
}
}
if (features.has_extended_dynamic_state_2_extra) {
UpdateLogicOp(regs);
}
if (features.has_extended_dynamic_state_3_enables) {
UpdateBlending(regs);
UpdateLineStippleEnable(regs);
}
}
// EDS3 - Conservative Rasterization Mode (granular)
if (dynamic_features.has_extended_dynamic_state_3_conservative_rasterization_mode) {
if (state_tracker.TouchStateEnable()) {
UpdateConservativeRasterizationMode(regs);
}
}
// EDS3 - Blending (composite feature: ColorBlendEnable + ColorBlendEquation + ColorWriteMask)
if (dynamic_features.has_extended_dynamic_state_3_blend) {
UpdateBlending(regs);
}
// Vertex Input Dynamic State
if (dynamic_features.has_dynamic_vertex_input) {
if (auto* gp = pipeline_cache.CurrentGraphicsPipeline(); gp && gp->HasDynamicVertexInput()) {
if (features.has_dynamic_vertex_input) {
if (auto* gp = pipeline_cache.CurrentGraphicsPipeline();
gp && gp->HasDynamicVertexInput()) {
UpdateVertexInput(regs);
}
}
@@ -1068,25 +1050,19 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg
return;
}
if (!regs.viewport_scale_offset_enabled) {
float x = static_cast<float>(regs.surface_clip.x);
float y = static_cast<float>(regs.surface_clip.y);
float width = std::max(1.0f, static_cast<float>(regs.surface_clip.width));
float height = std::max(1.0f, static_cast<float>(regs.surface_clip.height));
if (regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft) {
y += height;
height = -height;
}
const auto x = static_cast<float>(regs.surface_clip.x);
const auto y = static_cast<float>(regs.surface_clip.y);
const auto width = static_cast<float>(regs.surface_clip.width);
const auto height = static_cast<float>(regs.surface_clip.height);
VkViewport viewport{
.x = x,
.y = y,
.width = width,
.height = height,
.width = width != 0.0f ? width : 1.0f,
.height = height != 0.0f ? height : 1.0f,
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
scheduler.Record([viewport](vk::CommandBuffer cmdbuf) {
cmdbuf.SetViewport(0, viewport);
});
scheduler.Record([viewport](vk::CommandBuffer cmdbuf) { cmdbuf.SetViewport(0, viewport); });
return;
}
const bool is_rescaling{texture_cache.IsRescaling()};
@@ -1113,21 +1089,16 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs
return;
}
if (!regs.viewport_scale_offset_enabled) {
u32 x = regs.surface_clip.x;
u32 y = regs.surface_clip.y;
u32 width = std::max(1u, static_cast<u32>(regs.surface_clip.width));
u32 height = std::max(1u, static_cast<u32>(regs.surface_clip.height));
if (regs.window_origin.mode != Maxwell::WindowOrigin::Mode::UpperLeft) {
y = regs.surface_clip.height - (y + height);
}
VkRect2D scissor{};
scissor.offset.x = static_cast<int32_t>(x);
scissor.offset.y = static_cast<int32_t>(y);
scissor.extent.width = width;
scissor.extent.height = height;
scheduler.Record([scissor](vk::CommandBuffer cmdbuf) {
cmdbuf.SetScissor(0, scissor);
});
const auto x = static_cast<float>(regs.surface_clip.x);
const auto y = static_cast<float>(regs.surface_clip.y);
const auto width = static_cast<float>(regs.surface_clip.width);
const auto height = static_cast<float>(regs.surface_clip.height);
VkRect2D scissor;
scissor.offset.x = static_cast<u32>(x);
scissor.offset.y = static_cast<u32>(y);
scissor.extent.width = static_cast<u32>(width != 0.0f ? width : 1.0f);
scissor.extent.height = static_cast<u32>(height != 0.0f ? height : 1.0f);
scheduler.Record([scissor](vk::CommandBuffer cmdbuf) { cmdbuf.SetScissor(0, scissor); });
return;
}
u32 up_scale = 1;
@@ -1586,41 +1557,22 @@ void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) {
if (state_tracker.TouchBlendEquations()) {
std::array<VkColorBlendEquationEXT, Maxwell::NumRenderTargets> setup_blends{};
const auto blend_setup = [&](auto& host_blend, const auto& guest_blend) {
host_blend.srcColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_source);
host_blend.dstColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_dest);
host_blend.colorBlendOp = MaxwellToVK::BlendEquation(guest_blend.color_op);
host_blend.srcAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_source);
host_blend.dstAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_dest);
host_blend.alphaBlendOp = MaxwellToVK::BlendEquation(guest_blend.alpha_op);
};
// Single blend equation for all targets
if (!regs.blend_per_target_enabled) {
// Temporary workaround for games that use iterated blending
if (regs.iterated_blend.enable && Settings::values.use_squashed_iterated_blend) {
setup_blends[0].srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
setup_blends[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
setup_blends[0].colorBlendOp = VK_BLEND_OP_ADD;
setup_blends[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
setup_blends[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
setup_blends[0].alphaBlendOp = VK_BLEND_OP_ADD;
} else {
blend_setup(setup_blends[0], regs.blend);
}
// Copy first blend state to all other targets
for (size_t index = 1; index < Maxwell::NumRenderTargets; index++) {
setup_blends[index] = setup_blends[0];
}
} else {
// Per-target blending
for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) {
blend_setup(setup_blends[index], regs.blend_per_target[index]);
for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) {
const auto blend_setup = [&]<typename T>(const T& guest_blend) {
auto& host_blend = setup_blends[index];
host_blend.srcColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_source);
host_blend.dstColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_dest);
host_blend.colorBlendOp = MaxwellToVK::BlendEquation(guest_blend.color_op);
host_blend.srcAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_source);
host_blend.dstAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_dest);
host_blend.alphaBlendOp = MaxwellToVK::BlendEquation(guest_blend.alpha_op);
};
if (!regs.blend_per_target_enabled) {
blend_setup(regs.blend);
continue;
}
blend_setup(regs.blend_per_target[index]);
}
scheduler.Record([setup_blends](vk::CommandBuffer cmdbuf) {
cmdbuf.SetColorBlendEquationEXT(0, setup_blends);
});
@@ -1655,7 +1607,7 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
highest_dirty_attr = index;
}
}
for (size_t index = 0; index <= highest_dirty_attr; ++index) {
for (size_t index = 0; index < highest_dirty_attr; ++index) {
const Maxwell::VertexAttribute attribute{regs.vertex_attrib_format[index]};
const u32 binding{attribute.buffer};
dirty[Dirty::VertexAttribute0 + index] = false;

View File

@@ -8,7 +8,6 @@
#include <boost/container/static_vector.hpp>
#include "common/logging/log.h"
#include "video_core/renderer_vulkan/maxwell_to_vk.h"
#include "video_core/renderer_vulkan/vk_render_pass_cache.h"
#include "video_core/surface.h"
@@ -20,23 +19,6 @@ namespace {
using VideoCore::Surface::PixelFormat;
using VideoCore::Surface::SurfaceType;
// Check if the driver uses tile-based deferred rendering (TBDR) architecture
// These GPUs benefit from optimized load/store operations to keep data on-chip
//
// TBDR GPUs supported in Eden:
// - Qualcomm Adreno (Snapdragon): Most Android flagship/midrange devices
// - ARM Mali: Android devices (Samsung Exynos, MediaTek, etc.)
// - Imagination PowerVR: Older iOS devices, some Android tablets
// - Samsung Xclipse: Galaxy S22+ (AMD RDNA2-based, but uses TBDR mode)
// - Broadcom VideoCore: Raspberry Pi
[[nodiscard]] constexpr bool IsTBDRGPU(VkDriverId driver_id) {
return driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_IMAGINATION_PROPRIETARY ||
driver_id == VK_DRIVER_ID_SAMSUNG_PROPRIETARY ||
driver_id == VK_DRIVER_ID_BROADCOM_PROPRIETARY;
}
constexpr SurfaceType GetSurfaceType(PixelFormat format) {
switch (format) {
// Depth formats
@@ -62,57 +44,23 @@ using VideoCore::Surface::SurfaceType;
}
VkAttachmentDescription AttachmentDescription(const Device& device, PixelFormat format,
VkSampleCountFlagBits samples,
bool tbdr_will_clear,
bool tbdr_discard_after,
bool tbdr_read_only = false) {
VkSampleCountFlagBits samples) {
using MaxwellToVK::SurfaceFormat;
const SurfaceType surface_type = GetSurfaceType(format);
const bool has_stencil = surface_type == SurfaceType::DepthStencil ||
surface_type == SurfaceType::Stencil;
// TBDR optimization: Apply hints only on tile-based GPUs
// Desktop GPUs (NVIDIA/AMD/Intel) ignore these hints and use standard behavior
const bool is_tbdr = IsTBDRGPU(device.GetDriverID());
// On TBDR: Use DONT_CARE if clear is guaranteed (avoids loading from main memory)
// On Desktop: Always LOAD to preserve existing content (safer default)
VkAttachmentLoadOp load_op = VK_ATTACHMENT_LOAD_OP_LOAD;
if (is_tbdr && tbdr_will_clear) {
load_op = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
}
// On TBDR: Use DONT_CARE if content won't be read (avoids storing to main memory)
// On Desktop: Always STORE (safer default)
// VK_QCOM_render_pass_store_ops: Use NONE_QCOM for read-only attachments (preserves outside render area)
VkAttachmentStoreOp store_op = VK_ATTACHMENT_STORE_OP_STORE;
if (is_tbdr && tbdr_discard_after) {
store_op = VK_ATTACHMENT_STORE_OP_DONT_CARE;
} else if (is_tbdr && tbdr_read_only && device.IsQcomRenderPassStoreOpsSupported()) {
store_op = static_cast<VkAttachmentStoreOp>(1000301000); // VK_ATTACHMENT_STORE_OP_NONE_QCOM
}
// Stencil operations follow same logic
VkAttachmentLoadOp stencil_load_op = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
VkAttachmentStoreOp stencil_store_op = VK_ATTACHMENT_STORE_OP_DONT_CARE;
if (has_stencil && tbdr_read_only && device.IsQcomRenderPassStoreOpsSupported()) {
stencil_store_op = static_cast<VkAttachmentStoreOp>(1000301000); // VK_ATTACHMENT_STORE_OP_NONE_QCOM
} else if (has_stencil) {
stencil_load_op = (is_tbdr && tbdr_will_clear) ? VK_ATTACHMENT_LOAD_OP_DONT_CARE
: VK_ATTACHMENT_LOAD_OP_LOAD;
stencil_store_op = (is_tbdr && tbdr_discard_after) ? VK_ATTACHMENT_STORE_OP_DONT_CARE
: VK_ATTACHMENT_STORE_OP_STORE;
}
return {
.flags = {},
.format = SurfaceFormat(device, FormatType::Optimal, true, format).format,
.samples = samples,
.loadOp = load_op,
.storeOp = store_op,
.stencilLoadOp = stencil_load_op,
.stencilStoreOp = stencil_store_op,
.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.stencilLoadOp = has_stencil ? VK_ATTACHMENT_LOAD_OP_LOAD
: VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.stencilStoreOp = has_stencil ? VK_ATTACHMENT_STORE_OP_STORE
: VK_ATTACHMENT_STORE_OP_DONT_CARE,
.initialLayout = VK_IMAGE_LAYOUT_GENERAL,
.finalLayout = VK_IMAGE_LAYOUT_GENERAL,
};
@@ -127,13 +75,6 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
if (!is_new) {
return *pair->second;
}
const bool is_tbdr = IsTBDRGPU(device->GetDriverID());
if (is_tbdr && (key.tbdr_will_clear || key.tbdr_discard_after)) {
LOG_DEBUG(Render_Vulkan, "Creating TBDR-optimized render pass (driver={}, clear={}, discard={})",
static_cast<u32>(device->GetDriverID()), key.tbdr_will_clear, key.tbdr_discard_after);
}
boost::container::static_vector<VkAttachmentDescription, 9> descriptions;
std::array<VkAttachmentReference, 8> references{};
u32 num_attachments{};
@@ -146,8 +87,7 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
.layout = VK_IMAGE_LAYOUT_GENERAL,
};
if (is_valid) {
descriptions.push_back(AttachmentDescription(*device, format, key.samples,
key.tbdr_will_clear, key.tbdr_discard_after));
descriptions.push_back(AttachmentDescription(*device, format, key.samples));
num_attachments = static_cast<u32>(index + 1);
++num_colors;
}
@@ -159,19 +99,10 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
.attachment = num_colors,
.layout = VK_IMAGE_LAYOUT_GENERAL,
};
descriptions.push_back(AttachmentDescription(*device, key.depth_format, key.samples,
key.tbdr_will_clear, key.tbdr_discard_after, key.tbdr_read_only));
descriptions.push_back(AttachmentDescription(*device, key.depth_format, key.samples));
}
VkSubpassDescriptionFlags subpass_flags = 0;
if (key.qcom_shader_resolve) {
// VK_QCOM_render_pass_shader_resolve: enables custom shader resolve in fragment shader
// This flag allows using a programmable fragment shader for MSAA resolve instead of
// fixed-function hardware resolve, enabling better quality and HDR format support
subpass_flags |= 0x00000004; // VK_SUBPASS_DESCRIPTION_SHADER_RESOLVE_BIT_QCOM
}
const VkSubpassDescription subpass{
.flags = subpass_flags,
.flags = 0,
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.inputAttachmentCount = 0,
.pInputAttachments = nullptr,

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -20,15 +17,6 @@ struct RenderPassKey {
std::array<VideoCore::Surface::PixelFormat, 8> color_formats;
VideoCore::Surface::PixelFormat depth_format;
VkSampleCountFlagBits samples;
// TBDR optimization hints - only affect tile-based GPUs (Qualcomm, ARM, Imagination)
// These flags indicate the expected usage pattern to optimize load/store operations
bool tbdr_will_clear{false}; // Attachment will be cleared with vkCmdClearAttachments
bool tbdr_discard_after{false}; // Attachment won't be read after render pass
bool tbdr_read_only{false}; // Attachment is read-only (input attachment, depth test without writes)
// VK_QCOM_render_pass_shader_resolve support
bool qcom_shader_resolve{false}; // Use shader resolve instead of fixed-function (last subpass)
};
} // namespace Vulkan
@@ -39,8 +27,6 @@ struct hash<Vulkan::RenderPassKey> {
[[nodiscard]] size_t operator()(const Vulkan::RenderPassKey& key) const noexcept {
size_t value = static_cast<size_t>(key.depth_format) << 48;
value ^= static_cast<size_t>(key.samples) << 52;
value ^= (static_cast<size_t>(key.tbdr_will_clear) << 56);
value ^= (static_cast<size_t>(key.tbdr_discard_after) << 57);
for (size_t i = 0; i < key.color_formats.size(); ++i) {
value ^= static_cast<size_t>(key.color_formats[i]) << (i * 6);
}

View File

@@ -171,10 +171,6 @@ void Swapchain::Create(
resource_ticks.clear();
resource_ticks.resize(image_count);
// Initialize incremental-present probe flags for this swapchain.
incremental_present_usable = device.IsKhrIncrementalPresentSupported();
incremental_present_probed = false;
}
bool Swapchain::AcquireNextImage() {
@@ -206,13 +202,7 @@ bool Swapchain::AcquireNextImage() {
void Swapchain::Present(VkSemaphore render_semaphore) {
const auto present_queue{device.GetPresentQueue()};
// If the device advertises VK_KHR_incremental_present, we attempt a one-time probe
// on the first present to validate the driver/compositor accepts present-region info.
VkPresentRegionsKHR present_regions{};
VkPresentRegionKHR region{};
VkRectLayerKHR layer{};
VkPresentInfoKHR present_info{
const VkPresentInfoKHR present_info{
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.pNext = nullptr,
.waitSemaphoreCount = render_semaphore ? 1U : 0U,
@@ -222,20 +212,6 @@ void Swapchain::Present(VkSemaphore render_semaphore) {
.pImageIndices = &image_index,
.pResults = nullptr,
};
if (incremental_present_usable && !incremental_present_probed) {
// Build a minimal present-region describing a single 1x1 dirty rect at (0,0).
layer.offset = {0, 0};
layer.extent = {1, 1};
region.rectangleCount = 1;
region.pRectangles = &layer;
present_regions.sType = VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR;
present_regions.pNext = nullptr;
present_regions.swapchainCount = 1;
present_regions.pRegions = &region;
present_info.pNext = &present_regions;
}
std::scoped_lock lock{scheduler.submit_mutex};
switch (const VkResult result = present_queue.Present(present_info)) {
case VK_SUCCESS:
@@ -251,18 +227,8 @@ void Swapchain::Present(VkSemaphore render_semaphore) {
break;
default:
LOG_CRITICAL(Render_Vulkan, "Failed to present with error {}", string_VkResult(result));
// If the first present with incremental-present pNext failed, disable future use.
if (incremental_present_usable && !incremental_present_probed) {
incremental_present_usable = false;
LOG_WARNING(Render_Vulkan, "Disabling VK_KHR_incremental_present for this swapchain due to present failure: {}", string_VkResult(result));
}
break;
}
if (incremental_present_usable && !incremental_present_probed) {
// Mark probe as completed if we reached here (success or handled failure above).
incremental_present_probed = true;
LOG_INFO(Render_Vulkan, "VK_KHR_incremental_present probe completed: usable={}", incremental_present_usable);
}
++frame_index;
if (frame_index >= image_count) {
frame_index = 0;

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -161,8 +158,6 @@ private:
bool is_outdated{};
bool is_suboptimal{};
bool incremental_present_usable{};
bool incremental_present_probed{};
};
} // namespace Vulkan

View File

@@ -66,20 +66,10 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
}
}
[[nodiscard]] VkImageType ConvertImageType(const ImageType type, const Device& device) {
[[nodiscard]] VkImageType ConvertImageType(const ImageType type) {
switch (type) {
case ImageType::e1D:
// Mobile Vulkan (Adreno, Mali, PowerVR, IMG) lacks Sampled1D SPIR-V capability
// Emulate as 2D texture with height=1 on mobile, use native 1D on desktop
{
const auto driver_id = device.GetDriverID();
const bool is_mobile = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_MESA_TURNIP ||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_BROADCOM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_IMAGINATION_PROPRIETARY;
return is_mobile ? VK_IMAGE_TYPE_2D : VK_IMAGE_TYPE_1D;
}
return VK_IMAGE_TYPE_1D;
case ImageType::e2D:
case ImageType::Linear:
return VK_IMAGE_TYPE_2D;
@@ -151,7 +141,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = nullptr,
.flags = flags,
.imageType = ConvertImageType(info.type, device),
.imageType = ConvertImageType(info.type),
.format = format_info.format,
.extent{
.width = info.size.width >> samples_x,
@@ -170,40 +160,6 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
};
}
/// Emergency fallback: degrade MSAA to non-MSAA for HDR formats when no resolve support exists
[[nodiscard]] ImageInfo AdjustMSAAForHDRFormats(const Device& device, ImageInfo info) {
if (info.num_samples <= 1) {
return info;
}
const auto vk_format = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal,
false, info.format).format;
const bool is_hdr_format = vk_format == VK_FORMAT_B10G11R11_UFLOAT_PACK32;
if (!is_hdr_format) {
return info;
}
// Qualcomm: VK_QCOM_render_pass_shader_resolve handles HDR+MSAA
if (device.GetDriverID() == VK_DRIVER_ID_QUALCOMM_PROPRIETARY) {
if (device.IsQcomRenderPassShaderResolveSupported()) {
return info;
}
}
// Other vendors: shaderStorageImageMultisample handles HDR+MSAA
if (device.IsStorageImageMultisampleSupported()) {
return info;
}
// No suitable resolve method - degrade to non-MSAA
LOG_WARNING(Render_Vulkan, "HDR format {} with MSAA not supported, degrading to 1x samples",
vk_format);
info.num_samples = 1;
return info;
}
[[nodiscard]] vk::Image MakeImage(const Device& device, const MemoryAllocator& allocator,
const ImageInfo& info, std::span<const VkFormat> view_formats) {
if (info.type == ImageType::Buffer) {
@@ -316,18 +272,10 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
return VK_COMPONENT_SWIZZLE_ZERO;
}
[[nodiscard]] VkImageViewType ImageViewType(Shader::TextureType type, const Device& device) {
const auto driver_id = device.GetDriverID();
const bool is_mobile = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_MESA_TURNIP ||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_BROADCOM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_IMAGINATION_PROPRIETARY;
[[nodiscard]] VkImageViewType ImageViewType(Shader::TextureType type) {
switch (type) {
case Shader::TextureType::Color1D:
// Emulate 1D as 2D with height=1 on mobile (no Sampled1D capability)
return is_mobile ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_1D;
return VK_IMAGE_VIEW_TYPE_1D;
case Shader::TextureType::Color2D:
case Shader::TextureType::Color2DRect:
return VK_IMAGE_VIEW_TYPE_2D;
@@ -336,8 +284,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
case Shader::TextureType::Color3D:
return VK_IMAGE_VIEW_TYPE_3D;
case Shader::TextureType::ColorArray1D:
// Emulate 1D array as 2D array with height=1 on mobile
return is_mobile ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_1D_ARRAY;
return VK_IMAGE_VIEW_TYPE_1D_ARRAY;
case Shader::TextureType::ColorArray2D:
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
case Shader::TextureType::ColorArrayCube:
@@ -350,18 +297,10 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
return VK_IMAGE_VIEW_TYPE_2D;
}
[[nodiscard]] VkImageViewType ImageViewType(VideoCommon::ImageViewType type, const Device& device) {
const auto driver_id = device.GetDriverID();
const bool is_mobile = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_MESA_TURNIP ||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_BROADCOM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_IMAGINATION_PROPRIETARY;
[[nodiscard]] VkImageViewType ImageViewType(VideoCommon::ImageViewType type) {
switch (type) {
case VideoCommon::ImageViewType::e1D:
// Emulate 1D as 2D with height=1 on mobile (no Sampled1D capability)
return is_mobile ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_1D;
return VK_IMAGE_VIEW_TYPE_1D;
case VideoCommon::ImageViewType::e2D:
case VideoCommon::ImageViewType::Rect:
return VK_IMAGE_VIEW_TYPE_2D;
@@ -370,8 +309,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
case VideoCommon::ImageViewType::e3D:
return VK_IMAGE_VIEW_TYPE_3D;
case VideoCommon::ImageViewType::e1DArray:
// Emulate 1D array as 2D array with height=1 on mobile
return is_mobile ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_1D_ARRAY;
return VK_IMAGE_VIEW_TYPE_1D_ARRAY;
case VideoCommon::ImageViewType::e2DArray:
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
case VideoCommon::ImageViewType::CubeArray:
@@ -919,9 +857,6 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
astc_decoder_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
compute_pass_descriptor_queue, memory_allocator);
}
// MSAA copy support via compute shader (only for non-Qualcomm with shaderStorageImageMultisample)
// Qualcomm uses VK_QCOM_render_pass_shader_resolve (fragment shader in render pass)
if (device.IsStorageImageMultisampleSupported()) {
msaa_copy_pass = std::make_unique<MSAACopyPass>(
device, scheduler, descriptor_pool, staging_buffer_pool, compute_pass_descriptor_queue);
@@ -1388,6 +1323,7 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
case PixelFormat::ASTC_2D_8X6_SRGB:
case PixelFormat::ASTC_2D_6X5_UNORM:
case PixelFormat::ASTC_2D_6X5_SRGB:
case PixelFormat::E5B9G9R9_FLOAT:
case PixelFormat::D32_FLOAT:
case PixelFormat::D16_UNORM:
case PixelFormat::X8_D24_UNORM:
@@ -1551,23 +1487,6 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src,
void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src,
std::span<const VideoCommon::ImageCopy> copies) {
const bool msaa_to_non_msaa = src.info.num_samples > 1 && dst.info.num_samples == 1;
// Use VK_QCOM_render_pass_shader_resolve for HDR formats on Qualcomm
// This is more efficient than compute shader (stays on-chip in TBDR)
const bool is_hdr_format = src.info.format == PixelFormat::B10G11R11_FLOAT ||
dst.info.format == PixelFormat::B10G11R11_FLOAT;
const bool use_qcom_resolve = msaa_to_non_msaa &&
device.IsQcomRenderPassShaderResolveSupported() &&
is_hdr_format &&
copies.size() == 1; // QCOM resolve works best with single full copy
if (use_qcom_resolve) {
// Create temporary framebuffer with resolve target
// TODO Camille: Implement QCOM shader resolve path with proper framebuffer setup
// For now, fall through to standard path
LOG_DEBUG(Render_Vulkan, "QCOM shader resolve opportunity detected but not yet implemented");
}
if (msaa_copy_pass) {
return msaa_copy_pass->CopyImage(dst, src, copies, msaa_to_non_msaa);
}
@@ -1591,20 +1510,10 @@ void TextureCacheRuntime::TickFrame() {}
Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_,
VAddr cpu_addr_)
: VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), scheduler{&runtime_.scheduler},
runtime{&runtime_} {
// CRITICAL: Adjust MSAA for HDR formats if driver doesn't support shaderStorageImageMultisample
// This prevents texture corruption by degrading to non-MSAA when msaa_copy_pass would fail
const ImageInfo adjusted_info = AdjustMSAAForHDRFormats(runtime_.device, info_);
// Update our stored info with adjusted values (may have num_samples=1 now)
info = adjusted_info;
// Create image with adjusted info
original_image = MakeImage(runtime_.device, runtime_.memory_allocator, adjusted_info,
runtime->ViewFormats(adjusted_info.format));
aspect_mask = ImageAspectMask(adjusted_info.format);
if (IsPixelFormatASTC(adjusted_info.format) && !runtime->device.IsOptimalAstcSupported()) {
runtime{&runtime_}, original_image(MakeImage(runtime_.device, runtime_.memory_allocator, info,
runtime->ViewFormats(info.format))),
aspect_mask(ImageAspectMask(info.format)) {
if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported()) {
switch (Settings::values.accelerate_astc.GetValue()) {
case Settings::AstcDecodeMode::Gpu:
if (Settings::values.astc_recompression.GetValue() ==
@@ -2120,14 +2029,6 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
}
}
const auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format);
VkFormat view_format = format_info.format;
// TODO: Format reinterpretation toggles (per-game settings)
// Some games incorrectly use integer formats with float samplers:
// - R32_UINT with texture() instead of texelFetch() causes flickering
// - R8_UINT with LINEAR filter causes validation errors
// Cannot auto-detect: need user toggles to force format reinterpretation
if (ImageUsageFlags(format_info, format) != image.UsageFlags()) {
LOG_WARNING(Render_Vulkan,
"Image view format {} has different usage flags than image format {}", format,
@@ -2138,14 +2039,13 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
.pNext = nullptr,
.usage = ImageUsageFlags(format_info, format),
};
const VkImageViewCreateInfo create_info{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = &image_view_usage,
.flags = 0,
.image = image.Handle(),
.viewType = VkImageViewType{},
.format = view_format,
.format = format_info.format,
.components{
.r = ComponentSwizzle(swizzle[0]),
.g = ComponentSwizzle(swizzle[1]),
@@ -2156,7 +2056,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
};
const auto create = [&](TextureType tex_type, std::optional<u32> num_layers) {
VkImageViewCreateInfo ci{create_info};
ci.viewType = ImageViewType(tex_type, *device);
ci.viewType = ImageViewType(tex_type);
if (num_layers) {
ci.subresourceRange.layerCount = *num_layers;
}
@@ -2297,7 +2197,7 @@ vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_
.pNext = nullptr,
.flags = 0,
.image = image_handle,
.viewType = ImageViewType(type, *device),
.viewType = ImageViewType(type),
.format = vk_format,
.components{
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
@@ -2443,26 +2343,6 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
}
renderpass_key.samples = samples;
// Enable VK_QCOM_render_pass_shader_resolve for HDR+MSAA on Qualcomm
// This performs MSAA resolve using fragment shader IN the render pass (on-chip)
// Benefits: ~70% bandwidth reduction, better performance on TBDR architectures
// Requirements: pResolveAttachments configured + explicit shader execution
if (samples > VK_SAMPLE_COUNT_1_BIT && runtime.device.IsQcomRenderPassShaderResolveSupported()) {
// Check if any color attachment is HDR format that benefits from shader resolve
bool has_hdr_attachment = false;
for (size_t index = 0; index < NUM_RT && !has_hdr_attachment; ++index) {
const auto format = renderpass_key.color_formats[index];
// B10G11R11_FLOAT benefits most: compute shader limited, fixed-function slower
if (format == PixelFormat::B10G11R11_FLOAT) {
has_hdr_attachment = true;
}
}
if (has_hdr_attachment) {
renderpass_key.qcom_shader_resolve = true;
}
}
renderpass = runtime.render_pass_cache.Get(renderpass_key);
render_area.width = (std::min)(render_area.width, width);
render_area.height = (std::min)(render_area.height, height);

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -88,10 +85,6 @@ public:
return msaa_copy_pass.operator bool();
}
bool CanDownloadMSAA() const noexcept {
return msaa_copy_pass.operator bool();
}
void AccelerateImageUpload(Image&, const StagingBufferRef&,
std::span<const VideoCommon::SwizzleParameters>);

Some files were not shown because too many files have changed in this diff Show More