Compare commits

..

1 Commits

Author SHA1 Message Date
wildcard 9a59eb42d8 [memory] Add mirror mapping for buffer sync
will be added once determined safe for testing, plus remove the logging before merge
2026-05-18 23:46:56 +02:00
242 changed files with 19930 additions and 22247 deletions
+5 -6
View File
@@ -7,7 +7,7 @@
EXCLUDE_FILES="CPM.cmake CPMUtil.cmake GetSCMRev.cmake renderdoc_app.h tools/cpm tools/shellcheck.sh tools/update-cpm.sh tools/windows/vcvarsall.sh externals/stb externals/glad externals/getopt externals/gamemode externals/FidelityFX-FSR externals/demangle externals/bc_decoder externals/cmake-modules"
# license header constants, please change when needed :))))
YEAR=$(date "+%Y")
YEAR=2026
HOLDER="Eden Emulator Project"
LICENSE="GPL-3.0-or-later"
@@ -112,10 +112,10 @@ for file in $FILES; do
[ "$excluded" = "true" ] && continue
case "$file" in
*.cmake|*.sh|*.ps1|*.py|*.rb|*.perl|*.pl|*.nix|*CMakeLists.txt)
*.cmake|*.sh|*CMakeLists.txt)
begin="#"
;;
*.kt|*.kts|*.cpp|*.h|*.qml|*.c|*.hpp|*.hxx|*.cxx|*.h.in|*.inc)
*.kt*|*.cpp|*.h|*.qml)
begin="//"
;;
*)
@@ -185,12 +185,11 @@ if [ "$UPDATE" = "true" ]; then
for file in $SRC_FILES $OTHER_FILES; do
case $(basename -- "$file") in
# Windows Powershell wont use shebangs
*.cmake|*.ps1|*CMakeLists.txt)
*.cmake|*CMakeLists.txt)
begin="#"
shell="false"
;;
*.sh|*.py|*.rb|*.perl|*.pl|*.nix)
*.sh)
begin="#"
shell=true
;;
Executable
+22
View File
@@ -0,0 +1,22 @@
#!/bin/bash -ex
# git-archive-all
export PATH="$PATH:/home/$USER/.local/bin"
GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
GITREV="`git show -s --format='%h'`"
REV_NAME="eden-unified-source-${GITDATE}-${GITREV}"
COMPAT_LIST='dist/compatibility_list/compatibility_list.json'
mkdir artifacts
touch "${COMPAT_LIST}"
git describe --abbrev=0 --always HEAD > GIT-COMMIT
git describe --tags HEAD > GIT-TAG || echo 'unknown' > GIT-TAG
git-archive-all --include "${COMPAT_LIST}" --include GIT-COMMIT --include GIT-TAG --force-submodules artifacts/"${REV_NAME}.tar"
cd artifacts/
xz -T0 -9 "${REV_NAME}.tar"
sha256sum "${REV_NAME}.tar.xz" > "${REV_NAME}.tar.xz.sha256sum"
cd ..
+22 -18
View File
@@ -75,8 +75,6 @@ cmake_dependent_option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet im
set(YUZU_QT_MIRROR "" CACHE STRING "What mirror to use for downloading the bundled Qt libraries")
cmake_dependent_option(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF)
option(ENABLE_DEBUG_TOOLS "Enable debugging tools (maxwell disassembler, SPIRV translator, etc)" OFF)
# non-linux bundled qt are static
if (YUZU_USE_BUNDLED_QT AND (APPLE OR NOT UNIX))
set(YUZU_STATIC_BUILD ON)
@@ -584,6 +582,8 @@ if (ENABLE_QT)
else()
AddQt(6.9.3)
endif()
set(YUZU_STATIC_BUILD ON)
else()
message(STATUS "Using system Qt")
if (NOT Qt6_DIR)
@@ -592,7 +592,23 @@ if (ENABLE_QT)
list(APPEND CMAKE_PREFIX_PATH "${Qt6_DIR}")
endif()
find_package(Qt6 REQUIRED COMPONENTS Core)
find_package(Qt6 CONFIG REQUIRED COMPONENTS Widgets Charts Concurrent)
if (YUZU_USE_QT_MULTIMEDIA)
find_package(Qt6 REQUIRED COMPONENTS Multimedia)
endif()
if (PLATFORM_LINUX OR PLATFORM_FREEBSD)
# yes Qt, we get it
set(QT_NO_PRIVATE_MODULE_WARNING ON)
find_package(Qt6 REQUIRED COMPONENTS DBus OPTIONAL_COMPONENTS GuiPrivate)
elseif (UNIX AND NOT APPLE)
find_package(Qt6 REQUIRED COMPONENTS DBus Gui)
endif()
if (ENABLE_QT_TRANSLATION)
find_package(Qt6 REQUIRED COMPONENTS LinguistTools)
endif()
if (NOT DEFINED QT_TARGET_PATH)
get_target_property(qtcore_path Qt6::Core LOCATION_Release)
@@ -615,27 +631,21 @@ if (ENABLE_QT)
## Components ##
# Best practice is to ask for all components at once, so they are from the same version
set(YUZU_QT_COMPONENTS Core Widgets Charts Concurrent Gui)
if (PLATFORM_LINUX OR PLATFORM_FREEBSD)
set(YUZU_QT_COMPONENTS Core Widgets Charts Concurrent)
if (PLATFORM_LINUX)
list(APPEND YUZU_QT_COMPONENTS DBus)
# yes Qt, we get it
set(QT_NO_PRIVATE_MODULE_WARNING ON)
list(APPEND YUZU_QT_OPTIONAL GuiPrivate)
endif()
if (YUZU_USE_QT_MULTIMEDIA)
list(APPEND YUZU_QT_COMPONENTS Multimedia)
endif()
if (YUZU_USE_QT_WEB_ENGINE)
list(APPEND YUZU_QT_COMPONENTS WebEngineCore WebEngineWidgets)
endif()
if (ENABLE_QT_TRANSLATION)
list(APPEND YUZU_QT_COMPONENTS LinguistTools)
endif()
find_package(Qt6 REQUIRED COMPONENTS ${YUZU_QT_COMPONENTS} OPTIONAL_COMPONENTS ${YUZU_QT_OPTIONAL})
find_package(Qt6 REQUIRED COMPONENTS ${YUZU_QT_COMPONENTS})
set(QT_MAJOR_VERSION 6)
# Qt6 sets cxx_std_17 and we need to undo that
set_target_properties(Qt6::Platform PROPERTIES INTERFACE_COMPILE_FEATURES "")
@@ -703,12 +713,6 @@ endif()
add_subdirectory(src)
if (ENABLE_DEBUG_TOOLS)
add_subdirectory(tools/maxwell-disas)
add_subdirectory(tools/maxwell-spirv)
add_subdirectory(tools/maxwell-ir)
endif()
# Set yuzu project or yuzu-cmd project as default StartUp Project in Visual Studio depending on whether QT is enabled or not
if(ENABLE_QT)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu)
+4 -7
View File
@@ -715,8 +715,8 @@ function(AddCIPackage)
"${${ARTIFACT_PACKAGE}_SOURCE_DIR}" PARENT_SCOPE)
if (PKG_ARGS_MODULE)
list(PREPEND CMAKE_PREFIX_PATH "${${ARTIFACT_PACKAGE}_SOURCE_DIR}")
Propagate(CMAKE_PREFIX_PATH)
list(APPEND CMAKE_PREFIX_PATH "${${ARTIFACT_PACKAGE}_SOURCE_DIR}")
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE)
endif()
else()
find_package(${ARTIFACT_PACKAGE} ${ARTIFACT_MIN_VERSION} REQUIRED)
@@ -730,7 +730,7 @@ function(AddQt version)
endif()
AddCIPackage(
NAME qt
NAME Qt
PACKAGE Qt6
VERSION ${version}
MIN_VERSION 6
@@ -740,8 +740,5 @@ function(AddQt version)
freebsd-amd64 solaris-amd64 openbsd-amd64
MODULE)
find_package(Qt6 REQUIRED PATHS ${Qt6_SOURCE_DIR} NO_DEFAULT_PATH)
Propagate(CMAKE_PREFIX_PATH)
Propagate(Qt6_SOURCE_DIR)
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE)
endfunction()
+1 -2
View File
@@ -112,8 +112,7 @@
"options": [
"QUAZIP_QT_MAJOR_VERSION 6",
"QUAZIP_INSTALL OFF",
"QUAZIP_ENABLE_QTEXTCODEC OFF",
"QUAZIP_BZIP2 OFF"
"QUAZIP_ENABLE_QTEXTCODEC OFF"
]
}
}
+133 -74
View File
@@ -6,8 +6,8 @@
viewBox="0 0 512 512"
version="1.1"
id="svg7"
sodipodi:docname="base.svg.2026_01_12_14_43_47.0.svg"
inkscape:version="1.4.2 (ebf0e94, 2025-05-08)"
sodipodi:docname="1stanni.svg"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
inkscape:export-filename="base.svg.2026_01_12_14_43_47.0.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
@@ -19,36 +19,34 @@
<defs
id="defs7">
<linearGradient
id="linearGradient1"
id="linearGradient34"
inkscape:collect="always">
<stop
style="stop-color:#ff2e88;stop-opacity:0.5;"
style="stop-color:#ffd700;stop-opacity:1;"
offset="0"
id="stop3" />
id="stop34" />
<stop
style="stop-color:#bf42f6;stop-opacity:0.5;"
offset="0.44631511"
id="stop4" />
<stop
style="stop-color:#5da5ed;stop-opacity:0.5;"
offset="0.90088946"
id="stop2" />
style="stop-color:#ffd700;stop-opacity:0.48031053;"
offset="1"
id="stop35" />
</linearGradient>
<rect
x="20.999999"
y="287.30493"
width="487.07235"
height="134.69506"
id="rect22" />
<linearGradient
id="linearGradient138"
id="linearGradient21"
inkscape:collect="always">
<stop
style="stop-color:#ff2e88;stop-opacity:1;"
style="stop-color:#3a0057;stop-opacity:1;"
offset="0"
id="stop152" />
id="stop21" />
<stop
style="stop-color:#bf42f6;stop-opacity:1;"
offset="0.44971901"
id="stop137" />
<stop
style="stop-color:#5da5ed;stop-opacity:1;"
offset="0.89793283"
id="stop138" />
style="stop-color:#830091;stop-opacity:1;"
offset="1"
id="stop22" />
</linearGradient>
<linearGradient
id="swatch37"
@@ -116,33 +114,6 @@
width="521.34025"
height="248.94868"
id="rect24" />
<linearGradient
id="linearGradient11"
inkscape:collect="always">
<stop
style="stop-color:#ff2e88;stop-opacity:1;"
offset="0"
id="stop11" />
<stop
style="stop-color:#bf42f6;stop-opacity:1;"
offset="0.44971901"
id="stop154" />
<stop
style="stop-color:#5da5ed;stop-opacity:1;"
offset="0.89793283"
id="stop12" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient138"
id="linearGradient6"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.118028,0,0,1.116699,-46.314723,-42.388667)"
x1="270.39996"
y1="40.000019"
x2="270.39996"
y2="494.39996"
spreadMethod="pad" />
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath18">
@@ -165,16 +136,6 @@
inkscape:label="Circle"
r="191.89999" />
</clipPath>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient11"
id="linearGradient27"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-6.9401139e-5,-2.8678628)"
x1="256.00012"
y1="102.94693"
x2="256.00012"
y2="409.05307" />
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath128">
@@ -187,14 +148,106 @@
</clipPath>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1"
id="linearGradient2"
xlink:href="#linearGradient21"
id="linearGradient22"
x1="256"
y1="64"
y1="0"
x2="256"
y2="448"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.3229974,0,0,1.3214002,-82.687336,-82.290326)" />
y2="512"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient34"
id="linearGradient35"
x1="256"
y1="-0.048701428"
x2="256"
y2="512.04932"
gradientUnits="userSpaceOnUse" />
<filter
inkscape:label="Glowing Bubble"
inkscape:menu="Ridges"
inkscape:menu-tooltip="Bubble effect with refraction and glow"
x="-0.19420711"
y="-0.11239541"
width="1.3884142"
height="1.2247908"
style="color-interpolation-filters:sRGB;"
id="filter61">
<feGaussianBlur
stdDeviation="1"
result="result1"
id="feGaussianBlur56" />
<feGaussianBlur
stdDeviation="10"
result="result6"
in="result1"
id="feGaussianBlur57" />
<feComposite
operator="atop"
in="result6"
in2="result1"
result="result8"
id="feComposite57" />
<feComposite
operator="xor"
result="fbSourceGraphic"
in="result6"
in2="result8"
id="feComposite58" />
<feColorMatrix
result="fbSourceGraphicAlpha"
in="fbSourceGraphic"
values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 2 0 "
id="feColorMatrix58" />
<feGaussianBlur
result="result0"
in="fbSourceGraphicAlpha"
stdDeviation="1"
id="feGaussianBlur58" />
<feSpecularLighting
specularExponent="35"
specularConstant="1.5"
surfaceScale="-2"
lighting-color="rgb(255,255,255)"
result="result1"
in="result0"
id="feSpecularLighting58">
<feDistantLight
azimuth="230"
elevation="60"
id="feDistantLight58" />
</feSpecularLighting>
<feComposite
operator="in"
result="result2"
in="result1"
in2="fbSourceGraphicAlpha"
id="feComposite59" />
<feComposite
k3="1.2"
k2="1.1"
operator="arithmetic"
result="result4"
in="fbSourceGraphic"
in2="result2"
id="feComposite60" />
<feGaussianBlur
result="result80"
in="result4"
stdDeviation="0.5"
id="feGaussianBlur60" />
<feComposite
operator="atop"
in="result9"
in2="result80"
result="result91"
id="feComposite61" />
<feBlend
mode="multiply"
in2="result91"
id="feBlend61" />
</filter>
</defs>
<sodipodi:namedview
id="namedview7"
@@ -205,23 +258,29 @@
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="1.4142136"
inkscape:cx="261.62951"
inkscape:cy="230.87036"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="1080"
inkscape:window-y="351"
inkscape:zoom="1"
inkscape:cx="213.49999"
inkscape:cy="248.99999"
inkscape:window-width="1600"
inkscape:window-height="849"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg7" />
<circle
style="fill:url(#linearGradient22);fill-opacity:1;stroke:none;stroke-width:8"
id="path21"
cx="256"
cy="256"
r="256" />
<path
id="path8-7"
style="display:inline;mix-blend-mode:multiply;fill:url(#linearGradient6);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient2);stroke-width:3.9666;stroke-dasharray:none;stroke-opacity:0.566238;paint-order:stroke fill markers"
style="display:inline;mix-blend-mode:normal;fill:url(#linearGradient35);fill-opacity:1;fill-rule:nonzero;stroke:#320081;stroke-width:4.067;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
inkscape:label="Circle"
d="M 256,2.2792898 A 254.0155,253.71401 0 0 0 150.68475,25.115202 c 19.54414,1.070775 38.74692,5.250294 51.56848,11.647658 14.14361,7.056691 28.63804,19.185961 39.4212,29.347551 h 40.60981 c 1.03847,-0.68139 2.10297,-1.36938 3.1938,-2.05957 5.45602,-15.78533 14.79164,-43.183497 19.49612,-57.0097682 A 254.0155,253.71401 0 0 0 256,2.2792898 Z m 61.57106,7.567234 -18.26098,46.1544672 c 7.79702,-4.13918 16.35655,-7.87447 25.20671,-10.87081 23.1229,-7.828433 43.96931,-10.170904 54.94058,-10.868226 A 254.0155,253.71401 0 0 0 317.57106,9.8465238 Z m 65.39277,26.4001532 c -9.68256,4.806644 -33.05532,16.642034 -55.68217,29.863734 H 424.4677 A 254.0155,253.71401 0 0 0 382.96383,36.246677 Z M 113.90698,45.690231 A 254.0155,253.71401 0 0 0 87.532302,66.110411 H 194.2739 c -1.47402,-0.80231 -2.35141,-1.25949 -2.35141,-1.25949 l 10.4496,-11.83348 -38.40568,7.01234 c 0,1e-5 -12.21537,-4.60266 -40.17313,-12.27223 -3.45336,-0.94731 -6.75329,-1.61824 -9.8863,-2.06732 z m -36.803618,30.18635 a 254.0155,253.71401 0 0 0 -34.88372,43.090929 h 59.976738 c 18.11461,-12.04145 40.14252,-22.882149 62.31266,-24.534159 52.93006,-3.9444 70.16538,1.86342 70.16538,1.86342 0,0 -4.612,-4.8206 -14.51938,-13.36656 -2.72366,-2.34942 -6.0844,-4.77373 -9.52455,-7.05363 z m 174.472868,0 c 4.57322,4.7186 7.29716,7.83565 7.29716,7.83565 0,0 3.53501,-3.18484 9.62532,-7.83565 z m 60.27649,0 c -21.56573,15.45339 -25.4703,27.979669 -25.4703,27.979669 0,0 54.83326,-19.215729 100.70543,-0.31228 11.63986,4.79661 21.58481,10.13159 29.94832,15.42354 h 52.74419 A 254.0155,253.71401 0 0 0 434.89664,75.876581 Z M 36.250648,128.73367 A 254.0155,253.71401 0 0 0 16.372095,171.82459 H 147.45478 c 1.45695,-2.5815 3.06539,-5.08648 4.83979,-7.48982 14.23694,-19.28301 27.92088,-30.0088 36.86047,-35.6011 h -30.25323 c -5.87346,0.93472 -12.04945,1.99094 -18.28166,3.16937 -30.12936,5.69727 -81.157618,22.78945 -81.157618,22.78945 0,0 11.47125,-12.39249 29.11369,-25.95882 z m 265.630492,0 c 33.48676,11.2434 52.42799,26.78443 62.7752,43.09092 h 130.97157 a 254.0155,253.71401 0 0 0 -19.87856,-43.09092 h -44.81136 c 14.85233,11.5863 21.59948,20.9854 21.59948,20.9854 0,0 -33.5226,-12.37087 -66.0646,-20.9854 z m -45.96641,16.27007 c -1.00419,0.0106 -10.12705,0.72026 -44.98966,20.64729 -3.12132,1.78406 -6.25434,3.86182 -9.37468,6.17356 h 41.81911 c 7.17181,-17.34774 12.64083,-26.82085 12.64083,-26.82085 0,0 -0.0287,-7.1e-4 -0.0957,0 z m 14.18088,0.0465 c 0,0 -3.31228,9.32762 -7.30492,26.77438 h 51.78554 C 287.6577,146.14158 270.09561,145.0502 270.09561,145.0502 Z M 13.152456,181.59075 A 254.0155,253.71401 0 0 0 3.927651,224.68167 H 134.1447 c 0.56161,-12.72411 2.67825,-28.50188 8.61499,-43.09092 z m 176.661504,0 c -14.27121,13.10564 -27.60733,29.58761 -37.56073,43.09092 h 73.3721 c 4.47018,-16.79061 9.35068,-31.26371 13.86562,-43.09092 z m 70.85787,0 c -2.41384,11.76417 -4.9032,26.20707 -6.94831,43.09092 H 360.4832 c -8.32133,-10.88917 -20.66988,-26.17008 -36.35141,-43.09092 z m 109.17313,0 c 6.63611,15.24089 6.92441,30.5373 5.57882,43.09092 h 132.64857 a 254.0155,253.71401 0 0 0 -9.22481,-43.09092 z M 2.90181,234.44783 A 254.0155,253.71401 0 0 0 1.984498,255.9933 254.0155,253.71401 0 0 0 2.90181,277.53876 h 211.89923 c 2.25762,-15.52555 5.14325,-29.93448 8.3385,-43.09093 h -77.8863 c -6.46396,9.27617 -10.33076,15.56549 -10.33076,15.56549 0,0 -0.82623,-6.14945 -0.9354,-15.56549 z m 249.72093,0 c -1.3692,13.09684 -2.4456,27.49209 -3.02068,43.09093 h 259.49613 a 254.0155,253.71401 0 0 0 0.91731,-21.54546 254.0155,253.71401 0 0 0 -0.91731,-21.54547 H 374.02584 c -0.445,2.5469 -0.90878,4.89768 -1.32817,7.01751 0,0 -1.69726,-2.53821 -4.94056,-7.01751 z M 3.927651,287.30493 a 254.0155,253.71401 0 0 0 9.224805,43.09091 H 214.04393 c -1.29238,-15.40742 -1.57503,-30.04388 -0.41861,-43.09091 z m 245.385009,0 c -0.30355,13.54349 -0.22032,27.92598 0.36951,43.09091 h 249.16537 a 254.0155,253.71401 0 0 0 9.22481,-43.09091 z M 16.369511,340.16201 a 254.0155,253.71401 0 0 0 19.878554,43.09091 H 221.4677 c -2.69781,-14.4523 -4.96108,-29.01285 -6.4832,-43.09091 z m 233.842379,0 c 1.15864,15.47765 3.81286,29.83979 7.51679,43.09091 h 218.02325 a 254.0155,253.71401 0 0 0 19.87856,-43.09091 z M 42.217052,393.01909 a 254.0155,253.71401 0 0 0 34.88372,43.09093 H 233.09561 c -3.40902,-13.67281 -6.76794,-28.2531 -9.73902,-43.09093 z m 218.490958,0 c 5.34985,16.15926 12.22007,30.51982 19.68733,43.09093 h 154.50389 a 254.0155,253.71401 0 0 0 34.88371,-43.09093 z M 87.529722,445.87618 a 254.0155,253.71401 0 0 0 166.229968,63.8208 c -3.67805,-12.0825 -10.85464,-35.49828 -18.18088,-63.8208 z m 199.010328,0 c 17.5887,26.43772 36.99259,43.60598 47.33592,51.61309 a 254.0155,253.71401 0 0 0 90.59431,-51.61309 z" />
<path
id="path27"
style="display:inline;mix-blend-mode:multiply;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient27);stroke-width:3;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke fill markers"
style="display:inline;mix-blend-mode:multiply;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke fill markers"
d="m 318.98012,441.7375 c -9.87518,-6.73978 -64.39137,-49.0272 -67.68975,-127.81978 -3.69298,-88.21893 15.36468,-141.91029 15.36468,-141.91029 0,0 16.00378,0.99513 39.80316,26.53195 23.79939,25.53753 37.74965,46.43102 37.74965,46.43102 3.91262,-19.79992 12.84563,-66.32402 -60.72865,-87.55523 0,0 12.82326,-5.38883 39.3925,-3.81382 26.56907,1.57572 81.6822,21.93799 81.6822,21.93799 0,0 -14.79766,-20.63773 -49.47063,-34.94295 -34.67291,-14.30533 -76.1182,0.23644 -76.1182,0.23644 0,0 3.86959,-12.43127 27.22669,-26.38478 23.35718,-13.9537 49.27409,-26.501533 49.27409,-26.501533 0,0 -21.97854,-0.26548 -47.67725,8.44535 -6.68948,2.267506 -13.15863,5.094213 -19.05208,8.226563 l 16.05803,-40.634103 -4.4617,-1.89059 -5.1305,-0.95965 c 0,0 -11.24072,33.12428 -16.92051,49.576513 -12.13137,7.68489 -20.11005,14.87735 -20.11005,14.87735 0,0 -21.90573,-25.09227 -42.79668,-35.527803 -26.03412,-13.00525 -86.88249,-13.90359 -94.0044,10.401173 0,0 13.56804,-7.884703 34.70032,-2.080917 21.13214,5.803997 30.3644,9.287307 30.3644,9.287307 l 29.02989,-5.30681 -7.89811,8.95527 c 0,0 13.8496,7.21324 21.33822,13.68063 7.48859,6.46722 10.9757,10.11472 10.9757,10.11472 0,0 -13.02739,-4.39388 -53.03507,-1.40893 -40.00771,2.98473 -79.40016,45.60209 -79.40016,45.60209 0,0 38.57037,-12.93531 61.34393,-17.24677 22.77354,-4.31126 44.52166,-6.46757 44.52166,-6.46757 0,0 -17.23298,5.97003 -35.69792,31.00932 -18.46522,25.03987 -13.13146,64.83866 -13.13146,64.83866 0,0 29.33874,-47.7577 57.44675,-63.84249 28.10798,-16.08527 34.0799,-15.6238 34.0799,-15.6238 0,0 -22.56785,39.13486 -31.39017,101.98268 -8.03005,57.2039 26.77689,163.75449 31.1572,178.89699"
sodipodi:nodetypes="cscsccscscscsccccccscscccscscscscscsc"
inkscape:label="MainOutline"

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 14 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 256 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 335 KiB

