Compare commits
244 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 40af1111c2 | |||
| 553be194f6 | |||
| 048d3e2404 | |||
| 3c925a7282 | |||
| e37d00332c | |||
| d3114c620d | |||
| 26b76d2eaf | |||
| e2164f3417 | |||
| c0fb5e876d | |||
| 64c2ccb0cb | |||
| dbacb31f61 | |||
| 0b9f2c2f14 | |||
| e158167139 | |||
| 3da4280e81 | |||
| 77177a7e33 | |||
| 133a68ee9b | |||
| b1cd6cec19 | |||
| d9336860d7 | |||
| 4496030ea9 | |||
| eb74ef474b | |||
| 682c50715c | |||
| c3cae9d992 | |||
| 224a19758e | |||
| 8c9e238a7b | |||
| 55e6d0dae0 | |||
| 9632434243 | |||
| ec9550ced5 | |||
| 47a2efee73 | |||
| 5b7c0f13d3 | |||
| ddf64e56af | |||
| 155213484b | |||
| b7ad83383f | |||
| 6f101e0f02 | |||
| 972b93bf00 | |||
| a5476541f2 | |||
| 1e35ade1ec | |||
| b8777b6653 | |||
| 752659aef3 | |||
| 9574429c5f | |||
| 20cf09471a | |||
| 61883d8820 | |||
| bb86fc573f | |||
| 1effa578f1 | |||
| df6dffa30b | |||
| aedd739631 | |||
| ca3db0d7c9 | |||
| 0d99b7962d | |||
| c80ed6d81f | |||
| 903705043d | |||
| 11e1cbbdbd | |||
| fa342cae22 | |||
| fedd983f96 | |||
| d97d409647 | |||
| c2b7de66b3 | |||
| 8a372035db | |||
| 9982cff98b | |||
| fe24c65153 | |||
| 1a9b71b1c6 | |||
| cdce7f781b | |||
| 8d774e7415 | |||
| ada09778d9 | |||
| 8fd1d769fe | |||
| afab6c143c | |||
| fd7afda1e8 | |||
| 770e19f51a | |||
| 8bb604b3be | |||
| a9ca39f859 | |||
| b59ca4df0c | |||
| 98317f2b77 | |||
| f5fd6b5c86 | |||
| b2099fbdcc | |||
| 7cfa28a666 | |||
| 5a568b1655 | |||
| 3d02143476 | |||
| ba34cf0a69 | |||
| a283eda320 | |||
| 359f22b808 | |||
| 5caa150e9a | |||
| bc8b3d225e | |||
| 4d60410dd9 | |||
| 98b5e236d4 | |||
| 920429fde7 | |||
| 2931101e6f | |||
| 668e80a9f4 | |||
| e44ac8b821 | |||
| f350c3d74e | |||
| 9cf4c8831d | |||
| e462191482 | |||
| bb74973bba | |||
| 6fc4012396 | |||
| feb49c822d | |||
| c6ea0c650e | |||
| 0f4ae3cc52 | |||
| 835b950f7e | |||
| cbaf3fb433 | |||
| b617874724 | |||
| 3f8e7a5585 | |||
| d7990c159e | |||
| 2c62563ab5 | |||
| 139ea93512 | |||
| c77b8df12e | |||
| ad038609c8 | |||
| 68d9504a04 | |||
| af35dbcf63 | |||
| de0e8eff42 | |||
| 3cbe352c18 | |||
| a21b8824fb | |||
| d30b885d71 | |||
| 39a5ce4e69 | |||
| ac104a24d1 | |||
| 7b7f6f1cb7 | |||
| 31d4bc6953 | |||
| 1a49991676 | |||
| d55096ce85 | |||
| 1689530f52 | |||
| 3b5a937125 | |||
| 71fe9fd0f2 | |||
| 4774e32593 | |||
| fc0ace6048 | |||
| 92c0ad23eb | |||
| 7969d4d5de | |||
| e85c19adcb | |||
| 35d3e7db2a | |||
| ae7062d522 | |||
| 61399de5db | |||
| 1485daff06 | |||
| d9d566bd3f | |||
| 673de3995b | |||
| 3ce0ef04dd | |||
| afea5c163f | |||
| 8a3d22c4bd | |||
| f6d57d7dd9 | |||
| da8864d002 | |||
| e8d71712e7 | |||
| 80a3a73123 | |||
| 188932768e | |||
| a60b669ef4 | |||
| 1225627515 | |||
| 1dba5fab62 | |||
| 2a752bbd64 | |||
| 001168d191 | |||
| 181a54a37b | |||
| 4242064560 | |||
| 700f1d498e | |||
| 31e3437a2f | |||
| 2213927253 | |||
| b80f7faebe | |||
| 4b40799e03 | |||
| d9e375acc3 | |||
| dbb9d601df | |||
| 2e3aad41aa | |||
| 9015a512c2 | |||
| 0f6007b405 | |||
| ad9f97cd8d | |||
| 41e855bd42 | |||
| acc887cc34 | |||
| f55ed1cab0 | |||
| 087c6c2ef1 | |||
| 465c46387d | |||
| 23589ad9b8 | |||
| 1b1b99fbd5 | |||
| d794ced303 | |||
| 5082efef6c | |||
| 86ccc87111 | |||
| 12baf88dc8 | |||
| f711d1ce52 | |||
| 70abb13a43 | |||
| c2c3f2c3e7 | |||
| 3b77dec188 | |||
| 4a493cb10f | |||
| db88eaa346 | |||
| c8da75b7ed | |||
| 56b8a9ba6e | |||
| 5a74ced59a | |||
| c891497b61 | |||
| 00616c7ad8 | |||
| 75d6fe3669 | |||
| e48e22eb52 | |||
| da0fd51642 | |||
| 08d81e0bd8 | |||
| b59b967280 | |||
| 37b4f79b54 | |||
| 09a87966e0 | |||
| 4213f1c126 | |||
| 8d4458ef24 | |||
| 89af4bfba8 | |||
| 3a5f9409c8 | |||
| 7239470dad | |||
| 7172339c7a | |||
| 9c32f29af1 | |||
| 4a7a771340 | |||
| eb726677b2 | |||
| 92813f01a4 | |||
| 49870baea4 | |||
| 344006b856 | |||
| 8dafe15600 | |||
| 44ccec7846 | |||
| 882dfa36ae | |||
| 672e61d802 | |||
| 60aa942210 | |||
| 809126c94a | |||
| 7e3cdfc453 | |||
| d5d6322640 | |||
| 6b1cb73350 | |||
| e4bc7b8611 | |||
| b2c2138af7 | |||
| b862d5d8d8 | |||
| 36c77761cf | |||
| 7a5d235d94 | |||
| d1f3c121a0 | |||
| e9109cb5f2 | |||
| cb2a33babc | |||
| a278fa6e2a | |||
| d55046c5e9 | |||
| 1c7dae966d | |||
| 88007077e2 | |||
| aa11d73bba | |||
| 1694c55d62 | |||
| 8f207bd93d | |||
| 92b1f8d5da | |||
| f5e635adda | |||
| 9c6cd93195 | |||
| 063b23cc58 | |||
| 4834961736 | |||
| caa138b33f | |||
| 19a4e12e6e | |||
| 848f69eb19 | |||
| 9dc9e501de | |||
| 12f7d42d32 | |||
| 45b343d1d0 | |||
| c976613ab2 | |||
| e339ec0e00 | |||
| 3dbaafe1f3 | |||
| f958cbc737 | |||
| 606cdb17d3 | |||
| 658e1ee426 | |||
| 83a24ad638 | |||
| 9d3b190465 | |||
| 260430c849 | |||
| 6b58db9ccd | |||
| d77fe3b1c2 | |||
| 69bd6cd490 | |||
| dc915aff62 | |||
| bfb7cbc292 |
@@ -33,16 +33,14 @@ DESTDIR="$PWD/AppDir" ninja install
|
||||
rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester
|
||||
|
||||
# Download tools needed to build an AppImage
|
||||
wget -nc https://github.com/yuzu-emu/ext-linux-bin/raw/main/appimage/linuxdeploy-x86_64.AppImage
|
||||
wget -nc https://github.com/yuzu-emu/ext-linux-bin/raw/main/appimage/linuxdeploy-plugin-qt-x86_64.AppImage
|
||||
wget -nc https://github.com/yuzu-emu/ext-linux-bin/raw/main/appimage/AppRun-patched-x86_64
|
||||
wget -nc https://raw.githubusercontent.com/yuzu-emu/ext-linux-bin/main/gcc/deploy-linux.sh
|
||||
wget -nc https://raw.githubusercontent.com/yuzu-emu/AppImageKit-checkrt/old/AppRun.sh
|
||||
wget -nc https://github.com/yuzu-emu/ext-linux-bin/raw/main/appimage/exec-x86_64.so
|
||||
# Set executable bit
|
||||
chmod 755 \
|
||||
AppRun-patched-x86_64 \
|
||||
deploy-linux.sh \
|
||||
AppRun.sh \
|
||||
exec-x86_64.so \
|
||||
linuxdeploy-x86_64.AppImage \
|
||||
linuxdeploy-plugin-qt-x86_64.AppImage
|
||||
|
||||
# Workaround for https://github.com/AppImage/AppImageKit/issues/828
|
||||
export APPIMAGE_EXTRACT_AND_RUN=1
|
||||
@@ -52,7 +50,7 @@ mkdir -p AppDir/usr/optional/libstdc++
|
||||
mkdir -p AppDir/usr/optional/libgcc_s
|
||||
|
||||
# Deploy yuzu's needed dependencies
|
||||
./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt
|
||||
DEPLOY_QT=1 ./deploy-linux.sh AppDir/usr/bin/yuzu AppDir
|
||||
|
||||
# Workaround for libQt5MultimediaGstTools indirectly requiring libwayland-client and breaking Vulkan usage on end-user systems
|
||||
find AppDir -type f -regex '.*libwayland-client\.so.*' -delete -print
|
||||
@@ -60,6 +58,6 @@ find AppDir -type f -regex '.*libwayland-client\.so.*' -delete -print
|
||||
# Workaround for building yuzu with GCC 10 but also trying to distribute it to Ubuntu 18.04 et al.
|
||||
# See https://github.com/darealshinji/AppImageKit-checkrt
|
||||
cp exec-x86_64.so AppDir/usr/optional/exec.so
|
||||
cp AppRun-patched-x86_64 AppDir/AppRun
|
||||
cp AppRun.sh AppDir/AppRun
|
||||
cp --dereference /usr/lib/x86_64-linux-gnu/libstdc++.so.6 AppDir/usr/optional/libstdc++/libstdc++.so.6
|
||||
cp --dereference /lib/x86_64-linux-gnu/libgcc_s.so.1 AppDir/usr/optional/libgcc_s/libgcc_s.so.1
|
||||
|
||||
@@ -5,21 +5,24 @@
|
||||
|
||||
. .ci/scripts/common/pre-upload.sh
|
||||
|
||||
APPIMAGE_NAME="yuzu-${GITDATE}-${GITREV}.AppImage"
|
||||
REV_NAME="yuzu-linux-${GITDATE}-${GITREV}"
|
||||
APPIMAGE_NAME="yuzu-${RELEASE_NAME}-${GITDATE}-${GITREV}.AppImage"
|
||||
BASE_NAME="yuzu-linux"
|
||||
REV_NAME="${BASE_NAME}-${GITDATE}-${GITREV}"
|
||||
ARCHIVE_NAME="${REV_NAME}.tar.xz"
|
||||
COMPRESSION_FLAGS="-cJvf"
|
||||
|
||||
if [ "${RELEASE_NAME}" = "mainline" ]; then
|
||||
DIR_NAME="${REV_NAME}"
|
||||
if [ "${RELEASE_NAME}" = "mainline" ] || [ "${RELEASE_NAME}" = "early-access" ]; then
|
||||
DIR_NAME="${BASE_NAME}-${RELEASE_NAME}"
|
||||
else
|
||||
DIR_NAME="${REV_NAME}_${RELEASE_NAME}"
|
||||
DIR_NAME="${REV_NAME}-${RELEASE_NAME}"
|
||||
fi
|
||||
|
||||
mkdir "$DIR_NAME"
|
||||
|
||||
cp build/bin/yuzu-cmd "$DIR_NAME"
|
||||
cp build/bin/yuzu "$DIR_NAME"
|
||||
if [ "${RELEASE_NAME}" != "early-access" ] && [ "${RELEASE_NAME}" != "mainline" ]; then
|
||||
cp build/bin/yuzu "$DIR_NAME"
|
||||
fi
|
||||
|
||||
# Build an AppImage
|
||||
cd build
|
||||
@@ -32,6 +35,11 @@ if ! ./appimagetool-x86_64.AppImage --version; then
|
||||
export APPIMAGE_EXTRACT_AND_RUN=1
|
||||
fi
|
||||
|
||||
# Don't let AppImageLauncher ask to integrate EA
|
||||
if [ "${RELEASE_NAME}" = "mainline" ] || [ "${RELEASE_NAME}" = "early-access" ]; then
|
||||
echo "X-AppImage-Integrate=false" >> AppDir/org.yuzu_emu.yuzu.desktop
|
||||
fi
|
||||
|
||||
if [ "${RELEASE_NAME}" = "mainline" ]; then
|
||||
# Generate update information if releasing to mainline
|
||||
./appimagetool-x86_64.AppImage -u "gh-releases-zsync|yuzu-emu|yuzu-${RELEASE_NAME}|latest|yuzu-*.AppImage.zsync" AppDir "${APPIMAGE_NAME}"
|
||||
@@ -46,4 +54,9 @@ if [ -f "build/${APPIMAGE_NAME}.zsync" ]; then
|
||||
cp "build/${APPIMAGE_NAME}.zsync" "${ARTIFACTS_DIR}/"
|
||||
fi
|
||||
|
||||
# Copy the AppImage to the general release directory and remove git revision info
|
||||
if [ "${RELEASE_NAME}" = "mainline" ] || [ "${RELEASE_NAME}" = "early-access" ]; then
|
||||
cp "build/${APPIMAGE_NAME}" "${DIR_NAME}/yuzu-${RELEASE_NAME}.AppImage"
|
||||
fi
|
||||
|
||||
. .ci/scripts/common/post-upload.sh
|
||||
|
||||
@@ -10,17 +10,14 @@ set -e
|
||||
ccache -sv
|
||||
|
||||
mkdir -p build && cd build
|
||||
export LDFLAGS="-fuse-ld=lld"
|
||||
# -femulated-tls required due to an incompatibility between GCC and Clang
|
||||
# TODO(lat9nq): If this is widespread, we probably need to add this to CMakeLists where appropriate
|
||||
export CXXFLAGS="-femulated-tls"
|
||||
cmake .. \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_TOOLCHAIN_FILE="${PWD}/../CMakeModules/MinGWClangCross.cmake" \
|
||||
-DCMAKE_TOOLCHAIN_FILE="${PWD}/../CMakeModules/MinGWCross.cmake" \
|
||||
-DDISPLAY_VERSION="$1" \
|
||||
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
||||
-DENABLE_QT_TRANSLATION=ON \
|
||||
-DUSE_CCACHE=ON \
|
||||
-DYUZU_CRASH_DUMPS=ON \
|
||||
-DYUZU_USE_BUNDLED_SDL2=OFF \
|
||||
-DYUZU_USE_EXTERNAL_SDL2=OFF \
|
||||
-DYUZU_TESTS=OFF \
|
||||
|
||||
@@ -65,8 +65,8 @@ if ("$env:GITHUB_ACTIONS" -eq "true") {
|
||||
# None of the other GHA builds are including source, so commenting out today
|
||||
#Copy-Item $MSVC_SOURCE_TARXZ -Destination "artifacts"
|
||||
|
||||
# Are debug symbols important?
|
||||
# cp .\build\bin\yuzu*.pdb .\pdb\
|
||||
# Debugging symbols
|
||||
cp .\build\bin\yuzu*.pdb .\artifacts\
|
||||
|
||||
# Write out a tag BUILD_TAG to environment for the Upload step
|
||||
# We're getting ${{ github.event.number }} as $env:PR_NUMBER"
|
||||
|
||||
@@ -9,7 +9,7 @@ parameters:
|
||||
steps:
|
||||
- script: choco install vulkan-sdk
|
||||
displayName: 'Install vulkan-sdk'
|
||||
- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 17 2022" -A x64 -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DYUZU_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release .. && cd ..
|
||||
- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 17 2022" -A x64 -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DYUZU_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release -DYUZU_CRASH_DUMPS=ON .. && cd ..
|
||||
displayName: 'Configure CMake'
|
||||
- task: MSBuild@1
|
||||
displayName: 'Build'
|
||||
|
||||
@@ -11,9 +11,30 @@ stages:
|
||||
- stage: build
|
||||
displayName: 'build'
|
||||
jobs:
|
||||
- job: build
|
||||
- job: linux
|
||||
timeoutInMinutes: 120
|
||||
displayName: 'windows-msvc'
|
||||
displayName: 'linux'
|
||||
pool:
|
||||
vmImage: ubuntu-latest
|
||||
strategy:
|
||||
maxParallel: 10
|
||||
matrix:
|
||||
linux:
|
||||
BuildSuffix: 'linux'
|
||||
ScriptFolder: 'linux'
|
||||
steps:
|
||||
- template: ./templates/sync-source.yml
|
||||
parameters:
|
||||
artifactSource: $(parameters.artifactSource)
|
||||
needSubmodules: 'true'
|
||||
- template: ./templates/build-single.yml
|
||||
parameters:
|
||||
artifactSource: 'false'
|
||||
cache: $(parameters.cache)
|
||||
version: $(DisplayVersion)
|
||||
- job: msvc
|
||||
timeoutInMinutes: 120
|
||||
displayName: 'windows'
|
||||
pool:
|
||||
vmImage: windows-2022
|
||||
steps:
|
||||
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
run: |
|
||||
glslangValidator --version
|
||||
mkdir build
|
||||
cmake . -B build -GNinja -DCMAKE_TOOLCHAIN_FILE="CMakeModules/MSVCCache.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release -DGIT_BRANCH=pr-verify
|
||||
cmake . -B build -GNinja -DCMAKE_TOOLCHAIN_FILE="CMakeModules/MSVCCache.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release -DGIT_BRANCH=pr-verify -DYUZU_CRASH_DUMPS=ON
|
||||
- name: Build
|
||||
run: cmake --build build
|
||||
- name: Cache Summary
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Build directory
|
||||
[Bb]uild/
|
||||
[Bb]uild*/
|
||||
doc-build/
|
||||
|
||||
# Generated source files
|
||||
|
||||
+15
-3
@@ -38,6 +38,8 @@ option(YUZU_USE_BUNDLED_OPUS "Compile bundled opus" ON)
|
||||
|
||||
option(YUZU_TESTS "Compile tests" ON)
|
||||
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile Windows crash dump (Minidump) support" OFF "WIN32" OFF)
|
||||
|
||||
option(YUZU_USE_BUNDLED_VCPKG "Use vcpkg for yuzu dependencies" "${MSVC}")
|
||||
|
||||
option(YUZU_CHECK_SUBMODULES "Check if submodules are present" ON)
|
||||
@@ -46,6 +48,9 @@ if (YUZU_USE_BUNDLED_VCPKG)
|
||||
if (YUZU_TESTS)
|
||||
list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests")
|
||||
endif()
|
||||
if (YUZU_CRASH_DUMPS)
|
||||
list(APPEND VCPKG_MANIFEST_FEATURES "dbghelp")
|
||||
endif()
|
||||
|
||||
include(${CMAKE_SOURCE_DIR}/externals/vcpkg/scripts/buildsystems/vcpkg.cmake)
|
||||
elseif(NOT "$ENV{VCPKG_TOOLCHAIN_FILE}" STREQUAL "")
|
||||
@@ -247,7 +252,7 @@ if(ENABLE_QT)
|
||||
endif()
|
||||
|
||||
# Check for headers
|
||||
Include(FindPkgConfig REQUIRED)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(QT_DEP_GLU QUIET glu>=9.0.0)
|
||||
if (NOT QT_DEP_GLU_FOUND)
|
||||
message(FATAL_ERROR "Qt bundled pacakge dependency `glu` not found. \
|
||||
@@ -381,7 +386,7 @@ endif()
|
||||
|
||||
# Ensure libusb is properly configured (based on dolphin libusb include)
|
||||
if(NOT APPLE AND NOT YUZU_USE_BUNDLED_LIBUSB)
|
||||
include(FindPkgConfig)
|
||||
find_package(PkgConfig)
|
||||
if (PKG_CONFIG_FOUND AND NOT CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD")
|
||||
pkg_check_modules(LIBUSB QUIET libusb-1.0>=1.0.24)
|
||||
else()
|
||||
@@ -405,7 +410,7 @@ set(FFmpeg_COMPONENTS
|
||||
swscale)
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
Include(FindPkgConfig REQUIRED)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBVA libva)
|
||||
endif()
|
||||
if (NOT YUZU_USE_BUNDLED_FFMPEG)
|
||||
@@ -447,6 +452,13 @@ elseif (WIN32)
|
||||
# PSAPI is the Process Status API
|
||||
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version)
|
||||
endif()
|
||||
|
||||
if (YUZU_CRASH_DUMPS)
|
||||
find_library(DBGHELP_LIBRARY dbghelp)
|
||||
if ("${DBGHELP_LIBRARY}" STREQUAL "DBGHELP_LIBRARY-NOTFOUND")
|
||||
message(FATAL_ERROR "YUZU_CRASH_DUMPS enabled but dbghelp library not found")
|
||||
endif()
|
||||
endif()
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
|
||||
set(PLATFORM_LIBRARIES rt)
|
||||
endif()
|
||||
|
||||
@@ -7,11 +7,6 @@ function(get_timestamp _var)
|
||||
set(${_var} "${timestamp}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${SRC_DIR}/externals/cmake-modules")
|
||||
|
||||
# Find the package here with the known path so that the GetGit commands can find it as well
|
||||
find_package(Git QUIET PATHS "${GIT_EXECUTABLE}")
|
||||
|
||||
# generate git/build information
|
||||
include(GetGitRevisionDescription)
|
||||
if(NOT GIT_REF_SPEC)
|
||||
@@ -29,6 +24,7 @@ get_timestamp(BUILD_DATE)
|
||||
# Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well
|
||||
set(REPO_NAME "")
|
||||
set(BUILD_VERSION "0")
|
||||
set(BUILD_ID ${DISPLAY_VERSION})
|
||||
if (BUILD_REPOSITORY)
|
||||
# regex capture the string nightly or canary into CMAKE_MATCH_1
|
||||
string(REGEX MATCH "yuzu-emu/yuzu-?(.*)" OUTVAR ${BUILD_REPOSITORY})
|
||||
@@ -57,6 +53,4 @@ if (BUILD_REPOSITORY)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# The variable SRC_DIR must be passed into the script
|
||||
# (since it uses the current build directory for all values of CMAKE_*_DIR)
|
||||
configure_file("${SRC_DIR}/src/common/scm_rev.cpp.in" "scm_rev.cpp" @ONLY)
|
||||
configure_file(scm_rev.cpp.in scm_rev.cpp @ONLY)
|
||||
|
||||
Vendored
+522
-456
File diff suppressed because it is too large
Load Diff
Vendored
+522
-456
File diff suppressed because it is too large
Load Diff
Vendored
+524
-458
File diff suppressed because it is too large
Load Diff
Vendored
+588
-522
File diff suppressed because it is too large
Load Diff
Vendored
+549
-478
File diff suppressed because it is too large
Load Diff
Vendored
+547
-480
File diff suppressed because it is too large
Load Diff
Vendored
+663
-577
File diff suppressed because it is too large
Load Diff
Vendored
+522
-456
File diff suppressed because it is too large
Load Diff
Vendored
+555
-489
File diff suppressed because it is too large
Load Diff
Vendored
+548
-480
File diff suppressed because it is too large
Load Diff
Vendored
+660
-589
File diff suppressed because it is too large
Load Diff
Vendored
+522
-456
File diff suppressed because it is too large
Load Diff
Vendored
+522
-456
File diff suppressed because it is too large
Load Diff
Vendored
+522
-456
File diff suppressed because it is too large
Load Diff
Vendored
+572
-504
File diff suppressed because it is too large
Load Diff
Vendored
+572
-504
File diff suppressed because it is too large
Load Diff
Vendored
+526
-458
File diff suppressed because it is too large
Load Diff
Vendored
+522
-456
File diff suppressed because it is too large
Load Diff
Vendored
+653
-581
File diff suppressed because it is too large
Load Diff
Vendored
+522
-456
File diff suppressed because it is too large
Load Diff
Vendored
+522
-456
File diff suppressed because it is too large
Load Diff
Vendored
+531
-463
File diff suppressed because it is too large
Load Diff
Vendored
+524
-456
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
@@ -43,7 +43,7 @@ if (NOT WIN32)
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
endforeach()
|
||||
|
||||
Include(FindPkgConfig REQUIRED)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBVA libva)
|
||||
pkg_check_modules(CUDA cuda)
|
||||
pkg_check_modules(FFNVCODEC ffnvcodec)
|
||||
|
||||
Vendored
+1
-1
@@ -108,7 +108,7 @@ if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") OR APPLE)
|
||||
target_include_directories(usb INTERFACE "${LIBUSB_INCLUDE_DIRS}")
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
Include(FindPkgConfig)
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(LIBUDEV REQUIRED libudev)
|
||||
|
||||
if (LIBUDEV_FOUND)
|
||||
|
||||
@@ -121,6 +121,7 @@ else()
|
||||
|
||||
if (ARCHITECTURE_x86_64)
|
||||
add_compile_options("-mcx16")
|
||||
add_compile_options("-fwrapv")
|
||||
endif()
|
||||
|
||||
if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>(system)} {
|
||||
AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>()} {
|
||||
CreateSinks();
|
||||
// Must be created after the sinks
|
||||
adsp = std::make_unique<AudioRenderer::ADSP::ADSP>(system, *output_sink);
|
||||
|
||||
@@ -82,7 +82,7 @@ u32 Manager::GetDeviceNames(std::vector<AudioRenderer::AudioDevice::AudioDeviceN
|
||||
|
||||
auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)};
|
||||
if (input_devices.size() > 1) {
|
||||
names.push_back(AudioRenderer::AudioDevice::AudioDeviceName("Uac"));
|
||||
names.emplace_back("Uac");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/audio_in_manager.h"
|
||||
#include "audio_core/audio_manager.h"
|
||||
#include "audio_core/audio_out_manager.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
AudioManager::AudioManager(Core::System& system_) : system{system_} {
|
||||
AudioManager::AudioManager() {
|
||||
thread = std::jthread([this]() { ThreadFunc(); });
|
||||
}
|
||||
|
||||
@@ -27,7 +26,7 @@ Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
|
||||
|
||||
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
|
||||
if (buffer_events[index] == nullptr) {
|
||||
buffer_events[index] = buffer_func;
|
||||
buffer_events[index] = std::move(buffer_func);
|
||||
needs_update = true;
|
||||
events.SetAudioEvent(Event::Type::AudioOutManager, true);
|
||||
}
|
||||
@@ -43,7 +42,7 @@ Result AudioManager::SetInManager(BufferEventFunc buffer_func) {
|
||||
|
||||
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
|
||||
if (buffer_events[index] == nullptr) {
|
||||
buffer_events[index] = buffer_func;
|
||||
buffer_events[index] = std::move(buffer_func);
|
||||
needs_update = true;
|
||||
events.SetAudioEvent(Event::Type::AudioInManager, true);
|
||||
}
|
||||
@@ -60,19 +59,21 @@ void AudioManager::ThreadFunc() {
|
||||
running = true;
|
||||
|
||||
while (running) {
|
||||
auto timed_out{events.Wait(l, std::chrono::seconds(2))};
|
||||
const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
|
||||
|
||||
if (events.CheckAudioEventSet(Event::Type::Max)) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < buffer_events.size(); i++) {
|
||||
if (events.CheckAudioEventSet(Event::Type(i)) || timed_out) {
|
||||
const auto event_type = static_cast<Event::Type>(i);
|
||||
|
||||
if (events.CheckAudioEventSet(event_type) || timed_out) {
|
||||
if (buffer_events[i]) {
|
||||
buffer_events[i]();
|
||||
}
|
||||
}
|
||||
events.SetAudioEvent(Event::Type(i), false);
|
||||
events.SetAudioEvent(event_type, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,22 +10,11 @@
|
||||
#include <thread>
|
||||
|
||||
#include "audio_core/audio_event.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
union Result;
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
namespace AudioOut {
|
||||
class Manager;
|
||||
}
|
||||
|
||||
namespace AudioIn {
|
||||
class Manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* The AudioManager's main purpose is to wait for buffer events for the audio in and out managers,
|
||||
* and call an associated callback to release buffers.
|
||||
@@ -43,7 +32,7 @@ class AudioManager {
|
||||
using BufferEventFunc = std::function<void()>;
|
||||
|
||||
public:
|
||||
explicit AudioManager(Core::System& system);
|
||||
explicit AudioManager();
|
||||
|
||||
/**
|
||||
* Shutdown the audio manager.
|
||||
@@ -80,10 +69,6 @@ private:
|
||||
*/
|
||||
void ThreadFunc();
|
||||
|
||||
/// Core system
|
||||
Core::System& system;
|
||||
/// Have sessions started palying?
|
||||
bool sessions_started{};
|
||||
/// Is the main thread running?
|
||||
std::atomic<bool> running{};
|
||||
/// Unused
|
||||
|
||||
@@ -74,7 +74,7 @@ void Manager::BufferReleaseAndRegister() {
|
||||
|
||||
u32 Manager::GetAudioOutDeviceNames(
|
||||
std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names) const {
|
||||
names.push_back({"DeviceOut"});
|
||||
names.emplace_back("DeviceOut");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ SystemManager& Manager::GetSystemManager() {
|
||||
return *system_manager;
|
||||
}
|
||||
|
||||
auto Manager::GetWorkBufferSize(const AudioRendererParameterInternal& params, u64& out_count)
|
||||
-> Result {
|
||||
Result Manager::GetWorkBufferSize(const AudioRendererParameterInternal& params,
|
||||
u64& out_count) const {
|
||||
if (!CheckValidRevision(params.revision)) {
|
||||
return Service::Audio::ERR_INVALID_REVISION;
|
||||
}
|
||||
@@ -54,7 +54,7 @@ void Manager::ReleaseSessionId(const s32 session_id) {
|
||||
session_ids[--session_count] = session_id;
|
||||
}
|
||||
|
||||
u32 Manager::GetSessionCount() {
|
||||
u32 Manager::GetSessionCount() const {
|
||||
std::scoped_lock l{session_lock};
|
||||
return session_count;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
* @param out_count - Output size of the required workbuffer.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result GetWorkBufferSize(const AudioRendererParameterInternal& params, u64& out_count);
|
||||
Result GetWorkBufferSize(const AudioRendererParameterInternal& params, u64& out_count) const;
|
||||
|
||||
/**
|
||||
* Get a new session id.
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
*
|
||||
* @return The number of active sessions.
|
||||
*/
|
||||
u32 GetSessionCount();
|
||||
u32 GetSessionCount() const;
|
||||
|
||||
/**
|
||||
* Add a renderer system to the manager.
|
||||
@@ -94,7 +94,7 @@ private:
|
||||
/// Number of active renderers
|
||||
u32 session_count{};
|
||||
/// Lock for interacting with the sessions
|
||||
std::mutex session_lock{};
|
||||
mutable std::mutex session_lock{};
|
||||
/// Regularly generates commands from the registered systems for the AudioRenderer
|
||||
std::unique_ptr<SystemManager> system_manager{};
|
||||
};
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
*
|
||||
* @param buffer - The new buffer.
|
||||
*/
|
||||
void AppendBuffer(AudioBuffer& buffer) {
|
||||
void AppendBuffer(const AudioBuffer& buffer) {
|
||||
std::scoped_lock l{lock};
|
||||
buffers[appended_index] = buffer;
|
||||
appended_count++;
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
*
|
||||
* @return Is the buffer was released.
|
||||
*/
|
||||
bool ReleaseBuffers(Core::Timing::CoreTiming& core_timing, DeviceSession& session) {
|
||||
bool ReleaseBuffers(const Core::Timing::CoreTiming& core_timing, const DeviceSession& session) {
|
||||
std::scoped_lock l{lock};
|
||||
bool buffer_released{false};
|
||||
while (registered_count > 0) {
|
||||
|
||||
@@ -73,12 +73,12 @@ void DeviceSession::Stop() {
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceSession::AppendBuffers(std::span<AudioBuffer> buffers) const {
|
||||
for (size_t i = 0; i < buffers.size(); i++) {
|
||||
void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const {
|
||||
for (const auto& buffer : buffers) {
|
||||
Sink::SinkBuffer new_buffer{
|
||||
.frames = buffers[i].size / (channel_count * sizeof(s16)),
|
||||
.frames = buffer.size / (channel_count * sizeof(s16)),
|
||||
.frames_played = 0,
|
||||
.tag = buffers[i].tag,
|
||||
.tag = buffer.tag,
|
||||
.consumed = false,
|
||||
};
|
||||
|
||||
@@ -86,21 +86,21 @@ void DeviceSession::AppendBuffers(std::span<AudioBuffer> buffers) const {
|
||||
std::vector<s16> samples{};
|
||||
stream->AppendBuffer(new_buffer, samples);
|
||||
} else {
|
||||
std::vector<s16> samples(buffers[i].size / sizeof(s16));
|
||||
system.Memory().ReadBlockUnsafe(buffers[i].samples, samples.data(), buffers[i].size);
|
||||
std::vector<s16> samples(buffer.size / sizeof(s16));
|
||||
system.Memory().ReadBlockUnsafe(buffer.samples, samples.data(), buffer.size);
|
||||
stream->AppendBuffer(new_buffer, samples);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceSession::ReleaseBuffer(AudioBuffer& buffer) const {
|
||||
void DeviceSession::ReleaseBuffer(const AudioBuffer& buffer) const {
|
||||
if (type == Sink::StreamType::In) {
|
||||
auto samples{stream->ReleaseBuffer(buffer.size / sizeof(s16))};
|
||||
system.Memory().WriteBlockUnsafe(buffer.samples, samples.data(), buffer.size);
|
||||
}
|
||||
}
|
||||
|
||||
bool DeviceSession::IsBufferConsumed(AudioBuffer& buffer) const {
|
||||
bool DeviceSession::IsBufferConsumed(const AudioBuffer& buffer) const {
|
||||
return played_sample_count >= buffer.end_timestamp;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,14 +62,14 @@ public:
|
||||
*
|
||||
* @param buffers - The buffers to play.
|
||||
*/
|
||||
void AppendBuffers(std::span<AudioBuffer> buffers) const;
|
||||
void AppendBuffers(std::span<const AudioBuffer> buffers) const;
|
||||
|
||||
/**
|
||||
* (Audio In only) Pop samples from the backend, and write them back to this buffer's address.
|
||||
*
|
||||
* @param buffer - The buffer to write to.
|
||||
*/
|
||||
void ReleaseBuffer(AudioBuffer& buffer) const;
|
||||
void ReleaseBuffer(const AudioBuffer& buffer) const;
|
||||
|
||||
/**
|
||||
* Check if the buffer for the given tag has been consumed by the backend.
|
||||
@@ -78,7 +78,7 @@ public:
|
||||
*
|
||||
* @return true if the buffer has been consumed, otherwise false.
|
||||
*/
|
||||
bool IsBufferConsumed(AudioBuffer& buffer) const;
|
||||
bool IsBufferConsumed(const AudioBuffer& buffer) const;
|
||||
|
||||
/**
|
||||
* Start this device session, starting the backend stream.
|
||||
|
||||
@@ -72,7 +72,7 @@ Kernel::KReadableEvent& In::GetBufferEvent() {
|
||||
return event->GetReadableEvent();
|
||||
}
|
||||
|
||||
f32 In::GetVolume() {
|
||||
f32 In::GetVolume() const {
|
||||
std::scoped_lock l{parent_mutex};
|
||||
return system.GetVolume();
|
||||
}
|
||||
@@ -82,17 +82,17 @@ void In::SetVolume(f32 volume) {
|
||||
system.SetVolume(volume);
|
||||
}
|
||||
|
||||
bool In::ContainsAudioBuffer(u64 tag) {
|
||||
bool In::ContainsAudioBuffer(u64 tag) const {
|
||||
std::scoped_lock l{parent_mutex};
|
||||
return system.ContainsAudioBuffer(tag);
|
||||
}
|
||||
|
||||
u32 In::GetBufferCount() {
|
||||
u32 In::GetBufferCount() const {
|
||||
std::scoped_lock l{parent_mutex};
|
||||
return system.GetBufferCount();
|
||||
}
|
||||
|
||||
u64 In::GetPlayedSampleCount() {
|
||||
u64 In::GetPlayedSampleCount() const {
|
||||
std::scoped_lock l{parent_mutex};
|
||||
return system.GetPlayedSampleCount();
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ public:
|
||||
*
|
||||
* @return The current volume.
|
||||
*/
|
||||
f32 GetVolume();
|
||||
f32 GetVolume() const;
|
||||
|
||||
/**
|
||||
* Set the system volume.
|
||||
@@ -117,21 +117,21 @@ public:
|
||||
* @param tag - The tag to search for.
|
||||
* @return True if the buffer is in the system, otherwise false.
|
||||
*/
|
||||
bool ContainsAudioBuffer(u64 tag);
|
||||
bool ContainsAudioBuffer(u64 tag) const;
|
||||
|
||||
/**
|
||||
* Get the maximum number of buffers.
|
||||
*
|
||||
* @return The maximum number of buffers.
|
||||
*/
|
||||
u32 GetBufferCount();
|
||||
u32 GetBufferCount() const;
|
||||
|
||||
/**
|
||||
* Get the total played sample count for this audio in.
|
||||
*
|
||||
* @return The played sample count.
|
||||
*/
|
||||
u64 GetPlayedSampleCount();
|
||||
u64 GetPlayedSampleCount() const;
|
||||
|
||||
private:
|
||||
/// The AudioIn::Manager this audio in is registered with
|
||||
|
||||
@@ -34,16 +34,16 @@ size_t System::GetSessionId() const {
|
||||
return session_id;
|
||||
}
|
||||
|
||||
std::string_view System::GetDefaultDeviceName() {
|
||||
std::string_view System::GetDefaultDeviceName() const {
|
||||
return "BuiltInHeadset";
|
||||
}
|
||||
|
||||
std::string_view System::GetDefaultUacDeviceName() {
|
||||
std::string_view System::GetDefaultUacDeviceName() const {
|
||||
return "Uac";
|
||||
}
|
||||
|
||||
Result System::IsConfigValid(const std::string_view device_name,
|
||||
const AudioInParameter& in_params) {
|
||||
const AudioInParameter& in_params) const {
|
||||
if ((device_name.size() > 0) &&
|
||||
(device_name != GetDefaultDeviceName() && device_name != GetDefaultUacDeviceName())) {
|
||||
return Service::Audio::ERR_INVALID_DEVICE_NAME;
|
||||
@@ -114,12 +114,14 @@ bool System::AppendBuffer(const AudioInBuffer& buffer, const u64 tag) {
|
||||
}
|
||||
|
||||
const auto timestamp{buffers.GetNextTimestamp()};
|
||||
AudioBuffer new_buffer{.start_timestamp = timestamp,
|
||||
.end_timestamp = timestamp + buffer.size / (channel_count * sizeof(s16)),
|
||||
.played_timestamp = 0,
|
||||
.samples = buffer.samples,
|
||||
.tag = tag,
|
||||
.size = buffer.size};
|
||||
const AudioBuffer new_buffer{
|
||||
.start_timestamp = timestamp,
|
||||
.end_timestamp = timestamp + buffer.size / (channel_count * sizeof(s16)),
|
||||
.played_timestamp = 0,
|
||||
.samples = buffer.samples,
|
||||
.tag = tag,
|
||||
.size = buffer.size,
|
||||
};
|
||||
|
||||
buffers.AppendBuffer(new_buffer);
|
||||
RegisterBuffers();
|
||||
@@ -200,11 +202,11 @@ void System::SetVolume(const f32 volume_) {
|
||||
session->SetVolume(volume_);
|
||||
}
|
||||
|
||||
bool System::ContainsAudioBuffer(const u64 tag) {
|
||||
bool System::ContainsAudioBuffer(const u64 tag) const {
|
||||
return buffers.ContainsBuffer(tag);
|
||||
}
|
||||
|
||||
u32 System::GetBufferCount() {
|
||||
u32 System::GetBufferCount() const {
|
||||
return buffers.GetAppendedRegisteredCount();
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
*
|
||||
* @return The default audio input device name.
|
||||
*/
|
||||
std::string_view GetDefaultDeviceName();
|
||||
std::string_view GetDefaultDeviceName() const;
|
||||
|
||||
/**
|
||||
* Get the default USB audio input device name.
|
||||
@@ -77,7 +77,7 @@ public:
|
||||
*
|
||||
* @return The default USB audio input device name.
|
||||
*/
|
||||
std::string_view GetDefaultUacDeviceName();
|
||||
std::string_view GetDefaultUacDeviceName() const;
|
||||
|
||||
/**
|
||||
* Is the given initialize config valid?
|
||||
@@ -86,7 +86,7 @@ public:
|
||||
* @param in_params - Input parameters, see AudioInParameter.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result IsConfigValid(std::string_view device_name, const AudioInParameter& in_params);
|
||||
Result IsConfigValid(std::string_view device_name, const AudioInParameter& in_params) const;
|
||||
|
||||
/**
|
||||
* Initialize this system.
|
||||
@@ -218,14 +218,14 @@ public:
|
||||
* @param tag - Unique tag to search for.
|
||||
* @return True if the buffer is in the system, otherwise false.
|
||||
*/
|
||||
bool ContainsAudioBuffer(u64 tag);
|
||||
bool ContainsAudioBuffer(u64 tag) const;
|
||||
|
||||
/**
|
||||
* Get the maximum number of usable buffers (default 32).
|
||||
*
|
||||
* @return The number of buffers.
|
||||
*/
|
||||
u32 GetBufferCount();
|
||||
u32 GetBufferCount() const;
|
||||
|
||||
/**
|
||||
* Get the total number of samples played by this system.
|
||||
|
||||
@@ -72,7 +72,7 @@ Kernel::KReadableEvent& Out::GetBufferEvent() {
|
||||
return event->GetReadableEvent();
|
||||
}
|
||||
|
||||
f32 Out::GetVolume() {
|
||||
f32 Out::GetVolume() const {
|
||||
std::scoped_lock l{parent_mutex};
|
||||
return system.GetVolume();
|
||||
}
|
||||
@@ -82,17 +82,17 @@ void Out::SetVolume(const f32 volume) {
|
||||
system.SetVolume(volume);
|
||||
}
|
||||
|
||||
bool Out::ContainsAudioBuffer(const u64 tag) {
|
||||
bool Out::ContainsAudioBuffer(const u64 tag) const {
|
||||
std::scoped_lock l{parent_mutex};
|
||||
return system.ContainsAudioBuffer(tag);
|
||||
}
|
||||
|
||||
u32 Out::GetBufferCount() {
|
||||
u32 Out::GetBufferCount() const {
|
||||
std::scoped_lock l{parent_mutex};
|
||||
return system.GetBufferCount();
|
||||
}
|
||||
|
||||
u64 Out::GetPlayedSampleCount() {
|
||||
u64 Out::GetPlayedSampleCount() const {
|
||||
std::scoped_lock l{parent_mutex};
|
||||
return system.GetPlayedSampleCount();
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ public:
|
||||
*
|
||||
* @return The current volume.
|
||||
*/
|
||||
f32 GetVolume();
|
||||
f32 GetVolume() const;
|
||||
|
||||
/**
|
||||
* Set the system volume.
|
||||
@@ -117,21 +117,21 @@ public:
|
||||
* @param tag - The tag to search for.
|
||||
* @return True if the buffer is in the system, otherwise false.
|
||||
*/
|
||||
bool ContainsAudioBuffer(u64 tag);
|
||||
bool ContainsAudioBuffer(u64 tag) const;
|
||||
|
||||
/**
|
||||
* Get the maximum number of buffers.
|
||||
*
|
||||
* @return The maximum number of buffers.
|
||||
*/
|
||||
u32 GetBufferCount();
|
||||
u32 GetBufferCount() const;
|
||||
|
||||
/**
|
||||
* Get the total played sample count for this audio out.
|
||||
*
|
||||
* @return The played sample count.
|
||||
*/
|
||||
u64 GetPlayedSampleCount();
|
||||
u64 GetPlayedSampleCount() const;
|
||||
|
||||
private:
|
||||
/// The AudioOut::Manager this audio out is registered with
|
||||
|
||||
@@ -27,11 +27,12 @@ void System::Finalize() {
|
||||
buffer_event->GetWritableEvent().Signal();
|
||||
}
|
||||
|
||||
std::string_view System::GetDefaultOutputDeviceName() {
|
||||
std::string_view System::GetDefaultOutputDeviceName() const {
|
||||
return "DeviceOut";
|
||||
}
|
||||
|
||||
Result System::IsConfigValid(std::string_view device_name, const AudioOutParameter& in_params) {
|
||||
Result System::IsConfigValid(std::string_view device_name,
|
||||
const AudioOutParameter& in_params) const {
|
||||
if ((device_name.size() > 0) && (device_name != GetDefaultOutputDeviceName())) {
|
||||
return Service::Audio::ERR_INVALID_DEVICE_NAME;
|
||||
}
|
||||
@@ -113,12 +114,14 @@ bool System::AppendBuffer(const AudioOutBuffer& buffer, u64 tag) {
|
||||
}
|
||||
|
||||
const auto timestamp{buffers.GetNextTimestamp()};
|
||||
AudioBuffer new_buffer{.start_timestamp = timestamp,
|
||||
.end_timestamp = timestamp + buffer.size / (channel_count * sizeof(s16)),
|
||||
.played_timestamp = 0,
|
||||
.samples = buffer.samples,
|
||||
.tag = tag,
|
||||
.size = buffer.size};
|
||||
const AudioBuffer new_buffer{
|
||||
.start_timestamp = timestamp,
|
||||
.end_timestamp = timestamp + buffer.size / (channel_count * sizeof(s16)),
|
||||
.played_timestamp = 0,
|
||||
.samples = buffer.samples,
|
||||
.tag = tag,
|
||||
.size = buffer.size,
|
||||
};
|
||||
|
||||
buffers.AppendBuffer(new_buffer);
|
||||
RegisterBuffers();
|
||||
@@ -198,11 +201,11 @@ void System::SetVolume(const f32 volume_) {
|
||||
session->SetVolume(volume_);
|
||||
}
|
||||
|
||||
bool System::ContainsAudioBuffer(const u64 tag) {
|
||||
bool System::ContainsAudioBuffer(const u64 tag) const {
|
||||
return buffers.ContainsBuffer(tag);
|
||||
}
|
||||
|
||||
u32 System::GetBufferCount() {
|
||||
u32 System::GetBufferCount() const {
|
||||
return buffers.GetAppendedRegisteredCount();
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
*
|
||||
* @return The default audio output device name.
|
||||
*/
|
||||
std::string_view GetDefaultOutputDeviceName();
|
||||
std::string_view GetDefaultOutputDeviceName() const;
|
||||
|
||||
/**
|
||||
* Is the given initialize config valid?
|
||||
@@ -77,7 +77,7 @@ public:
|
||||
* @param in_params - Input parameters, see AudioOutParameter.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result IsConfigValid(std::string_view device_name, const AudioOutParameter& in_params);
|
||||
Result IsConfigValid(std::string_view device_name, const AudioOutParameter& in_params) const;
|
||||
|
||||
/**
|
||||
* Initialize this system.
|
||||
@@ -209,14 +209,14 @@ public:
|
||||
* @param tag - Unique tag to search for.
|
||||
* @return True if the buffer is in the system, otherwise false.
|
||||
*/
|
||||
bool ContainsAudioBuffer(u64 tag);
|
||||
bool ContainsAudioBuffer(u64 tag) const;
|
||||
|
||||
/**
|
||||
* Get the maximum number of usable buffers (default 32).
|
||||
*
|
||||
* @return The number of buffers.
|
||||
*/
|
||||
u32 GetBufferCount();
|
||||
u32 GetBufferCount() const;
|
||||
|
||||
/**
|
||||
* Get the total number of samples played by this system.
|
||||
|
||||
@@ -50,7 +50,7 @@ u32 ADSP::GetRemainCommandCount(const u32 session_id) const {
|
||||
return render_mailbox.GetRemainCommandCount(session_id);
|
||||
}
|
||||
|
||||
void ADSP::SendCommandBuffer(const u32 session_id, CommandBuffer& command_buffer) {
|
||||
void ADSP::SendCommandBuffer(const u32 session_id, const CommandBuffer& command_buffer) {
|
||||
render_mailbox.SetCommandBuffer(session_id, command_buffer);
|
||||
}
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ public:
|
||||
* @param session_id - The session id to check (0 or 1).
|
||||
* @param command_buffer - The command buffer to process.
|
||||
*/
|
||||
void SendCommandBuffer(u32 session_id, CommandBuffer& command_buffer);
|
||||
void SendCommandBuffer(u32 session_id, const CommandBuffer& command_buffer);
|
||||
|
||||
/**
|
||||
* Clear the command buffers (does not clear the time taken or the remaining command count)
|
||||
|
||||
@@ -47,11 +47,11 @@ RenderMessage AudioRenderer_Mailbox::ADSPWaitMessage() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
CommandBuffer& AudioRenderer_Mailbox::GetCommandBuffer(const s32 session_id) {
|
||||
CommandBuffer& AudioRenderer_Mailbox::GetCommandBuffer(const u32 session_id) {
|
||||
return command_buffers[session_id];
|
||||
}
|
||||
|
||||
void AudioRenderer_Mailbox::SetCommandBuffer(const u32 session_id, CommandBuffer& buffer) {
|
||||
void AudioRenderer_Mailbox::SetCommandBuffer(const u32 session_id, const CommandBuffer& buffer) {
|
||||
command_buffers[session_id] = buffer;
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ void AudioRenderer::CreateSinkStreams() {
|
||||
}
|
||||
|
||||
void AudioRenderer::ThreadFunc() {
|
||||
constexpr char name[]{"yuzu:AudioRenderer"};
|
||||
constexpr char name[]{"AudioRenderer"};
|
||||
MicroProfileOnThreadCreate(name);
|
||||
Common::SetCurrentThreadName(name);
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
|
||||
|
||||
@@ -83,7 +83,7 @@ public:
|
||||
* @param session_id - The session id to get (0 or 1).
|
||||
* @return The command buffer.
|
||||
*/
|
||||
CommandBuffer& GetCommandBuffer(s32 session_id);
|
||||
CommandBuffer& GetCommandBuffer(u32 session_id);
|
||||
|
||||
/**
|
||||
* Set the command buffer with the given session id (0 or 1).
|
||||
@@ -91,7 +91,7 @@ public:
|
||||
* @param session_id - The session id to get (0 or 1).
|
||||
* @param buffer - The command buffer to set.
|
||||
*/
|
||||
void SetCommandBuffer(u32 session_id, CommandBuffer& buffer);
|
||||
void SetCommandBuffer(u32 session_id, const CommandBuffer& buffer);
|
||||
|
||||
/**
|
||||
* Get the total render time taken for the last command lists sent.
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <span>
|
||||
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "audio_core/common/feature_support.h"
|
||||
#include "audio_core/renderer/audio_device.h"
|
||||
@@ -9,14 +12,33 @@
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
|
||||
constexpr std::array usb_device_names{
|
||||
AudioDevice::AudioDeviceName{"AudioStereoJackOutput"},
|
||||
AudioDevice::AudioDeviceName{"AudioBuiltInSpeakerOutput"},
|
||||
AudioDevice::AudioDeviceName{"AudioTvOutput"},
|
||||
AudioDevice::AudioDeviceName{"AudioUsbDeviceOutput"},
|
||||
};
|
||||
|
||||
constexpr std::array device_names{
|
||||
AudioDevice::AudioDeviceName{"AudioStereoJackOutput"},
|
||||
AudioDevice::AudioDeviceName{"AudioBuiltInSpeakerOutput"},
|
||||
AudioDevice::AudioDeviceName{"AudioTvOutput"},
|
||||
};
|
||||
|
||||
constexpr std::array output_device_names{
|
||||
AudioDevice::AudioDeviceName{"AudioBuiltInSpeakerOutput"},
|
||||
AudioDevice::AudioDeviceName{"AudioTvOutput"},
|
||||
AudioDevice::AudioDeviceName{"AudioExternalOutput"},
|
||||
};
|
||||
|
||||
AudioDevice::AudioDevice(Core::System& system, const u64 applet_resource_user_id_,
|
||||
const u32 revision)
|
||||
: output_sink{system.AudioCore().GetOutputSink()},
|
||||
applet_resource_user_id{applet_resource_user_id_}, user_revision{revision} {}
|
||||
|
||||
u32 AudioDevice::ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer,
|
||||
const size_t max_count) {
|
||||
std::span<AudioDeviceName> names{};
|
||||
const size_t max_count) const {
|
||||
std::span<const AudioDeviceName> names{};
|
||||
|
||||
if (CheckFeatureSupported(SupportTags::AudioUsbDeviceOutput, user_revision)) {
|
||||
names = usb_device_names;
|
||||
@@ -24,7 +46,7 @@ u32 AudioDevice::ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer,
|
||||
names = device_names;
|
||||
}
|
||||
|
||||
u32 out_count{static_cast<u32>(std::min(max_count, names.size()))};
|
||||
const u32 out_count{static_cast<u32>(std::min(max_count, names.size()))};
|
||||
for (u32 i = 0; i < out_count; i++) {
|
||||
out_buffer.push_back(names[i]);
|
||||
}
|
||||
@@ -32,8 +54,8 @@ u32 AudioDevice::ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer,
|
||||
}
|
||||
|
||||
u32 AudioDevice::ListAudioOutputDeviceName(std::vector<AudioDeviceName>& out_buffer,
|
||||
const size_t max_count) {
|
||||
u32 out_count{static_cast<u32>(std::min(max_count, output_device_names.size()))};
|
||||
const size_t max_count) const {
|
||||
const u32 out_count{static_cast<u32>(std::min(max_count, output_device_names.size()))};
|
||||
|
||||
for (u32 i = 0; i < out_count; i++) {
|
||||
out_buffer.push_back(output_device_names[i]);
|
||||
@@ -45,7 +67,7 @@ void AudioDevice::SetDeviceVolumes(const f32 volume) {
|
||||
output_sink.SetDeviceVolume(volume);
|
||||
}
|
||||
|
||||
f32 AudioDevice::GetDeviceVolume([[maybe_unused]] std::string_view name) {
|
||||
f32 AudioDevice::GetDeviceVolume([[maybe_unused]] std::string_view name) const {
|
||||
return output_sink.GetDeviceVolume();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
#include "audio_core/audio_render_manager.h"
|
||||
|
||||
@@ -23,21 +23,13 @@ namespace AudioRenderer {
|
||||
class AudioDevice {
|
||||
public:
|
||||
struct AudioDeviceName {
|
||||
std::array<char, 0x100> name;
|
||||
std::array<char, 0x100> name{};
|
||||
|
||||
AudioDeviceName(const char* name_) {
|
||||
std::strncpy(name.data(), name_, name.size());
|
||||
constexpr AudioDeviceName(std::string_view name_) {
|
||||
name_.copy(name.data(), name.size() - 1);
|
||||
}
|
||||
};
|
||||
|
||||
std::array<AudioDeviceName, 4> usb_device_names{"AudioStereoJackOutput",
|
||||
"AudioBuiltInSpeakerOutput", "AudioTvOutput",
|
||||
"AudioUsbDeviceOutput"};
|
||||
std::array<AudioDeviceName, 3> device_names{"AudioStereoJackOutput",
|
||||
"AudioBuiltInSpeakerOutput", "AudioTvOutput"};
|
||||
std::array<AudioDeviceName, 3> output_device_names{"AudioBuiltInSpeakerOutput", "AudioTvOutput",
|
||||
"AudioExternalOutput"};
|
||||
|
||||
explicit AudioDevice(Core::System& system, u64 applet_resource_user_id, u32 revision);
|
||||
|
||||
/**
|
||||
@@ -47,7 +39,7 @@ public:
|
||||
* @param max_count - Maximum number of devices to write (count of out_buffer).
|
||||
* @return Number of device names written.
|
||||
*/
|
||||
u32 ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer, size_t max_count);
|
||||
u32 ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer, size_t max_count) const;
|
||||
|
||||
/**
|
||||
* Get a list of the available output devices.
|
||||
@@ -57,7 +49,7 @@ public:
|
||||
* @param max_count - Maximum number of devices to write (count of out_buffer).
|
||||
* @return Number of device names written.
|
||||
*/
|
||||
u32 ListAudioOutputDeviceName(std::vector<AudioDeviceName>& out_buffer, size_t max_count);
|
||||
u32 ListAudioOutputDeviceName(std::vector<AudioDeviceName>& out_buffer, size_t max_count) const;
|
||||
|
||||
/**
|
||||
* Set the volume of all streams in the backend sink.
|
||||
@@ -73,7 +65,7 @@ public:
|
||||
* @param name - Name of the device to check. Unused.
|
||||
* @return Volume of the device.
|
||||
*/
|
||||
f32 GetDeviceVolume(std::string_view name);
|
||||
f32 GetDeviceVolume(std::string_view name) const;
|
||||
|
||||
private:
|
||||
/// Backend output sink for the device
|
||||
|
||||
@@ -34,7 +34,7 @@ void BehaviorInfo::ClearError() {
|
||||
error_count = 0;
|
||||
}
|
||||
|
||||
void BehaviorInfo::AppendError(ErrorInfo& error) {
|
||||
void BehaviorInfo::AppendError(const ErrorInfo& error) {
|
||||
LOG_ERROR(Service_Audio, "Error during RequestUpdate, reporting code {:04X} address {:08X}",
|
||||
error.error_code.raw, error.address);
|
||||
if (error_count < MaxErrors) {
|
||||
@@ -42,7 +42,7 @@ void BehaviorInfo::AppendError(ErrorInfo& error) {
|
||||
}
|
||||
}
|
||||
|
||||
void BehaviorInfo::CopyErrorInfo(std::span<ErrorInfo> out_errors, u32& out_count) {
|
||||
void BehaviorInfo::CopyErrorInfo(std::span<ErrorInfo> out_errors, u32& out_count) const {
|
||||
out_count = std::min(error_count, MaxErrors);
|
||||
|
||||
for (size_t i = 0; i < MaxErrors; i++) {
|
||||
|
||||
@@ -94,7 +94,7 @@ public:
|
||||
*
|
||||
* @param error - The new error.
|
||||
*/
|
||||
void AppendError(ErrorInfo& error);
|
||||
void AppendError(const ErrorInfo& error);
|
||||
|
||||
/**
|
||||
* Copy errors to the given output container.
|
||||
@@ -102,7 +102,7 @@ public:
|
||||
* @param out_errors - Output container to receive the errors.
|
||||
* @param out_count - The number of errors written.
|
||||
*/
|
||||
void CopyErrorInfo(std::span<ErrorInfo> out_errors, u32& out_count);
|
||||
void CopyErrorInfo(std::span<ErrorInfo> out_errors, u32& out_count) const;
|
||||
|
||||
/**
|
||||
* Update the behaviour flags.
|
||||
|
||||
@@ -485,7 +485,7 @@ Result InfoUpdater::UpdateBehaviorInfo(BehaviorInfo& behaviour_) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result InfoUpdater::UpdateErrorInfo(BehaviorInfo& behaviour_) {
|
||||
Result InfoUpdater::UpdateErrorInfo(const BehaviorInfo& behaviour_) {
|
||||
auto out_params{reinterpret_cast<BehaviorInfo::OutStatus*>(output)};
|
||||
behaviour_.CopyErrorInfo(out_params->errors, out_params->error_count);
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ public:
|
||||
* @param behaviour - Behaviour to update.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result UpdateErrorInfo(BehaviorInfo& behaviour);
|
||||
Result UpdateErrorInfo(const BehaviorInfo& behaviour);
|
||||
|
||||
/**
|
||||
* Update splitter.
|
||||
|
||||
@@ -99,7 +99,7 @@ public:
|
||||
return out_sample;
|
||||
}
|
||||
|
||||
Common::FixedPoint<50, 14> Read() {
|
||||
Common::FixedPoint<50, 14> Read() const {
|
||||
return *output;
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
Common::FixedPoint<50, 14> TapOut(const s32 index) {
|
||||
Common::FixedPoint<50, 14> TapOut(const s32 index) const {
|
||||
auto out{input - (index + 1)};
|
||||
if (out < buffer.data()) {
|
||||
out += max_delay + 1;
|
||||
|
||||
@@ -95,7 +95,7 @@ public:
|
||||
return out_sample;
|
||||
}
|
||||
|
||||
Common::FixedPoint<50, 14> Read() {
|
||||
Common::FixedPoint<50, 14> Read() const {
|
||||
return *output;
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
Common::FixedPoint<50, 14> TapOut(const s32 index) {
|
||||
Common::FixedPoint<50, 14> TapOut(const s32 index) const {
|
||||
auto out{input - (index + 1)};
|
||||
if (out < buffer.data()) {
|
||||
out += sample_count;
|
||||
|
||||
@@ -56,7 +56,7 @@ class NodeStates {
|
||||
*
|
||||
* @return The current stack position.
|
||||
*/
|
||||
u32 Count() {
|
||||
u32 Count() const {
|
||||
return pos;
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ class NodeStates {
|
||||
*
|
||||
* @return The node on the top of the stack.
|
||||
*/
|
||||
u32 top() {
|
||||
u32 top() const {
|
||||
return stack[pos - 1];
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ bool SystemManager::Remove(System& system_) {
|
||||
}
|
||||
|
||||
void SystemManager::ThreadFunc() {
|
||||
constexpr char name[]{"yuzu:AudioRenderSystemManager"};
|
||||
constexpr char name[]{"AudioRenderSystemManager"};
|
||||
MicroProfileOnThreadCreate(name);
|
||||
Common::SetCurrentThreadName(name);
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
|
||||
|
||||
@@ -66,10 +66,10 @@ public:
|
||||
const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &minimum_latency);
|
||||
if (latency_error != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error);
|
||||
minimum_latency = 256U;
|
||||
minimum_latency = TargetSampleCount * 2;
|
||||
}
|
||||
|
||||
minimum_latency = std::max(minimum_latency, 256u);
|
||||
minimum_latency = std::max(minimum_latency, TargetSampleCount * 2);
|
||||
|
||||
LOG_INFO(Service_Audio,
|
||||
"Opening cubeb stream {} type {} with: rate {} channels {} (system channels {}) "
|
||||
@@ -326,4 +326,31 @@ std::vector<std::string> ListCubebSinkDevices(bool capture) {
|
||||
return device_list;
|
||||
}
|
||||
|
||||
u32 GetCubebLatency() {
|
||||
cubeb* ctx;
|
||||
|
||||
if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
|
||||
// Return a large latency so we choose SDL instead.
|
||||
return 10000u;
|
||||
}
|
||||
|
||||
cubeb_stream_params params{};
|
||||
params.rate = TargetSampleRate;
|
||||
params.channels = 2;
|
||||
params.format = CUBEB_SAMPLE_S16LE;
|
||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
params.layout = CUBEB_LAYOUT_STEREO;
|
||||
|
||||
u32 latency{0};
|
||||
const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &latency);
|
||||
if (latency_error != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error);
|
||||
latency = TargetSampleCount * 2;
|
||||
}
|
||||
latency = std::max(latency, TargetSampleCount * 2);
|
||||
cubeb_destroy(ctx);
|
||||
return latency;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
|
||||
@@ -96,4 +96,11 @@ private:
|
||||
*/
|
||||
std::vector<std::string> ListCubebSinkDevices(bool capture);
|
||||
|
||||
/**
|
||||
* Get the reported latency for this sink.
|
||||
*
|
||||
* @return Minimum latency for this sink.
|
||||
*/
|
||||
u32 GetCubebLatency();
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
|
||||
@@ -47,11 +47,7 @@ public:
|
||||
spec.freq = TargetSampleRate;
|
||||
spec.channels = static_cast<u8>(device_channels);
|
||||
spec.format = AUDIO_S16SYS;
|
||||
if (type == StreamType::Render) {
|
||||
spec.samples = TargetSampleCount;
|
||||
} else {
|
||||
spec.samples = 1024;
|
||||
}
|
||||
spec.samples = TargetSampleCount * 2;
|
||||
spec.callback = &SDLSinkStream::DataCallback;
|
||||
spec.userdata = this;
|
||||
|
||||
@@ -240,4 +236,8 @@ std::vector<std::string> ListSDLSinkDevices(bool capture) {
|
||||
return device_list;
|
||||
}
|
||||
|
||||
u32 GetSDLLatency() {
|
||||
return TargetSampleCount * 2;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
|
||||
@@ -87,4 +87,11 @@ private:
|
||||
*/
|
||||
std::vector<std::string> ListSDLSinkDevices(bool capture);
|
||||
|
||||
/**
|
||||
* Get the reported latency for this sink.
|
||||
*
|
||||
* @return Minimum latency for this sink.
|
||||
*/
|
||||
u32 GetSDLLatency();
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
|
||||
@@ -21,58 +21,80 @@ namespace {
|
||||
struct SinkDetails {
|
||||
using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view);
|
||||
using ListDevicesFn = std::vector<std::string> (*)(bool);
|
||||
using LatencyFn = u32 (*)();
|
||||
|
||||
/// Name for this sink.
|
||||
const char* id;
|
||||
std::string_view id;
|
||||
/// A method to call to construct an instance of this type of sink.
|
||||
FactoryFn factory;
|
||||
/// A method to call to list available devices.
|
||||
ListDevicesFn list_devices;
|
||||
/// Method to get the latency of this backend.
|
||||
LatencyFn latency;
|
||||
};
|
||||
|
||||
// sink_details is ordered in terms of desirability, with the best choice at the top.
|
||||
constexpr SinkDetails sink_details[] = {
|
||||
#ifdef HAVE_CUBEB
|
||||
SinkDetails{"cubeb",
|
||||
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
||||
return std::make_unique<CubebSink>(device_id);
|
||||
},
|
||||
&ListCubebSinkDevices},
|
||||
SinkDetails{
|
||||
"cubeb",
|
||||
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
||||
return std::make_unique<CubebSink>(device_id);
|
||||
},
|
||||
&ListCubebSinkDevices,
|
||||
&GetCubebLatency,
|
||||
},
|
||||
#endif
|
||||
#ifdef HAVE_SDL2
|
||||
SinkDetails{"sdl2",
|
||||
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
||||
return std::make_unique<SDLSink>(device_id);
|
||||
},
|
||||
&ListSDLSinkDevices},
|
||||
SinkDetails{
|
||||
"sdl",
|
||||
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
||||
return std::make_unique<SDLSink>(device_id);
|
||||
},
|
||||
&ListSDLSinkDevices,
|
||||
&GetSDLLatency,
|
||||
},
|
||||
#endif
|
||||
SinkDetails{"null",
|
||||
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
||||
return std::make_unique<NullSink>(device_id);
|
||||
},
|
||||
[](bool capture) { return std::vector<std::string>{"null"}; }},
|
||||
[](bool capture) { return std::vector<std::string>{"null"}; }, []() { return 0u; }},
|
||||
};
|
||||
|
||||
const SinkDetails& GetOutputSinkDetails(std::string_view sink_id) {
|
||||
auto iter =
|
||||
std::find_if(std::begin(sink_details), std::end(sink_details),
|
||||
[sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; });
|
||||
const auto find_backend{[](std::string_view id) {
|
||||
return std::find_if(std::begin(sink_details), std::end(sink_details),
|
||||
[&id](const auto& sink_detail) { return sink_detail.id == id; });
|
||||
}};
|
||||
|
||||
if (sink_id == "auto" || iter == std::end(sink_details)) {
|
||||
if (sink_id != "auto") {
|
||||
LOG_ERROR(Audio, "Invalid sink_id {}", sink_id);
|
||||
auto iter = find_backend(sink_id);
|
||||
|
||||
if (sink_id == "auto") {
|
||||
// Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which
|
||||
// causes audio issues, in that case go with SDL.
|
||||
#if defined(HAVE_CUBEB) && defined(HAVE_SDL2)
|
||||
iter = find_backend("cubeb");
|
||||
if (iter->latency() > TargetSampleCount * 3) {
|
||||
iter = find_backend("sdl");
|
||||
}
|
||||
// Auto-select.
|
||||
// sink_details is ordered in terms of desirability, with the best choice at the front.
|
||||
#else
|
||||
iter = std::begin(sink_details);
|
||||
#endif
|
||||
LOG_INFO(Service_Audio, "Auto-selecting the {} backend", iter->id);
|
||||
}
|
||||
|
||||
if (iter == std::end(sink_details)) {
|
||||
LOG_ERROR(Audio, "Invalid sink_id {}", sink_id);
|
||||
iter = find_backend("null");
|
||||
}
|
||||
|
||||
return *iter;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
std::vector<const char*> GetSinkIDs() {
|
||||
std::vector<const char*> sink_ids(std::size(sink_details));
|
||||
std::vector<std::string_view> GetSinkIDs() {
|
||||
std::vector<std::string_view> sink_ids(std::size(sink_details));
|
||||
|
||||
std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids),
|
||||
[](const auto& sink) { return sink.id; });
|
||||
|
||||
@@ -19,7 +19,7 @@ class Sink;
|
||||
*
|
||||
* @return Vector of available sink names.
|
||||
*/
|
||||
std::vector<const char*> GetSinkIDs();
|
||||
std::vector<std::string_view> GetSinkIDs();
|
||||
|
||||
/**
|
||||
* Gets the list of devices for a particular sink identified by the given ID.
|
||||
|
||||
@@ -214,8 +214,13 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
|
||||
// video play out without attempting to stall.
|
||||
// Can hopefully remove this later with a more complete NVDEC implementation.
|
||||
const auto nvdec_active{system.AudioCore().IsNVDECActive()};
|
||||
if (!nvdec_active && queued_buffers > max_queue_size) {
|
||||
|
||||
// Core timing cannot be paused in single-core mode, so Stall ends up being called over and over
|
||||
// and never recovers to a normal state, so just skip attempting to sync things on single-core.
|
||||
if (system.IsMulticore() && !nvdec_active && queued_buffers > max_queue_size) {
|
||||
Stall();
|
||||
} else if (system.IsMulticore() && queued_buffers <= max_queue_size) {
|
||||
Unstall();
|
||||
}
|
||||
|
||||
while (frames_written < num_frames) {
|
||||
@@ -255,7 +260,7 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
|
||||
std::memcpy(&last_frame[0], &output_buffer[(frames_written - 1) * frame_size],
|
||||
frame_size_bytes);
|
||||
|
||||
if (stalled && queued_buffers <= max_queue_size) {
|
||||
if (system.IsMulticore() && queued_buffers <= max_queue_size) {
|
||||
Unstall();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ public:
|
||||
*
|
||||
* @return The number of queued buffers.
|
||||
*/
|
||||
u32 GetQueueSize() {
|
||||
u32 GetQueueSize() const {
|
||||
return queued_buffers.load();
|
||||
}
|
||||
|
||||
|
||||
+11
-28
@@ -14,34 +14,11 @@ if (DEFINED ENV{DISPLAYVERSION})
|
||||
set(DISPLAY_VERSION $ENV{DISPLAYVERSION})
|
||||
endif ()
|
||||
|
||||
# Pass the path to git to the GenerateSCMRev.cmake as well
|
||||
find_package(Git QUIET)
|
||||
|
||||
add_custom_command(OUTPUT scm_rev.cpp
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DSRC_DIR=${CMAKE_SOURCE_DIR}
|
||||
-DBUILD_REPOSITORY=${BUILD_REPOSITORY}
|
||||
-DTITLE_BAR_FORMAT_IDLE=${TITLE_BAR_FORMAT_IDLE}
|
||||
-DTITLE_BAR_FORMAT_RUNNING=${TITLE_BAR_FORMAT_RUNNING}
|
||||
-DBUILD_TAG=${BUILD_TAG}
|
||||
-DBUILD_ID=${DISPLAY_VERSION}
|
||||
-DGIT_REF_SPEC=${GIT_REF_SPEC}
|
||||
-DGIT_REV=${GIT_REV}
|
||||
-DGIT_DESC=${GIT_DESC}
|
||||
-DGIT_BRANCH=${GIT_BRANCH}
|
||||
-DBUILD_FULLNAME=${BUILD_FULLNAME}
|
||||
-DGIT_EXECUTABLE=${GIT_EXECUTABLE}
|
||||
-P ${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake
|
||||
DEPENDS
|
||||
# Check that the scm_rev files haven't changed
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.h"
|
||||
# technically we should regenerate if the git version changed, but its not worth the effort imo
|
||||
"${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake"
|
||||
VERBATIM
|
||||
)
|
||||
include(GenerateSCMRev)
|
||||
|
||||
add_library(common STATIC
|
||||
address_space.cpp
|
||||
address_space.h
|
||||
algorithm.h
|
||||
alignment.h
|
||||
announce_multiplayer_room.h
|
||||
@@ -106,6 +83,8 @@ add_library(common STATIC
|
||||
microprofile.cpp
|
||||
microprofile.h
|
||||
microprofileui.h
|
||||
multi_level_page_table.cpp
|
||||
multi_level_page_table.h
|
||||
nvidia_flags.cpp
|
||||
nvidia_flags.h
|
||||
page_table.cpp
|
||||
@@ -117,7 +96,7 @@ add_library(common STATIC
|
||||
quaternion.h
|
||||
reader_writer_queue.h
|
||||
ring_buffer.h
|
||||
scm_rev.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
|
||||
scm_rev.h
|
||||
scope_exit.h
|
||||
settings.cpp
|
||||
@@ -190,7 +169,11 @@ endif()
|
||||
create_target_directory_groups(common)
|
||||
|
||||
target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads)
|
||||
target_link_libraries(common PRIVATE lz4::lz4)
|
||||
if (TARGET lz4::lz4)
|
||||
target_link_libraries(common PRIVATE lz4::lz4)
|
||||
else()
|
||||
target_link_libraries(common PRIVATE LZ4::lz4_shared)
|
||||
endif()
|
||||
if (TARGET zstd::zstd)
|
||||
target_link_libraries(common PRIVATE zstd::zstd)
|
||||
else()
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "common/address_space.inc"
|
||||
|
||||
namespace Common {
|
||||
|
||||
template class Common::FlatAllocator<u32, 0, 32>;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
template <typename VaType, size_t AddressSpaceBits>
|
||||
concept AddressSpaceValid = std::is_unsigned_v<VaType> && sizeof(VaType) * 8 >= AddressSpaceBits;
|
||||
|
||||
struct EmptyStruct {};
|
||||
|
||||
/**
|
||||
* @brief FlatAddressSpaceMap provides a generic VA->PA mapping implementation using a sorted vector
|
||||
*/
|
||||
template <typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa,
|
||||
bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo = EmptyStruct>
|
||||
requires AddressSpaceValid<VaType, AddressSpaceBits>
|
||||
class FlatAddressSpaceMap {
|
||||
public:
|
||||
/// The maximum VA that this AS can technically reach
|
||||
static constexpr VaType VaMaximum{(1ULL << (AddressSpaceBits - 1)) +
|
||||
((1ULL << (AddressSpaceBits - 1)) - 1)};
|
||||
|
||||
explicit FlatAddressSpaceMap(VaType va_limit,
|
||||
std::function<void(VaType, VaType)> unmap_callback = {});
|
||||
|
||||
FlatAddressSpaceMap() = default;
|
||||
|
||||
void Map(VaType virt, PaType phys, VaType size, ExtraBlockInfo extra_info = {}) {
|
||||
std::scoped_lock lock(block_mutex);
|
||||
MapLocked(virt, phys, size, extra_info);
|
||||
}
|
||||
|
||||
void Unmap(VaType virt, VaType size) {
|
||||
std::scoped_lock lock(block_mutex);
|
||||
UnmapLocked(virt, size);
|
||||
}
|
||||
|
||||
VaType GetVALimit() const {
|
||||
return va_limit;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Represents a block of memory in the AS, the physical mapping is contiguous until
|
||||
* another block with a different phys address is hit
|
||||
*/
|
||||
struct Block {
|
||||
/// VA of the block
|
||||
VaType virt{UnmappedVa};
|
||||
/// PA of the block, will increase 1-1 with VA until a new block is encountered
|
||||
PaType phys{UnmappedPa};
|
||||
[[no_unique_address]] ExtraBlockInfo extra_info;
|
||||
|
||||
Block() = default;
|
||||
|
||||
Block(VaType virt_, PaType phys_, ExtraBlockInfo extra_info_)
|
||||
: virt(virt_), phys(phys_), extra_info(extra_info_) {}
|
||||
|
||||
bool Valid() const {
|
||||
return virt != UnmappedVa;
|
||||
}
|
||||
|
||||
bool Mapped() const {
|
||||
return phys != UnmappedPa;
|
||||
}
|
||||
|
||||
bool Unmapped() const {
|
||||
return phys == UnmappedPa;
|
||||
}
|
||||
|
||||
bool operator<(const VaType& p_virt) const {
|
||||
return virt < p_virt;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Maps a PA range into the given AS region
|
||||
* @note block_mutex MUST be locked when calling this
|
||||
*/
|
||||
void MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInfo extra_info);
|
||||
|
||||
/**
|
||||
* @brief Unmaps the given range and merges it with other unmapped regions
|
||||
* @note block_mutex MUST be locked when calling this
|
||||
*/
|
||||
void UnmapLocked(VaType virt, VaType size);
|
||||
|
||||
std::mutex block_mutex;
|
||||
std::vector<Block> blocks{Block{}};
|
||||
|
||||
/// a soft limit on the maximum VA of the AS
|
||||
VaType va_limit{VaMaximum};
|
||||
|
||||
private:
|
||||
/// Callback called when the mappings in an region have changed
|
||||
std::function<void(VaType, VaType)> unmap_callback{};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief FlatMemoryManager specialises FlatAddressSpaceMap to work as an allocator, with an
|
||||
* initial, fast linear pass and a subsequent slower pass that iterates until it finds a free block
|
||||
*/
|
||||
template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits>
|
||||
requires AddressSpaceValid<VaType, AddressSpaceBits>
|
||||
class FlatAllocator
|
||||
: public FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits> {
|
||||
private:
|
||||
using Base = FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits>;
|
||||
|
||||
public:
|
||||
explicit FlatAllocator(VaType virt_start, VaType va_limit = Base::VaMaximum);
|
||||
|
||||
/**
|
||||
* @brief Allocates a region in the AS of the given size and returns its address
|
||||
*/
|
||||
VaType Allocate(VaType size);
|
||||
|
||||
/**
|
||||
* @brief Marks the given region in the AS as allocated
|
||||
*/
|
||||
void AllocateFixed(VaType virt, VaType size);
|
||||
|
||||
/**
|
||||
* @brief Frees an AS region so it can be used again
|
||||
*/
|
||||
void Free(VaType virt, VaType size);
|
||||
|
||||
VaType GetVAStart() const {
|
||||
return virt_start;
|
||||
}
|
||||
|
||||
private:
|
||||
/// The base VA of the allocator, no allocations will be below this
|
||||
VaType virt_start;
|
||||
|
||||
/**
|
||||
* The end address for the initial linear allocation pass
|
||||
* Once this reaches the AS limit the slower allocation path will be used
|
||||
*/
|
||||
VaType current_linear_alloc_end;
|
||||
};
|
||||
} // namespace Common
|
||||
@@ -0,0 +1,366 @@
|
||||
// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "common/address_space.h"
|
||||
#include "common/assert.h"
|
||||
|
||||
#define MAP_MEMBER(returnType) \
|
||||
template <typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa, \
|
||||
bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo> \
|
||||
requires AddressSpaceValid<VaType, AddressSpaceBits> returnType FlatAddressSpaceMap< \
|
||||
VaType, UnmappedVa, PaType, UnmappedPa, PaContigSplit, AddressSpaceBits, ExtraBlockInfo>
|
||||
#define MAP_MEMBER_CONST() \
|
||||
template <typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa, \
|
||||
bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo> \
|
||||
requires AddressSpaceValid<VaType, AddressSpaceBits> FlatAddressSpaceMap< \
|
||||
VaType, UnmappedVa, PaType, UnmappedPa, PaContigSplit, AddressSpaceBits, ExtraBlockInfo>
|
||||
|
||||
#define MM_MEMBER(returnType) \
|
||||
template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> \
|
||||
requires AddressSpaceValid<VaType, AddressSpaceBits> returnType \
|
||||
FlatMemoryManager<VaType, UnmappedVa, AddressSpaceBits>
|
||||
|
||||
#define ALLOC_MEMBER(returnType) \
|
||||
template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> \
|
||||
requires AddressSpaceValid<VaType, AddressSpaceBits> returnType \
|
||||
FlatAllocator<VaType, UnmappedVa, AddressSpaceBits>
|
||||
#define ALLOC_MEMBER_CONST() \
|
||||
template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> \
|
||||
requires AddressSpaceValid<VaType, AddressSpaceBits> \
|
||||
FlatAllocator<VaType, UnmappedVa, AddressSpaceBits>
|
||||
|
||||
namespace Common {
|
||||
MAP_MEMBER_CONST()::FlatAddressSpaceMap(VaType va_limit_,
|
||||
std::function<void(VaType, VaType)> unmap_callback_)
|
||||
: va_limit{va_limit_}, unmap_callback{std::move(unmap_callback_)} {
|
||||
if (va_limit > VaMaximum) {
|
||||
ASSERT_MSG(false, "Invalid VA limit!");
|
||||
}
|
||||
}
|
||||
|
||||
MAP_MEMBER(void)::MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInfo extra_info) {
|
||||
VaType virt_end{virt + size};
|
||||
|
||||
if (virt_end > va_limit) {
|
||||
ASSERT_MSG(false,
|
||||
"Trying to map a block past the VA limit: virt_end: 0x{:X}, va_limit: 0x{:X}",
|
||||
virt_end, va_limit);
|
||||
}
|
||||
|
||||
auto block_end_successor{std::lower_bound(blocks.begin(), blocks.end(), virt_end)};
|
||||
if (block_end_successor == blocks.begin()) {
|
||||
ASSERT_MSG(false, "Trying to map a block before the VA start: virt_end: 0x{:X}", virt_end);
|
||||
}
|
||||
|
||||
auto block_end_predecessor{std::prev(block_end_successor)};
|
||||
|
||||
if (block_end_successor != blocks.end()) {
|
||||
// We have blocks in front of us, if one is directly in front then we don't have to add a
|
||||
// tail
|
||||
if (block_end_successor->virt != virt_end) {
|
||||
PaType tailPhys{[&]() -> PaType {
|
||||
if constexpr (!PaContigSplit) {
|
||||
// Always propagate unmapped regions rather than calculating offset
|
||||
return block_end_predecessor->phys;
|
||||
} else {
|
||||
if (block_end_predecessor->Unmapped()) {
|
||||
// Always propagate unmapped regions rather than calculating offset
|
||||
return block_end_predecessor->phys;
|
||||
} else {
|
||||
return block_end_predecessor->phys + virt_end - block_end_predecessor->virt;
|
||||
}
|
||||
}
|
||||
}()};
|
||||
|
||||
if (block_end_predecessor->virt >= virt) {
|
||||
// If this block's start would be overlapped by the map then reuse it as a tail
|
||||
// block
|
||||
block_end_predecessor->virt = virt_end;
|
||||
block_end_predecessor->phys = tailPhys;
|
||||
block_end_predecessor->extra_info = block_end_predecessor->extra_info;
|
||||
|
||||
// No longer predecessor anymore
|
||||
block_end_successor = block_end_predecessor--;
|
||||
} else {
|
||||
// Else insert a new one and we're done
|
||||
blocks.insert(block_end_successor,
|
||||
{Block(virt, phys, extra_info),
|
||||
Block(virt_end, tailPhys, block_end_predecessor->extra_info)});
|
||||
if (unmap_callback) {
|
||||
unmap_callback(virt, size);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// block_end_predecessor will always be unmapped as blocks has to be terminated by an
|
||||
// unmapped chunk
|
||||
if (block_end_predecessor != blocks.begin() && block_end_predecessor->virt >= virt) {
|
||||
// Move the unmapped block start backwards
|
||||
block_end_predecessor->virt = virt_end;
|
||||
|
||||
// No longer predecessor anymore
|
||||
block_end_successor = block_end_predecessor--;
|
||||
} else {
|
||||
// Else insert a new one and we're done
|
||||
blocks.insert(block_end_successor,
|
||||
{Block(virt, phys, extra_info), Block(virt_end, UnmappedPa, {})});
|
||||
if (unmap_callback) {
|
||||
unmap_callback(virt, size);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto block_start_successor{block_end_successor};
|
||||
|
||||
// Walk the block vector to find the start successor as this is more efficient than another
|
||||
// binary search in most scenarios
|
||||
while (std::prev(block_start_successor)->virt >= virt) {
|
||||
block_start_successor--;
|
||||
}
|
||||
|
||||
// Check that the start successor is either the end block or something in between
|
||||
if (block_start_successor->virt > virt_end) {
|
||||
ASSERT_MSG(false, "Unsorted block in AS map: virt: 0x{:X}", block_start_successor->virt);
|
||||
} else if (block_start_successor->virt == virt_end) {
|
||||
// We need to create a new block as there are none spare that we would overwrite
|
||||
blocks.insert(block_start_successor, Block(virt, phys, extra_info));
|
||||
} else {
|
||||
// Erase overwritten blocks
|
||||
if (auto eraseStart{std::next(block_start_successor)}; eraseStart != block_end_successor) {
|
||||
blocks.erase(eraseStart, block_end_successor);
|
||||
}
|
||||
|
||||
// Reuse a block that would otherwise be overwritten as a start block
|
||||
block_start_successor->virt = virt;
|
||||
block_start_successor->phys = phys;
|
||||
block_start_successor->extra_info = extra_info;
|
||||
}
|
||||
|
||||
if (unmap_callback) {
|
||||
unmap_callback(virt, size);
|
||||
}
|
||||
}
|
||||
|
||||
MAP_MEMBER(void)::UnmapLocked(VaType virt, VaType size) {
|
||||
VaType virt_end{virt + size};
|
||||
|
||||
if (virt_end > va_limit) {
|
||||
ASSERT_MSG(false,
|
||||
"Trying to map a block past the VA limit: virt_end: 0x{:X}, va_limit: 0x{:X}",
|
||||
virt_end, va_limit);
|
||||
}
|
||||
|
||||
auto block_end_successor{std::lower_bound(blocks.begin(), blocks.end(), virt_end)};
|
||||
if (block_end_successor == blocks.begin()) {
|
||||
ASSERT_MSG(false, "Trying to unmap a block before the VA start: virt_end: 0x{:X}",
|
||||
virt_end);
|
||||
}
|
||||
|
||||
auto block_end_predecessor{std::prev(block_end_successor)};
|
||||
|
||||
auto walk_back_to_predecessor{[&](auto iter) {
|
||||
while (iter->virt >= virt) {
|
||||
iter--;
|
||||
}
|
||||
|
||||
return iter;
|
||||
}};
|
||||
|
||||
auto erase_blocks_with_end_unmapped{[&](auto unmappedEnd) {
|
||||
auto block_start_predecessor{walk_back_to_predecessor(unmappedEnd)};
|
||||
auto block_start_successor{std::next(block_start_predecessor)};
|
||||
|
||||
auto eraseEnd{[&]() {
|
||||
if (block_start_predecessor->Unmapped()) {
|
||||
// If the start predecessor is unmapped then we can erase everything in our region
|
||||
// and be done
|
||||
return std::next(unmappedEnd);
|
||||
} else {
|
||||
// Else reuse the end predecessor as the start of our unmapped region then erase all
|
||||
// up to it
|
||||
unmappedEnd->virt = virt;
|
||||
return unmappedEnd;
|
||||
}
|
||||
}()};
|
||||
|
||||
// We can't have two unmapped regions after each other
|
||||
if (eraseEnd != blocks.end() &&
|
||||
(eraseEnd == block_start_successor ||
|
||||
(block_start_predecessor->Unmapped() && eraseEnd->Unmapped()))) {
|
||||
ASSERT_MSG(false, "Multiple contiguous unmapped regions are unsupported!");
|
||||
}
|
||||
|
||||
blocks.erase(block_start_successor, eraseEnd);
|
||||
}};
|
||||
|
||||
// We can avoid any splitting logic if these are the case
|
||||
if (block_end_predecessor->Unmapped()) {
|
||||
if (block_end_predecessor->virt > virt) {
|
||||
erase_blocks_with_end_unmapped(block_end_predecessor);
|
||||
}
|
||||
|
||||
if (unmap_callback) {
|
||||
unmap_callback(virt, size);
|
||||
}
|
||||
|
||||
return; // The region is unmapped, bail out early
|
||||
} else if (block_end_successor->virt == virt_end && block_end_successor->Unmapped()) {
|
||||
erase_blocks_with_end_unmapped(block_end_successor);
|
||||
|
||||
if (unmap_callback) {
|
||||
unmap_callback(virt, size);
|
||||
}
|
||||
|
||||
return; // The region is unmapped here and doesn't need splitting, bail out early
|
||||
} else if (block_end_successor == blocks.end()) {
|
||||
// This should never happen as the end should always follow an unmapped block
|
||||
ASSERT_MSG(false, "Unexpected Memory Manager state!");
|
||||
} else if (block_end_successor->virt != virt_end) {
|
||||
// If one block is directly in front then we don't have to add a tail
|
||||
|
||||
// The previous block is mapped so we will need to add a tail with an offset
|
||||
PaType tailPhys{[&]() {
|
||||
if constexpr (PaContigSplit) {
|
||||
return block_end_predecessor->phys + virt_end - block_end_predecessor->virt;
|
||||
} else {
|
||||
return block_end_predecessor->phys;
|
||||
}
|
||||
}()};
|
||||
|
||||
if (block_end_predecessor->virt >= virt) {
|
||||
// If this block's start would be overlapped by the unmap then reuse it as a tail block
|
||||
block_end_predecessor->virt = virt_end;
|
||||
block_end_predecessor->phys = tailPhys;
|
||||
|
||||
// No longer predecessor anymore
|
||||
block_end_successor = block_end_predecessor--;
|
||||
} else {
|
||||
blocks.insert(block_end_successor,
|
||||
{Block(virt, UnmappedPa, {}),
|
||||
Block(virt_end, tailPhys, block_end_predecessor->extra_info)});
|
||||
if (unmap_callback) {
|
||||
unmap_callback(virt, size);
|
||||
}
|
||||
|
||||
// The previous block is mapped and ends before
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Walk the block vector to find the start predecessor as this is more efficient than another
|
||||
// binary search in most scenarios
|
||||
auto block_start_predecessor{walk_back_to_predecessor(block_end_successor)};
|
||||
auto block_start_successor{std::next(block_start_predecessor)};
|
||||
|
||||
if (block_start_successor->virt > virt_end) {
|
||||
ASSERT_MSG(false, "Unsorted block in AS map: virt: 0x{:X}", block_start_successor->virt);
|
||||
} else if (block_start_successor->virt == virt_end) {
|
||||
// There are no blocks between the start and the end that would let us skip inserting a new
|
||||
// one for head
|
||||
|
||||
// The previous block is may be unmapped, if so we don't need to insert any unmaps after it
|
||||
if (block_start_predecessor->Mapped()) {
|
||||
blocks.insert(block_start_successor, Block(virt, UnmappedPa, {}));
|
||||
}
|
||||
} else if (block_start_predecessor->Unmapped()) {
|
||||
// If the previous block is unmapped
|
||||
blocks.erase(block_start_successor, block_end_predecessor);
|
||||
} else {
|
||||
// Erase overwritten blocks, skipping the first one as we have written the unmapped start
|
||||
// block there
|
||||
if (auto eraseStart{std::next(block_start_successor)}; eraseStart != block_end_successor) {
|
||||
blocks.erase(eraseStart, block_end_successor);
|
||||
}
|
||||
|
||||
// Add in the unmapped block header
|
||||
block_start_successor->virt = virt;
|
||||
block_start_successor->phys = UnmappedPa;
|
||||
}
|
||||
|
||||
if (unmap_callback)
|
||||
unmap_callback(virt, size);
|
||||
}
|
||||
|
||||
ALLOC_MEMBER_CONST()::FlatAllocator(VaType virt_start_, VaType va_limit_)
|
||||
: Base{va_limit_}, virt_start{virt_start_}, current_linear_alloc_end{virt_start_} {}
|
||||
|
||||
ALLOC_MEMBER(VaType)::Allocate(VaType size) {
|
||||
std::scoped_lock lock(this->block_mutex);
|
||||
|
||||
VaType alloc_start{UnmappedVa};
|
||||
VaType alloc_end{current_linear_alloc_end + size};
|
||||
|
||||
// Avoid searching backwards in the address space if possible
|
||||
if (alloc_end >= current_linear_alloc_end && alloc_end <= this->va_limit) {
|
||||
auto alloc_end_successor{
|
||||
std::lower_bound(this->blocks.begin(), this->blocks.end(), alloc_end)};
|
||||
if (alloc_end_successor == this->blocks.begin()) {
|
||||
ASSERT_MSG(false, "First block in AS map is invalid!");
|
||||
}
|
||||
|
||||
auto alloc_end_predecessor{std::prev(alloc_end_successor)};
|
||||
if (alloc_end_predecessor->virt <= current_linear_alloc_end) {
|
||||
alloc_start = current_linear_alloc_end;
|
||||
} else {
|
||||
// Skip over fixed any mappings in front of us
|
||||
while (alloc_end_successor != this->blocks.end()) {
|
||||
if (alloc_end_successor->virt - alloc_end_predecessor->virt < size ||
|
||||
alloc_end_predecessor->Mapped()) {
|
||||
alloc_start = alloc_end_predecessor->virt;
|
||||
break;
|
||||
}
|
||||
|
||||
alloc_end_predecessor = alloc_end_successor++;
|
||||
|
||||
// Use the VA limit to calculate if we can fit in the final block since it has no
|
||||
// successor
|
||||
if (alloc_end_successor == this->blocks.end()) {
|
||||
alloc_end = alloc_end_predecessor->virt + size;
|
||||
|
||||
if (alloc_end >= alloc_end_predecessor->virt && alloc_end <= this->va_limit) {
|
||||
alloc_start = alloc_end_predecessor->virt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (alloc_start != UnmappedVa) {
|
||||
current_linear_alloc_end = alloc_start + size;
|
||||
} else { // If linear allocation overflows the AS then find a gap
|
||||
if (this->blocks.size() <= 2) {
|
||||
ASSERT_MSG(false, "Unexpected allocator state!");
|
||||
}
|
||||
|
||||
auto search_predecessor{this->blocks.begin()};
|
||||
auto search_successor{std::next(search_predecessor)};
|
||||
|
||||
while (search_successor != this->blocks.end() &&
|
||||
(search_successor->virt - search_predecessor->virt < size ||
|
||||
search_predecessor->Mapped())) {
|
||||
search_predecessor = search_successor++;
|
||||
}
|
||||
|
||||
if (search_successor != this->blocks.end()) {
|
||||
alloc_start = search_predecessor->virt;
|
||||
} else {
|
||||
return {}; // AS is full
|
||||
}
|
||||
}
|
||||
|
||||
this->MapLocked(alloc_start, true, size, {});
|
||||
return alloc_start;
|
||||
}
|
||||
|
||||
ALLOC_MEMBER(void)::AllocateFixed(VaType virt, VaType size) {
|
||||
this->Map(virt, true, size);
|
||||
}
|
||||
|
||||
ALLOC_MEMBER(void)::Free(VaType virt, VaType size) {
|
||||
this->Unmap(virt, size);
|
||||
}
|
||||
} // namespace Common
|
||||
@@ -24,4 +24,12 @@ template <class ForwardIt, class T, class Compare = std::less<>>
|
||||
return first != last && !comp(value, *first) ? first : last;
|
||||
}
|
||||
|
||||
template <typename T, typename Func, typename... Args>
|
||||
T FoldRight(T initial_value, Func&& func, Args&&... args) {
|
||||
T value{initial_value};
|
||||
const auto high_func = [&value, &func]<typename U>(U x) { value = func(value, x); };
|
||||
(std::invoke(high_func, std::forward<Args>(args)), ...);
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -18,4 +18,11 @@ struct PairHash {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct IdentityHash {
|
||||
[[nodiscard]] size_t operator()(T value) const noexcept {
|
||||
return static_cast<size_t>(value);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
+29
-1
@@ -76,6 +76,19 @@ enum class PollingError {
|
||||
Unknown,
|
||||
};
|
||||
|
||||
// Nfc reply from the controller
|
||||
enum class NfcState {
|
||||
Success,
|
||||
NewAmiibo,
|
||||
WaitingForAmiibo,
|
||||
AmiiboRemoved,
|
||||
NotAnAmiibo,
|
||||
NotSupported,
|
||||
WrongDeviceState,
|
||||
WriteFailed,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
// Ir camera reply from the controller
|
||||
enum class CameraError {
|
||||
None,
|
||||
@@ -202,6 +215,11 @@ struct CameraStatus {
|
||||
std::vector<u8> data{};
|
||||
};
|
||||
|
||||
struct NfcStatus {
|
||||
NfcState state{};
|
||||
std::vector<u8> data{};
|
||||
};
|
||||
|
||||
// List of buttons to be passed to Qt that can be translated
|
||||
enum class ButtonNames {
|
||||
Undefined,
|
||||
@@ -259,7 +277,9 @@ struct CallbackStatus {
|
||||
BodyColorStatus color_status{};
|
||||
BatteryStatus battery_status{};
|
||||
VibrationStatus vibration_status{};
|
||||
CameraStatus camera_status{};
|
||||
CameraFormat camera_status{CameraFormat::None};
|
||||
NfcState nfc_status{NfcState::Unknown};
|
||||
std::vector<u8> raw_data{};
|
||||
};
|
||||
|
||||
// Triggered once every input change
|
||||
@@ -312,6 +332,14 @@ public:
|
||||
virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) {
|
||||
return CameraError::NotSupported;
|
||||
}
|
||||
|
||||
virtual NfcState SupportsNfc() const {
|
||||
return NfcState::NotSupported;
|
||||
}
|
||||
|
||||
virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) {
|
||||
return NfcState::NotSupported;
|
||||
}
|
||||
};
|
||||
|
||||
/// An abstract class template for a factory that can create input devices.
|
||||
|
||||
@@ -219,7 +219,7 @@ private:
|
||||
|
||||
void StartBackendThread() {
|
||||
backend_thread = std::jthread([this](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("yuzu:Log");
|
||||
Common::SetCurrentThreadName("Logger");
|
||||
Entry entry;
|
||||
const auto write_logs = [this, &entry]() {
|
||||
ForEachBackend([&entry](Backend& backend) { backend.Write(entry); });
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/multi_level_page_table.inc"
|
||||
|
||||
namespace Common {
|
||||
template class Common::MultiLevelPageTable<u64>;
|
||||
template class Common::MultiLevelPageTable<u32>;
|
||||
} // namespace Common
|
||||
@@ -0,0 +1,78 @@
|
||||
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename BaseAddr>
|
||||
class MultiLevelPageTable final {
|
||||
public:
|
||||
constexpr MultiLevelPageTable() = default;
|
||||
explicit MultiLevelPageTable(std::size_t address_space_bits, std::size_t first_level_bits,
|
||||
std::size_t page_bits);
|
||||
|
||||
~MultiLevelPageTable() noexcept;
|
||||
|
||||
MultiLevelPageTable(const MultiLevelPageTable&) = delete;
|
||||
MultiLevelPageTable& operator=(const MultiLevelPageTable&) = delete;
|
||||
|
||||
MultiLevelPageTable(MultiLevelPageTable&& other) noexcept
|
||||
: address_space_bits{std::exchange(other.address_space_bits, 0)},
|
||||
first_level_bits{std::exchange(other.first_level_bits, 0)}, page_bits{std::exchange(
|
||||
other.page_bits, 0)},
|
||||
first_level_shift{std::exchange(other.first_level_shift, 0)},
|
||||
first_level_chunk_size{std::exchange(other.first_level_chunk_size, 0)},
|
||||
first_level_map{std::move(other.first_level_map)}, base_ptr{std::exchange(other.base_ptr,
|
||||
nullptr)} {}
|
||||
|
||||
MultiLevelPageTable& operator=(MultiLevelPageTable&& other) noexcept {
|
||||
address_space_bits = std::exchange(other.address_space_bits, 0);
|
||||
first_level_bits = std::exchange(other.first_level_bits, 0);
|
||||
page_bits = std::exchange(other.page_bits, 0);
|
||||
first_level_shift = std::exchange(other.first_level_shift, 0);
|
||||
first_level_chunk_size = std::exchange(other.first_level_chunk_size, 0);
|
||||
alloc_size = std::exchange(other.alloc_size, 0);
|
||||
first_level_map = std::move(other.first_level_map);
|
||||
base_ptr = std::exchange(other.base_ptr, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ReserveRange(u64 start, std::size_t size);
|
||||
|
||||
[[nodiscard]] const BaseAddr& operator[](std::size_t index) const {
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
[[nodiscard]] BaseAddr& operator[](std::size_t index) {
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
[[nodiscard]] BaseAddr* data() {
|
||||
return base_ptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] const BaseAddr* data() const {
|
||||
return base_ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
void AllocateLevel(u64 level);
|
||||
|
||||
std::size_t address_space_bits{};
|
||||
std::size_t first_level_bits{};
|
||||
std::size_t page_bits{};
|
||||
std::size_t first_level_shift{};
|
||||
std::size_t first_level_chunk_size{};
|
||||
std::size_t alloc_size{};
|
||||
std::vector<void*> first_level_map{};
|
||||
BaseAddr* base_ptr{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -0,0 +1,84 @@
|
||||
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/multi_level_page_table.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename BaseAddr>
|
||||
MultiLevelPageTable<BaseAddr>::MultiLevelPageTable(std::size_t address_space_bits_,
|
||||
std::size_t first_level_bits_,
|
||||
std::size_t page_bits_)
|
||||
: address_space_bits{address_space_bits_},
|
||||
first_level_bits{first_level_bits_}, page_bits{page_bits_} {
|
||||
if (page_bits == 0) {
|
||||
return;
|
||||
}
|
||||
first_level_shift = address_space_bits - first_level_bits;
|
||||
first_level_chunk_size = (1ULL << (first_level_shift - page_bits)) * sizeof(BaseAddr);
|
||||
alloc_size = (1ULL << (address_space_bits - page_bits)) * sizeof(BaseAddr);
|
||||
std::size_t first_level_size = 1ULL << first_level_bits;
|
||||
first_level_map.resize(first_level_size, nullptr);
|
||||
#ifdef _WIN32
|
||||
void* base{VirtualAlloc(nullptr, alloc_size, MEM_RESERVE, PAGE_READWRITE)};
|
||||
#else
|
||||
void* base{mmap(nullptr, alloc_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)};
|
||||
|
||||
if (base == MAP_FAILED) {
|
||||
base = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
ASSERT(base);
|
||||
base_ptr = reinterpret_cast<BaseAddr*>(base);
|
||||
}
|
||||
|
||||
template <typename BaseAddr>
|
||||
MultiLevelPageTable<BaseAddr>::~MultiLevelPageTable() noexcept {
|
||||
if (!base_ptr) {
|
||||
return;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
ASSERT(VirtualFree(base_ptr, 0, MEM_RELEASE));
|
||||
#else
|
||||
ASSERT(munmap(base_ptr, alloc_size) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename BaseAddr>
|
||||
void MultiLevelPageTable<BaseAddr>::ReserveRange(u64 start, std::size_t size) {
|
||||
const u64 new_start = start >> first_level_shift;
|
||||
const u64 new_end = (start + size) >> first_level_shift;
|
||||
for (u64 i = new_start; i <= new_end; i++) {
|
||||
if (!first_level_map[i]) {
|
||||
AllocateLevel(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename BaseAddr>
|
||||
void MultiLevelPageTable<BaseAddr>::AllocateLevel(u64 level) {
|
||||
void* ptr = reinterpret_cast<char *>(base_ptr) + level * first_level_chunk_size;
|
||||
#ifdef _WIN32
|
||||
void* base{VirtualAlloc(ptr, first_level_chunk_size, MEM_COMMIT, PAGE_READWRITE)};
|
||||
#else
|
||||
void* base{mmap(ptr, first_level_chunk_size, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)};
|
||||
|
||||
if (base == MAP_FAILED) {
|
||||
base = nullptr;
|
||||
}
|
||||
#endif
|
||||
ASSERT(base);
|
||||
|
||||
first_level_map[level] = base;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -431,7 +431,7 @@ struct Values {
|
||||
FullscreenMode::Exclusive,
|
||||
#endif
|
||||
FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"};
|
||||
SwitchableSetting<int, true> aspect_ratio{0, 0, 3, "aspect_ratio"};
|
||||
SwitchableSetting<int, true> aspect_ratio{0, 0, 4, "aspect_ratio"};
|
||||
SwitchableSetting<int, true> max_anisotropy{0, 0, 5, "max_anisotropy"};
|
||||
SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"};
|
||||
SwitchableSetting<u16, true> speed_limit{100, 0, 9999, "speed_limit"};
|
||||
@@ -530,6 +530,8 @@ struct Values {
|
||||
Setting<bool> use_debug_asserts{false, "use_debug_asserts"};
|
||||
Setting<bool> use_auto_stub{false, "use_auto_stub"};
|
||||
Setting<bool> enable_all_controllers{false, "enable_all_controllers"};
|
||||
Setting<bool> create_crash_dumps{false, "create_crash_dumps"};
|
||||
Setting<bool> perform_vulkan_check{true, "perform_vulkan_check"};
|
||||
|
||||
// Miscellaneous
|
||||
Setting<std::string> log_filter{"*:Info", "log_filter"};
|
||||
|
||||
@@ -54,6 +54,10 @@ public:
|
||||
is_set = false;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsSet() {
|
||||
return is_set;
|
||||
}
|
||||
|
||||
private:
|
||||
std::condition_variable condvar;
|
||||
std::mutex mutex;
|
||||
|
||||
+16
-10
@@ -4,12 +4,6 @@
|
||||
add_library(core STATIC
|
||||
arm/arm_interface.h
|
||||
arm/arm_interface.cpp
|
||||
arm/dynarmic/arm_dynarmic_32.cpp
|
||||
arm/dynarmic/arm_dynarmic_32.h
|
||||
arm/dynarmic/arm_dynarmic_64.cpp
|
||||
arm/dynarmic/arm_dynarmic_64.h
|
||||
arm/dynarmic/arm_dynarmic_cp15.cpp
|
||||
arm/dynarmic/arm_dynarmic_cp15.h
|
||||
arm/dynarmic/arm_exclusive_monitor.cpp
|
||||
arm/dynarmic/arm_exclusive_monitor.h
|
||||
arm/exclusive_monitor.cpp
|
||||
@@ -144,8 +138,6 @@ add_library(core STATIC
|
||||
frontend/emu_window.h
|
||||
frontend/framebuffer_layout.cpp
|
||||
frontend/framebuffer_layout.h
|
||||
hardware_interrupt_manager.cpp
|
||||
hardware_interrupt_manager.h
|
||||
hid/emulated_console.cpp
|
||||
hid/emulated_console.h
|
||||
hid/emulated_controller.cpp
|
||||
@@ -466,6 +458,8 @@ add_library(core STATIC
|
||||
hle/service/hid/controllers/mouse.h
|
||||
hle/service/hid/controllers/npad.cpp
|
||||
hle/service/hid/controllers/npad.h
|
||||
hle/service/hid/controllers/palma.cpp
|
||||
hle/service/hid/controllers/palma.h
|
||||
hle/service/hid/controllers/stubbed.cpp
|
||||
hle/service/hid/controllers/stubbed.h
|
||||
hle/service/hid/controllers/touchscreen.cpp
|
||||
@@ -500,6 +494,8 @@ add_library(core STATIC
|
||||
hle/service/jit/jit.h
|
||||
hle/service/lbl/lbl.cpp
|
||||
hle/service/lbl/lbl.h
|
||||
hle/service/ldn/lan_discovery.cpp
|
||||
hle/service/ldn/lan_discovery.h
|
||||
hle/service/ldn/ldn_results.h
|
||||
hle/service/ldn/ldn.cpp
|
||||
hle/service/ldn/ldn.h
|
||||
@@ -525,8 +521,14 @@ add_library(core STATIC
|
||||
hle/service/ncm/ncm.h
|
||||
hle/service/nfc/nfc.cpp
|
||||
hle/service/nfc/nfc.h
|
||||
hle/service/nfp/amiibo_crypto.cpp
|
||||
hle/service/nfp/amiibo_crypto.h
|
||||
hle/service/nfp/nfp.cpp
|
||||
hle/service/nfp/nfp.h
|
||||
hle/service/nfp/nfp_device.cpp
|
||||
hle/service/nfp/nfp_device.h
|
||||
hle/service/nfp/nfp_result.h
|
||||
hle/service/nfp/nfp_types.h
|
||||
hle/service/nfp/nfp_user.cpp
|
||||
hle/service/nfp/nfp_user.h
|
||||
hle/service/ngct/ngct.cpp
|
||||
@@ -546,6 +548,12 @@ add_library(core STATIC
|
||||
hle/service/ns/ns.h
|
||||
hle/service/ns/pdm_qry.cpp
|
||||
hle/service/ns/pdm_qry.h
|
||||
hle/service/nvdrv/core/container.cpp
|
||||
hle/service/nvdrv/core/container.h
|
||||
hle/service/nvdrv/core/nvmap.cpp
|
||||
hle/service/nvdrv/core/nvmap.h
|
||||
hle/service/nvdrv/core/syncpoint_manager.cpp
|
||||
hle/service/nvdrv/core/syncpoint_manager.h
|
||||
hle/service/nvdrv/devices/nvdevice.h
|
||||
hle/service/nvdrv/devices/nvdisp_disp0.cpp
|
||||
hle/service/nvdrv/devices/nvdisp_disp0.h
|
||||
@@ -574,8 +582,6 @@ add_library(core STATIC
|
||||
hle/service/nvdrv/nvdrv_interface.h
|
||||
hle/service/nvdrv/nvmemp.cpp
|
||||
hle/service/nvdrv/nvmemp.h
|
||||
hle/service/nvdrv/syncpoint_manager.cpp
|
||||
hle/service/nvdrv/syncpoint_manager.h
|
||||
hle/service/nvflinger/binder.h
|
||||
hle/service/nvflinger/buffer_item.h
|
||||
hle/service/nvflinger/buffer_item_consumer.cpp
|
||||
|
||||
+8
-7
@@ -27,7 +27,6 @@
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/file_sys/vfs_concat.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
#include "core/hardware_interrupt_manager.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
@@ -51,6 +50,7 @@
|
||||
#include "core/telemetry_session.h"
|
||||
#include "core/tools/freezer.h"
|
||||
#include "network/network.h"
|
||||
#include "video_core/host1x/host1x.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
@@ -215,6 +215,7 @@ struct System::Impl {
|
||||
|
||||
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
||||
|
||||
host1x_core = std::make_unique<Tegra::Host1x::Host1x>(system);
|
||||
gpu_core = VideoCore::CreateGPU(emu_window, system);
|
||||
if (!gpu_core) {
|
||||
return SystemResultStatus::ErrorVideoCore;
|
||||
@@ -224,7 +225,6 @@ struct System::Impl {
|
||||
|
||||
service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
|
||||
services = std::make_unique<Service::Services>(service_manager, system);
|
||||
interrupt_manager = std::make_unique<Hardware::InterruptManager>(system);
|
||||
|
||||
// Initialize time manager, which must happen after kernel is created
|
||||
time_manager.Initialize();
|
||||
@@ -373,6 +373,7 @@ struct System::Impl {
|
||||
app_loader.reset();
|
||||
audio_core.reset();
|
||||
gpu_core.reset();
|
||||
host1x_core.reset();
|
||||
perf_stats.reset();
|
||||
kernel.Shutdown();
|
||||
memory.Reset();
|
||||
@@ -450,7 +451,7 @@ struct System::Impl {
|
||||
/// AppLoader used to load the current executing application
|
||||
std::unique_ptr<Loader::AppLoader> app_loader;
|
||||
std::unique_ptr<Tegra::GPU> gpu_core;
|
||||
std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
|
||||
std::unique_ptr<Tegra::Host1x::Host1x> host1x_core;
|
||||
std::unique_ptr<Core::DeviceMemory> device_memory;
|
||||
std::unique_ptr<AudioCore::AudioCore> audio_core;
|
||||
Core::Memory::Memory memory;
|
||||
@@ -668,12 +669,12 @@ const Tegra::GPU& System::GPU() const {
|
||||
return *impl->gpu_core;
|
||||
}
|
||||
|
||||
Core::Hardware::InterruptManager& System::InterruptManager() {
|
||||
return *impl->interrupt_manager;
|
||||
Tegra::Host1x::Host1x& System::Host1x() {
|
||||
return *impl->host1x_core;
|
||||
}
|
||||
|
||||
const Core::Hardware::InterruptManager& System::InterruptManager() const {
|
||||
return *impl->interrupt_manager;
|
||||
const Tegra::Host1x::Host1x& System::Host1x() const {
|
||||
return *impl->host1x_core;
|
||||
}
|
||||
|
||||
VideoCore::RendererBase& System::Renderer() {
|
||||
|
||||
+9
-10
@@ -74,6 +74,9 @@ class TimeManager;
|
||||
namespace Tegra {
|
||||
class DebugContext;
|
||||
class GPU;
|
||||
namespace Host1x {
|
||||
class Host1x;
|
||||
} // namespace Host1x
|
||||
} // namespace Tegra
|
||||
|
||||
namespace VideoCore {
|
||||
@@ -88,10 +91,6 @@ namespace Core::Timing {
|
||||
class CoreTiming;
|
||||
}
|
||||
|
||||
namespace Core::Hardware {
|
||||
class InterruptManager;
|
||||
}
|
||||
|
||||
namespace Core::HID {
|
||||
class HIDCore;
|
||||
}
|
||||
@@ -260,6 +259,12 @@ public:
|
||||
/// Gets an immutable reference to the GPU interface.
|
||||
[[nodiscard]] const Tegra::GPU& GPU() const;
|
||||
|
||||
/// Gets a mutable reference to the Host1x interface
|
||||
[[nodiscard]] Tegra::Host1x::Host1x& Host1x();
|
||||
|
||||
/// Gets an immutable reference to the Host1x interface.
|
||||
[[nodiscard]] const Tegra::Host1x::Host1x& Host1x() const;
|
||||
|
||||
/// Gets a mutable reference to the renderer.
|
||||
[[nodiscard]] VideoCore::RendererBase& Renderer();
|
||||
|
||||
@@ -296,12 +301,6 @@ public:
|
||||
/// Provides a constant reference to the core timing instance.
|
||||
[[nodiscard]] const Timing::CoreTiming& CoreTiming() const;
|
||||
|
||||
/// Provides a reference to the interrupt manager instance.
|
||||
[[nodiscard]] Core::Hardware::InterruptManager& InterruptManager();
|
||||
|
||||
/// Provides a constant reference to the interrupt manager instance.
|
||||
[[nodiscard]] const Core::Hardware::InterruptManager& InterruptManager() const;
|
||||
|
||||
/// Provides a reference to the kernel instance.
|
||||
[[nodiscard]] Kernel::KernelCore& Kernel();
|
||||
|
||||
|
||||
+43
-18
@@ -43,7 +43,7 @@ CoreTiming::CoreTiming()
|
||||
CoreTiming::~CoreTiming() = default;
|
||||
|
||||
void CoreTiming::ThreadEntry(CoreTiming& instance) {
|
||||
constexpr char name[] = "yuzu:HostTiming";
|
||||
constexpr char name[] = "HostTiming";
|
||||
MicroProfileOnThreadCreate(name);
|
||||
Common::SetCurrentThreadName(name);
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
|
||||
@@ -134,13 +134,17 @@ void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
|
||||
std::chrono::nanoseconds resched_time,
|
||||
const std::shared_ptr<EventType>& event_type,
|
||||
std::uintptr_t user_data, bool absolute_time) {
|
||||
std::scoped_lock scope{basic_lock};
|
||||
const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
|
||||
{
|
||||
std::scoped_lock scope{basic_lock};
|
||||
const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
|
||||
|
||||
event_queue.emplace_back(
|
||||
Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()});
|
||||
event_queue.emplace_back(
|
||||
Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()});
|
||||
|
||||
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
}
|
||||
|
||||
event.Set();
|
||||
}
|
||||
|
||||
void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
|
||||
@@ -229,17 +233,17 @@ std::optional<s64> CoreTiming::Advance() {
|
||||
basic_lock.lock();
|
||||
|
||||
if (evt.reschedule_time != 0) {
|
||||
// If this event was scheduled into a pause, its time now is going to be way behind.
|
||||
// Re-set this event to continue from the end of the pause.
|
||||
auto next_time{evt.time + evt.reschedule_time};
|
||||
if (evt.time < pause_end_time) {
|
||||
next_time = pause_end_time + evt.reschedule_time;
|
||||
}
|
||||
|
||||
const auto next_schedule_time{new_schedule_time.has_value()
|
||||
? new_schedule_time.value().count()
|
||||
: evt.reschedule_time};
|
||||
|
||||
// If this event was scheduled into a pause, its time now is going to be way behind.
|
||||
// Re-set this event to continue from the end of the pause.
|
||||
auto next_time{evt.time + next_schedule_time};
|
||||
if (evt.time < pause_end_time) {
|
||||
next_time = pause_end_time + next_schedule_time;
|
||||
}
|
||||
|
||||
event_queue.emplace_back(
|
||||
Event{next_time, event_fifo_id++, evt.user_data, evt.type, next_schedule_time});
|
||||
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
@@ -250,8 +254,7 @@ std::optional<s64> CoreTiming::Advance() {
|
||||
}
|
||||
|
||||
if (!event_queue.empty()) {
|
||||
const s64 next_time = event_queue.front().time - global_timer;
|
||||
return next_time;
|
||||
return event_queue.front().time;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -264,11 +267,33 @@ void CoreTiming::ThreadLoop() {
|
||||
paused_set = false;
|
||||
const auto next_time = Advance();
|
||||
if (next_time) {
|
||||
if (*next_time > 0) {
|
||||
std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time);
|
||||
event.WaitFor(next_time_ns);
|
||||
// There are more events left in the queue, wait until the next event.
|
||||
const auto wait_time = *next_time - GetGlobalTimeNs().count();
|
||||
if (wait_time > 0) {
|
||||
#ifdef _WIN32
|
||||
// Assume a timer resolution of 1ms.
|
||||
static constexpr s64 TimerResolutionNS = 1000000;
|
||||
|
||||
// Sleep in discrete intervals of the timer resolution, and spin the rest.
|
||||
const auto sleep_time = wait_time - (wait_time % TimerResolutionNS);
|
||||
if (sleep_time > 0) {
|
||||
event.WaitFor(std::chrono::nanoseconds(sleep_time));
|
||||
}
|
||||
|
||||
while (!paused && !event.IsSet() && GetGlobalTimeNs().count() < *next_time) {
|
||||
// Yield to reduce thread starvation.
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
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 continue.
|
||||
wait_set = true;
|
||||
event.Wait();
|
||||
}
|
||||
|
||||
@@ -189,9 +189,9 @@ void CpuManager::RunThread(std::size_t core) {
|
||||
system.RegisterCoreThread(core);
|
||||
std::string name;
|
||||
if (is_multicore) {
|
||||
name = "yuzu:CPUCore_" + std::to_string(core);
|
||||
name = "CPUCore_" + std::to_string(core);
|
||||
} else {
|
||||
name = "yuzu:CPUThread";
|
||||
name = "CPUThread";
|
||||
}
|
||||
MicroProfileOnThreadCreate(name.c_str());
|
||||
Common::SetCurrentThreadName(name.c_str());
|
||||
|
||||
@@ -140,7 +140,7 @@ private:
|
||||
}
|
||||
|
||||
void ThreadLoop(std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("yuzu:Debugger");
|
||||
Common::SetCurrentThreadName("Debugger");
|
||||
|
||||
// Set up the client signals for new data.
|
||||
AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); });
|
||||
|
||||
@@ -33,11 +33,55 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
|
||||
return Loader::ResultStatus::ErrorBadACIHeader;
|
||||
}
|
||||
|
||||
if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset)) {
|
||||
// Load acid_file_access per-component instead of the entire struct, since this struct does not
|
||||
// reflect the layout of the real data.
|
||||
std::size_t current_offset = acid_header.fac_offset;
|
||||
if (sizeof(FileAccessControl::version) != file->ReadBytes(&acid_file_access.version,
|
||||
sizeof(FileAccessControl::version),
|
||||
current_offset)) {
|
||||
return Loader::ResultStatus::ErrorBadFileAccessControl;
|
||||
}
|
||||
if (sizeof(FileAccessControl::permissions) !=
|
||||
file->ReadBytes(&acid_file_access.permissions, sizeof(FileAccessControl::permissions),
|
||||
current_offset += sizeof(FileAccessControl::version) + 3)) {
|
||||
return Loader::ResultStatus::ErrorBadFileAccessControl;
|
||||
}
|
||||
if (sizeof(FileAccessControl::unknown) !=
|
||||
file->ReadBytes(&acid_file_access.unknown, sizeof(FileAccessControl::unknown),
|
||||
current_offset + sizeof(FileAccessControl::permissions))) {
|
||||
return Loader::ResultStatus::ErrorBadFileAccessControl;
|
||||
}
|
||||
|
||||
if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) {
|
||||
// Load aci_file_access per-component instead of the entire struct, same as acid_file_access
|
||||
current_offset = aci_header.fah_offset;
|
||||
if (sizeof(FileAccessHeader::version) != file->ReadBytes(&aci_file_access.version,
|
||||
sizeof(FileAccessHeader::version),
|
||||
current_offset)) {
|
||||
return Loader::ResultStatus::ErrorBadFileAccessHeader;
|
||||
}
|
||||
if (sizeof(FileAccessHeader::permissions) !=
|
||||
file->ReadBytes(&aci_file_access.permissions, sizeof(FileAccessHeader::permissions),
|
||||
current_offset += sizeof(FileAccessHeader::version) + 3)) {
|
||||
return Loader::ResultStatus::ErrorBadFileAccessHeader;
|
||||
}
|
||||
if (sizeof(FileAccessHeader::unk_offset) !=
|
||||
file->ReadBytes(&aci_file_access.unk_offset, sizeof(FileAccessHeader::unk_offset),
|
||||
current_offset += sizeof(FileAccessHeader::permissions))) {
|
||||
return Loader::ResultStatus::ErrorBadFileAccessHeader;
|
||||
}
|
||||
if (sizeof(FileAccessHeader::unk_size) !=
|
||||
file->ReadBytes(&aci_file_access.unk_size, sizeof(FileAccessHeader::unk_size),
|
||||
current_offset += sizeof(FileAccessHeader::unk_offset))) {
|
||||
return Loader::ResultStatus::ErrorBadFileAccessHeader;
|
||||
}
|
||||
if (sizeof(FileAccessHeader::unk_offset_2) !=
|
||||
file->ReadBytes(&aci_file_access.unk_offset_2, sizeof(FileAccessHeader::unk_offset_2),
|
||||
current_offset += sizeof(FileAccessHeader::unk_size))) {
|
||||
return Loader::ResultStatus::ErrorBadFileAccessHeader;
|
||||
}
|
||||
if (sizeof(FileAccessHeader::unk_size_2) !=
|
||||
file->ReadBytes(&aci_file_access.unk_size_2, sizeof(FileAccessHeader::unk_size_2),
|
||||
current_offset + sizeof(FileAccessHeader::unk_offset_2))) {
|
||||
return Loader::ResultStatus::ErrorBadFileAccessHeader;
|
||||
}
|
||||
|
||||
@@ -152,9 +196,7 @@ void ProgramMetadata::Print() const {
|
||||
LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.is_retail ? "YES" : "NO");
|
||||
LOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min);
|
||||
LOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max);
|
||||
u64_le permissions_l; // local copy to fix alignment error
|
||||
std::memcpy(&permissions_l, &acid_file_access.permissions, sizeof(permissions_l));
|
||||
LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", permissions_l);
|
||||
LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions);
|
||||
|
||||
// Begin ACI0 printing (actual perms, unsigned)
|
||||
LOG_DEBUG(Service_FS, "Magic: {:.4}", aci_header.magic.data());
|
||||
|
||||
@@ -144,20 +144,18 @@ private:
|
||||
|
||||
static_assert(sizeof(AciHeader) == 0x40, "ACI0 header structure size is wrong");
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
// FileAccessControl and FileAccessHeader need loaded per-component: this layout does not
|
||||
// reflect the real layout to avoid reference binding to misaligned addresses
|
||||
struct FileAccessControl {
|
||||
u8 version;
|
||||
INSERT_PADDING_BYTES(3);
|
||||
// 3 padding bytes
|
||||
u64_le permissions;
|
||||
std::array<u8, 0x20> unknown;
|
||||
};
|
||||
|
||||
static_assert(sizeof(FileAccessControl) == 0x2C, "FS access control structure size is wrong");
|
||||
|
||||
struct FileAccessHeader {
|
||||
u8 version;
|
||||
INSERT_PADDING_BYTES(3);
|
||||
// 3 padding bytes
|
||||
u64_le permissions;
|
||||
u32_le unk_offset;
|
||||
u32_le unk_size;
|
||||
@@ -165,10 +163,6 @@ private:
|
||||
u32_le unk_size_2;
|
||||
};
|
||||
|
||||
static_assert(sizeof(FileAccessHeader) == 0x1C, "FS access header structure size is wrong");
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
Header npdm_header;
|
||||
AciHeader aci_header;
|
||||
AcidHeader acid_header;
|
||||
|
||||
@@ -67,6 +67,8 @@ float EmulationAspectRatio(AspectRatio aspect, float window_aspect_ratio) {
|
||||
return 3.0f / 4.0f;
|
||||
case AspectRatio::R21_9:
|
||||
return 9.0f / 21.0f;
|
||||
case AspectRatio::R16_10:
|
||||
return 10.0f / 16.0f;
|
||||
case AspectRatio::StretchToWindow:
|
||||
return window_aspect_ratio;
|
||||
default:
|
||||
|
||||
@@ -27,6 +27,7 @@ enum class AspectRatio {
|
||||
Default,
|
||||
R4_3,
|
||||
R21_9,
|
||||
R16_10,
|
||||
StretchToWindow,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hardware_interrupt_manager.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv_interface.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Core::Hardware {
|
||||
|
||||
InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) {
|
||||
gpu_interrupt_event = Core::Timing::CreateEvent(
|
||||
"GPUInterrupt",
|
||||
[this](std::uintptr_t message, u64 time,
|
||||
std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
|
||||
auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");
|
||||
const u32 syncpt = static_cast<u32>(message >> 32);
|
||||
const u32 value = static_cast<u32>(message);
|
||||
nvdrv->SignalGPUInterruptSyncpt(syncpt, value);
|
||||
return std::nullopt;
|
||||
});
|
||||
}
|
||||
|
||||
InterruptManager::~InterruptManager() = default;
|
||||
|
||||
void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
|
||||
const u64 msg = (static_cast<u64>(syncpoint_id) << 32ULL) | value;
|
||||
system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{10}, gpu_interrupt_event, msg);
|
||||
}
|
||||
|
||||
} // namespace Core::Hardware
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user