After

Width:  |  Height:  |  Size: 556 KiB

+590 -584
View File
File diff suppressed because it is too large Load Diff
+590 -584
View File
File diff suppressed because it is too large Load Diff
+592 -586
View File
File diff suppressed because it is too large Load Diff
+592 -586
View File
File diff suppressed because it is too large Load Diff
+590 -584
View File
File diff suppressed because it is too large Load Diff
+592 -586
View File
File diff suppressed because it is too large Load Diff
+590 -584
View File
File diff suppressed because it is too large Load Diff
+592 -586
View File
File diff suppressed because it is too large Load Diff
+588 -582
View File
File diff suppressed because it is too large Load Diff
+590 -584
View File
File diff suppressed because it is too large Load Diff
+590 -584
View File
File diff suppressed because it is too large Load Diff
+588 -582
View File
File diff suppressed because it is too large Load Diff
+590 -584
View File
File diff suppressed because it is too large Load Diff
+1365 -1446
View File
File diff suppressed because it is too large Load Diff
+590 -584
View File
File diff suppressed because it is too large Load Diff
+592 -586
View File
File diff suppressed because it is too large Load Diff
+590 -582
View File
File diff suppressed because it is too large Load Diff
+650 -647
View File
File diff suppressed because it is too large Load Diff
+590 -584
View File
File diff suppressed because it is too large Load Diff
+590 -584
View File
File diff suppressed because it is too large Load Diff
+590 -582
View File
File diff suppressed because it is too large Load Diff
+590 -584
View File
File diff suppressed because it is too large Load Diff
+590 -584
View File
File diff suppressed because it is too large Load Diff
+592 -586
View File
File diff suppressed because it is too large Load Diff
+592 -586
View File
File diff suppressed because it is too large Load Diff
+590 -582
View File
File diff suppressed because it is too large Load Diff
+590 -584
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 27 KiB

+1 -1
View File
@@ -46,7 +46,7 @@ Qt Widgets appears to be broken. For now, add `-DENABLE_QT=OFF` to your configur
This is needed for some dependencies that call cc directly (tz):
```sh
echo '#!/bin/sh -e' >cc
echo '#!/bin/sh' >cc
echo 'gcc $@' >>cc
chmod +x cc
export PATH="$PATH:$PWD"
-8
View File
@@ -9,11 +9,3 @@ At the moment of writing, we do not support CFW such as Atmosphere, due to:
We do however, maintain HLE compatibility with the former mentioned CFW, applications that require Atmosphere to run will run fine in the emulator without any adjustments.
If they don't run - then that's a bug!
## Atmosphere
Fusee Galee, the bootloader and other low-level mechanisms are not emulated at the moment.
Having OFW is recommended, but may not be required (untested).
Extract the contents of Atmosphere into `sdmc`. Then to launch simply use `-hlaunch` instead (orthogonal to `-qlaunch`).
-3
View File
@@ -10,9 +10,6 @@ There are two main applications, an SDL-based app (`eden-cli`) and a Qt based ap
- `-u <number>`: Select the index of the user to load as.
- `-input-profile <name>`: Specifies input profile name to use (for player #0 only).
- `-qlaunch`: Launch QLaunch.
- `-hlaunch`: Launch homebrew launcher `nx-hbloader`.
- Requires a copy of Atmosphere to be extracted onto `sdmc`.
- This is a shorthand for `<eden folder>/sdmc/atmosphere/hbl.nsp`.
- `-setup`: Launch setup applet.
## eden-cli
-4
View File
@@ -44,10 +44,6 @@ Various graphical filters exist - each of them aimed at a specific target/image
- **MMPX**: Nearest-neighbour filter aimed at providing higher pixel-art quality.
- **Pros**: Offers decent pixel-art upscaling.
- **Cons**: Only works for pixel-art.
- **SGSR**: Uses Snapdragon Studios Game Super Resolution to enhance image quality (similar to FSR, but for Adreno devices).
- **Pros**: Optimized for Adreno devices.
- **Cons**: Doesn't play nicely with non-Adreno devices.
- **SGSR Edge**: Almost the same pipeline as SGSR, but with improved edge detection.
### Anisotropy values
+1 -1
View File
@@ -59,7 +59,7 @@ EmuDeck will automatically create an *Emulators - Emulators* parser for ***Steam
4. Paste the following code into the contents of the file, save and close the file.
```bash
#!/bin/sh -e
#!/bin/bash
emuName="eden" #parameterize me
. "$HOME/.config/EmuDeck/backend/functions/all.sh"
-1
View File
@@ -13,7 +13,6 @@ The main origin repository is always at https://git.eden-emu.dev/eden-emu/eden.
- https://github.com/eden-emulator/mirror
- https://git.crueter.xyz/mirror/eden
- https://codeberg.org/eden-emu/eden
- https://collective.taymaerz.de/eden/eden
Other mirrors obviously exist on the internet, but we can't guarantee their reliability and/or availability.
-5
View File
@@ -82,11 +82,6 @@ if (ARCHITECTURE_riscv64)
AddJsonPackage(biscuit)
endif()
# Lagoon
if (ARCHITECTURE_loongarch64)
AddJsonPackage(lagoon)
endif()
# Vulkan stuff
AddDependentPackages(vulkan-headers vulkan-utility-libraries)
-6
View File
@@ -67,12 +67,6 @@
"tag": "v%VERSION%",
"hash": "9697e80a7d5d9bcb3ce51051a9a24962fb90ca79d215f1f03ae6b58da8ba13a63b5dda1b4dde3d26ac6445029696b8ef2883f4e5a777b342bba01283ed293856"
},
"lagoon": {
"repo": "loongson-community/lagoon",
"tag": "%VERSION%",
"version": "1.0.0",
"hash": "b9380f99c6effaeccc6d8f81d4942e852c11ad28613df637e155451556ae5826f93765bee57a5c87a9740d2bd1db463ad0f55a947772fe9d57eeabae3efa373e"
},
"libadrenotools": {
"repo": "eden-emulator/libadrenotools",
"sha": "8ba23b42d7",
-3
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 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
+6 -7
View File
@@ -1,7 +1,5 @@
#!/bin/sh -e
#!/bin/sh
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2015 Citra Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
@@ -12,19 +10,20 @@ paths_to_check="src/ CMakeLists.txt"
# If there are whitespace errors, print the offending file names and fail.
if ! git diff --cached --check -- $paths_to_check ; then
cat<<EOF
cat<<END
Error: This commit would contain trailing spaces or tabs, which is against this repo's policy.
Please correct those issues before committing. (Use 'git diff --check' for more details)
If you know what you are doing, you can try 'git commit --no-verify' to bypass the check
EOF
END
exit 1
fi
# Check for tabs, since tab-in-indent catches only those at the beginning of a line
if git diff --cached -- $paths_to_check | egrep '^\+.* '; then
cat<<EOF
cat<<END
Error: This commit would contain a tab, which is against this repo's policy.
If you know what you are doing, you can try 'git commit --no-verify' to bypass the check.
EOF
END
exit 1
fi
Executable → Regular
-4
View File
@@ -1,7 +1,3 @@
#!/usr/bin/nix-shell
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
let
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-24.05";
pkgs = import nixpkgs { config = {}; overlays = []; };
+1 -22
View File
@@ -8,7 +8,7 @@
include_directories(.)
# Dynarmic
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64) AND NOT YUZU_STATIC_ROOM)
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64) AND NOT YUZU_STATIC_ROOM)
add_subdirectory(dynarmic)
add_library(dynarmic::dynarmic ALIAS dynarmic)
endif()
@@ -21,41 +21,20 @@ if (YUZU_STATIC_BUILD)
add_compile_definitions(QT_STATICPLUGIN)
endif()
# Build identifiers
if (NIGHTLY_BUILD)
add_compile_definitions(NIGHTLY_BUILD)
endif()
# Legacy (android only)
if (YUZU_LEGACY)
message(WARNING "Making legacy build. Performance may suffer.")
add_compile_definitions(YUZU_LEGACY)
endif()
# Genshin Spoof (android only)
if (GENSHIN_SPOOF)
message(WARNING "Making Genshin spoof build")
add_compile_definitions(GENSHIN_SPOOF)
endif()
# Build ID (mingw only right now)
# Pretty much just refers to the CI "target" for this build
if (NOT BUILD_ID)
if (ARCHITECTURE_x86_64)
set(BUILD_ID amd64)
elseif(ARCHITECTURE_arm64)
if (WIN32)
set(BUILD_ID arm64)
else()
set(BUILD_ID aarch64)
endif()
else()
set(BUILD_ID "${ARCHITECTURE}")
endif()
endif()
add_compile_definitions(BUILD_ID="${BUILD_ID}")
# Set compilation flags
if (MSVC AND NOT CXX_CLANG)
set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE)
@@ -127,8 +127,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
super.onCreate(savedInstanceState)
NativeConfig.reloadGlobalConfig()
InputHandler.updateControllerData()
val players = NativeConfig.getInputSettings(true)
var hasConfiguredControllers = false
@@ -16,7 +16,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_USE_SPEED_LIMIT("use_speed_limit"),
USE_CUSTOM_CPU_TICKS("use_custom_cpu_ticks"),
SKIP_CPU_INNER_INVALIDATION("skip_cpu_inner_invalidation"),
ANTIFLICKER("antiflicker"),
FIX_BLOOM_EFFECTS("fix_bloom_effects"),
EMULATE_BGR565("emulate_bgr565"),
RESCALE_HACK("rescale_hack"),
@@ -750,13 +750,6 @@ abstract class SettingsItem(
descriptionId = R.string.skip_cpu_inner_invalidation_description
)
)
put(
SwitchSetting(
BooleanSetting.ANTIFLICKER,
titleId = R.string.antiflicker,
descriptionId = R.string.antiflicker_description
)
)
put(
SwitchSetting(
BooleanSetting.FIX_BLOOM_EFFECTS,
@@ -76,25 +76,18 @@ class SettingsFragmentPresenter(
}
}
private fun isSharpnessScalingFilterSelected(): Boolean {
private fun isFsrScalingFilterSelected(): Boolean {
val fsrFilterValue = resolveFsrScalingFilterValue() ?: return false
val needsGlobal = getNeedsGlobalForKey(IntSetting.RENDERER_SCALING_FILTER.key)
val selectedFilter = IntSetting.RENDERER_SCALING_FILTER.getInt(needsGlobal)
return selectedFilter in resolveSharpnessScalingFilterValues()
return selectedFilter == fsrFilterValue
}
private fun resolveSharpnessScalingFilterValues(): Set<Int> {
private fun resolveFsrScalingFilterValue(): Int? {
val names = context.resources.getStringArray(R.array.rendererScalingFilterNames)
val values = context.resources.getIntArray(R.array.rendererScalingFilterValues)
val sharpnessFilterNames = setOf(
context.getString(R.string.scaling_filter_fsr),
context.getString(R.string.scaling_filter_sgsr),
context.getString(R.string.scaling_filter_sgsr_edge),
)
return names.asSequence()
.mapIndexedNotNull { index, name ->
if (name in sharpnessFilterNames && index in values.indices) values[index] else null
}
.toSet()
val fsrIndex = names.indexOf(context.getString(R.string.scaling_filter_fsr))
return if (fsrIndex in values.indices) values[fsrIndex] else null
}
// Allows you to show/hide abstract settings based on the paired setting key
@@ -274,7 +267,7 @@ class SettingsFragmentPresenter(
add(IntSetting.RENDERER_RESOLUTION.key)
add(IntSetting.RENDERER_VSYNC.key)
add(IntSetting.RENDERER_SCALING_FILTER.key)
if (isSharpnessScalingFilterSelected()) {
if (isFsrScalingFilterSelected()) {
add(IntSetting.FSR_SHARPENING_SLIDER.key)
}
add(IntSetting.RENDERER_ANTI_ALIASING.key)
@@ -298,7 +291,6 @@ class SettingsFragmentPresenter(
add(IntSetting.FAST_GPU_TIME.key)
add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key)
add(BooleanSetting.ANTIFLICKER.key)
add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
add(BooleanSetting.EMULATE_BGR565.key)
add(BooleanSetting.RESCALE_HACK.key)
@@ -1090,7 +1090,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private fun addQuickSettings() {
binding.quickSettingsSheet.apply {
val container = binding.quickSettingsSheet.findViewById<ViewGroup>(R.id.quick_settings_container)
val isSharpnessFilterSelected = isSharpnessScalingFilterSelected()
val isFsrSelected = isFsrScalingFilterSelected()
container.removeAllViews()
@@ -1176,7 +1176,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
addQuickSettings()
}
if (isSharpnessFilterSelected) {
if (isFsrSelected) {
quickSettings.addSliderSetting(
R.string.fsr_sharpness,
container,
@@ -1197,24 +1197,17 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
private fun isSharpnessScalingFilterSelected(): Boolean {
private fun isFsrScalingFilterSelected(): Boolean {
val fsrFilterValue = resolveFsrScalingFilterValue() ?: return false
val selectedFilter = IntSetting.RENDERER_SCALING_FILTER.getInt(needsGlobal = false)
return selectedFilter in resolveSharpnessScalingFilterValues()
return selectedFilter == fsrFilterValue
}
private fun resolveSharpnessScalingFilterValues(): Set<Int> {
private fun resolveFsrScalingFilterValue(): Int? {
val names = resources.getStringArray(R.array.rendererScalingFilterNames)
val values = resources.getIntArray(R.array.rendererScalingFilterValues)
val sharpnessFilterNames = setOf(
getString(R.string.scaling_filter_fsr),
getString(R.string.scaling_filter_sgsr),
getString(R.string.scaling_filter_sgsr_edge),
)
return names.asSequence()
.mapIndexedNotNull { index, name ->
if (name in sharpnessFilterNames && index in values.indices) values[index] else null
}
.toSet()
val fsrIndex = names.indexOf(getString(R.string.scaling_filter_fsr))
return if (fsrIndex in values.indices) values[fsrIndex] else null
}
private fun openQuickSettingsMenu() {
@@ -19,9 +19,6 @@ AndroidConfig::AndroidConfig(const std::string& config_name, ConfigType config_t
}
void AndroidConfig::ReloadAllValues() {
// Ensure the INI file is current before reloading values.
SetUpIni();
Reload();
ReadAndroidValues();
SaveAndroidValues();
Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 61 KiB

@@ -459,9 +459,6 @@
<string name="renderer_anti_aliasing">Méthode d\'anticrénelage</string>
<string name="advanced">Avancé</string>
<string name="renderer_accuracy">Mode GPU</string>
<string name="dma_accuracy">Précision DMA</string>
<string name="dma_accuracy_description">Contrôle la précision du DMA. Une précision sûre peut résoudre les problèmes dans certains jeux, mais peut aussi affecter les performances dans certains cas. Si vous n\'êtes pas sûr, laissez ce paramètre sur Par défaut.</string>
<string name="anisotropic_filtering">Filtrage anisotropique</string>
@@ -477,29 +474,15 @@
<string name="use_disk_shader_cache_description">Réduire les saccades en stockant et en chargeant localement les shaders générés</string>
<string name="renderer_force_max_clock">Forcer les fréquences maximales (Adreno uniquement)</string>
<string name="renderer_force_max_clock_description">Forcer le GPU à fonctionner à ses fréquences maximales possibles (les contraintes thermiques seront toujours appliquées).</string>
<string name="renderer_asynchronous_gpu_emulation">Émulation GPU asynchrone </string>
<string name="renderer_asynchronous_gpu_emulation_description">Ce contournement peut améliorer les performances en faisant tourner l\'émulation GPU de manière asynchrone au détriment de la fidélité graphique et de la stabilité (plantages plus fréquents) dus à des erreurs de cadence.</string>
<string name="renderer_reactive_flushing">Utiliser le vidage réactif</string>
<string name="renderer_reactive_flushing_description">Améliore la précision du rendu dans certains jeux au détriment des performances.</string>
<string name="enable_buffer_history">Activer l\'historique du tampon</string>
<string name="hacks">Contournements</string>
<string name="fast_gpu_time">Temps GPU rapide</string>
<string name="skip_cpu_inner_invalidation">Ignorer l\'invalidation interne du CPU</string>
<string name="skip_cpu_inner_invalidation_description">Ignore certaines invalidations de cache côté CPU lors des mises à jour mémoire, réduisant l\'utilisation du CPU et améliorant ses performances. Peut causer des bugs ou plantages sur certains jeux.</string>
<string name="emulate_bgr565">Emuler BGR565</string>
<string name="renderer_asynchronous_shaders">Utiliser les shaders asynchrones</string>
<string name="renderer_asynchronous_shaders_description">Compile les shaders de manière asynchrone. Cela peut réduire les saccades mais peut aussi provoquer des problèmes graphiques.</string>
<string name="gpu_unswizzle_disabled">Désactivé</string>
<string name="gpu_unswizzle_default_button">Par défaut</string>
<string name="extensions">Extensions</string>
<string name="dyna_state">État dynamique étendu</string>
<string name="disabled">Désactivé</string>
<string name="vertex_input_dynamic_state">État dynamique d\'entrée de sommet</string>
<string name="sample_shading_fraction">Échantillonnage de shading</string>
<string name="display">Affichage</string>
<string name="renderer_screen_layout">Orientation</string>
@@ -525,21 +508,6 @@
<string name="flush_by_line">Vider les journaux de débogage ligne par ligne</string>
<string name="flush_by_line_description">Vide les journaux de débogage à chaque ligne écrite, facilitant le débogage en cas de plantage ou de gel.</string>
<!-- GPU Logging strings -->
<string name="gpu_logging_header">Journalisation GPU</string>
<string name="gpu_logging_enabled">Activer la journalisation GPU</string>
<string name="gpu_log_level">Niveau de journalisation</string>
<string name="gpu_log_vulkan_calls">Journaliser les appels API Vulkan</string>
<string name="gpu_log_shader_dumps">Extraire les shaders</string>
<string name="gpu_log_shader_dumps_description">Sauvegarder le shader SPIR-V complié dans les fichiers</string>
<string name="gpu_log_memory_tracking">Monitorer la mémoire GPU</string>
<string name="gpu_log_memory_tracking_description">Monitorer les allocations et désallocations de la mémoire GPU</string>
<string name="gpu_log_driver_debug">Informations de débogage du pilote</string>
<string name="gpu_log_ring_buffer_size_description">Nombre d\'appels Vulkans récents à monitorer (par défaut : 512)</string>
<string name="gpu_log_ring_buffer_size_hint">64 à 4096 entrées</string>
<string name="general">Général</string>
<!-- Audio settings strings -->
<string name="audio_output_engine">Moteur de sortie</string>
<string name="audio_volume">Volume</string>
@@ -622,7 +590,6 @@
<!-- Miscellaneous -->
<string name="slider_default">Par défaut</string>
<string name="default_string">Par défaut</string>
<string name="loading">Chargement...</string>
<string name="shutting_down">Extinction en cours...</string>
<string name="reset_setting_confirmation">Voulez-vous réinitialiser ce paramètre à sa valeur par défaut ?</string>
@@ -661,7 +628,6 @@
<string name="select_gpu_driver_default">Par défaut</string>
<string name="select_gpu_driver_error">Pilote non valide sélectionné</string>
<string name="driver_already_installed">Pilote déjà installé</string>
<string name="installed_label">%1$s (Installé)</string>
<string name="system_gpu_driver">Pilote du GPU du système</string>
<string name="installing_driver">Installation du pilote...</string>
@@ -681,7 +647,6 @@
<string name="installing">Installation en cours…</string>
<string name="latest">Dernière</string>
<string name="recommended_driver">Pilote recommandé :</string>
<string name="gpu_model">Modèle GPU</string>
<string name="unsupported_gpu">GPU non pris en charge</string>
<string name="unsupported_gpu_warning">Votre GPU ne prend pas en charge l\'injection de pilotes. Il n\'est pas recommandé de définir des pilotes personnalisés.</string>
@@ -691,9 +656,6 @@
<string name="preferences_system_description">Mode TV, région, langue</string>
<string name="preferences_graphics">Vidéo</string>
<string name="preferences_graphics_description">Niveau de précision, résolution, cache de shaders</string>
<string name="quick_settings">Paramètres rapides</string>
<string name="enable_quick_settings">Activer les paramètres rapides</string>
<string name="enable_quick_settings_description">Autoriser l\'accès aux paramètres rapides par le balayage de l\'écran et le bouton du menu</string>
<string name="preferences_audio">Audio</string>
<string name="preferences_audio_description">Moteur de sortie, volume</string>
<string name="preferences_controls">Contrôles</string>
@@ -701,25 +663,6 @@
<string name="preferences_player">Joueur %d</string>
<string name="preferences_debug">Débogage</string>
<string name="preferences_debug_description">Débogage CPU/GPU, API graphique, fastmem</string>
<string name="preferences_custom_paths">Chemins personnalisés</string>
<string name="preferences_custom_paths_description">Sauvegarder le répertoire des données</string>
<!-- Custom Paths settings -->
<string name="custom_save_directory">Sauvegarder le répertoire des données</string>
<string name="custom_save_directory_description">Définir un chemin personnalisé pour les sauvegardes</string>
<string name="reset_to_nand">Réinitialiser par défaut</string>
<string name="migrate_save_data">Migrer les données de sauvegarde</string>
<string name="save_migration_complete">Données de sauvegarde supprimées avec succès</string>
<string name="save_migration_failed">Échec de la migration des données de sauvegarde</string>
<string name="destination_has_saves">La destination contient déjà des données. Voulez-vous les écraser \?</string>
<string name="grant_permission">Accorder la permission</string>
<string name="custom_nand_directory">Dossier NAND</string>
<string name="custom_nand_directory_description">Définir un chemin personnalisé pour le stockage NAND</string>
<string name="custom_sdmc_directory">Répertoire de carte SD</string>
<string name="custom_sdmc_directory_description">Définir un chemin personnalisé pour le stockage de la carte SD virtuelle</string>
<string name="path_set">Chemin défini avec succès</string>
<string name="skip_migration">Sauter</string>
<!-- Game properties -->
<string name="info">Info</string>
<string name="info_description">ID du programme, développeur, version</string>
@@ -733,7 +676,6 @@
<string name="copy_details">Copier les détails</string>
<string name="add_ons">Extensions</string>
<string name="add_ons_description">Activer les mods, mises à jour et DLC</string>
<string name="playtime">Temps de jeu :</string>
<string name="reset_playtime">Réinitialiser le Temps de Jeu</string>
<string name="reset_playtime_description">Réinitialiser le temps de jeu du jeu actuel à 0 seconde</string>
<string name="reset_playtime_warning_description">Cela effacera les données de temps de jeu du jeu actuel. Êtes-vous sûr\?</string>
@@ -741,9 +683,6 @@
<string name="edit_playtime">Modifier le Temps de Jeu</string>
<string name="hours">Heures</string>
<string name="minutes">Minutes</string>
<string name="hours_abbr">h</string>
<string name="minutes_abbr">m</string>
<string name="seconds_abbr">s</string>
<string name="hours_must_be_between_0_and_9999">Les heures doivent être comprises entre 0 et 9999</string>
<string name="minutes_must_be_between_0_and_59">Les minutes doivent être comprises entre 0 et 59</string>
<string name="seconds_must_be_between_0_and_59">Les secondes doivent être comprises entre 0 et 59</string>
@@ -775,7 +714,6 @@
<string name="confirm_uninstall">Confirmer la désinstallation</string>
<string name="confirm_uninstall_description">Êtes-vous sûr de vouloir désinstaller cette extension ?</string>
<string name="verify_integrity">Vérifier l\'intégrité</string>
<string name="verifying">Vérification...</string>
<string name="verify_success">La vérification de l\'intégrité a réussi !</string>
<string name="verify_failure">La vérification de l\'intégrité a échoué !</string>
<string name="verify_failure_description">Le contenu d\'un fichier peut être corrompu</string>
@@ -847,7 +785,6 @@
<string name="emulation_control_opacity">Opacité</string>
<string name="emulation_touch_overlay_reset">Réinitialiser l\'overlay</string>
<string name="emulation_touch_overlay_edit">Modifier l\'overlay</string>
<string name="emulation_snap_to_grid">Aimanter à la grille</string>
<string name="emulation_pause">Mettre en pause l\'émulation</string>
<string name="emulation_unpause">Reprendre l\'émulation</string>
<string name="emulation_input_overlay">Options de l\'overlay</string>
@@ -901,32 +838,6 @@
<string name="clock_boost">Boost (1700MHz)</string>
<string name="clock_fast">Rapide (2000MHz)</string>
<!-- GPU overclock factors -->
<string name="off">Désactivé</string>
<string name="fast_gpu_medium">Moyen (256)</string>
<string name="fast_gpu_high">Élevé (512)</string>
<!-- GPU swizzle texture size -->
<string name="gpu_texturesizeswizzle_verysmall">Très petit (16 Mo)</string>
<string name="gpu_texturesizeswizzle_small">Petit (32 Mo)</string>
<string name="gpu_texturesizeswizzle_normal">Normal (128 Mo)</string>
<string name="gpu_texturesizeswizzle_large">Large (256 Mo)</string>
<string name="gpu_texturesizeswizzle_verylarge">Très large (512 Mo)</string>
<!-- GPU swizzle streams -->
<string name="gpu_swizzle_verylow">Très faible (4 Mo)</string>
<string name="gpu_swizzle_low">Faible (8 Mo)</string>
<string name="gpu_swizzle_normal">Normal (16 Mo)</string>
<string name="gpu_swizzle_medium">Moyen (32 Mo)</string>
<string name="gpu_swizzle_high">Élevé (64 Mo)</string>
<!-- GPU swizzle chunks -->
<string name="gpu_swizzlechunk_verylow">Très faible (32)</string>
<string name="gpu_swizzlechunk_low">Faible (64)</string>
<string name="gpu_swizzlechunk_normal">Normal (128)</string>
<string name="gpu_swizzlechunk_medium">Moyen (256)</string>
<string name="gpu_swizzlechunk_high">Élevé (512)</string>
<!-- Temperature Units -->
<string name="temperature_celsius">Celsius</string>
<string name="temperature_fahrenheit">Fahrenheit</string>
@@ -942,11 +853,6 @@
<string name="renderer_none">Aucune</string>
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Rapide</string>
<string name="renderer_accuracy_medium">Moyen</string>
<string name="renderer_accuracy_high">Précis</string>
<!-- DMA Accuracy -->
<string name="dma_accuracy_default">Défaut</string>
<string name="dma_accuracy_unsafe">Dangereux</string>
@@ -980,26 +886,6 @@
<string name="cpu_accuracy_paranoid">Paranoïaque</string>
<string name="cpu_accuracy_debugging">Débogage</string>
<!-- Freedreno Settings -->
<string name="freedreno_settings_title">Paramètres de Freedreno</string>
<string name="gpu_driver_settings">Paramètres du pilote GPU</string>
<string name="freedreno_presets">Préréglages rapides</string>
<string name="freedreno_current_settings">Paramètres actuels</string>
<string name="freedreno_debug">Paramètres avancés</string>
<string name="freedreno_var_value">Valeur de la variable</string>
<string name="freedreno_add_variable">Ajouter la variable</string>
<string name="freedreno_clear_all">Effacer tout</string>
<string name="freedreno_saved">Configuration Freedreno sauvegardée</string>
<string name="freedreno_cleared_all">Toutes les variables Freedreno effacées</string>
<string name="freedreno_variable_added">Variable %1$s ajouté</string>
<string name="freedreno_preset_applied">Préréglage \'%1$s\' appliqué</string>
<string name="freedreno_error_empty_name">Le nom de la variable ne peut pas être vide</string>
<string name="freedreno_error_setting_variable">Échec de l\'assignation de la variable</string>
<string name="freedreno_info_title">À propos de la configuration Freedreno</string>
<string name="freedreno_per_game_title">Paramètres de Freedreno</string>
<string name="freedreno_per_game_description">Configurer les paramètres du pilote GPU pour ce jeu</string>
<string name="freedreno_per_game_saved">Configuration Freedreno sauvegardée</string>
<!-- Gamepad Buttons -->
<string name="gamepad_d_pad">Pavé directionnel</string>
<string name="gamepad_left_stick">Stick gauche</string>
@@ -1016,37 +902,26 @@
<string name="theme_material_you">Material You</string>
<string name="app_settings">Paramètres de lApp</string>
<string name="theme_and_color">Thème et Couleur</string>
<string name="fullscreen_mode">Mode plein écran</string>
<!-- Theme Modes -->
<string name="change_theme_mode">Changer le mode de thème</string>
<string name="theme_mode_follow_system">Automatique</string>
<string name="theme_mode_light">Lumineux</string>
<string name="theme_mode_dark">Sombre</string>
<string name="multiplier_none">Aucun</string>
<!-- Black backgrounds theme -->
<string name="use_black_backgrounds">Arrière-plan noir</string>
<string name="use_black_backgrounds_description">Lorsque vous utilisez le thème sombre, appliquer un arrière-plan noir.</string>
<!-- Buttons -->
<string name="enable_folder_button">Dossier</string>
<string name="enable_folder_button_description">Afficher le bouton pour ajouter les dossiers de jeu</string>
<string name="enable_qlaunch_button">QLaunch</string>
<string name="enable_qlaunch_button_description">Afficher le bouton pour lancer QLaunch</string>
<!-- App Language -->
<string name="app_language">Language de l\'application</string>
<string name="app_language_description">Changer la langue de l\'interface</string>
<string name="app_language_system">Suivre le système</string>
<!-- Static Themes -->
<string name="static_theme_color">Couleur du thème</string>
<string name="eden_theme">Eden</string>
<string name="violet">Violet (Par défaut)</string>
<string name="blue">Bleu</string>
<string name="cyan">Cyan</string>
<string name="red">Rouge</string>
<string name="green">Vert</string>
<string name="yellow">Jaune</string>
<string name="orange">Orange</string>
<string name="pink">Rose</string>
@@ -1065,8 +940,6 @@
<!-- Applet Modes -->
<string name="applets_menu">Applets</string>
<string name="applets_menu_description">Modifier les frontends et paramètres des applets</string>
<string name="applet_hle">Frontend personnalisé</string>
<string name="applet_lle">Applet réel</string>
@@ -1075,39 +948,7 @@
<string name="airplane_mode">Mode avion</string>
<string name="airplane_mode_description">Passe le mode avion au système d\'exploitation Switch</string>
<string name="enable_overlay">Activer l\'applet d\'overlay</string>
<!-- Profile Management -->
<string name="profile_manager">Gestionnaire de profil</string>
<string name="profile_manager_description">Gérer les profils utilisateurs</string>
<string name="profile_add_user">Ajouter un utilisateur</string>
<string name="profile_new_user">Nouvel utilisateur</string>
<string name="profile_edit_user">Modifier l\'utilisateur</string>
<string name="profile_edit">Éditer</string>
<string name="profile_delete">Supprimer</string>
<string name="profile_username">Nom d\'utilisateur</string>
<string name="profile_uuid">ID de l\'utilisateur (UUID)</string>
<string name="profile_uuid_description">Il s\'agit de l\'identifiant unique de ce profil utilisateur. Il ne peut pas être changé après sa création.</string>
<string name="profile_generate">Générer</string>
<string name="profile_avatar">Avatar de l\'utilisateur</string>
<string name="profile_select_image">Sélectionner l\'image</string>
<string name="profile_firmware_avatars">Avatars du firmware</string>
<string name="profile_firmware_avatars_unavailable">Avatars du firmware non disponibles. Veuillez installer le firmware pour utiliser cette fonctionnalité.</string>
<string name="profile_revert_image">Restaurer à la valeur par défaut</string>
<string name="profile_current_user">Utilisateur actuel</string>
<string name="profile_max_users_title">Nombre d\'Utilisateurs Maximum Atteint</string>
<string name="profile_max_users_message">Vous ne pouvez pas créer plus du 8 profils utilisateurs. Veuillez supprimer un des profils existants pour en créer un nouveau.</string>
<string name="profile_delete_confirm_title">Supprimer le profil \?</string>
<string name="profile_create_failed">Échec de la création du profil utilisateur</string>
<string name="profile_update_failed">Échec de la mise à jour du profil utilisateur</string>
<string name="profile_image_load_error">Échec du chargement de l\'image : %1$s</string>
<string name="profile_image_save_error">Échec de la sauvegarde de l\'image : %1$s</string>
<string name="error">Erreur</string>
<!-- Licenses screen strings -->
<string name="licenses">Licences</string>
<string name="license_fidelityfx_fsr_description">Mise à l\'échelle de haute qualité par AMD.</string>
<string name="external_content">Contenu externe</string>
<string name="add_folders">Ajouter un dossier</string>
<string name="percent">%1$d%%</string>
</resources>
</resources>
@@ -6,8 +6,8 @@
<string name="notification_permission_not_granted">알림 권한이 부여되지 않았습니다!</string>
<!-- Stats Overlay settings -->
<string name="process_ram">프로세스 RAM: %1$d MB</string>
<string name="shaders_prefix">구축 중</string>
<string name="shaders_suffix">개 셰이더</string>
<string name="shaders_prefix">셰이더</string>
<string name="shaders_suffix">빌드 중</string>
<string name="charging">(충전 중)</string>
<string name="system_info_label">시스템:</string>
@@ -3,7 +3,7 @@
<string name="app_disclaimer">Цей застосунок запускає ігри для ігрової консолі Nintendo Switch. Він не містить ігор чи ключів.<br /><br />Перш ніж почати, укажіть розташування файлу <![CDATA[<b> prod.keys </b>]]> у пам’яті вашого пристрою.<br /><br /><![CDATA[<a href=\"https://yuzu-mirror.github.io/help/quickstart\">Дізнатися більше</a>]]></string>
<string name="notice_notification_channel_name">Сповіщення та помилки</string>
<string name="notice_notification_channel_description">Показує сповіщення у разі виникнення проблем.</string>
<string name="notice_notification_channel_description">Виводить сповіщення у разі виникнення проблем.</string>
<string name="notification_permission_not_granted">Дозвіл на сповіщення не надано!</string>
<string name="app_notification_channel_description">Сповіщення емулятора Switch Eden</string>
<string name="app_notification_running">Eden працює</string>
@@ -106,7 +106,7 @@
<!-- NVDEC Emulation -->
<string name="nvdec_emulation">NVDEC模拟</string>
<string name="nvdec_emulation_description">播放过场与开场动画期间的视频解码处理方式(NVDEC)。</string>
<string name="nvdec_emulation_description">选择视频解码处理方式</string>
<string name="nvdec_emulation_none">禁用</string>
<!-- Optimize SPIRV output -->
@@ -181,14 +181,14 @@
<string name="multiplayer_hide_full_rooms">隐藏满员房间</string>
<string name="multiplayer_hide_empty_rooms">隐藏空房间</string>
<string name="multiplayer_tap_refresh_to_check_again">点击刷新重试</string>
<string name="multiplayer_search_public_lobbies">搜索游戏大厅</string>
<string name="multiplayer_search_public_lobbies">搜索房间</string>
<string name="multiplayer_preferred_game_name">首选游戏</string>
<string name="multiplayer_lobby_type">游戏大厅类型</string>
<string name="multiplayer_lobby_type">大厅类型</string>
<string name="multiplayer_room_name_error">长度需为3-20个字符</string>
<string name="multiplayer_required">必填</string>
<string name="multiplayer_token_required">需要Web令牌,请前往高级设置 -> 系统 -> 网络</string>
<string name="multiplayer_ip_error">IP格式无效</string>
<string name="multiplayer_username_error">必须为420个字符,且仅包含字母数字、点号、连字符、下划线和空格</string>
<string name="multiplayer_username_error">必须为4-20个字符(仅字母数字、点号、连字符、下划线和空格</string>
<string name="multiplayer_nickname_invalid">用户名无效,请在系统→网络中检查设置</string>
<string name="multiplayer_token_error">必须为48个字符,且仅包含小写字母a-z</string>
<string name="multiplayer_port_error">端口需为1-65535</string>
@@ -437,15 +437,15 @@
<string name="custom_cpu_ticks_description">设置自定义的CPU时钟值。更高的值可能提高性能,但也可能导致游戏卡顿。建议范围为77-21000。</string>
<string name="cpu_ticks">时钟</string>
<string name="memory_layout">内存布局</string>
<string name="memory_layout_description">(实验性) 更改模拟内存布局。此设置不会提性能,但可能有助于游戏通过 mods 来利用高分辨率。请不要在内存不大于 8GB 的手机上使用。仅适用于 DynamicJIT)后端。</string>
<string name="memory_layout_description">(实验性) 更改模拟内存布局。此设置不会提性能,但可能有助于通过模组使用高分辨率的游戏。不要在 RAM 为 8GB 或更少的手机上使用。</string>
<string name="generate">生成</string>
<!-- Network settings strings -->
<string name="web_token">网络令牌</string>
<string name="web_token_description">用于创建公共游戏大厅的 web token。这是一个仅包含小写字母 a-z 的 48 位字符串</string>
<string name="web_token_description">用于创建公共房间的网络令牌。它是一个48个字符的字符串,仅包含小写字母a-z。</string>
<string name="web_username">网络用户名</string>
<string name="web_username_description">多人游戏大厅中显示的用户名。用户名必须为420个字符,且仅可包含字母数字、连字符、点号、下划线和空格。</string>
<string name="web_username_description">多人游戏房间中显示的用户名。必须为4-20个字符(仅字母数字、连字符、点号、下划线和空格</string>
<string name="network">网络</string>
<!-- Graphics settings strings -->
@@ -453,7 +453,7 @@
<string name="renderer_vsync">垂直同步模式</string>
<string name="renderer_scaling_filter">窗口滤镜</string>
<string name="fsr_sharpness">FSR 锐化度</string>
<string name="fsr_sharpness_description">定使用 FSR 的动态对比度功能时的图像锐化程度</string>
<string name="fsr_sharpness_description">定使用 FSR 图像锐化程度</string>
<string name="renderer_anti_aliasing">抗锯齿方式</string>
@@ -496,7 +496,7 @@
<string name="fix_bloom_effects">修复 Bloom 效果</string>
<string name="fix_bloom_effects_description">减少《塞尔达传说:智慧的再现》(Adreno A6XX - A7XX/ Turnip)中的 bloom 模糊,并移除《Burnout》中的 bloom 效果。警告:可能会导致在其他游戏中出现图形异常。</string>
<string name="emulate_bgr565">模拟 BGR565</string>
<string name="emulate_bgr565_description">修复游戏中的颜色反转或是异常画面瑕疵或阴影问题</string>
<string name="emulate_bgr565_description">修复游戏中的颜色反转以及出现的异常画面瑕疵或奇怪阴影问题</string>
<string name="rescale_hack">启用旧版缩放处理</string>
<string name="rescale_hack_description">启用通过使用快速缩放路径,来为游戏提供缩放配置处理的传统处理方式</string>
<string name="renderer_asynchronous_shaders">使用异步着色器</string>
@@ -508,9 +508,9 @@
<string name="gpu_unswizzle_texture_size">GPU 还原最大纹理尺寸</string>
<string name="gpu_unswizzle_texture_size_description">设置基于 GPU 的纹理还原的最大尺寸(单位:MiB)。\n虽然 GPU 在处理中型和大型纹理时速度更快,但对于非常小的纹理,CPU 的效率可能更高。\n调整此设置,以便在 GPU 加速和 CPU 开销之间找到最佳平衡点。</string>
<string name="gpu_unswizzle_stream_size">GPU 还原流大小</string>
<string name="gpu_unswizzle_stream_size_description">设置用于 unswizzling 大型纹理时的每帧数据限制。较高的数值可以加快纹理加载速度,但增加帧延迟。而较低的数值可以降低 GPU 开销,但可能会导致可见的纹理 闪现</string>
<string name="gpu_unswizzle_stream_size_description">设置每帧还原大型纹理的数据限制。较高的数值可以加快纹理加载速度,但代价是增加帧延迟(影响响应速度/平滑度);较低的数值可以减少 GPU 开销,但可能会导致明显的纹理突然出现(Pop-in)现象</string>
<string name="gpu_unswizzle_chunk_size">GPU 还原块大小</string>
<string name="gpu_unswizzle_chunk_size_description">定义了 3D 纹理每批次处理的深度切片数量。增加此数值可在高性能 GPU 上提升吞吐效率,但在性能较弱的硬件上可能会导致卡顿或驱动超时</string>
<string name="gpu_unswizzle_chunk_size_description">定义了 3D 纹理在单个批次(Batch)中处理的深度切片(Depth Slices数量。增加此数值可以提升强力 GPU 吞吐效率,但在性能较弱的硬件上可能会引起卡顿或驱动程序超时(Driver Timeouts</string>
<string name="gpu_unswizzle_default_button">默认</string>
@@ -240,8 +240,6 @@
<item>@string/scaling_filter_bspline</item>
<item>@string/scaling_filter_mitchell</item>
<item>@string/scaling_filter_spline1</item>
<item>@string/scaling_filter_sgsr</item>
<item>@string/scaling_filter_sgsr_edge</item>
</string-array>
<integer-array name="rendererScalingFilterValues">
@@ -258,8 +256,6 @@
<item>10</item>
<item>11</item>
<item>12</item>
<item>13</item>
<item>14</item>
</integer-array>
<string-array name="rendererAntiAliasingNames">
@@ -1 +1 @@
<?xml version='1.0' encoding='utf-8'?><resources><color name='ic_launcher_background'>#1F143C</color></resources>
<?xml version='1.0' encoding='utf-8'?><resources><color name='ic_launcher_background'>#ffd700</color></resources>
@@ -468,8 +468,8 @@
<string name="renderer_resolution">Resolution (Handheld/Docked)</string>
<string name="renderer_vsync">VSync mode</string>
<string name="renderer_scaling_filter">Window adapting filter</string>
<string name="fsr_sharpness">FSR/SGSR sharpness</string>
<string name="fsr_sharpness_description">Determines how sharpened the image will look while using FSR or SGSR filters</string>
<string name="fsr_sharpness">FSR sharpness</string>
<string name="fsr_sharpness_description">Determines how sharpened the image will look while using FSR\'s dynamic contrast</string>
<string name="renderer_anti_aliasing">Anti-aliasing method</string>
@@ -509,8 +509,6 @@
<string name="fast_gpu_time_description">Forces most games to run at their highest native resolution. Use 256 for maximal performance and 512 for maximal graphics fidelity.</string>
<string name="skip_cpu_inner_invalidation">Skip CPU Inner Invalidation</string>
<string name="skip_cpu_inner_invalidation_description">Skips certain CPU-side cache invalidations during memory updates, reducing CPU usage and improving it\'s performance. This may cause glitches or crashes on some games.</string>
<string name="antiflicker">Anti-Flicker</string>
<string name="antiflicker_description">Forces GPU fence callbacks to wait for submitted GPU work. Use with Fast GPU mode, to avoid flicker with lower performance impact.</string>
<string name="fix_bloom_effects">Fix Bloom Effects</string>
<string name="fix_bloom_effects_description">Reduces bloom blur in LA/EOW (Adreno A6XX - A7XX/ Turnip), removes bloom in Burnout. Warning: may cause graphical artifacts in other games.</string>
<string name="emulate_bgr565">Emulate BGR565</string>
@@ -1080,8 +1078,6 @@
<string name="scaling_filter_bspline" translatable="false">B-Spline</string>
<string name="scaling_filter_mitchell" translatable="false">Mitchell</string>
<string name="scaling_filter_mmpx" translatable="false">MMPX</string>
<string name="scaling_filter_sgsr" translatable="false">Snapdragon GSR</string>
<string name="scaling_filter_sgsr_edge" translatable="false">Snapdragon GSR EdgeDir</string>
<!-- Anti-Aliasing -->
<string name="anti_aliasing_none">None</string>
@@ -29,96 +29,8 @@ void AudioRenderer::Start() {
CreateSinkStreams();
mailbox.Initialize(AppMailboxId::AudioRenderer);
// Main AudioRenderer thread, responsible for processing the command lists.
main_thread = std::jthread([this](std::stop_token stop_token) {
Common::SetCurrentThreadName("DSP_AudioRenderer_Main");
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
// TODO: Create buffer map/unmap thread + mailbox
// TODO: Create gMix devices, initialize them here
if (mailbox.Receive(Direction::DSP) != Message::InitializeOK) {
LOG_ERROR(Service_Audio, "ADSP Audio Renderer -- Failed to receive initialize message from host!");
return;
}
mailbox.Send(Direction::Host, Message::InitializeOK);
// 0.12 seconds (2,304,000 / 19,200,000)
constexpr u64 max_process_time{2'304'000ULL};
while (!stop_token.stop_requested()) {
auto msg{mailbox.Receive(Direction::DSP)};
switch (msg) {
case Message::Shutdown:
mailbox.Send(Direction::Host, Message::Shutdown);
return;
case Message::Render: {
if (system.IsShuttingDown()) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
mailbox.Send(Direction::Host, Message::RenderResponse);
continue;
}
std::array<bool, MaxRendererSessions> buffers_reset{};
std::array<u64, MaxRendererSessions> render_times_taken{};
const auto start_time{system.CoreTiming().GetGlobalTimeUs().count()};
for (u32 index = 0; index < MaxRendererSessions; index++) {
auto& command_buffer{command_buffers[index]};
auto& command_list_processor{command_list_processors[index]};
// Check this buffer is valid, as it may not be used.
if (command_buffer.buffer != 0) {
// If there are no remaining commands (from the previous list),
// this is a new command list, initialize it.
if (command_buffer.remaining_command_count == 0) {
command_list_processor.Initialize(system, *command_buffer.process,
command_buffer.buffer,
command_buffer.size, streams[index]);
}
if (command_buffer.reset_buffer && !buffers_reset[index]) {
streams[index]->ClearQueue();
buffers_reset[index] = true;
}
u64 max_time{max_process_time};
if (index == 1 && command_buffer.applet_resource_user_id ==
command_buffers[0].applet_resource_user_id) {
max_time = max_process_time - render_times_taken[0];
if (render_times_taken[0] > max_process_time) {
max_time = 0;
}
}
max_time = (std::min)(command_buffer.time_limit, max_time);
command_list_processor.SetProcessTimeMax(max_time);
if (index == 0) {
streams[index]->WaitFreeSpace(stop_token);
}
// Process the command list
{
render_times_taken[index] =
command_list_processor.Process(index) - start_time;
}
const auto end_time{system.CoreTiming().GetGlobalTimeUs().count()};
command_buffer.remaining_command_count =
command_list_processor.GetRemainingCommandCount();
command_buffer.render_time_taken_us = end_time - start_time;
}
}
mailbox.Send(Direction::Host, Message::RenderResponse);
} break;
default:
LOG_WARNING(Service_Audio, "ADSP AudioRenderer received an invalid message, msg={:02X}!", msg);
break;
}
}
});
main_thread = std::jthread([this](std::stop_token stop_token) { Main(stop_token); });
mailbox.Send(Direction::DSP, Message::InitializeOK);
if (mailbox.Receive(Direction::Host) != Message::InitializeOK) {
@@ -217,4 +129,95 @@ void AudioRenderer::CreateSinkStreams() {
}
}
void AudioRenderer::Main(std::stop_token stop_token) {
Common::SetCurrentThreadName("DSP_AudioRenderer_Main");
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
// TODO: Create buffer map/unmap thread + mailbox
// TODO: Create gMix devices, initialize them here
if (mailbox.Receive(Direction::DSP) != Message::InitializeOK) {
LOG_ERROR(Service_Audio, "ADSP Audio Renderer -- Failed to receive initialize message from host!");
return;
}
mailbox.Send(Direction::Host, Message::InitializeOK);
// 0.12 seconds (2,304,000 / 19,200,000)
constexpr u64 max_process_time{2'304'000ULL};
while (!stop_token.stop_requested()) {
auto msg{mailbox.Receive(Direction::DSP)};
switch (msg) {
case Message::Shutdown:
mailbox.Send(Direction::Host, Message::Shutdown);
return;
case Message::Render: {
if (system.IsShuttingDown()) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
mailbox.Send(Direction::Host, Message::RenderResponse);
continue;
}
std::array<bool, MaxRendererSessions> buffers_reset{};
std::array<u64, MaxRendererSessions> render_times_taken{};
const auto start_time{system.CoreTiming().GetGlobalTimeUs().count()};
for (u32 index = 0; index < MaxRendererSessions; index++) {
auto& command_buffer{command_buffers[index]};
auto& command_list_processor{command_list_processors[index]};
// Check this buffer is valid, as it may not be used.
if (command_buffer.buffer != 0) {
// If there are no remaining commands (from the previous list),
// this is a new command list, initialize it.
if (command_buffer.remaining_command_count == 0) {
command_list_processor.Initialize(system, *command_buffer.process,
command_buffer.buffer,
command_buffer.size, streams[index]);
}
if (command_buffer.reset_buffer && !buffers_reset[index]) {
streams[index]->ClearQueue();
buffers_reset[index] = true;
}
u64 max_time{max_process_time};
if (index == 1 && command_buffer.applet_resource_user_id ==
command_buffers[0].applet_resource_user_id) {
max_time = max_process_time - render_times_taken[0];
if (render_times_taken[0] > max_process_time) {
max_time = 0;
}
}
max_time = (std::min)(command_buffer.time_limit, max_time);
command_list_processor.SetProcessTimeMax(max_time);
if (index == 0) {
streams[index]->WaitFreeSpace(stop_token);
}
// Process the command list
{
render_times_taken[index] =
command_list_processor.Process(index) - start_time;
}
const auto end_time{system.CoreTiming().GetGlobalTimeUs().count()};
command_buffer.remaining_command_count =
command_list_processor.GetRemainingCommandCount();
command_buffer.render_time_taken_us = end_time - start_time;
}
}
mailbox.Send(Direction::Host, Message::RenderResponse);
} break;
default:
LOG_WARNING(Service_Audio, "ADSP AudioRenderer received an invalid message, msg={:02X}!", msg);
break;
}
}
}
} // namespace AudioCore::ADSP::AudioRenderer
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -82,6 +82,11 @@ public:
u64 GetRenderingStartTick(s32 session_id) const noexcept;
private:
/**
* Main AudioRenderer thread, responsible for processing the command lists.
*/
void Main(std::stop_token stop_token);
/**
* Creates the streams which will receive the processed samples.
*/
+195 -194
View File
@@ -37,9 +37,7 @@ bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 stereo_stream_co
} // namespace
OpusDecoder::OpusDecoder(Core::System& system_) : system{system_} {
init_thread = std::jthread([this](std::stop_token stop_token) {
Init(stop_token);
});
init_thread = std::jthread([this](std::stop_token stop_token) { Init(stop_token); });
}
OpusDecoder::~OpusDecoder() {
@@ -66,203 +64,206 @@ u32 OpusDecoder::Receive(Direction dir, std::stop_token stop_token) {
return mailbox.Receive(dir, stop_token);
}
void OpusDecoder::Init(std::stop_token rc_stop_token) {
void OpusDecoder::Init(std::stop_token stop_token) {
Common::SetCurrentThreadName("DSP_OpusDecoder_Init");
if (Receive(Direction::DSP, rc_stop_token) != Message::Start) {
LOG_ERROR(Service_Audio, "DSP OpusDecoder failed to receive Start message. Opus initialization failed.");
if (Receive(Direction::DSP, stop_token) != Message::Start) {
LOG_ERROR(Service_Audio,
"DSP OpusDecoder failed to receive Start message. Opus initialization failed.");
return;
}
// Main OpusDecoder thread, responsible for processing the incoming Opus packets.
main_thread = std::jthread([this](std::stop_token stop_token) {
Common::SetCurrentThreadName("DSP_OpusDecoder_Main");
while (!stop_token.stop_requested()) {
auto msg = Receive(Direction::DSP, stop_token);
switch (msg) {
case Shutdown:
Send(Direction::Host, Message::ShutdownOK);
return;
case GetWorkBufferSize: {
auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]);
ASSERT(IsValidChannelCount(channel_count));
shared_memory->dsp_return_data[0] = OpusDecodeObject::GetWorkBufferSize(channel_count);
Send(Direction::Host, Message::GetWorkBufferSizeOK);
} break;
case InitializeDecodeObject: {
auto buffer = shared_memory->host_send_data[0];
auto buffer_size = shared_memory->host_send_data[1];
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]);
ASSERT(sample_rate >= 0);
ASSERT(IsValidChannelCount(channel_count));
ASSERT(buffer_size >= OpusDecodeObject::GetWorkBufferSize(channel_count));
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
shared_memory->dsp_return_data[0] =
decoder_object.InitializeDecoder(sample_rate, channel_count);
Send(Direction::Host, Message::InitializeDecodeObjectOK);
} break;
case ShutdownDecodeObject: {
auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
shared_memory->dsp_return_data[0] = decoder_object.Shutdown();
Send(Direction::Host, Message::ShutdownDecodeObjectOK);
} break;
case DecodeInterleaved: {
auto start_time = system.CoreTiming().GetGlobalTimeUs();
auto buffer = shared_memory->host_send_data[0];
auto input_data = shared_memory->host_send_data[1];
auto input_data_size = shared_memory->host_send_data[2];
auto output_data = shared_memory->host_send_data[3];
auto output_data_size = shared_memory->host_send_data[4];
auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
auto reset_requested = shared_memory->host_send_data[6];
u32 decoded_samples{0};
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
s32 error_code{OPUS_OK};
if (reset_requested) {
error_code = decoder_object.ResetDecoder();
}
if (error_code == OPUS_OK) {
error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size,
input_data, input_data_size);
}
if (error_code == OPUS_OK) {
if (final_range && decoder_object.GetFinalRange() != final_range) {
error_code = OPUS_INVALID_PACKET;
}
}
auto end_time = system.CoreTiming().GetGlobalTimeUs();
shared_memory->dsp_return_data[0] = error_code;
shared_memory->dsp_return_data[1] = decoded_samples;
shared_memory->dsp_return_data[2] = (end_time - start_time).count();
Send(Direction::Host, Message::DecodeInterleavedOK);
} break;
case MapMemory: {
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
Send(Direction::Host, Message::MapMemoryOK);
} break;
case UnmapMemory: {
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
Send(Direction::Host, Message::UnmapMemoryOK);
} break;
case GetWorkBufferSizeForMultiStream: {
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[0]);
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[1]);
ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count));
shared_memory->dsp_return_data[0] = OpusMultiStreamDecodeObject::GetWorkBufferSize(
total_stream_count, stereo_stream_count);
Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK);
} break;
case InitializeMultiStreamDecodeObject: {
auto buffer = shared_memory->host_send_data[0];
auto buffer_size = shared_memory->host_send_data[1];
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]);
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[4]);
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[5]);
// Nintendo seem to have a bug here, they try to use &host_send_data[6] for the channel
// mappings, but [6] is never set, and there is not enough room in the argument data for
// more than 40 channels, when 255 are possible.
// It also means the mapping values are undefined, though likely always 0,
// and the mappings given by the game are ignored. The mappings are copied to this
// dedicated buffer host side, so let's do as intended.
auto mappings = shared_memory->channel_mapping.data();
ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count));
ASSERT(sample_rate >= 0);
ASSERT(buffer_size >= OpusMultiStreamDecodeObject::GetWorkBufferSize(
total_stream_count, stereo_stream_count));
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
shared_memory->dsp_return_data[0] = decoder_object.InitializeDecoder(
sample_rate, total_stream_count, channel_count, stereo_stream_count, mappings);
Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK);
} break;
case ShutdownMultiStreamDecodeObject: {
auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
shared_memory->dsp_return_data[0] = decoder_object.Shutdown();
Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK);
} break;
case DecodeInterleavedForMultiStream: {
auto start_time = system.CoreTiming().GetGlobalTimeUs();
auto buffer = shared_memory->host_send_data[0];
auto input_data = shared_memory->host_send_data[1];
auto input_data_size = shared_memory->host_send_data[2];
auto output_data = shared_memory->host_send_data[3];
auto output_data_size = shared_memory->host_send_data[4];
auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
auto reset_requested = shared_memory->host_send_data[6];
u32 decoded_samples{0};
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
s32 error_code{OPUS_OK};
if (reset_requested) {
error_code = decoder_object.ResetDecoder();
}
if (error_code == OPUS_OK) {
error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size,
input_data, input_data_size);
}
if (error_code == OPUS_OK) {
if (final_range && decoder_object.GetFinalRange() != final_range) {
error_code = OPUS_INVALID_PACKET;
}
}
auto end_time = system.CoreTiming().GetGlobalTimeUs();
shared_memory->dsp_return_data[0] = error_code;
shared_memory->dsp_return_data[1] = decoded_samples;
shared_memory->dsp_return_data[2] = (end_time - start_time).count();
Send(Direction::Host, Message::DecodeInterleavedForMultiStreamOK);
} break;
default:
LOG_ERROR(Service_Audio, "Invalid OpusDecoder command {}", msg);
continue;
}
}
});
main_thread = std::jthread([this](std::stop_token st) { Main(st); });
running = true;
Send(Direction::Host, Message::StartOK);
}
void OpusDecoder::Main(std::stop_token stop_token) {
Common::SetCurrentThreadName("DSP_OpusDecoder_Main");
while (!stop_token.stop_requested()) {
auto msg = Receive(Direction::DSP, stop_token);
switch (msg) {
case Shutdown:
Send(Direction::Host, Message::ShutdownOK);
return;
case GetWorkBufferSize: {
auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]);
ASSERT(IsValidChannelCount(channel_count));
shared_memory->dsp_return_data[0] = OpusDecodeObject::GetWorkBufferSize(channel_count);
Send(Direction::Host, Message::GetWorkBufferSizeOK);
} break;
case InitializeDecodeObject: {
auto buffer = shared_memory->host_send_data[0];
auto buffer_size = shared_memory->host_send_data[1];
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]);
ASSERT(sample_rate >= 0);
ASSERT(IsValidChannelCount(channel_count));
ASSERT(buffer_size >= OpusDecodeObject::GetWorkBufferSize(channel_count));
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
shared_memory->dsp_return_data[0] =
decoder_object.InitializeDecoder(sample_rate, channel_count);
Send(Direction::Host, Message::InitializeDecodeObjectOK);
} break;
case ShutdownDecodeObject: {
auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
shared_memory->dsp_return_data[0] = decoder_object.Shutdown();
Send(Direction::Host, Message::ShutdownDecodeObjectOK);
} break;
case DecodeInterleaved: {
auto start_time = system.CoreTiming().GetGlobalTimeUs();
auto buffer = shared_memory->host_send_data[0];
auto input_data = shared_memory->host_send_data[1];
auto input_data_size = shared_memory->host_send_data[2];
auto output_data = shared_memory->host_send_data[3];
auto output_data_size = shared_memory->host_send_data[4];
auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
auto reset_requested = shared_memory->host_send_data[6];
u32 decoded_samples{0};
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
s32 error_code{OPUS_OK};
if (reset_requested) {
error_code = decoder_object.ResetDecoder();
}
if (error_code == OPUS_OK) {
error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size,
input_data, input_data_size);
}
if (error_code == OPUS_OK) {
if (final_range && decoder_object.GetFinalRange() != final_range) {
error_code = OPUS_INVALID_PACKET;
}
}
auto end_time = system.CoreTiming().GetGlobalTimeUs();
shared_memory->dsp_return_data[0] = error_code;
shared_memory->dsp_return_data[1] = decoded_samples;
shared_memory->dsp_return_data[2] = (end_time - start_time).count();
Send(Direction::Host, Message::DecodeInterleavedOK);
} break;
case MapMemory: {
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
Send(Direction::Host, Message::MapMemoryOK);
} break;
case UnmapMemory: {
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
Send(Direction::Host, Message::UnmapMemoryOK);
} break;
case GetWorkBufferSizeForMultiStream: {
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[0]);
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[1]);
ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count));
shared_memory->dsp_return_data[0] = OpusMultiStreamDecodeObject::GetWorkBufferSize(
total_stream_count, stereo_stream_count);
Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK);
} break;
case InitializeMultiStreamDecodeObject: {
auto buffer = shared_memory->host_send_data[0];
auto buffer_size = shared_memory->host_send_data[1];
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]);
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[4]);
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[5]);
// Nintendo seem to have a bug here, they try to use &host_send_data[6] for the channel
// mappings, but [6] is never set, and there is not enough room in the argument data for
// more than 40 channels, when 255 are possible.
// It also means the mapping values are undefined, though likely always 0,
// and the mappings given by the game are ignored. The mappings are copied to this
// dedicated buffer host side, so let's do as intended.
auto mappings = shared_memory->channel_mapping.data();
ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count));
ASSERT(sample_rate >= 0);
ASSERT(buffer_size >= OpusMultiStreamDecodeObject::GetWorkBufferSize(
total_stream_count, stereo_stream_count));
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
shared_memory->dsp_return_data[0] = decoder_object.InitializeDecoder(
sample_rate, total_stream_count, channel_count, stereo_stream_count, mappings);
Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK);
} break;
case ShutdownMultiStreamDecodeObject: {
auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
shared_memory->dsp_return_data[0] = decoder_object.Shutdown();
Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK);
} break;
case DecodeInterleavedForMultiStream: {
auto start_time = system.CoreTiming().GetGlobalTimeUs();
auto buffer = shared_memory->host_send_data[0];
auto input_data = shared_memory->host_send_data[1];
auto input_data_size = shared_memory->host_send_data[2];
auto output_data = shared_memory->host_send_data[3];
auto output_data_size = shared_memory->host_send_data[4];
auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
auto reset_requested = shared_memory->host_send_data[6];
u32 decoded_samples{0};
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
s32 error_code{OPUS_OK};
if (reset_requested) {
error_code = decoder_object.ResetDecoder();
}
if (error_code == OPUS_OK) {
error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size,
input_data, input_data_size);
}
if (error_code == OPUS_OK) {
if (final_range && decoder_object.GetFinalRange() != final_range) {
error_code = OPUS_INVALID_PACKET;
}
}
auto end_time = system.CoreTiming().GetGlobalTimeUs();
shared_memory->dsp_return_data[0] = error_code;
shared_memory->dsp_return_data[1] = decoded_samples;
shared_memory->dsp_return_data[2] = (end_time - start_time).count();
Send(Direction::Host, Message::DecodeInterleavedForMultiStreamOK);
} break;
default:
LOG_ERROR(Service_Audio, "Invalid OpusDecoder command {}", msg);
continue;
}
}
}
} // namespace AudioCore::ADSP::OpusDecoder
+4 -3
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -72,6 +69,10 @@ private:
* Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread.
*/
void Init(std::stop_token stop_token);
/**
* Main OpusDecoder thread, responsible for processing the incoming Opus packets.
*/
void Main(std::stop_token stop_token);
/// Core system
Core::System& system;
+7 -4
View File
@@ -141,12 +141,12 @@ add_library(
vector_math.h
virtual_buffer.cpp
virtual_buffer.h
wall_clock.cpp
wall_clock.h
zstd_compression.cpp
zstd_compression.h
fs/ryujinx_compat.h fs/ryujinx_compat.cpp
fs/symlink.h fs/symlink.cpp
cpu_features.cpp
cpu_features.h
httplib.h
net/net.h net/net.cpp)
@@ -180,7 +180,10 @@ endif()
if(ARCHITECTURE_x86_64)
target_sources(
common
PRIVATE
PRIVATE x64/cpu_detect.cpp
x64/cpu_detect.h
x64/cpu_wait.cpp
x64/cpu_wait.h
x64/rdtsc.cpp
x64/rdtsc.h
x64/xbyak.h)
@@ -231,7 +234,7 @@ if(CXX_CLANG)
endif()
if (BOOST_NO_HEADERS)
target_link_libraries(common PUBLIC Boost::algorithm Boost::heap Boost::icl Boost::pool)
target_link_libraries(common PUBLIC Boost::algorithm Boost::icl Boost::pool)
else()
target_link_libraries(common PUBLIC Boost::headers)
endif()
-5
View File
@@ -1,12 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2005-2014 Daniel James
// SPDX-FileCopyrightText: 2016 Austin Appleby
// SPDX-License-Identifier: BSL-1.0
#pragma once
#include <array>
#include <climits>
#include <cstdint>
+1 -4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 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
@@ -63,7 +60,7 @@ public:
template <typename Func>
void ForEachItemBelow(TickType tick, Func&& func) {
static constexpr bool RETURNS_BOOL =
std::is_same_v<std::invoke_result_t<Func, ObjectType>, bool>;
std::is_same_v<std::invoke_result<Func, ObjectType>, bool>;
Item* iterator = first_item;
while (iterator) {
if (static_cast<s64>(tick) - static_cast<s64>(iterator->tick) < 0) {
+9 -13
View File
@@ -62,20 +62,16 @@ std::vector<Asset> Release::GetPlatformAssets() const {
#ifdef _WIN32
#ifdef ARCHITECTURE_x86_64
#ifdef _MSC_VER
find_asset("Standard", {"amd64-msvc-standard.exe", "amd64-msvc-standard.zip"});
#else // _MSC_VER
find_asset("Standard", {BUILD_ID "-gcc-standard.exe", BUILD_ID "-gcc-standard.zip"});
find_asset("PGO", {BUILD_ID "-clang-pgo.exe", BUILD_ID "-clang-pgo.zip"});
#endif // _MSC_VER
find_asset("Standard", {"amd64-msvc-standard.exe", "amd64-msvc-standard.zip", "mingw-amd64-gcc-standard.exe", "mingw-amd64-gcc-standard.zip"});
find_asset("PGO", {"mingw-amd64-clang-pgo.exe", "mingw-amd64-clang-pgo.zip"});
#elif defined(ARCHITECTURE_arm64)
find_asset("Standard", {"arm64-clang-standard.exe", "arm64-clang-standard.zip"});
find_asset("PGO", {"arm64-clang-pgo.exe", "arm64-clang-pgo.zip"});
#endif // ARCHITECTURE_arm64
find_asset("Standard", {"mingw-arm64-clang-standard.exe", "mingw-arm64-clang-standard.zip"});
find_asset("PGO", {"mingw-arm64-clang-pgo.exe", "mingw-arm64-clang-pgo.zip"});
#endif
#elif defined(__APPLE__)
#ifdef ARCHITECTURE_arm64
find_asset("Standard", {".dmg", ".tar.gz"});
#endif // ARCHITECTURE_arm64
#endif
#elif defined(__ANDROID__)
#ifdef ARCHITECTURE_x86_64
find_asset("Standard", {"chromeos.apk"});
@@ -86,9 +82,9 @@ std::vector<Asset> Release::GetPlatformAssets() const {
find_asset("Standard", {"optimized.apk"});
#else
find_asset("Standard", {"standard.apk"});
#endif // GENSHIN_SPOOF
#endif // ARCHITECTURE_arm64
#endif // __APPLE__
#endif
#endif
#endif
return found_assets;
}
-12
View File
@@ -435,16 +435,4 @@ void ToggleSlowMode() {
SetSpeedMode(SpeedMode::Standard);
}
bool IsOpenGL() {
const auto backend = Settings::values.renderer_backend.GetValue();
switch (backend) {
case RendererBackend::OpenGL_GLSL:
case RendererBackend::OpenGL_GLASM:
case RendererBackend::OpenGL_SPIRV:
return true;
default:
return false;
}
}
} // namespace Settings
-9
View File
@@ -545,13 +545,6 @@ struct Values {
Specialization::Default,
true,
true};
SwitchableSetting<bool> antiflicker{linkage,
false,
"antiflicker",
Category::RendererHacks,
Specialization::Default,
true,
true};
SwitchableSetting<bool> async_presentation{linkage,
#ifdef ANDROID
false,
@@ -870,8 +863,6 @@ bool IsFastmemEnabled();
void SetNceEnabled(bool is_64bit);
bool IsNceEnabled();
bool IsOpenGL();
bool IsDockedMode();
float Volume();
+1 -1
View File
@@ -145,7 +145,7 @@ ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never);
ENUM(FullscreenMode, Borderless, Exclusive);
ENUM(NvdecEmulation, Off, Cpu, Gpu);
ENUM(ResolutionSetup, Res1_4X, Res1_2X, Res3_4X, Res1X, Res5_4X, Res3_2X, Res2X, Res3X, Res4X, Res5X, Res6X, Res7X, Res8X);
ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczos, ScaleForce, Fsr, Area, ZeroTangent, BSpline, Mitchell, Spline1, Mmpx, Sgsr, SgsrEdge, MaxEnum);
ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczos, ScaleForce, Fsr, Area, ZeroTangent, BSpline, Mitchell, Spline1, Mmpx, MaxEnum);
ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);
ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
ENUM(ConsoleMode, Handheld, Docked);
+18 -20
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -20,14 +20,10 @@
namespace Common {
struct SlotId {
static constexpr u32 TAGGED_MASK = 0x7fffffff;
static constexpr u32 TAGGED_VALUE = 0x80000000;
static constexpr u32 INVALID_INDEX = (std::numeric_limits<u32>::max)();
constexpr u32 Value() const noexcept {
return index & (~TAGGED_VALUE);
}
constexpr auto operator<=>(const SlotId&) const noexcept = default;
constexpr explicit operator bool() const noexcept {
return index != INVALID_INDEX;
}
@@ -51,12 +47,12 @@ public:
Iterator& operator++() noexcept {
const u64* const bitset = slot_vector->stored_bitset.data();
const u32 size = static_cast<u32>(slot_vector->stored_bitset.size()) * 64;
if (id.Value() < size) {
if (id.index < size) {
do {
++id.index;
} while (id.Value() < size && !IsValid(bitset));
if (id.Value() == size) {
id = SlotId{};
} while (id.index < size && !IsValid(bitset));
if (id.index == size) {
id.index = SlotId::INVALID_INDEX;
}
}
return *this;
@@ -89,7 +85,7 @@ public:
: slot_vector{slot_vector_}, id{id_} {}
bool IsValid(const u64* bitset) const noexcept {
return ((bitset[id.Value() / 64] >> (id.Value() % 64)) & 1) != 0;
return ((bitset[id.index / 64] >> (id.index % 64)) & 1) != 0;
}
SlotVector<T>* slot_vector;
@@ -111,12 +107,12 @@ public:
[[nodiscard]] T& operator[](SlotId id) noexcept {
ValidateIndex(id);
return values[id.Value()].object;
return values[id.index].object;
}
[[nodiscard]] const T& operator[](SlotId id) const noexcept {
ValidateIndex(id);
return values[id.Value()].object;
return values[id.index].object;
}
template <typename... Args>
@@ -129,9 +125,9 @@ public:
}
void erase(SlotId id) noexcept {
values[id.Value()].object.~T();
free_list.push_back(id.Value());
ResetStorageBit(id.Value());
values[id.index].object.~T();
free_list.push_back(id.index);
ResetStorageBit(id.index);
}
[[nodiscard]] Iterator begin() noexcept {
@@ -145,7 +141,7 @@ public:
}
[[nodiscard]] Iterator end() noexcept {
return Iterator(this, SlotId{});
return Iterator(this, SlotId{SlotId::INVALID_INDEX});
}
[[nodiscard]] size_t size() const noexcept {
@@ -179,8 +175,8 @@ private:
void ValidateIndex(SlotId id) const noexcept {
DEBUG_ASSERT(id);
DEBUG_ASSERT(id.Value() / 64 < stored_bitset.size());
DEBUG_ASSERT(((stored_bitset[id.Value() / 64] >> (id.Value() % 64)) & 1) != 0);
DEBUG_ASSERT(id.index / 64 < stored_bitset.size());
DEBUG_ASSERT(((stored_bitset[id.index / 64] >> (id.index % 64)) & 1) != 0);
}
[[nodiscard]] u32 FreeValueIndex() noexcept {
@@ -212,7 +208,9 @@ private:
const size_t old_free_size = free_list.size();
free_list.resize(old_free_size + (new_capacity - values_capacity));
std::iota(free_list.begin() + old_free_size, free_list.end(), u32(values_capacity));
std::iota(free_list.begin() + old_free_size, free_list.end(),
static_cast<u32>(values_capacity));
delete[] values;
values = new_values;
values_capacity = new_capacity;
-112
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 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
@@ -31,118 +28,9 @@ enum class Type {
/// Protocol values for sockets
enum class Protocol : u8 {
Unspecified, ///< Represents 0, usable in various places
IP,
ICMP,
TCP,
UDP,
IPV6,
RAW,
IGMP,
GGP,
IPV4,
ST,
EGP,
PIGP,
RCCMON,
NVPII,
PUP,
ARGUS,
EMCON,
XNET,
CHAOS,
MUX,
MEAS,
HMP,
PRM,
IDP,
TRUNK1,
TRUNK2,
LEAF1,
LEAF2,
RDP,
IRTP,
TP,
BLT,
NSP,
INP,
DCCP,
//TODO: 3PC,
IDPR,
XTP,
DDP,
CMTP,
TPXX,
IL,
SDRP,
ROUTING,
FRAGMENT,
IDRP,
RSVP,
GRE,
MHRP,
BHA,
ESP,
AH,
INLSP,
SWIPE,
NHRP,
MOBILE,
TLSP,
SKIP,
ICMPV6,
NONE,
DSTOPTS,
AHIP,
CFTP,
HELLO,
SATEXPAK,
KRYPTOLAN,
RVD,
IPPC,
ADFS,
SATMON,
VISA,
IPCV,
CPNX,
CPHB,
WSN,
PVP,
BRSATMON,
ND,
WBMON,
WBEXPAK,
EON,
VMTP,
SVMTP,
VINES,
TTP,
IGP,
DGP,
TCF,
IGRP,
OSPFIGP,
SRPC,
LARP,
MTP,
AX25,
IPEIP,
MICP,
SCCSP,
ETHERIP,
ENCAP,
APES,
GMTP,
IPCOMP,
SCTP,
MH,
UDPLITE,
HIP,
SHIM6,
PIM,
CARP,
PGM,
MPLS,
PFSYNC
};
/// Shutdown mode
+2 -104
View File
@@ -4,8 +4,6 @@
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include <limits>
#include <string>
#include <thread>
@@ -20,35 +18,24 @@
#elif defined(_WIN32)
#include <windows.h>
#include "common/string_util.h"
#include "common/windows/timer_resolution.h"
#else
#if defined(__FreeBSD__)
#include <sys/cpuset.h>
#include <sys/_cpuset.h>
#include <pthread_np.h>
// Compatibility with CPUset
#define cpu_set_t cpuset_t
#elif defined(__DragonFly__) || defined(__OpenBSD__) || defined(__Bitrig__)
#include <pthread_np.h>
#endif
#include <pthread.h>
#include <sched.h>
#endif
#ifndef _WIN32
#include <unistd.h>
#endif
#include "common/cpu_features.h"
#ifdef ARCHITECTURE_x86_64
#ifdef _MSC_VER
#include <intrin.h>
#else
#include <x86intrin.h>
#ifdef __FreeBSD__
# define cpu_set_t cpuset_t
#endif
#include "common/x64/rdtsc.h"
#endif
#include "core/core_timing.h"
namespace Common {
@@ -157,93 +144,4 @@ void PinCurrentThreadToPerformanceCore(size_t core_id) {
}
}
#ifdef ARCHITECTURE_x86_64
// On Linux and UNIX systems, a futex would nominally be used to cover the costs
// the idea is that it's intuitivelly cheaper to use a direct instruction as opposed to a full futex call
// the underlying libc++ implementation uses pthread_cond_timedwait which MAY invoke a futex
// Let's pretend the OS is too expensive to jump into, and avoid ANY context switches
// this should *IN THEORY* lower CPU usage while just waiting for stuff effectively
// For windows the minimal quanta resolution is about 500us, and normal CRT cond var is 1.5ms(?)
// so may as well avoid that too
// Let's just give ALL platforms the same mechanisms (almost) for when they have umonitor OR waitpkg
#ifdef __clang__
__attribute__((target("waitpkg,mwaitx")))
#elif defined(__GNUC__)
#pragma GCC target("waitpkg")
#pragma GCC target("mwaitx")
#endif
bool Event::WaitFor(const std::chrono::nanoseconds time) {
#ifdef _WIN32
auto const start = Common::X64::FencedRDTSC();
auto const& caps = Common::g_cpu_caps;
[[maybe_unused]] auto const end = start + Common::g_wall_clock.NsToTicks(time);
if (caps.monitorx) {
while (true) {
// Armed monitor, as per manual, MWAITX must be conditional if the condition isn't satisfied
// to prevent a race condition.
_mm_monitorx(reinterpret_cast<u64*>(std::addressof(is_set)), 0, 0);
if (!is_set.load()) {
// RDTSC may be fenced here due to atomic load
auto const now = _rdtsc();
if (end > now) {
u32 const cycles = std::min<u32>((std::numeric_limits<u32>::max)(), s64(end) - s64(now));
// See here: https://github.com/torvalds/linux/blob/948a64995aca6820abefd17f1a4258f5835c5ad9/arch/x86/lib/delay.c#L93
// MWAITX accepts a 32-bit input timer which determines the total number of cycles to wait for
// NOT THE TOTAL ABSOLUTE TSC VALUE, it's just a delta
// BIT[1] = use a timer
// Hint = 0: Use C1 state when sleepy (means slower wakeup but better savings)
_mm_mwaitx(1 << 1, 0u, cycles);
if (!is_set.load())
return false;
} else
return false; //timeout
}
bool expected = true;
if (is_set.compare_exchange_weak(expected, false, std::memory_order_release))
return true;
}
} else if (caps.waitpkg) {
// #UD If CPUID.7.0:ECX.WAITPKG[bit 5]=0.
while (true) {
_umonitor(std::addressof(is_set));
if (!is_set.load() && !_umwait(0, end)) //umwait is absolute time!!!
return false;
bool expected = true;
if (is_set.compare_exchange_weak(expected, false, std::memory_order_release))
return true;
}
} else {
while (!is_set.load() && end > _rdtsc())
Common::Windows::SleepForOneTick();
if (is_set.load())
Reset();
return true;
}
#else
std::unique_lock lk{mutex};
if (!condvar.wait_for(lk, time, [this] { return is_set.load(); }))
return false;
is_set = false;
return true;
#endif
}
#else
bool Event::WaitFor(const std::chrono::nanoseconds time) {
#ifdef _WIN32
auto const end = Common::g_wall_clock.GetTimeNS() + time;
while (!is_set.load() && end > Common::g_wall_clock.GetTimeNS())
Common::Windows::SleepForOneTick();
if (is_set.load())
Reset();
return true;
#else
std::unique_lock lk{mutex};
if (!condvar.wait_for(lk, time, [this] { return is_set.load(); }))
return false;
is_set = false;
return true;
#endif
}
#endif
} // namespace Common
+10 -4
View File
@@ -34,10 +34,16 @@ public:
is_set = false;
}
bool WaitFor(const std::chrono::nanoseconds time);
bool WaitFor(const std::chrono::nanoseconds& time) {
std::unique_lock lk{mutex};
if (!condvar.wait_for(lk, time, [this] { return is_set.load(); }))
return false;
is_set = false;
return true;
}
template<class Clock, class Duration>
bool WaitUntil(const std::chrono::time_point<Clock, Duration> time) {
template <class Clock, class Duration>
bool WaitUntil(const std::chrono::time_point<Clock, Duration>& time) {
std::unique_lock lk{mutex};
if (!condvar.wait_until(lk, time, [this] { return is_set.load(); }))
return false;
@@ -57,9 +63,9 @@ public:
}
private:
alignas(64) std::atomic<bool> is_set{false};
std::condition_variable condvar;
std::mutex mutex;
std::atomic_bool is_set{false};
};
class Barrier {
+1 -2
View File
@@ -38,8 +38,7 @@ public:
VirtualBuffer& operator=(const VirtualBuffer&) = delete;
VirtualBuffer(VirtualBuffer&& other) noexcept
: alloc_size{std::exchange(other.alloc_size, 0)}
, base_ptr{std::exchange(other.base_ptr, nullptr)}
: alloc_size{std::exchange(other.alloc_size, 0)}, base_ptr{std::exchange(other.base_ptr), nullptr}
{}
VirtualBuffer& operator=(VirtualBuffer&& other) noexcept {
+197
View File
@@ -0,0 +1,197 @@
// SPDX-FileCopyrightText: Copyright 2026 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
#include "common/steady_clock.h"
#include "common/uint128.h"
#include "common/wall_clock.h"
#ifdef __ANDROID__
#include <sys/system_properties.h>
#endif
#ifdef ARCHITECTURE_x86_64
#include "common/x64/cpu_detect.h"
#include "common/x64/rdtsc.h"
#endif
namespace Common {
#if defined(ARCHITECTURE_x86_64)
WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept
: invariant{invariant_}
, rdtsc_frequency{rdtsc_frequency_}
, ns_rdtsc_factor{invariant_ ? 0 : GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_)}
, us_rdtsc_factor{invariant_ ? 0 : GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_)}
, ms_rdtsc_factor{invariant_ ? 0 : GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_)}
, cntpct_rdtsc_factor{invariant_ ? 0 : GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_)}
, gputick_rdtsc_factor{invariant_ ? 0 : GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_)}
{}
std::chrono::nanoseconds WallClock::GetTimeNS() const {
if (invariant)
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch());
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_rdtsc_factor)};
}
std::chrono::microseconds WallClock::GetTimeUS() const {
if (invariant)
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch());
return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_rdtsc_factor)};
}
std::chrono::milliseconds WallClock::GetTimeMS() const {
if (invariant)
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_rdtsc_factor)};
}
s64 WallClock::GetCNTPCT() const {
if (invariant)
return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
return MultiplyHigh(GetUptime(), cntpct_rdtsc_factor);
}
s64 WallClock::GetGPUTick() const {
if (invariant)
return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
return MultiplyHigh(GetUptime(), gputick_rdtsc_factor);
}
s64 WallClock::GetUptime() const {
if (invariant)
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
return s64(Common::X64::FencedRDTSC());
}
bool WallClock::IsNative() const {
if (invariant)
return false;
return true;
}
#elif defined(HAS_NCE)
namespace {
[[nodiscard]] WallClock::FactorType GetFixedPointFactor(u64 num, u64 den) noexcept {
return (WallClock::FactorType(num) << 64) / den;
}
[[nodiscard]] u64 MultiplyHigh(u64 m, WallClock::FactorType factor) noexcept {
return static_cast<u64>((m * factor) >> 64);
}
[[nodiscard]] s64 GetHostCNTFRQ() noexcept {
u64 cntfrq_el0 = 0;
#ifdef ANDROID
std::string_view board{""};
char buffer[PROP_VALUE_MAX];
int len{__system_property_get("ro.product.board", buffer)};
board = std::string_view(buffer, static_cast<size_t>(len));
if (board == "s5e9925") { // Exynos 2200
cntfrq_el0 = 25600000;
} else if (board == "exynos2100") { // Exynos 2100
cntfrq_el0 = 26000000;
} else if (board == "exynos9810") { // Exynos 9810
cntfrq_el0 = 26000000;
} else if (board == "s5e8825") { // Exynos 1280
cntfrq_el0 = 26000000;
} else {
asm volatile("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
}
return cntfrq_el0;
#else
asm volatile("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
return cntfrq_el0;
#endif
}
} // namespace
WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept {
const u64 host_cntfrq = std::max<u64>(GetHostCNTFRQ(), 1);
ns_cntfrq_factor = GetFixedPointFactor(NsRatio::den, host_cntfrq);
us_cntfrq_factor = GetFixedPointFactor(UsRatio::den, host_cntfrq);
ms_cntfrq_factor = GetFixedPointFactor(MsRatio::den, host_cntfrq);
guest_cntfrq_factor = GetFixedPointFactor(CNTFRQ, host_cntfrq);
gputick_cntfrq_factor = GetFixedPointFactor(GPUTickFreq, host_cntfrq);
}
std::chrono::nanoseconds WallClock::GetTimeNS() const {
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_cntfrq_factor)};
}
std::chrono::microseconds WallClock::GetTimeUS() const {
return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_cntfrq_factor)};
}
std::chrono::milliseconds WallClock::GetTimeMS() const {
return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_cntfrq_factor)};
}
s64 WallClock::GetCNTPCT() const {
return MultiplyHigh(GetUptime(), guest_cntfrq_factor);
}
s64 WallClock::GetGPUTick() const {
return MultiplyHigh(GetUptime(), gputick_cntfrq_factor);
}
s64 WallClock::GetUptime() const {
s64 cntvct_el0 = 0;
asm volatile(
"dsb ish\n\t"
"mrs %[cntvct_el0], cntvct_el0\n\t"
"dsb ish\n\t"
: [cntvct_el0] "=r"(cntvct_el0)
);
return cntvct_el0;
}
bool WallClock::IsNative() const {
return true;
}
#else
WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept {}
std::chrono::nanoseconds WallClock::GetTimeNS() const {
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch());
}
std::chrono::microseconds WallClock::GetTimeUS() const {
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch());
}
std::chrono::milliseconds WallClock::GetTimeMS() const {
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
}
s64 WallClock::GetCNTPCT() const {
return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
}
s64 WallClock::GetGPUTick() const {
return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
}
s64 WallClock::GetUptime() const {
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
}
bool WallClock::IsNative() const {
return false;
}
#endif
WallClock CreateOptimalClock() noexcept {
#if defined(ARCHITECTURE_x86_64)
auto const& caps = GetCPUCaps();
return WallClock(!(caps.invariant_tsc && caps.tsc_frequency >= std::nano::den), caps.tsc_frequency);
#elif defined(HAS_NCE)
return WallClock(false, 1);
#else
return WallClock(true, 1);
#endif
}
} // namespace Common
@@ -1,14 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <optional>
#include <string_view>
#include <chrono>
#include <memory>
#include <ratio>
@@ -46,9 +43,6 @@ public:
/// @returns Whether the clock directly uses the host's hardware clock.
bool IsNative() const;
// @returns Nanoseconds to native ticks
u64 NsToTicks(std::chrono::nanoseconds ns) const;
static inline u64 NSToCNTPCT(u64 ns) {
return ns * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
}
@@ -75,6 +69,7 @@ public:
return cpu_tick * CPUTickToGPUTickRatio::num / CPUTickToGPUTickRatio::den;
}
protected:
using NsRatio = std::nano;
using UsRatio = std::micro;
using MsRatio = std::milli;
@@ -92,96 +87,31 @@ public:
using CPUTickToGPUTickRatio = std::ratio<GPUTickFreq, CPUTickFreq>;
#if defined(ARCHITECTURE_x86_64)
bool invariant;
u64 rdtsc_frequency;
u64 ns_rdtsc_factor;
u64 us_rdtsc_factor;
u64 ms_rdtsc_factor;
u64 rdtsc_ns_factor;
u64 cntpct_rdtsc_factor;
u64 gputick_rdtsc_factor;
bool invariant;
#elif defined(HAS_NCE)
public:
using FactorType = unsigned __int128;
[[nodiscard]] inline FactorType GetGuestCNTFRQFactor() const {
FactorType GetGuestCNTFRQFactor() const {
return guest_cntfrq_factor;
}
protected:
FactorType ns_cntfrq_factor;
FactorType us_cntfrq_factor;
FactorType ms_cntfrq_factor;
FactorType cntfrq_ns_factor;
FactorType guest_cntfrq_factor;
FactorType gputick_cntfrq_factor;
#endif
};
#ifdef ARCHITECTURE_x86_64
/// x86/x64 CPU capabilities that may be detected by this module
struct CPUCaps {
enum class Manufacturer : u8 {
Unknown = 0,
Intel = 1,
AMD = 2,
Hygon = 3,
};
static Manufacturer ParseManufacturer(std::string_view brand_string);
Manufacturer manufacturer;
char brand_string[13];
char cpu_string[48];
u32 base_frequency;
u32 max_frequency;
u32 bus_frequency;
u32 tsc_crystal_ratio_denominator;
u32 tsc_crystal_ratio_numerator;
u32 crystal_frequency;
u64 tsc_frequency; // Derived from the above three values
bool sse3 : 1;
bool ssse3 : 1;
bool sse4_1 : 1;
bool sse4_2 : 1;
bool avx : 1;
bool avx2 : 1;
bool avx512f : 1;
bool avx512dq : 1;
bool avx512cd : 1;
bool avx512bw : 1;
bool avx512vl : 1;
bool avx512vbmi : 1;
bool avx512bitalg : 1;
bool aes : 1;
bool bmi1 : 1;
bool bmi2 : 1;
bool f16c : 1;
bool fma : 1;
bool gfni : 1;
bool invariant_tsc : 1;
bool lzcnt : 1;
bool monitorx : 1;
bool movbe : 1;
bool pclmulqdq : 1;
bool popcnt : 1;
bool sha : 1;
bool waitpkg : 1;
};
#else
struct CPUCaps {
bool padding;
};
#endif
};
/// Detects CPU core count
std::optional<int> GetProcessorCount();
/// @brief Global cpu caps
extern const CPUCaps g_cpu_caps;
/// @brief Global wall clock
extern const WallClock g_wall_clock;
[[nodiscard]] WallClock CreateOptimalClock() noexcept;
} // namespace Common
@@ -10,32 +10,34 @@
#include <fstream>
#include <iterator>
#include <optional>
#include <algorithm>
#include <string_view>
#include <thread>
#include <vector>
#ifdef _WIN32
#include <windows.h>
#elif defined(__DragonFly__) || defined(__FreeBSD__)
#include <sys/types.h>
#include <machine/cpufunc.h>
#elif defined(__ANDROID__)
#include <sys/system_properties.h>
#endif
#include "common/steady_clock.h"
#include "common/uint128.h"
#include "common/bit_util.h"
#include "common/common_types.h"
#include "common/cpu_features.h"
#include "common/logging.h"
#ifdef ARCHITECTURE_x86_64
#include "common/x64/cpu_detect.h"
#include "common/x64/rdtsc.h"
#ifdef _WIN32
#include <windows.h>
#endif
#ifdef _MSC_VER
#include <intrin.h>
static inline u64 xgetbv(u32 index) { return _xgetbv(index); }
static inline u64 xgetbv(u32 index) {
return _xgetbv(index);
}
#else
#if defined(__DragonFly__) || defined(__FreeBSD__)
// clang-format off
#include <sys/types.h>
#include <machine/cpufunc.h>
// clang-format on
#endif
static inline void __cpuidex(int info[4], u32 function_id, u32 subfunction_id) {
#if defined(__DragonFly__) || defined(__FreeBSD__)
// Despite the name, this is just do_cpuid() with ECX as second input.
@@ -48,7 +50,11 @@ static inline void __cpuidex(int info[4], u32 function_id, u32 subfunction_id) {
: "a"(function_id), "c"(subfunction_id));
#endif
}
static inline void __cpuid(int info[4], u32 function_id) { return __cpuidex(info, function_id, 0); }
static inline void __cpuid(int info[4], u32 function_id) {
return __cpuidex(info, function_id, 0);
}
#define _XCR_XFEATURE_ENABLED_MASK 0
static inline u64 xgetbv(u32 index) {
u32 eax, edx;
@@ -56,10 +62,9 @@ static inline u64 xgetbv(u32 index) {
return ((u64)edx << 32) | eax;
}
#endif // _MSC_VER
#endif
namespace Common {
#ifdef ARCHITECTURE_x86_64
CPUCaps::Manufacturer CPUCaps::ParseManufacturer(std::string_view brand_string) {
if (brand_string == "GenuineIntel") {
return Manufacturer::Intel;
@@ -71,6 +76,142 @@ CPUCaps::Manufacturer CPUCaps::ParseManufacturer(std::string_view brand_string)
return Manufacturer::Unknown;
}
// Detects the various CPU features
static CPUCaps Detect() {
CPUCaps caps = {};
// Assumes the CPU supports the CPUID instruction. Those that don't would likely not support
// yuzu at all anyway
int cpu_id[4];
// Detect CPU's CPUID capabilities and grab manufacturer string
__cpuid(cpu_id, 0x00000000);
const u32 max_std_fn = cpu_id[0]; // EAX
std::memset(caps.brand_string, 0, std::size(caps.brand_string));
std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(u32));
std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(u32));
std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(u32));
caps.manufacturer = CPUCaps::ParseManufacturer(caps.brand_string);
// Set reasonable default cpu string even if brand string not available
std::strncpy(caps.cpu_string, caps.brand_string, std::size(caps.brand_string));
__cpuid(cpu_id, 0x80000000);
const u32 max_ex_fn = cpu_id[0];
// Detect family and other miscellaneous features
if (max_std_fn >= 1) {
__cpuid(cpu_id, 0x00000001);
caps.sse = Common::Bit<25>(cpu_id[3]);
caps.sse2 = Common::Bit<26>(cpu_id[3]);
caps.sse3 = Common::Bit<0>(cpu_id[2]);
caps.pclmulqdq = Common::Bit<1>(cpu_id[2]);
caps.ssse3 = Common::Bit<9>(cpu_id[2]);
caps.sse4_1 = Common::Bit<19>(cpu_id[2]);
caps.sse4_2 = Common::Bit<20>(cpu_id[2]);
caps.movbe = Common::Bit<22>(cpu_id[2]);
caps.popcnt = Common::Bit<23>(cpu_id[2]);
caps.aes = Common::Bit<25>(cpu_id[2]);
caps.f16c = Common::Bit<29>(cpu_id[2]);
// AVX support requires 3 separate checks:
// - Is the AVX bit set in CPUID?
// - Is the XSAVE bit set in CPUID?
// - XGETBV result has the XCR bit set.
if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) {
if ((xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
caps.avx = true;
if (Common::Bit<12>(cpu_id[2]))
caps.fma = true;
}
}
if (max_std_fn >= 7) {
__cpuidex(cpu_id, 0x00000007, 0x00000000);
// Can't enable AVX{2,512} unless the XSAVE/XGETBV checks above passed
if (caps.avx) {
caps.avx2 = Common::Bit<5>(cpu_id[1]);
caps.avx512f = Common::Bit<16>(cpu_id[1]);
caps.avx512dq = Common::Bit<17>(cpu_id[1]);
caps.avx512cd = Common::Bit<28>(cpu_id[1]);
caps.avx512bw = Common::Bit<30>(cpu_id[1]);
caps.avx512vl = Common::Bit<31>(cpu_id[1]);
caps.avx512vbmi = Common::Bit<1>(cpu_id[2]);
caps.avx512bitalg = Common::Bit<12>(cpu_id[2]);
}
caps.bmi1 = Common::Bit<3>(cpu_id[1]);
caps.bmi2 = Common::Bit<8>(cpu_id[1]);
caps.sha = Common::Bit<29>(cpu_id[1]);
caps.waitpkg = Common::Bit<5>(cpu_id[2]);
caps.gfni = Common::Bit<8>(cpu_id[2]);
__cpuidex(cpu_id, 0x00000007, 0x00000001);
caps.avx_vnni = caps.avx && Common::Bit<4>(cpu_id[0]);
}
}
if (max_ex_fn >= 0x80000004) {
// Extract CPU model string
__cpuid(cpu_id, 0x80000002);
std::memcpy(caps.cpu_string, cpu_id, sizeof(cpu_id));
__cpuid(cpu_id, 0x80000003);
std::memcpy(caps.cpu_string + 16, cpu_id, sizeof(cpu_id));
__cpuid(cpu_id, 0x80000004);
std::memcpy(caps.cpu_string + 32, cpu_id, sizeof(cpu_id));
}
if (max_ex_fn >= 0x80000001) {
// Check for more features
__cpuid(cpu_id, 0x80000001);
caps.lzcnt = Common::Bit<5>(cpu_id[2]);
caps.fma4 = Common::Bit<16>(cpu_id[2]);
caps.monitorx = Common::Bit<29>(cpu_id[2]);
}
if (max_ex_fn >= 0x80000007) {
__cpuid(cpu_id, 0x80000007);
caps.invariant_tsc = Common::Bit<8>(cpu_id[3]);
}
if (max_std_fn >= 0x15) {
__cpuid(cpu_id, 0x15);
caps.tsc_crystal_ratio_denominator = cpu_id[0];
caps.tsc_crystal_ratio_numerator = cpu_id[1];
caps.crystal_frequency = cpu_id[2];
// Some CPU models might not return a crystal frequency.
// The CPU model can be detected to use the values from turbostat
// https://github.com/torvalds/linux/blob/master/tools/power/x86/turbostat/turbostat.c#L5569
// but it's easier to just estimate the TSC tick rate for these cases.
if (caps.tsc_crystal_ratio_denominator) {
caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) *
caps.tsc_crystal_ratio_numerator /
caps.tsc_crystal_ratio_denominator;
} else {
caps.tsc_frequency = X64::EstimateRDTSCFrequency();
}
}
if (max_std_fn >= 0x16) {
__cpuid(cpu_id, 0x16);
caps.base_frequency = cpu_id[0];
caps.max_frequency = cpu_id[1];
caps.bus_frequency = cpu_id[2];
}
return caps;
}
const CPUCaps& GetCPUCaps() {
static CPUCaps caps = Detect();
return caps;
}
std::optional<int> GetProcessorCount() {
#if defined(_WIN32)
// Get the buffer length.
@@ -112,315 +253,4 @@ std::optional<int> GetProcessorCount() {
#endif
}
/// @brief Detects the various CPU features
const CPUCaps g_cpu_caps = [] {
CPUCaps caps = {};
// Assumes the CPU supports the CPUID instruction. Those that don't would likely not support
// yuzu at all anyway
int cpu_id[4];
// Detect CPU's CPUID capabilities and grab manufacturer string
__cpuid(cpu_id, 0x00000000);
const u32 max_std_fn = cpu_id[0]; // EAX
std::memset(caps.brand_string, 0, std::size(caps.brand_string));
std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(u32));
std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(u32));
std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(u32));
caps.manufacturer = CPUCaps::ParseManufacturer(caps.brand_string);
// Set reasonable default cpu string even if brand string not available
std::strncpy(caps.cpu_string, caps.brand_string, std::size(caps.brand_string));
__cpuid(cpu_id, 0x80000000);
const u32 max_ex_fn = cpu_id[0];
// Detect family and other miscellaneous features
if (max_std_fn >= 1) {
__cpuid(cpu_id, 0x00000001);
caps.sse3 = Common::Bit<0>(cpu_id[2]);
caps.pclmulqdq = Common::Bit<1>(cpu_id[2]);
caps.ssse3 = Common::Bit<9>(cpu_id[2]);
caps.sse4_1 = Common::Bit<19>(cpu_id[2]);
caps.sse4_2 = Common::Bit<20>(cpu_id[2]);
caps.movbe = Common::Bit<22>(cpu_id[2]);
caps.popcnt = Common::Bit<23>(cpu_id[2]);
caps.aes = Common::Bit<25>(cpu_id[2]);
caps.f16c = Common::Bit<29>(cpu_id[2]);
// AVX support requires 3 separate checks:
// - Is the AVX bit set in CPUID?
// - Is the XSAVE bit set in CPUID?
// - XGETBV result has the XCR bit set.
if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) {
if ((xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
caps.avx = true;
if (Common::Bit<12>(cpu_id[2]))
caps.fma = true;
}
}
if (max_std_fn >= 7) {
__cpuidex(cpu_id, 0x00000007, 0x00000000);
// Can't enable AVX{2,512} unless the XSAVE/XGETBV checks above passed
if (caps.avx) {
caps.avx2 = Common::Bit<5>(cpu_id[1]);
caps.avx512f = Common::Bit<16>(cpu_id[1]);
caps.avx512dq = Common::Bit<17>(cpu_id[1]);
caps.avx512cd = Common::Bit<28>(cpu_id[1]);
caps.avx512bw = Common::Bit<30>(cpu_id[1]);
caps.avx512vl = Common::Bit<31>(cpu_id[1]);
caps.avx512vbmi = Common::Bit<1>(cpu_id[2]);
caps.avx512bitalg = Common::Bit<12>(cpu_id[2]);
}
caps.bmi1 = Common::Bit<3>(cpu_id[1]);
caps.bmi2 = Common::Bit<8>(cpu_id[1]);
caps.sha = Common::Bit<29>(cpu_id[1]);
caps.waitpkg = Common::Bit<5>(cpu_id[2]);
caps.gfni = Common::Bit<8>(cpu_id[2]);
}
}
if (max_ex_fn >= 0x80000004) {
// Extract CPU model string
__cpuid(cpu_id, 0x80000002);
std::memcpy(caps.cpu_string, cpu_id, sizeof(cpu_id));
__cpuid(cpu_id, 0x80000003);
std::memcpy(caps.cpu_string + 16, cpu_id, sizeof(cpu_id));
__cpuid(cpu_id, 0x80000004);
std::memcpy(caps.cpu_string + 32, cpu_id, sizeof(cpu_id));
}
if (max_ex_fn >= 0x80000001) {
// Check for more features
__cpuid(cpu_id, 0x80000001);
caps.lzcnt = Common::Bit<5>(cpu_id[2]);
caps.monitorx = Common::Bit<29>(cpu_id[2]);
}
if (max_ex_fn >= 0x80000007) {
__cpuid(cpu_id, 0x80000007);
caps.invariant_tsc = Common::Bit<8>(cpu_id[3]);
}
if (max_std_fn >= 0x15) {
__cpuid(cpu_id, 0x15);
caps.tsc_crystal_ratio_denominator = cpu_id[0];
caps.tsc_crystal_ratio_numerator = cpu_id[1];
caps.crystal_frequency = cpu_id[2];
// Some CPU models might not return a crystal frequency.
// The CPU model can be detected to use the values from turbostat
// https://github.com/torvalds/linux/blob/master/tools/power/x86/turbostat/turbostat.c#L5569
// but it's easier to just estimate the TSC tick rate for these cases.
if (caps.tsc_crystal_ratio_denominator) {
caps.tsc_frequency = u64(caps.crystal_frequency)
* caps.tsc_crystal_ratio_numerator / caps.tsc_crystal_ratio_denominator;
} else {
caps.tsc_frequency = X64::EstimateRDTSCFrequency();
}
}
if (max_std_fn >= 0x16) {
__cpuid(cpu_id, 0x16);
caps.base_frequency = cpu_id[0];
caps.max_frequency = cpu_id[1];
caps.bus_frequency = cpu_id[2];
}
return caps;
}();
#else
#endif
#if defined(ARCHITECTURE_x86_64)
WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept
: rdtsc_frequency{rdtsc_frequency_}
, ns_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_) : 0}
, us_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_) : 0}
, ms_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_) : 0}
, rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_, NsRatio::den) : 1}
, cntpct_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_) : 0}
, gputick_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_) : 0}
, invariant{invariant_}
{}
std::chrono::nanoseconds WallClock::GetTimeNS() const {
if (!invariant)
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch());
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_rdtsc_factor)};
}
std::chrono::microseconds WallClock::GetTimeUS() const {
if (!invariant)
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now().time_since_epoch());
return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_rdtsc_factor)};
}
std::chrono::milliseconds WallClock::GetTimeMS() const {
if (!invariant)
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch());
return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_rdtsc_factor)};
}
s64 WallClock::GetCNTPCT() const {
if (!invariant)
return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
return MultiplyHigh(GetUptime(), cntpct_rdtsc_factor);
}
s64 WallClock::GetGPUTick() const {
if (!invariant)
return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
return MultiplyHigh(GetUptime(), gputick_rdtsc_factor);
}
s64 WallClock::GetUptime() const {
if (!invariant)
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
return s64(Common::X64::FencedRDTSC());
}
bool WallClock::IsNative() const {
return invariant;
}
u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
return invariant ? MultiplyHigh(ns.count(), rdtsc_ns_factor) : ns.count();
}
#elif defined(HAS_NCE)
namespace {
[[nodiscard]] Common::WallClock::FactorType GetFixedPointFactor(u64 num, u64 den) noexcept {
return (Common::WallClock::FactorType(num) << 64) / den;
}
[[nodiscard]] u64 MultiplyHigh(u64 m, Common::WallClock::FactorType factor) noexcept {
return static_cast<u64>((m * factor) >> 64);
}
[[nodiscard]] s64 GetHostCNTFRQ() noexcept {
u64 cntfrq_el0 = 0;
#ifdef ANDROID
std::string_view board{""};
char buffer[PROP_VALUE_MAX];
int len{__system_property_get("ro.product.board", buffer)};
board = std::string_view(buffer, static_cast<size_t>(len));
if (board == "s5e9925") { // Exynos 2200
cntfrq_el0 = 25600000;
} else if (board == "exynos2100") { // Exynos 2100
cntfrq_el0 = 26000000;
} else if (board == "exynos9810") { // Exynos 9810
cntfrq_el0 = 26000000;
} else if (board == "s5e8825") { // Exynos 1280
cntfrq_el0 = 26000000;
} else {
asm volatile("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
}
return cntfrq_el0;
#else
asm volatile("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
return cntfrq_el0;
#endif
}
} // namespace
WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept {
const u64 host_cntfrq = std::max<u64>(GetHostCNTFRQ(), 1);
ns_cntfrq_factor = GetFixedPointFactor(NsRatio::den, host_cntfrq);
us_cntfrq_factor = GetFixedPointFactor(UsRatio::den, host_cntfrq);
ms_cntfrq_factor = GetFixedPointFactor(MsRatio::den, host_cntfrq);
cntfrq_ns_factor = GetFixedPointFactor(host_cntfrq, NsRatio::den);
guest_cntfrq_factor = GetFixedPointFactor(CNTFRQ, host_cntfrq);
gputick_cntfrq_factor = GetFixedPointFactor(GPUTickFreq, host_cntfrq);
}
std::chrono::nanoseconds WallClock::GetTimeNS() const {
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_cntfrq_factor)};
}
std::chrono::microseconds WallClock::GetTimeUS() const {
return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_cntfrq_factor)};
}
std::chrono::milliseconds WallClock::GetTimeMS() const {
return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_cntfrq_factor)};
}
s64 WallClock::GetCNTPCT() const {
return MultiplyHigh(GetUptime(), guest_cntfrq_factor);
}
s64 WallClock::GetGPUTick() const {
return MultiplyHigh(GetUptime(), gputick_cntfrq_factor);
}
s64 WallClock::GetUptime() const {
s64 cntvct_el0 = 0;
asm volatile(
"dsb ish\n\t"
"mrs %[cntvct_el0], cntvct_el0\n\t"
"dsb ish\n\t"
: [cntvct_el0] "=r"(cntvct_el0)
);
return cntvct_el0;
}
bool WallClock::IsNative() const {
return true;
}
u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
return MultiplyHigh(ns.count(), cntfrq_ns_factor);
}
#else
WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept {}
std::chrono::nanoseconds WallClock::GetTimeNS() const {
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch());
}
std::chrono::microseconds WallClock::GetTimeUS() const {
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now().time_since_epoch());
}
std::chrono::milliseconds WallClock::GetTimeMS() const {
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch());
}
s64 WallClock::GetCNTPCT() const {
return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
}
s64 WallClock::GetGPUTick() const {
return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
}
s64 WallClock::GetUptime() const {
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
}
bool WallClock::IsNative() const {
return false;
}
u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
return ns.count();
}
#endif
// Wall clock MUST be initialized AFTER g_cpu_caps
// C++ only guarantees ctor init in the order they appear in TU
const WallClock g_wall_clock = [] {
#if defined(ARCHITECTURE_x86_64)
auto const& caps = Common::g_cpu_caps;
return WallClock(caps.invariant_tsc && caps.tsc_frequency >= std::nano::den, caps.tsc_frequency);
#elif defined(HAS_NCE)
return WallClock(false, 1);
#else
return WallClock(true, 1);
#endif
}();
} // namespace Common
+83
View File
@@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <optional>
#include <string_view>
#include "common/common_types.h"
namespace Common {
/// x86/x64 CPU capabilities that may be detected by this module
struct CPUCaps {
enum class Manufacturer : u8 {
Unknown = 0,
Intel = 1,
AMD = 2,
Hygon = 3,
};
static Manufacturer ParseManufacturer(std::string_view brand_string);
Manufacturer manufacturer;
char brand_string[13];
char cpu_string[48];
u32 base_frequency;
u32 max_frequency;
u32 bus_frequency;
u32 tsc_crystal_ratio_denominator;
u32 tsc_crystal_ratio_numerator;
u32 crystal_frequency;
u64 tsc_frequency; // Derived from the above three values
bool sse : 1;
bool sse2 : 1;
bool sse3 : 1;
bool ssse3 : 1;
bool sse4_1 : 1;
bool sse4_2 : 1;
bool avx : 1;
bool avx_vnni : 1;
bool avx2 : 1;
bool avx512f : 1;
bool avx512dq : 1;
bool avx512cd : 1;
bool avx512bw : 1;
bool avx512vl : 1;
bool avx512vbmi : 1;
bool avx512bitalg : 1;
bool aes : 1;
bool bmi1 : 1;
bool bmi2 : 1;
bool f16c : 1;
bool fma : 1;
bool fma4 : 1;
bool gfni : 1;
bool invariant_tsc : 1;
bool lzcnt : 1;
bool monitorx : 1;
bool movbe : 1;
bool pclmulqdq : 1;
bool popcnt : 1;
bool sha : 1;
bool waitpkg : 1;
};
/**
* Gets the supported capabilities of the host CPU
* @return Reference to a CPUCaps struct with the detected host CPU capabilities
*/
const CPUCaps& GetCPUCaps();
/// Detects CPU core count
std::optional<int> GetProcessorCount();
} // namespace Common
+75
View File
@@ -0,0 +1,75 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <thread>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#include "common/x64/cpu_detect.h"
#include "common/x64/cpu_wait.h"
#include "common/x64/rdtsc.h"
namespace Common::X64 {
namespace {
// 100,000 cycles is a reasonable amount of time to wait to save on CPU resources.
// For reference:
// At 1 GHz, 100K cycles is 100us
// At 2 GHz, 100K cycles is 50us
// At 4 GHz, 100K cycles is 25us
constexpr auto PauseCycles = 100'000U;
} // Anonymous namespace
#if defined(_MSC_VER) && !defined(__clang__)
__forceinline static void TPAUSE() {
static constexpr auto RequestC02State = 0U;
_tpause(RequestC02State, FencedRDTSC() + PauseCycles);
}
__forceinline static void MWAITX() {
static constexpr auto EnableWaitTimeFlag = 1U << 1;
static constexpr auto RequestC1State = 0U;
// monitor_var should be aligned to a cache line.
alignas(64) u64 monitor_var{};
_mm_monitorx(&monitor_var, 0, 0);
_mm_mwaitx(EnableWaitTimeFlag, RequestC1State, PauseCycles);
}
#else
static void TPAUSE() {
static constexpr auto RequestC02State = 0U;
const auto tsc = FencedRDTSC() + PauseCycles;
const auto eax = static_cast<u32>(tsc & 0xFFFFFFFF);
const auto edx = static_cast<u32>(tsc >> 32);
asm volatile("tpause %0" : : "r"(RequestC02State), "d"(edx), "a"(eax));
}
static void MWAITX() {
static constexpr auto EnableWaitTimeFlag = 1U << 1;
static constexpr auto RequestC1State = 0U;
// monitor_var should be aligned to a cache line.
alignas(64) u64 monitor_var{};
asm volatile("monitorx" : : "a"(&monitor_var), "c"(0), "d"(0));
asm volatile("mwaitx" : : "a"(RequestC1State), "b"(PauseCycles), "c"(EnableWaitTimeFlag));
}
#endif
void MicroSleep() {
static const bool has_waitpkg = GetCPUCaps().waitpkg;
static const bool has_monitorx = GetCPUCaps().monitorx;
if (has_waitpkg) {
TPAUSE();
} else if (has_monitorx) {
MWAITX();
} else {
std::this_thread::yield();
}
}
} // namespace Common::X64
+10
View File
@@ -0,0 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
namespace Common::X64 {
void MicroSleep();
} // namespace Common::X64
+1 -1
View File
@@ -1246,7 +1246,7 @@ if (HAS_NCE)
target_link_libraries(core PRIVATE merry::oaknut)
endif()
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64)
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64)
target_sources(core PRIVATE
arm/dynarmic/arm_dynarmic.h
arm/dynarmic/arm_dynarmic_64.cpp
+1 -3
View File
@@ -1,11 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <dynarmic/interface/halt_reason.h>
#include "core/arm/arm_interface.h"
+1 -1
View File
@@ -388,7 +388,7 @@ void ArmNce::SignalInterrupt(Kernel::KThread* thread) {
}
}
[[maybe_unused]] const std::size_t CACHE_PAGE_SIZE = 4096;
const std::size_t CACHE_PAGE_SIZE = 4096;
void ArmNce::ClearInstructionCache() {
#ifdef __aarch64__
+1 -3
View File
@@ -1,11 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 Skyline Team and Contributors
// SPDX-License-Identifier: MPL-2.0
#pragma once
#include "common/bit_field.h"
#include "common/common_types.h"
+3 -3
View File
@@ -12,9 +12,9 @@ namespace Core {
namespace {
// Prefetch tuning parameters
[[maybe_unused]] constexpr size_t CACHE_LINE_SIZE = 64;
[[maybe_unused]] constexpr size_t PREFETCH_STRIDE = 128; // 2 cache lines ahead
[[maybe_unused]] constexpr size_t SIMD_PREFETCH_THRESHOLD = 32; // Bytes
constexpr size_t CACHE_LINE_SIZE = 64;
constexpr size_t PREFETCH_STRIDE = 128; // 2 cache lines ahead
constexpr size_t SIMD_PREFETCH_THRESHOLD = 32; // Bytes
} // namespace
template <u32 BitSize>
+1 -1
View File
@@ -3,7 +3,7 @@
#include <numeric>
#include <bit>
#include "common/cpu_features.h"
#include "common/wall_clock.h"
#include "common/alignment.h"
#include "common/literals.h"
#include "core/arm/nce/arm_nce.h"
+28 -10
View File
@@ -8,13 +8,15 @@
#include <mutex>
#include <string>
#include <tuple>
#include "common/cpu_features.h"
#include "common/cpu_features.h"
#ifdef _WIN32
#include "common/windows/timer_resolution.h"
#endif
#ifdef ARCHITECTURE_x86_64
#include "common/x64/cpu_wait.h"
#endif
#include "common/settings.h"
#include "core/core_timing.h"
#include "core/hardware_properties.h"
@@ -45,7 +47,8 @@ struct CoreTiming::Event {
}
};
CoreTiming::CoreTiming() = default;
CoreTiming::CoreTiming() : clock{Common::CreateOptimalClock()} {}
CoreTiming::~CoreTiming() {
Reset();
}
@@ -61,16 +64,31 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
on_thread_init();
has_started = true;
// base frequency in MHz: 1ns (10^-9) = 1GHz (10^9)
while (!stop_token.stop_requested()) {
while (!paused && !stop_token.stop_requested()) {
paused_set = false;
if (auto const next_time = Advance(); next_time) {
// There are more events left in the queue, wait until the next event.
auto const wait_time = *next_time - GetGlobalTimeNs().count();
auto wait_time = *next_time - GetGlobalTimeNs().count();
if (wait_time > 0) {
#ifdef _WIN32
while (!paused && !event.IsSet() && wait_time > 0) {
wait_time = *next_time - GetGlobalTimeNs().count();
if (wait_time >= timer_resolution_ns) {
Common::Windows::SleepForOneTick();
} else {
#ifdef ARCHITECTURE_x86_64
Common::X64::MicroSleep();
#else
std::this_thread::yield();
#endif
}
}
if (event.IsSet())
event.Reset();
#else
event.WaitFor(std::chrono::nanoseconds(wait_time));
#endif
}
} else {
// Queue is empty, wait until another event is scheduled and signals us to
@@ -208,7 +226,7 @@ void CoreTiming::ResetTicks() {
}
u64 CoreTiming::GetClockTicks() const {
u64 fres = is_multicore ? Common::g_wall_clock.GetCNTPCT() : Common::WallClock::CPUTickToCNTPCT(cpu_ticks);
u64 fres = is_multicore ? clock.GetCNTPCT() : Common::WallClock::CPUTickToCNTPCT(cpu_ticks);
if (auto const overclock = Settings::values.fast_cpu_time.GetValue(); overclock != Settings::CpuClock::Off) {
fres = u64(f64(fres) * (1.7 + 0.3 * u32(overclock)));
}
@@ -222,7 +240,7 @@ u64 CoreTiming::GetClockTicks() const {
u64 CoreTiming::GetGPUTicks() const {
return is_multicore
? Common::g_wall_clock.GetGPUTick()
? clock.GetGPUTick()
: Common::WallClock::CPUTickToGPUTick(cpu_ticks);
}
@@ -299,14 +317,14 @@ void CoreTiming::Reset() {
/// @brief Returns current time in nanoseconds.
std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const noexcept {
return is_multicore
? Common::g_wall_clock.GetTimeNS()
? clock.GetTimeNS()
: std::chrono::nanoseconds{Common::WallClock::CPUTickToNS(cpu_ticks)};
}
/// @brief Returns current time in microseconds.
std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const noexcept {
return is_multicore
? Common::g_wall_clock.GetTimeUS()
? clock.GetTimeUS()
: std::chrono::microseconds{Common::WallClock::CPUTickToUS(cpu_ticks)};
}
+18 -9
View File
@@ -19,7 +19,7 @@
#include "common/common_types.h"
#include "common/thread.h"
#include "common/cpu_features.h"
#include "common/wall_clock.h"
namespace Core::Timing {
@@ -142,28 +142,37 @@ public:
void Reset();
using heap_t = boost::heap::fibonacci_heap<CoreTiming::Event, boost::heap::compare<std::greater<>>>;
heap_t event_queue;
Common::WallClock clock;
s64 global_timer = 0;
#ifdef _WIN32
s64 timer_resolution_ns;
#endif
using heap_t =
boost::heap::fibonacci_heap<CoreTiming::Event, boost::heap::compare<std::greater<>>>;
heap_t event_queue;
u64 event_fifo_id = 0;
s64 pause_end_time{};
/// Cycle timing
u64 cpu_ticks{};
s64 downcount{};
Common::Event event{};
Common::Event pause_event{};
std::function<void()> on_thread_init{};
std::jthread timer_thread;
mutable std::mutex basic_lock;
std::mutex advance_lock;
std::jthread timer_thread;
std::atomic<bool> paused{};
std::atomic<bool> paused_set{};
std::atomic<bool> wait_set{};
std::atomic<bool> has_started{};
std::function<void()> on_thread_init{};
bool is_multicore{};
s64 pause_end_time{};
/// Cycle timing
u64 cpu_ticks{};
s64 downcount{};
};
/// Creates a core timing event with the given name and callback.
+79
View File
@@ -14,12 +14,17 @@
#include <mutex>
#include <vector>
#include <span>
#include <utility>
#include "common/common_types.h"
#include "common/range_mutex.h"
#include "common/scratch_buffer.h"
#include "common/virtual_buffer.h"
#if defined(__linux__)
#include <sys/mman.h>
#endif
namespace Core {
constexpr size_t DEVICE_PAGEBITS = 12ULL;
@@ -45,6 +50,74 @@ class DeviceMemoryManager {
using DeviceMethods = typename Traits::DeviceMethods;
public:
class MirrorMapping {
public:
MirrorMapping() = default;
MirrorMapping(u8* mapped_base_, size_t mapped_size_, size_t data_offset_)
: mapped_base{mapped_base_}, mapped_size{mapped_size_}, data_offset{data_offset_} {}
MirrorMapping(const MirrorMapping&) = delete;
MirrorMapping& operator=(const MirrorMapping&) = delete;
MirrorMapping(MirrorMapping&& other) noexcept {
MoveFrom(other);
}
MirrorMapping& operator=(MirrorMapping&& other) noexcept {
if (this != &other) {
Release();
MoveFrom(other);
}
return *this;
}
~MirrorMapping() {
Release();
}
[[nodiscard]] bool IsValid() const noexcept {
return mapped_base != nullptr;
}
[[nodiscard]] explicit operator bool() const noexcept {
return IsValid();
}
[[nodiscard]] u8* Data() noexcept {
return mapped_base ? mapped_base + data_offset : nullptr;
}
[[nodiscard]] const u8* Data() const noexcept {
return mapped_base ? mapped_base + data_offset : nullptr;
}
[[nodiscard]] size_t Size() const noexcept {
return mapped_size >= data_offset ? mapped_size - data_offset : 0;
}
private:
void MoveFrom(MirrorMapping& other) noexcept {
mapped_base = std::exchange(other.mapped_base, nullptr);
mapped_size = std::exchange(other.mapped_size, 0);
data_offset = std::exchange(other.data_offset, 0);
}
void Release() noexcept {
#if defined(__linux__)
if (mapped_base) {
munmap(mapped_base, mapped_size);
}
#endif
mapped_base = nullptr;
mapped_size = 0;
data_offset = 0;
}
u8* mapped_base{};
size_t mapped_size{};
size_t data_offset{};
};
DeviceMemoryManager(const DeviceMemory& device_memory);
~DeviceMemoryManager();
@@ -118,6 +191,11 @@ public:
void WriteBlock(DAddr address, const void* src_pointer, size_t size);
void WriteBlockUnsafe(DAddr address, const void* src_pointer, size_t size);
[[nodiscard]] MirrorMapping CreateMirrorMapping(DAddr address, size_t size) const;
[[nodiscard]] u64 GetMappingVersion() const noexcept {
return mapping_version.load(std::memory_order_acquire);
}
Asid RegisterProcess(Memory::Memory* memory);
void UnregisterProcess(Asid id);
@@ -236,6 +314,7 @@ private:
std::unique_ptr<CachedPages> cached_pages;
Common::RangeMutex counter_guard;
std::mutex mapping_guard;
std::atomic<u64> mapping_version{1};
};

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