Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| af310ddff8 |
@@ -1,27 +1,12 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
set -e
|
||||
|
||||
cd /yuzu
|
||||
|
||||
ccache -s
|
||||
|
||||
mkdir build || true && cd build
|
||||
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
|
||||
cmake .. \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_CXX_FLAGS="-femulated-tls" \
|
||||
-DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWClangCross.cmake" \
|
||||
-DDISPLAY_VERSION=$1 \
|
||||
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
||||
-DENABLE_QT_TRANSLATION=ON \
|
||||
-DUSE_CCACHE=ON \
|
||||
-DYUZU_USE_BUNDLED_SDL2=OFF \
|
||||
-DYUZU_USE_EXTERNAL_SDL2=OFF \
|
||||
-GNinja
|
||||
ninja yuzu yuzu-cmd
|
||||
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON
|
||||
ninja
|
||||
|
||||
ccache -s
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ steps:
|
||||
displayName: 'Install vulkan-sdk'
|
||||
- script: python -m pip install --upgrade pip conan
|
||||
displayName: 'Install conan'
|
||||
- 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 16 2019" -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 ..
|
||||
displayName: 'Configure CMake'
|
||||
- task: MSBuild@1
|
||||
displayName: 'Build'
|
||||
|
||||
@@ -47,7 +47,7 @@ stages:
|
||||
timeoutInMinutes: 120
|
||||
displayName: 'msvc'
|
||||
pool:
|
||||
vmImage: windows-2022
|
||||
vmImage: windows-2019
|
||||
steps:
|
||||
- template: ./templates/sync-source.yml
|
||||
parameters:
|
||||
|
||||
@@ -12,7 +12,7 @@ stages:
|
||||
timeoutInMinutes: 120
|
||||
displayName: 'windows-msvc'
|
||||
pool:
|
||||
vmImage: windows-2022
|
||||
vmImage: windows-2019
|
||||
steps:
|
||||
- template: ./templates/sync-source.yml
|
||||
parameters:
|
||||
|
||||
+15
-22
@@ -218,13 +218,8 @@ if (Boost_FOUND)
|
||||
list(APPEND Boost_LIBRARIES Boost::context)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Boost 1.79.0 or newer not found, falling back to Conan")
|
||||
list(APPEND CONAN_REQUIRED_LIBS "boost/1.79.0")
|
||||
endif()
|
||||
|
||||
# boost:asio has functions that require AcceptEx et al
|
||||
if (MINGW)
|
||||
find_library(MSWSOCK_LIBRARY mswsock REQUIRED)
|
||||
message(STATUS "Boost 1.73.0 or newer not found, falling back to Conan")
|
||||
list(APPEND CONAN_REQUIRED_LIBS "boost/1.78.0")
|
||||
endif()
|
||||
|
||||
# Attempt to locate any packages that are required and report the missing ones in CONAN_REQUIRED_LIBS
|
||||
@@ -410,11 +405,13 @@ if (CONAN_REQUIRED_LIBS)
|
||||
# Download conan.cmake automatically, you can also just copy the conan.cmake file
|
||||
if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake")
|
||||
message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan")
|
||||
file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/release/0.18/conan.cmake" "${CMAKE_BINARY_DIR}/conan.cmake")
|
||||
# TODO: Use a tagged release. The latest tagged release does not support VS2022 as of this writing.
|
||||
file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/43e385830ee35377dbd2dcbe8d5a9e750301ea00/conan.cmake"
|
||||
"${CMAKE_BINARY_DIR}/conan.cmake")
|
||||
endif()
|
||||
include(${CMAKE_BINARY_DIR}/conan.cmake)
|
||||
|
||||
conan_check(VERSION 1.45.0 REQUIRED)
|
||||
conan_check(VERSION 1.41.0 REQUIRED)
|
||||
|
||||
# Manually add iconv to fix a dep conflict between qt and sdl2
|
||||
# We don't need to add it through find_package or anything since the other two can find it just fine
|
||||
@@ -644,18 +641,14 @@ endif()
|
||||
# =========================
|
||||
|
||||
# Install freedesktop.org metadata files, following those specifications:
|
||||
# https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
|
||||
# https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
|
||||
# https://specifications.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
|
||||
# https://www.freedesktop.org/software/appstream/docs/
|
||||
# http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
|
||||
# http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
|
||||
# http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
|
||||
if(ENABLE_QT AND UNIX AND NOT APPLE)
|
||||
install(FILES "dist/org.yuzu_emu.yuzu.desktop"
|
||||
DESTINATION "share/applications")
|
||||
install(FILES "dist/yuzu.svg"
|
||||
DESTINATION "share/icons/hicolor/scalable/apps"
|
||||
RENAME "org.yuzu_emu.yuzu.svg")
|
||||
install(FILES "dist/org.yuzu_emu.yuzu.xml"
|
||||
DESTINATION "share/mime/packages")
|
||||
install(FILES "dist/org.yuzu_emu.yuzu.metainfo.xml"
|
||||
DESTINATION "share/metainfo")
|
||||
install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.desktop"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications")
|
||||
install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.svg"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps")
|
||||
install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.xml"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages")
|
||||
endif()
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
set(MINGW_PREFIX /usr/x86_64-w64-mingw32/)
|
||||
set(CMAKE_SYSTEM_NAME Windows)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX})
|
||||
set(SDL2_PATH ${MINGW_PREFIX})
|
||||
set(MINGW_TOOL_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-)
|
||||
|
||||
# Specify the cross compiler
|
||||
set(CMAKE_C_COMPILER ${MINGW_TOOL_PREFIX}clang)
|
||||
set(CMAKE_CXX_COMPILER ${MINGW_TOOL_PREFIX}clang++)
|
||||
set(CMAKE_RC_COMPILER ${MINGW_TOOL_PREFIX}windres)
|
||||
set(CMAKE_C_COMPILER_AR ${MINGW_TOOL_PREFIX}ar)
|
||||
set(CMAKE_CXX_COMPILER_AR ${MINGW_TOOL_PREFIX}ar)
|
||||
set(CMAKE_C_COMPILER_RANLIB ${MINGW_TOOL_PREFIX}ranlib)
|
||||
set(CMAKE_CXX_COMPILER_RANLIB ${MINGW_TOOL_PREFIX}ranlib)
|
||||
|
||||
# Mingw tools
|
||||
set(STRIP ${MINGW_TOOL_PREFIX}strip)
|
||||
set(WINDRES ${MINGW_TOOL_PREFIX}windres)
|
||||
set(ENV{PKG_CONFIG} ${MINGW_TOOL_PREFIX}pkg-config)
|
||||
|
||||
# ccache wrapper
|
||||
option(USE_CCACHE "Use ccache for compilation" OFF)
|
||||
if(USE_CCACHE)
|
||||
find_program(CCACHE ccache)
|
||||
if(CCACHE)
|
||||
message(STATUS "Using ccache found in PATH")
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE})
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE})
|
||||
else(CCACHE)
|
||||
message(WARNING "USE_CCACHE enabled, but no ccache found")
|
||||
endif(CCACHE)
|
||||
endif(USE_CCACHE)
|
||||
|
||||
# Search for programs in the build host directories
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
|
||||
|
||||
# Echo modified cmake vars to screen for debugging purposes
|
||||
if(NOT DEFINED ENV{MINGW_DEBUG_INFO})
|
||||
message("")
|
||||
message("Custom cmake vars: (blank = system default)")
|
||||
message("-----------------------------------------")
|
||||
message("* CMAKE_C_COMPILER : ${CMAKE_C_COMPILER}")
|
||||
message("* CMAKE_CXX_COMPILER : ${CMAKE_CXX_COMPILER}")
|
||||
message("* CMAKE_RC_COMPILER : ${CMAKE_RC_COMPILER}")
|
||||
message("* WINDRES : ${WINDRES}")
|
||||
message("* ENV{PKG_CONFIG} : $ENV{PKG_CONFIG}")
|
||||
message("* STRIP : ${STRIP}")
|
||||
message("* USE_CCACHE : ${USE_CCACHE}")
|
||||
message("")
|
||||
# So that the debug info only appears once
|
||||
set(ENV{MINGW_DEBUG_INFO} SHOWN)
|
||||
endif()
|
||||
Vendored
+556
-680
File diff suppressed because it is too large
Load Diff
Vendored
+556
-680
File diff suppressed because it is too large
Load Diff
Vendored
+556
-680
File diff suppressed because it is too large
Load Diff
Vendored
+554
-686
File diff suppressed because it is too large
Load Diff
Vendored
+607
-731
File diff suppressed because it is too large
Load Diff
Vendored
+556
-689
File diff suppressed because it is too large
Load Diff
Vendored
+613
-738
File diff suppressed because it is too large
Load Diff
Vendored
+570
-697
File diff suppressed because it is too large
Load Diff
Vendored
+562
-686
File diff suppressed because it is too large
Load Diff
Vendored
+690
-820
File diff suppressed because it is too large
Load Diff
Vendored
+569
-716
File diff suppressed because it is too large
Load Diff
Vendored
+558
-682
File diff suppressed because it is too large
Load Diff
Vendored
+558
-682
File diff suppressed because it is too large
Load Diff
Vendored
+559
-683
File diff suppressed because it is too large
Load Diff
Vendored
+551
-684
File diff suppressed because it is too large
Load Diff
Vendored
+558
-688
File diff suppressed because it is too large
Load Diff
Vendored
+837
-977
File diff suppressed because it is too large
Load Diff
Vendored
+555
-679
File diff suppressed because it is too large
Load Diff
Vendored
+558
-682
File diff suppressed because it is too large
Load Diff
Vendored
+555
-679
File diff suppressed because it is too large
Load Diff
Vendored
+555
-679
File diff suppressed because it is too large
Load Diff
Vendored
+553
-683
File diff suppressed because it is too large
Load Diff
Vendored
+552
-682
File diff suppressed because it is too large
Load Diff
Vendored
-56
@@ -1,56 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop-application">
|
||||
<id>org.yuzu_emu.yuzu</id>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<name>yuzu</name>
|
||||
<summary>Nintendo Switch emulator</summary>
|
||||
<description>
|
||||
<p>yuzu is the world's most popular, open-source, Nintendo Switch emulator — started by the creators of Citra.</p>
|
||||
<p>The emulator is capable of running most commercial games at full speed, provided you meet the necessary hardware requirements.</p>
|
||||
<p>For a full list of games yuzu support, please visit our Compatibility page.</p>
|
||||
<p>Check out our website for the latest news on exciting features, monthly progress reports, and more!</p>
|
||||
</description>
|
||||
<categories>
|
||||
<category>Game</category>
|
||||
<category>Emulator</category>
|
||||
</categories>
|
||||
<keywords>
|
||||
<keyword>switch</keyword>
|
||||
<keyword>emulator</keyword>
|
||||
</keywords>
|
||||
<url type="homepage">https://yuzu-emu.org/</url>
|
||||
<url type="bugtracker">https://github.com/yuzu-emu/yuzu/issues</url>
|
||||
<url type="faq">https://yuzu-emu.org/wiki/faq/</url>
|
||||
<url type="help">https://yuzu-emu.org/wiki/home/</url>
|
||||
<url type="donation">https://yuzu-emu.org/donate/</url>
|
||||
<url type="translate">https://www.transifex.com/projects/p/yuzu</url>
|
||||
<url type="contact">https://community.citra-emu.org/</url>
|
||||
<url type="vcs-browser">https://github.com/yuzu-emu/yuzu</url>
|
||||
<url type="contribute">https://yuzu-emu.org/wiki/contributing/</url>
|
||||
<launchable type="desktop-id">org.yuzu_emu.yuzu.desktop</launchable>
|
||||
<provides>
|
||||
<binary>yuzu</binary>
|
||||
<binary>yuzu-cmd</binary>
|
||||
</provides>
|
||||
<supports>
|
||||
<control>pointing</control>
|
||||
<control>keyboard</control>
|
||||
<control>gamepad</control>
|
||||
</supports>
|
||||
<requires>
|
||||
<memory>8192</memory>
|
||||
</requires>
|
||||
<recommends>
|
||||
<memory>16384</memory>
|
||||
</recommends>
|
||||
<project_license>GPL-3.0-or-later</project_license>
|
||||
<developer_name>yuzu Emulator Team</developer_name>
|
||||
<content_rating type="oars-1.0"/>
|
||||
<screenshots>
|
||||
<screenshot type="default">https://raw.githubusercontent.com/yuzu-emu/yuzu-emu.github.io/master/images/screenshots/001-Super%20Mario%20Odyssey%20.png</screenshot>
|
||||
<screenshot>https://raw.githubusercontent.com/yuzu-emu/yuzu-emu.github.io/master/images/screenshots/004-The%20Legend%20of%20Zelda%20Skyward%20Sword%20HD.png</screenshot>
|
||||
<screenshot>https://raw.githubusercontent.com/yuzu-emu/yuzu-emu.github.io/master/images/screenshots/007-Pokemon%20Sword.png</screenshot>
|
||||
<screenshot>https://raw.githubusercontent.com/yuzu-emu/yuzu-emu.github.io/master/images/screenshots/010-Hyrule%20Warriors%20Age%20of%20Calamity.png</screenshot>
|
||||
<screenshot>https://raw.githubusercontent.com/yuzu-emu/yuzu-emu.github.io/master/images/screenshots/039-Pok%C3%A9mon%20Mystery%20Dungeon%20Rescue%20Team%20DX.png.png.png</screenshot>
|
||||
</screenshots>
|
||||
</component>
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 10 KiB |
Vendored
-13
@@ -58,19 +58,6 @@ QPushButton#GPUStatusBarButton:!checked {
|
||||
color: #109010;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton {
|
||||
min-width: 0px;
|
||||
color: #000000;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
||||
|
||||
QPushButton#buttonRefreshDevices {
|
||||
min-width: 21px;
|
||||
min-height: 21px;
|
||||
|
||||
Vendored
-13
@@ -1304,19 +1304,6 @@ QPushButton#GPUStatusBarButton:!checked {
|
||||
color: #40dd40;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton {
|
||||
min-width: 0px;
|
||||
color: #ffffff;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
||||
|
||||
QPushButton#buttonRefreshDevices {
|
||||
min-width: 23px;
|
||||
min-height: 23px;
|
||||
|
||||
@@ -2207,19 +2207,6 @@ QPushButton#GPUStatusBarButton:!checked {
|
||||
color: #40dd40;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton {
|
||||
min-width: 0px;
|
||||
color: #ffffff;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
||||
|
||||
QPushButton#buttonRefreshDevices {
|
||||
min-width: 19px;
|
||||
min-height: 19px;
|
||||
|
||||
+2
-2
@@ -4,9 +4,9 @@ Type=Application
|
||||
Name=yuzu
|
||||
GenericName=Switch Emulator
|
||||
Comment=Nintendo Switch video game console emulator
|
||||
Icon=org.yuzu_emu.yuzu
|
||||
Icon=yuzu
|
||||
TryExec=yuzu
|
||||
Exec=yuzu %f
|
||||
Categories=Game;Emulator;Qt;
|
||||
MimeType=application/x-nx-nro;application/x-nx-nso;application/x-nx-nsp;application/x-nx-xci;
|
||||
Keywords=Nintendo;Switch;
|
||||
Keywords=Switch;Nintendo;
|
||||
+5
-5
@@ -3,7 +3,7 @@
|
||||
<mime-type type="application/x-nx-nro">
|
||||
<comment>Nintendo Switch homebrew executable</comment>
|
||||
<acronym>NRO</acronym>
|
||||
<icon name="org.yuzu_emu.yuzu"/>
|
||||
<icon name="yuzu"/>
|
||||
<glob pattern="*.nro"/>
|
||||
<magic><match value="NRO" type="string" offset="16"/></magic>
|
||||
</mime-type>
|
||||
@@ -11,7 +11,7 @@
|
||||
<mime-type type="application/x-nx-nso">
|
||||
<comment>Nintendo Switch homebrew executable</comment>
|
||||
<acronym>NSO</acronym>
|
||||
<icon name="org.yuzu_emu.yuzu"/>
|
||||
<icon name="yuzu"/>
|
||||
<glob pattern="*.nso"/>
|
||||
<magic><match value="NSO" type="string" offset="0"/></magic>
|
||||
</mime-type>
|
||||
@@ -19,7 +19,7 @@
|
||||
<mime-type type="application/x-nx-nsp">
|
||||
<comment>Nintendo Switch Package</comment>
|
||||
<acronym>NSP</acronym>
|
||||
<icon name="org.yuzu_emu.yuzu"/>
|
||||
<icon name="yuzu"/>
|
||||
<glob pattern="*.nsp"/>
|
||||
<magic><match value="PFS" type="string" offset="0"/></magic>
|
||||
</mime-type>
|
||||
@@ -27,7 +27,7 @@
|
||||
<mime-type type="application/x-nx-xci">
|
||||
<comment>Nintendo Switch Card Image</comment>
|
||||
<acronym>XCI</acronym>
|
||||
<icon name="org.yuzu_emu.yuzu"/>
|
||||
<icon name="yuzu"/>
|
||||
<glob pattern="*.xci"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
</mime-info>
|
||||
Vendored
-5
@@ -40,11 +40,6 @@ target_include_directories(mbedtls PUBLIC ./mbedtls/include)
|
||||
add_library(microprofile INTERFACE)
|
||||
target_include_directories(microprofile INTERFACE ./microprofile)
|
||||
|
||||
# GCC bugs
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND MINGW)
|
||||
target_compile_options(microprofile INTERFACE "-Wno-array-bounds")
|
||||
endif()
|
||||
|
||||
# libusb
|
||||
if (NOT LIBUSB_FOUND OR YUZU_USE_BUNDLED_LIBUSB)
|
||||
add_subdirectory(libusb)
|
||||
|
||||
Vendored
+1
-1
Submodule externals/Vulkan-Headers updated: 33d4dd987f...e005e1f817
Vendored
+1
-1
Submodule externals/cpp-httplib updated: 305a7abcb9...9648f950f5
Vendored
+1
-1
Submodule externals/dynarmic updated: 5ad1d02351...57af72a567
Vendored
+1
-1
Submodule externals/libressl updated: 8929f818fd...8289d0d07d
+1
-1
@@ -1246,7 +1246,7 @@ struct MicroProfileScopeLock
|
||||
{
|
||||
bool bUseLock;
|
||||
std::recursive_mutex& m;
|
||||
MicroProfileScopeLock(std::recursive_mutex& m_) : bUseLock(g_bUseLock), m(m_)
|
||||
MicroProfileScopeLock(std::recursive_mutex& m) : bUseLock(g_bUseLock), m(m)
|
||||
{
|
||||
if(bUseLock)
|
||||
m.lock();
|
||||
|
||||
+38
-37
@@ -213,8 +213,8 @@ struct MicroProfileCustom
|
||||
|
||||
struct SOptionDesc
|
||||
{
|
||||
SOptionDesc()=default;
|
||||
SOptionDesc(uint8_t nSubType_, uint8_t nIndex_, const char* fmt, ...):nSubType(nSubType_), nIndex(nIndex_)
|
||||
SOptionDesc(){}
|
||||
SOptionDesc(uint8_t nSubType, uint8_t nIndex, const char* fmt, ...):nSubType(nSubType), nIndex(nIndex)
|
||||
{
|
||||
va_list args;
|
||||
va_start (args, fmt);
|
||||
@@ -573,10 +573,10 @@ inline void MicroProfileToolTipMeta(MicroProfileStringArray* pToolTip)
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int k = 0; k < MICROPROFILE_META_MAX; ++k)
|
||||
for(int i = 0; i < MICROPROFILE_META_MAX; ++i)
|
||||
{
|
||||
nMetaSumInclusive[k] += nMetaSum[k];
|
||||
nMetaSum[k] = 0;
|
||||
nMetaSumInclusive[i] += nMetaSum[i];
|
||||
nMetaSum[i] = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -708,10 +708,10 @@ inline void MicroProfileDrawFloatTooltip(uint32_t nX, uint32_t nY, uint32_t nTok
|
||||
|
||||
if(UI.nMouseLeftMod)
|
||||
{
|
||||
int nToolTipIndex = (g_MicroProfileUI.LockedToolTipFront + MICROPROFILE_TOOLTIP_MAX_LOCKED - 1) % MICROPROFILE_TOOLTIP_MAX_LOCKED;
|
||||
g_MicroProfileUI.nLockedToolTipColor[nToolTipIndex] = S.TimerInfo[nTimerId].nColor;
|
||||
MicroProfileStringArrayCopy(&g_MicroProfileUI.LockedToolTips[nToolTipIndex], &ToolTip);
|
||||
g_MicroProfileUI.LockedToolTipFront = nToolTipIndex;
|
||||
int nIndex = (g_MicroProfileUI.LockedToolTipFront + MICROPROFILE_TOOLTIP_MAX_LOCKED - 1) % MICROPROFILE_TOOLTIP_MAX_LOCKED;
|
||||
g_MicroProfileUI.nLockedToolTipColor[nIndex] = S.TimerInfo[nTimerId].nColor;
|
||||
MicroProfileStringArrayCopy(&g_MicroProfileUI.LockedToolTips[nIndex], &ToolTip);
|
||||
g_MicroProfileUI.LockedToolTipFront = nIndex;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -917,8 +917,9 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
float fStart = floor(fMsBase*fRcpStep) * fStep;
|
||||
for(float f = fStart; f < fMsEnd; )
|
||||
{
|
||||
float fStart = f;
|
||||
float fNext = f + fStep;
|
||||
MicroProfileDrawBox(((f-fMsBase) * fMsToScreen), nBaseY, (fNext-fMsBase) * fMsToScreen+1, nBaseY + nHeight, UI.nOpacityBackground | g_nMicroProfileBackColors[nColorIndex++ & 1]);
|
||||
MicroProfileDrawBox(((fStart-fMsBase) * fMsToScreen), nBaseY, (fNext-fMsBase) * fMsToScreen+1, nBaseY + nHeight, UI.nOpacityBackground | g_nMicroProfileBackColors[nColorIndex++ & 1]);
|
||||
f = fNext;
|
||||
}
|
||||
}
|
||||
@@ -1115,9 +1116,9 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
|
||||
nMaxStackDepth = MicroProfileMax(nMaxStackDepth, nStackPos);
|
||||
float fMsStart = fToMs * MicroProfileLogTickDifference(nBaseTicks, nTickStart);
|
||||
float fMsEnd2 = fToMs * MicroProfileLogTickDifference(nBaseTicks, nTickEnd);
|
||||
float fMsEnd = fToMs * MicroProfileLogTickDifference(nBaseTicks, nTickEnd);
|
||||
float fXStart = fMsStart * fMsToScreen;
|
||||
float fXEnd = fMsEnd2 * fMsToScreen;
|
||||
float fXEnd = fMsEnd * fMsToScreen;
|
||||
float fYStart = (float)(nY + nStackPos * nYDelta);
|
||||
float fYEnd = fYStart + (MICROPROFILE_DETAILED_BAR_HEIGHT);
|
||||
float fXDist = MicroProfileMax(fXStart - fMouseX, fMouseX - fXEnd);
|
||||
@@ -1268,22 +1269,22 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
if(UI.nRangeBegin != UI.nRangeEnd)
|
||||
{
|
||||
float fMsStart = fToMsCpu * MicroProfileLogTickDifference(nBaseTicksCpu, UI.nRangeBegin);
|
||||
float fMsEnd3 = fToMsCpu * MicroProfileLogTickDifference(nBaseTicksCpu, UI.nRangeEnd);
|
||||
float fMsEnd = fToMsCpu * MicroProfileLogTickDifference(nBaseTicksCpu, UI.nRangeEnd);
|
||||
float fXStart = fMsStart * fMsToScreen;
|
||||
float fXEnd = fMsEnd3 * fMsToScreen;
|
||||
float fXEnd = fMsEnd * fMsToScreen;
|
||||
MicroProfileDrawBox(fXStart, nBaseY, fXEnd, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT, MicroProfileBoxTypeFlat);
|
||||
MicroProfileDrawLineVertical(fXStart, nBaseY, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT | 0x44000000);
|
||||
MicroProfileDrawLineVertical(fXEnd, nBaseY, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT | 0x44000000);
|
||||
|
||||
fMsStart += fDetailedOffset;
|
||||
fMsEnd3 += fDetailedOffset;
|
||||
fMsEnd += fDetailedOffset;
|
||||
char sBuffer[32];
|
||||
uint32_t nLenStart = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsStart);
|
||||
float fStartTextWidth = (float)((1+MICROPROFILE_TEXT_WIDTH) * nLenStart);
|
||||
float fStartTextX = fXStart - fStartTextWidth - 2;
|
||||
MicroProfileDrawBox(fStartTextX, nBaseY, fStartTextX + fStartTextWidth + 2, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
|
||||
MicroProfileDrawText(fStartTextX+1, nBaseY, UINT32_MAX, sBuffer, nLenStart);
|
||||
uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd3);
|
||||
uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd);
|
||||
MicroProfileDrawBox(fXEnd+1, nBaseY, fXEnd+1+(1+MICROPROFILE_TEXT_WIDTH) * nLenEnd + 3, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
|
||||
MicroProfileDrawText(fXEnd+2, nBaseY+1, UINT32_MAX, sBuffer, nLenEnd);
|
||||
|
||||
@@ -1296,9 +1297,9 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
if(UI.nRangeBeginGpu != UI.nRangeEndGpu)
|
||||
{
|
||||
float fMsStart = fToMsGpu * MicroProfileLogTickDifference(nBaseTicksGpu, UI.nRangeBeginGpu);
|
||||
float fMsEnd4 = fToMsGpu * MicroProfileLogTickDifference(nBaseTicksGpu, UI.nRangeEndGpu);
|
||||
float fMsEnd = fToMsGpu * MicroProfileLogTickDifference(nBaseTicksGpu, UI.nRangeEndGpu);
|
||||
float fXStart = fMsStart * fMsToScreen;
|
||||
float fXEnd = fMsEnd4 * fMsToScreen;
|
||||
float fXEnd = fMsEnd * fMsToScreen;
|
||||
MicroProfileDrawBox(fXStart, nBaseY, fXEnd, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT_GPU, MicroProfileBoxTypeFlat);
|
||||
MicroProfileDrawLineVertical(fXStart, nBaseY, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT_GPU | 0x44000000);
|
||||
MicroProfileDrawLineVertical(fXEnd, nBaseY, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT_GPU | 0x44000000);
|
||||
@@ -1306,14 +1307,14 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
nBaseY += MICROPROFILE_TEXT_HEIGHT+1;
|
||||
|
||||
fMsStart += fDetailedOffset;
|
||||
fMsEnd4 += fDetailedOffset;
|
||||
fMsEnd += fDetailedOffset;
|
||||
char sBuffer[32];
|
||||
uint32_t nLenStart = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsStart);
|
||||
float fStartTextWidth = (float)((1+MICROPROFILE_TEXT_WIDTH) * nLenStart);
|
||||
float fStartTextX = fXStart - fStartTextWidth - 2;
|
||||
MicroProfileDrawBox(fStartTextX, nBaseY, fStartTextX + fStartTextWidth + 2, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
|
||||
MicroProfileDrawText(fStartTextX+1, nBaseY, UINT32_MAX, sBuffer, nLenStart);
|
||||
uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd4);
|
||||
uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd);
|
||||
MicroProfileDrawBox(fXEnd+1, nBaseY, fXEnd+1+(1+MICROPROFILE_TEXT_WIDTH) * nLenEnd + 3, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
|
||||
MicroProfileDrawText(fXEnd+2, nBaseY+1, UINT32_MAX, sBuffer, nLenEnd);
|
||||
}
|
||||
@@ -1715,8 +1716,8 @@ bool MicroProfileDrawGraph(uint32_t nScreenWidth, uint32_t nScreenHeight)
|
||||
uint32_t nTextCount = 0;
|
||||
uint32_t nGraphIndex = (S.nGraphPut + MICROPROFILE_GRAPH_HISTORY - int(MICROPROFILE_GRAPH_HISTORY*(1.f - fMouseXPrc))) % MICROPROFILE_GRAPH_HISTORY;
|
||||
|
||||
uint32_t nMouseX = UI.nMouseX;
|
||||
uint32_t nMouseY = UI.nMouseY + 20;
|
||||
uint32_t nX = UI.nMouseX;
|
||||
uint32_t nY = UI.nMouseY + 20;
|
||||
|
||||
for(uint32_t i = 0; i < MICROPROFILE_MAX_GRAPHS; ++i)
|
||||
{
|
||||
@@ -1735,7 +1736,7 @@ bool MicroProfileDrawGraph(uint32_t nScreenWidth, uint32_t nScreenHeight)
|
||||
}
|
||||
if(nTextCount)
|
||||
{
|
||||
MicroProfileDrawFloatWindow(nMouseX, nMouseY, Strings.ppStrings, Strings.nNumStrings, 0, pColors);
|
||||
MicroProfileDrawFloatWindow(nX, nY, Strings.ppStrings, Strings.nNumStrings, 0, pColors);
|
||||
}
|
||||
|
||||
if(UI.nMouseRight)
|
||||
@@ -2320,8 +2321,8 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
|
||||
uint32_t nMenuX[MICROPROFILE_MENU_MAX] = {0};
|
||||
uint32_t nNumMenuItems = 0;
|
||||
|
||||
int nMPTextLen = snprintf(buffer, 127, "MicroProfile");
|
||||
MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nMPTextLen);
|
||||
int nLen = snprintf(buffer, 127, "MicroProfile");
|
||||
MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nLen);
|
||||
nX += (sizeof("MicroProfile")+2) * (MICROPROFILE_TEXT_WIDTH+1);
|
||||
pMenuText[nNumMenuItems++] = "Mode";
|
||||
pMenuText[nNumMenuItems++] = "Groups";
|
||||
@@ -2437,16 +2438,16 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
|
||||
int nNumLines = 0;
|
||||
bool bSelected = false;
|
||||
const char* pString = CB(nNumLines, &bSelected);
|
||||
uint32_t nTextWidth = 0, nTextHeight = 0;
|
||||
uint32_t nWidth = 0, nHeight = 0;
|
||||
while(pString)
|
||||
{
|
||||
nTextWidth = MicroProfileMax<int>(nTextWidth, (int)strlen(pString));
|
||||
nWidth = MicroProfileMax<int>(nWidth, (int)strlen(pString));
|
||||
nNumLines++;
|
||||
pString = CB(nNumLines, &bSelected);
|
||||
}
|
||||
nTextWidth = (2+nTextWidth) * (MICROPROFILE_TEXT_WIDTH+1);
|
||||
nTextHeight = nNumLines * (MICROPROFILE_TEXT_HEIGHT+1);
|
||||
if(UI.nMouseY <= nY + nTextHeight+0 && UI.nMouseY >= nY-0 && UI.nMouseX <= nX + nTextWidth + 0 && UI.nMouseX >= nX - 0)
|
||||
nWidth = (2+nWidth) * (MICROPROFILE_TEXT_WIDTH+1);
|
||||
nHeight = nNumLines * (MICROPROFILE_TEXT_HEIGHT+1);
|
||||
if(UI.nMouseY <= nY + nHeight+0 && UI.nMouseY >= nY-0 && UI.nMouseX <= nX + nWidth + 0 && UI.nMouseX >= nX - 0)
|
||||
{
|
||||
UI.nActiveMenu = nMenu;
|
||||
}
|
||||
@@ -2454,21 +2455,21 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
|
||||
{
|
||||
UI.nActiveMenu = UINT32_MAX;
|
||||
}
|
||||
MicroProfileDrawBox(nX, nY, nX + nTextWidth, nY + nTextHeight, 0xff000000|g_nMicroProfileBackColors[1]);
|
||||
MicroProfileDrawBox(nX, nY, nX + nWidth, nY + nHeight, 0xff000000|g_nMicroProfileBackColors[1]);
|
||||
for(int i = 0; i < nNumLines; ++i)
|
||||
{
|
||||
bool bSelected2 = false;
|
||||
const char* pString2 = CB(i, &bSelected2);
|
||||
bool bSelected = false;
|
||||
const char* pString = CB(i, &bSelected);
|
||||
if(UI.nMouseY >= nY && UI.nMouseY < nY + MICROPROFILE_TEXT_HEIGHT + 1)
|
||||
{
|
||||
if(UI.nMouseLeft || UI.nMouseRight)
|
||||
{
|
||||
CBClick[nMenu](i);
|
||||
}
|
||||
MicroProfileDrawBox(nX, nY, nX + nTextWidth, nY + MICROPROFILE_TEXT_HEIGHT + 1, 0xff888888);
|
||||
MicroProfileDrawBox(nX, nY, nX + nWidth, nY + MICROPROFILE_TEXT_HEIGHT + 1, 0xff888888);
|
||||
}
|
||||
int nTextLen = snprintf(buffer, SBUF_SIZE-1, "%c %s", bSelected2 ? '*' : ' ' ,pString2);
|
||||
MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nTextLen);
|
||||
int nLen = snprintf(buffer, SBUF_SIZE-1, "%c %s", bSelected ? '*' : ' ' ,pString);
|
||||
MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nLen);
|
||||
nY += MICROPROFILE_TEXT_HEIGHT+1;
|
||||
}
|
||||
}
|
||||
@@ -2604,7 +2605,7 @@ inline void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight)
|
||||
for(uint32_t i = 0; i < nCount; ++i)
|
||||
{
|
||||
nOffsetY += (1+MICROPROFILE_TEXT_HEIGHT);
|
||||
nWidth = MicroProfileMin(nMaxWidth, (uint32_t)(nMaxWidth * pMs[i] * fRcpReference));
|
||||
uint32_t nWidth = MicroProfileMin(nMaxWidth, (uint32_t)(nMaxWidth * pMs[i] * fRcpReference));
|
||||
MicroProfileDrawBox(nMaxOffsetX, nOffsetY, nMaxOffsetX+nWidth, nOffsetY+MICROPROFILE_TEXT_HEIGHT, pColors[i]|0xff000000);
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
Submodule externals/sirit updated: aa292d5665...a39596358a
@@ -65,10 +65,6 @@ if (MSVC)
|
||||
/we4305 # 'context': truncation from 'type1' to 'type2'
|
||||
/we4388 # 'expression': signed/unsigned mismatch
|
||||
/we4389 # 'operator': signed/unsigned mismatch
|
||||
/we4456 # Declaration of 'identifier' hides previous local declaration
|
||||
/we4457 # Declaration of 'identifier' hides function parameter
|
||||
/we4458 # Declaration of 'identifier' hides class member
|
||||
/we4459 # Declaration of 'identifier' hides global declaration
|
||||
/we4505 # 'function': unreferenced local function has been removed
|
||||
/we4547 # 'operator': operator before comma has no effect; expected operator with side-effect
|
||||
/we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
|
||||
@@ -96,7 +92,6 @@ else()
|
||||
-Werror=missing-declarations
|
||||
-Werror=missing-field-initializers
|
||||
-Werror=reorder
|
||||
-Werror=shadow
|
||||
-Werror=sign-compare
|
||||
-Werror=switch
|
||||
-Werror=uninitialized
|
||||
|
||||
@@ -49,6 +49,9 @@ if (NOT MSVC)
|
||||
target_compile_options(audio_core PRIVATE
|
||||
-Werror=conversion
|
||||
-Werror=ignored-qualifiers
|
||||
-Werror=shadow
|
||||
-Werror=unused-parameter
|
||||
-Werror=unused-variable
|
||||
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
|
||||
|
||||
@@ -98,13 +98,13 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing_, Core::Memor
|
||||
|
||||
AudioRenderer::~AudioRenderer() = default;
|
||||
|
||||
Result AudioRenderer::Start() {
|
||||
ResultCode AudioRenderer::Start() {
|
||||
audio_out->StartStream(stream);
|
||||
ReleaseAndQueueBuffers();
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result AudioRenderer::Stop() {
|
||||
ResultCode AudioRenderer::Stop() {
|
||||
audio_out->StopStream(stream);
|
||||
return ResultSuccess;
|
||||
}
|
||||
@@ -125,8 +125,8 @@ Stream::State AudioRenderer::GetStreamState() const {
|
||||
return stream->GetState();
|
||||
}
|
||||
|
||||
Result AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params,
|
||||
std::vector<u8>& output_params) {
|
||||
ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params,
|
||||
std::vector<u8>& output_params) {
|
||||
std::scoped_lock lock{mutex};
|
||||
InfoUpdater info_updater{input_params, output_params, behavior_info};
|
||||
|
||||
|
||||
@@ -43,10 +43,10 @@ public:
|
||||
Stream::ReleaseCallback&& release_callback, std::size_t instance_number);
|
||||
~AudioRenderer();
|
||||
|
||||
[[nodiscard]] Result UpdateAudioRenderer(const std::vector<u8>& input_params,
|
||||
std::vector<u8>& output_params);
|
||||
[[nodiscard]] Result Start();
|
||||
[[nodiscard]] Result Stop();
|
||||
[[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
|
||||
std::vector<u8>& output_params);
|
||||
[[nodiscard]] ResultCode Start();
|
||||
[[nodiscard]] ResultCode Stop();
|
||||
void QueueMixedBuffer(Buffer::Tag tag);
|
||||
void ReleaseAndQueueBuffers();
|
||||
[[nodiscard]] u32 GetSampleRate() const;
|
||||
|
||||
@@ -129,17 +129,17 @@ s32 ToS32(float sample) {
|
||||
return static_cast<s32>(rescaled_sample);
|
||||
}
|
||||
|
||||
constexpr std::array<u8, 20> REVERB_TAP_INDEX_1CH{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_1CH{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
constexpr std::array<u8, 20> REVERB_TAP_INDEX_2CH{0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
|
||||
1, 1, 1, 0, 0, 0, 0, 1, 1, 1};
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_2CH{0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
|
||||
1, 1, 1, 0, 0, 0, 0, 1, 1, 1};
|
||||
|
||||
constexpr std::array<u8, 20> REVERB_TAP_INDEX_4CH{0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
|
||||
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_4CH{0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
|
||||
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
||||
|
||||
constexpr std::array<u8, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 2, 2, 2,
|
||||
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 2, 2, 2,
|
||||
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
||||
|
||||
template <std::size_t CHANNEL_COUNT>
|
||||
void ApplyReverbGeneric(
|
||||
@@ -429,7 +429,7 @@ void CommandGenerator::GenerateDataSourceCommand(ServerVoiceInfo& voice_info, Vo
|
||||
in_params.node_id);
|
||||
break;
|
||||
default:
|
||||
ASSERT_MSG(false, "Unimplemented sample format={}", in_params.sample_format);
|
||||
UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1312,7 +1312,7 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, std::s
|
||||
samples_to_read - samples_read, channel, temp_mix_offset);
|
||||
break;
|
||||
default:
|
||||
ASSERT_MSG(false, "Unimplemented sample format={}", in_params.sample_format);
|
||||
UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format);
|
||||
}
|
||||
|
||||
temp_mix_offset += samples_decoded;
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
|
||||
namespace AudioCommon {
|
||||
namespace Audren {
|
||||
constexpr Result ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41};
|
||||
constexpr Result ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43};
|
||||
constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41};
|
||||
constexpr ResultCode ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43};
|
||||
} // namespace Audren
|
||||
|
||||
constexpr u8 BASE_REVISION = '0';
|
||||
|
||||
@@ -50,7 +50,7 @@ EffectBase* EffectContext::RetargetEffect(std::size_t i, EffectType effect) {
|
||||
effects[i] = std::make_unique<EffectBiquadFilter>();
|
||||
break;
|
||||
default:
|
||||
ASSERT_MSG(false, "Unimplemented effect {}", effect);
|
||||
UNREACHABLE_MSG("Unimplemented effect {}", effect);
|
||||
effects[i] = std::make_unique<EffectStubbed>();
|
||||
}
|
||||
return GetInfo(i);
|
||||
@@ -104,7 +104,7 @@ void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
|
||||
auto& params = GetParams();
|
||||
const auto* reverb_params = reinterpret_cast<I3dl2ReverbParams*>(in_params.raw.data());
|
||||
if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
|
||||
ASSERT_MSG(false, "Invalid reverb max channel count {}", reverb_params->max_channels);
|
||||
UNREACHABLE_MSG("Invalid reverb max channel count {}", reverb_params->max_channels);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -285,8 +285,9 @@ bool InfoUpdater::UpdateSplitterInfo(SplitterContext& splitter_context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Result InfoUpdater::UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count,
|
||||
SplitterContext& splitter_context, EffectContext& effect_context) {
|
||||
ResultCode InfoUpdater::UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count,
|
||||
SplitterContext& splitter_context,
|
||||
EffectContext& effect_context) {
|
||||
std::vector<MixInfo::InParams> mix_in_params;
|
||||
|
||||
if (!behavior_info.IsMixInParameterDirtyOnlyUpdateSupported()) {
|
||||
|
||||
@@ -32,8 +32,8 @@ public:
|
||||
VAddr audio_codec_dsp_addr);
|
||||
bool UpdateEffects(EffectContext& effect_context, bool is_active);
|
||||
bool UpdateSplitterInfo(SplitterContext& splitter_context);
|
||||
Result UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count,
|
||||
SplitterContext& splitter_context, EffectContext& effect_context);
|
||||
ResultCode UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count,
|
||||
SplitterContext& splitter_context, EffectContext& effect_context);
|
||||
bool UpdateSinks(SinkContext& sink_context);
|
||||
bool UpdatePerformanceBuffer();
|
||||
bool UpdateErrorInfo(BehaviorInfo& in_behavior_info);
|
||||
|
||||
@@ -483,7 +483,7 @@ bool NodeStates::DepthFirstSearch(EdgeMatrix& edge_matrix) {
|
||||
// Add more work
|
||||
index_stack.push(j);
|
||||
} else if (node_state == NodeStates::State::InFound) {
|
||||
ASSERT_MSG(false, "Node start marked as found");
|
||||
UNREACHABLE_MSG("Node start marked as found");
|
||||
ResetState();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ void ServerVoiceInfo::UpdateParameters(const VoiceInfo::InParams& voice_in,
|
||||
in_params.current_playstate = ServerPlayState::Play;
|
||||
break;
|
||||
default:
|
||||
ASSERT_MSG(false, "Unknown playstate {}", voice_in.play_state);
|
||||
UNREACHABLE_MSG("Unknown playstate {}", voice_in.play_state);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -410,7 +410,7 @@ bool ServerVoiceInfo::UpdateParametersForCommandGeneration(
|
||||
return in_params.should_depop;
|
||||
}
|
||||
default:
|
||||
ASSERT_MSG(false, "Invalid playstate {}", in_params.current_playstate);
|
||||
UNREACHABLE_MSG("Invalid playstate {}", in_params.current_playstate);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -58,7 +58,6 @@ add_library(common STATIC
|
||||
div_ceil.h
|
||||
dynamic_library.cpp
|
||||
dynamic_library.h
|
||||
elf.h
|
||||
error.cpp
|
||||
error.h
|
||||
expected.h
|
||||
|
||||
@@ -6,13 +6,8 @@
|
||||
|
||||
#include "common/settings.h"
|
||||
|
||||
void assert_fail_impl() {
|
||||
void assert_handle_failure() {
|
||||
if (Settings::values.use_debug_asserts) {
|
||||
Crash();
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]] void unreachable_impl() {
|
||||
Crash();
|
||||
throw std::runtime_error("Unreachable code");
|
||||
}
|
||||
|
||||
+31
-30
@@ -1,6 +1,6 @@
|
||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -9,43 +9,44 @@
|
||||
// Sometimes we want to try to continue even after hitting an assert.
|
||||
// However touching this file yields a global recompilation as this header is included almost
|
||||
// everywhere. So let's just move the handling of the failed assert to a single cpp file.
|
||||
void assert_handle_failure();
|
||||
|
||||
void assert_fail_impl();
|
||||
[[noreturn]] void unreachable_impl();
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define YUZU_NO_INLINE __declspec(noinline)
|
||||
#else
|
||||
#define YUZU_NO_INLINE __attribute__((noinline))
|
||||
// For asserts we'd like to keep all the junk executed when an assert happens away from the
|
||||
// important code in the function. One way of doing this is to put all the relevant code inside a
|
||||
// lambda and force the compiler to not inline it. Unfortunately, MSVC seems to have no syntax to
|
||||
// specify __declspec on lambda functions, so what we do instead is define a noinline wrapper
|
||||
// template that calls the lambda. This seems to generate an extra instruction at the call-site
|
||||
// compared to the ideal implementation (which wouldn't support ASSERT_MSG parameters), but is good
|
||||
// enough for our purposes.
|
||||
template <typename Fn>
|
||||
#if defined(_MSC_VER)
|
||||
[[msvc::noinline]]
|
||||
#elif defined(__GNUC__)
|
||||
[[gnu::cold, gnu::noinline]]
|
||||
#endif
|
||||
static void
|
||||
assert_noinline_call(const Fn& fn) {
|
||||
fn();
|
||||
assert_handle_failure();
|
||||
}
|
||||
|
||||
#define ASSERT(_a_) \
|
||||
([&]() YUZU_NO_INLINE { \
|
||||
if (!(_a_)) [[unlikely]] { \
|
||||
LOG_CRITICAL(Debug, "Assertion Failed!"); \
|
||||
assert_fail_impl(); \
|
||||
do \
|
||||
if (!(_a_)) { \
|
||||
assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \
|
||||
} \
|
||||
}())
|
||||
while (0)
|
||||
|
||||
#define ASSERT_MSG(_a_, ...) \
|
||||
([&]() YUZU_NO_INLINE { \
|
||||
if (!(_a_)) [[unlikely]] { \
|
||||
LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); \
|
||||
assert_fail_impl(); \
|
||||
do \
|
||||
if (!(_a_)) { \
|
||||
assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
|
||||
} \
|
||||
}())
|
||||
|
||||
#define UNREACHABLE() \
|
||||
do { \
|
||||
LOG_CRITICAL(Debug, "Unreachable code!"); \
|
||||
unreachable_impl(); \
|
||||
} while (0)
|
||||
while (0)
|
||||
|
||||
#define UNREACHABLE() assert_noinline_call([] { LOG_CRITICAL(Debug, "Unreachable code!"); })
|
||||
#define UNREACHABLE_MSG(...) \
|
||||
do { \
|
||||
LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); \
|
||||
unreachable_impl(); \
|
||||
} while (0)
|
||||
assert_noinline_call([&] { LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); })
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define DEBUG_ASSERT(_a_) ASSERT(_a_)
|
||||
|
||||
+29
-3
@@ -1,6 +1,32 @@
|
||||
// SPDX-FileCopyrightText: 2014 Tony Wasserka
|
||||
// SPDX-FileCopyrightText: 2014 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: BSD-3-Clause AND GPL-2.0-or-later
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <stop_token>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace Common {
|
||||
|
||||
#if defined(__cpp_lib_hardware_interference_size)
|
||||
constexpr size_t hardware_interference_size = std::hardware_destructive_interference_size;
|
||||
#else
|
||||
constexpr size_t hardware_interference_size = 64;
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4324)
|
||||
#endif
|
||||
|
||||
template <typename T, size_t capacity = 0x400>
|
||||
class MPSCQueue {
|
||||
public:
|
||||
explicit MPSCQueue() : allocator{std::allocator<Slot<T>>()} {
|
||||
// Allocate one extra slot to prevent false sharing on the last slot
|
||||
slots = allocator.allocate(capacity + 1);
|
||||
// Allocators are not required to honor alignment for over-aligned types
|
||||
// (see http://eel.is/c++draft/allocator.requirements#10) so we verify
|
||||
// alignment here
|
||||
if (reinterpret_cast<uintptr_t>(slots) % alignof(Slot<T>) != 0) {
|
||||
allocator.deallocate(slots, capacity + 1);
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
for (size_t i = 0; i < capacity; ++i) {
|
||||
std::construct_at(&slots[i]);
|
||||
}
|
||||
static_assert(std::has_single_bit(capacity), "capacity must be an integer power of 2");
|
||||
static_assert(alignof(Slot<T>) == hardware_interference_size,
|
||||
"Slot must be aligned to cache line boundary to prevent false sharing");
|
||||
static_assert(sizeof(Slot<T>) % hardware_interference_size == 0,
|
||||
"Slot size must be a multiple of cache line size to prevent "
|
||||
"false sharing between adjacent slots");
|
||||
static_assert(sizeof(MPSCQueue) % hardware_interference_size == 0,
|
||||
"Queue size must be a multiple of cache line size to "
|
||||
"prevent false sharing between adjacent queues");
|
||||
}
|
||||
|
||||
~MPSCQueue() noexcept {
|
||||
for (size_t i = 0; i < capacity; ++i) {
|
||||
std::destroy_at(&slots[i]);
|
||||
}
|
||||
allocator.deallocate(slots, capacity + 1);
|
||||
}
|
||||
|
||||
// The queue must be both non-copyable and non-movable
|
||||
MPSCQueue(const MPSCQueue&) = delete;
|
||||
MPSCQueue& operator=(const MPSCQueue&) = delete;
|
||||
|
||||
MPSCQueue(MPSCQueue&&) = delete;
|
||||
MPSCQueue& operator=(MPSCQueue&&) = delete;
|
||||
|
||||
void Push(const T& v) noexcept {
|
||||
static_assert(std::is_nothrow_copy_constructible_v<T>,
|
||||
"T must be nothrow copy constructible");
|
||||
emplace(v);
|
||||
}
|
||||
|
||||
template <typename P, typename = std::enable_if_t<std::is_nothrow_constructible_v<T, P&&>>>
|
||||
void Push(P&& v) noexcept {
|
||||
emplace(std::forward<P>(v));
|
||||
}
|
||||
|
||||
void Pop(T& v, std::stop_token stop) noexcept {
|
||||
auto const tail = tail_.fetch_add(1);
|
||||
auto& slot = slots[idx(tail)];
|
||||
if (!slot.turn.test()) {
|
||||
std::unique_lock lock{cv_mutex};
|
||||
cv.wait(lock, stop, [&slot] { return slot.turn.test(); });
|
||||
}
|
||||
v = slot.move();
|
||||
slot.destroy();
|
||||
slot.turn.clear();
|
||||
slot.turn.notify_one();
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename U = T>
|
||||
struct Slot {
|
||||
~Slot() noexcept {
|
||||
if (turn.test()) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void construct(Args&&... args) noexcept {
|
||||
static_assert(std::is_nothrow_constructible_v<U, Args&&...>,
|
||||
"T must be nothrow constructible with Args&&...");
|
||||
std::construct_at(reinterpret_cast<U*>(&storage), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void destroy() noexcept {
|
||||
static_assert(std::is_nothrow_destructible_v<U>, "T must be nothrow destructible");
|
||||
std::destroy_at(reinterpret_cast<U*>(&storage));
|
||||
}
|
||||
|
||||
U&& move() noexcept {
|
||||
return reinterpret_cast<U&&>(storage);
|
||||
}
|
||||
|
||||
// Align to avoid false sharing between adjacent slots
|
||||
alignas(hardware_interference_size) std::atomic_flag turn{};
|
||||
struct aligned_store {
|
||||
struct type {
|
||||
alignas(U) unsigned char data[sizeof(U)];
|
||||
};
|
||||
};
|
||||
typename aligned_store::type storage;
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
void emplace(Args&&... args) noexcept {
|
||||
static_assert(std::is_nothrow_constructible_v<T, Args&&...>,
|
||||
"T must be nothrow constructible with Args&&...");
|
||||
auto const head = head_.fetch_add(1);
|
||||
auto& slot = slots[idx(head)];
|
||||
slot.turn.wait(true);
|
||||
slot.construct(std::forward<Args>(args)...);
|
||||
slot.turn.test_and_set();
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
constexpr size_t idx(size_t i) const noexcept {
|
||||
return i & mask;
|
||||
}
|
||||
|
||||
static constexpr size_t mask = capacity - 1;
|
||||
|
||||
// Align to avoid false sharing between head_ and tail_
|
||||
alignas(hardware_interference_size) std::atomic<size_t> head_{0};
|
||||
alignas(hardware_interference_size) std::atomic<size_t> tail_{0};
|
||||
|
||||
std::mutex cv_mutex;
|
||||
std::condition_variable_any cv;
|
||||
|
||||
Slot<T>* slots;
|
||||
[[no_unique_address]] std::allocator<Slot<T>> allocator;
|
||||
|
||||
static_assert(std::is_nothrow_copy_assignable_v<T> || std::is_nothrow_move_assignable_v<T>,
|
||||
"T must be nothrow copy or move assignable");
|
||||
|
||||
static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible");
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
} // namespace Common
|
||||
+20
-5
@@ -1,8 +1,23 @@
|
||||
// SPDX-FileCopyrightText: 2011 Google, Inc.
|
||||
// SPDX-FileContributor: Geoff Pike
|
||||
// SPDX-FileContributor: Jyrki Alakuijala
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Copyright (c) 2011 Google, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
// CityHash, by Geoff Pike and Jyrki Alakuijala
|
||||
//
|
||||
// This file provides CityHash64() and related functions.
|
||||
|
||||
+20
-5
@@ -1,8 +1,23 @@
|
||||
// SPDX-FileCopyrightText: 2011 Google, Inc.
|
||||
// SPDX-FileContributor: Geoff Pike
|
||||
// SPDX-FileContributor: Jyrki Alakuijala
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Copyright (c) 2011 Google, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
// CityHash, by Geoff Pike and Jyrki Alakuijala
|
||||
//
|
||||
// http://code.google.com/p/cityhash/
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// SPDX-FileCopyrightText: 2012 Gekko Emulator
|
||||
// SPDX-FileContributor: ShizZy <shizzy247@gmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
/**
|
||||
* Copyright (C) 2005-2012 Gekko Emulator
|
||||
*
|
||||
|
||||
@@ -33,9 +33,9 @@ void DetachedTasks::AddTask(std::function<void()> task) {
|
||||
++instance->count;
|
||||
std::thread([task{std::move(task)}]() {
|
||||
task();
|
||||
std::unique_lock thread_lock{instance->mutex};
|
||||
std::unique_lock lock{instance->mutex};
|
||||
--instance->count;
|
||||
std::notify_all_at_thread_exit(instance->cv, std::move(thread_lock));
|
||||
std::notify_all_at_thread_exit(instance->cv, std::move(lock));
|
||||
}).detach();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// SPDX-FileCopyrightText: 2019 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright 2019 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// SPDX-FileCopyrightText: 2019 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright 2019 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -1,333 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
|
||||
#include "common_types.h"
|
||||
|
||||
namespace Common {
|
||||
namespace ELF {
|
||||
|
||||
/* Type for a 16-bit quantity. */
|
||||
using Elf32_Half = u16;
|
||||
using Elf64_Half = u16;
|
||||
|
||||
/* Types for signed and unsigned 32-bit quantities. */
|
||||
using Elf32_Word = u32;
|
||||
using Elf32_Sword = s32;
|
||||
using Elf64_Word = u32;
|
||||
using Elf64_Sword = s32;
|
||||
|
||||
/* Types for signed and unsigned 64-bit quantities. */
|
||||
using Elf32_Xword = u64;
|
||||
using Elf32_Sxword = s64;
|
||||
using Elf64_Xword = u64;
|
||||
using Elf64_Sxword = s64;
|
||||
|
||||
/* Type of addresses. */
|
||||
using Elf32_Addr = u32;
|
||||
using Elf64_Addr = u64;
|
||||
|
||||
/* Type of file offsets. */
|
||||
using Elf32_Off = u32;
|
||||
using Elf64_Off = u64;
|
||||
|
||||
/* Type for section indices, which are 16-bit quantities. */
|
||||
using Elf32_Section = u16;
|
||||
using Elf64_Section = u16;
|
||||
|
||||
/* Type for version symbol information. */
|
||||
using Elf32_Versym = Elf32_Half;
|
||||
using Elf64_Versym = Elf64_Half;
|
||||
|
||||
constexpr size_t ElfIdentSize = 16;
|
||||
|
||||
/* The ELF file header. This appears at the start of every ELF file. */
|
||||
|
||||
struct Elf32_Ehdr {
|
||||
std::array<u8, ElfIdentSize> e_ident; /* Magic number and other info */
|
||||
Elf32_Half e_type; /* Object file type */
|
||||
Elf32_Half e_machine; /* Architecture */
|
||||
Elf32_Word e_version; /* Object file version */
|
||||
Elf32_Addr e_entry; /* Entry point virtual address */
|
||||
Elf32_Off e_phoff; /* Program header table file offset */
|
||||
Elf32_Off e_shoff; /* Section header table file offset */
|
||||
Elf32_Word e_flags; /* Processor-specific flags */
|
||||
Elf32_Half e_ehsize; /* ELF header size in bytes */
|
||||
Elf32_Half e_phentsize; /* Program header table entry size */
|
||||
Elf32_Half e_phnum; /* Program header table entry count */
|
||||
Elf32_Half e_shentsize; /* Section header table entry size */
|
||||
Elf32_Half e_shnum; /* Section header table entry count */
|
||||
Elf32_Half e_shstrndx; /* Section header string table index */
|
||||
};
|
||||
|
||||
struct Elf64_Ehdr {
|
||||
std::array<u8, ElfIdentSize> e_ident; /* Magic number and other info */
|
||||
Elf64_Half e_type; /* Object file type */
|
||||
Elf64_Half e_machine; /* Architecture */
|
||||
Elf64_Word e_version; /* Object file version */
|
||||
Elf64_Addr e_entry; /* Entry point virtual address */
|
||||
Elf64_Off e_phoff; /* Program header table file offset */
|
||||
Elf64_Off e_shoff; /* Section header table file offset */
|
||||
Elf64_Word e_flags; /* Processor-specific flags */
|
||||
Elf64_Half e_ehsize; /* ELF header size in bytes */
|
||||
Elf64_Half e_phentsize; /* Program header table entry size */
|
||||
Elf64_Half e_phnum; /* Program header table entry count */
|
||||
Elf64_Half e_shentsize; /* Section header table entry size */
|
||||
Elf64_Half e_shnum; /* Section header table entry count */
|
||||
Elf64_Half e_shstrndx; /* Section header string table index */
|
||||
};
|
||||
|
||||
constexpr u8 ElfClass32 = 1; /* 32-bit objects */
|
||||
constexpr u8 ElfClass64 = 2; /* 64-bit objects */
|
||||
constexpr u8 ElfData2Lsb = 1; /* 2's complement, little endian */
|
||||
constexpr u8 ElfVersionCurrent = 1; /* EV_CURRENT */
|
||||
constexpr u8 ElfOsAbiNone = 0; /* System V ABI */
|
||||
|
||||
constexpr u16 ElfTypeNone = 0; /* No file type */
|
||||
constexpr u16 ElfTypeRel = 0; /* Relocatable file */
|
||||
constexpr u16 ElfTypeExec = 0; /* Executable file */
|
||||
constexpr u16 ElfTypeDyn = 0; /* Shared object file */
|
||||
|
||||
constexpr u16 ElfMachineArm = 40; /* ARM */
|
||||
constexpr u16 ElfMachineAArch64 = 183; /* ARM AARCH64 */
|
||||
|
||||
constexpr std::array<u8, ElfIdentSize> Elf32Ident{
|
||||
0x7f, 'E', 'L', 'F', ElfClass32, ElfData2Lsb, ElfVersionCurrent, ElfOsAbiNone};
|
||||
|
||||
constexpr std::array<u8, ElfIdentSize> Elf64Ident{
|
||||
0x7f, 'E', 'L', 'F', ElfClass64, ElfData2Lsb, ElfVersionCurrent, ElfOsAbiNone};
|
||||
|
||||
/* Section header. */
|
||||
|
||||
struct Elf32_Shdr {
|
||||
Elf32_Word sh_name; /* Section name (string tbl index) */
|
||||
Elf32_Word sh_type; /* Section type */
|
||||
Elf32_Word sh_flags; /* Section flags */
|
||||
Elf32_Addr sh_addr; /* Section virtual addr at execution */
|
||||
Elf32_Off sh_offset; /* Section file offset */
|
||||
Elf32_Word sh_size; /* Section size in bytes */
|
||||
Elf32_Word sh_link; /* Link to another section */
|
||||
Elf32_Word sh_info; /* Additional section information */
|
||||
Elf32_Word sh_addralign; /* Section alignment */
|
||||
Elf32_Word sh_entsize; /* Entry size if section holds table */
|
||||
};
|
||||
|
||||
struct Elf64_Shdr {
|
||||
Elf64_Word sh_name; /* Section name (string tbl index) */
|
||||
Elf64_Word sh_type; /* Section type */
|
||||
Elf64_Xword sh_flags; /* Section flags */
|
||||
Elf64_Addr sh_addr; /* Section virtual addr at execution */
|
||||
Elf64_Off sh_offset; /* Section file offset */
|
||||
Elf64_Xword sh_size; /* Section size in bytes */
|
||||
Elf64_Word sh_link; /* Link to another section */
|
||||
Elf64_Word sh_info; /* Additional section information */
|
||||
Elf64_Xword sh_addralign; /* Section alignment */
|
||||
Elf64_Xword sh_entsize; /* Entry size if section holds table */
|
||||
};
|
||||
|
||||
constexpr u32 ElfShnUndef = 0; /* Undefined section */
|
||||
|
||||
constexpr u32 ElfShtNull = 0; /* Section header table entry unused */
|
||||
constexpr u32 ElfShtProgBits = 1; /* Program data */
|
||||
constexpr u32 ElfShtSymtab = 2; /* Symbol table */
|
||||
constexpr u32 ElfShtStrtab = 3; /* String table */
|
||||
constexpr u32 ElfShtRela = 4; /* Relocation entries with addends */
|
||||
constexpr u32 ElfShtDynamic = 6; /* Dynamic linking information */
|
||||
constexpr u32 ElfShtNobits = 7; /* Program space with no data (bss) */
|
||||
constexpr u32 ElfShtRel = 9; /* Relocation entries, no addends */
|
||||
constexpr u32 ElfShtDynsym = 11; /* Dynamic linker symbol table */
|
||||
|
||||
/* Symbol table entry. */
|
||||
|
||||
struct Elf32_Sym {
|
||||
Elf32_Word st_name; /* Symbol name (string tbl index) */
|
||||
Elf32_Addr st_value; /* Symbol value */
|
||||
Elf32_Word st_size; /* Symbol size */
|
||||
u8 st_info; /* Symbol type and binding */
|
||||
u8 st_other; /* Symbol visibility */
|
||||
Elf32_Section st_shndx; /* Section index */
|
||||
};
|
||||
|
||||
struct Elf64_Sym {
|
||||
Elf64_Word st_name; /* Symbol name (string tbl index) */
|
||||
u8 st_info; /* Symbol type and binding */
|
||||
u8 st_other; /* Symbol visibility */
|
||||
Elf64_Section st_shndx; /* Section index */
|
||||
Elf64_Addr st_value; /* Symbol value */
|
||||
Elf64_Xword st_size; /* Symbol size */
|
||||
};
|
||||
|
||||
/* How to extract and insert information held in the st_info field. */
|
||||
|
||||
static inline u8 ElfStBind(u8 st_info) {
|
||||
return st_info >> 4;
|
||||
}
|
||||
static inline u8 ElfStType(u8 st_info) {
|
||||
return st_info & 0xf;
|
||||
}
|
||||
static inline u8 ElfStInfo(u8 st_bind, u8 st_type) {
|
||||
return static_cast<u8>((st_bind << 4) + (st_type & 0xf));
|
||||
}
|
||||
|
||||
constexpr u8 ElfBindLocal = 0; /* Local symbol */
|
||||
constexpr u8 ElfBindGlobal = 1; /* Global symbol */
|
||||
constexpr u8 ElfBindWeak = 2; /* Weak symbol */
|
||||
|
||||
constexpr u8 ElfTypeUnspec = 0; /* Symbol type is unspecified */
|
||||
constexpr u8 ElfTypeObject = 1; /* Symbol is a data object */
|
||||
constexpr u8 ElfTypeFunc = 2; /* Symbol is a code object */
|
||||
|
||||
static inline u8 ElfStVisibility(u8 st_other) {
|
||||
return static_cast<u8>(st_other & 0x3);
|
||||
}
|
||||
|
||||
constexpr u8 ElfVisibilityDefault = 0; /* Default symbol visibility rules */
|
||||
constexpr u8 ElfVisibilityInternal = 1; /* Processor specific hidden class */
|
||||
constexpr u8 ElfVisibilityHidden = 2; /* Sym unavailable in other modules */
|
||||
constexpr u8 ElfVisibilityProtected = 3; /* Not preemptible, not exported */
|
||||
|
||||
/* Relocation table entry without addend (in section of type ShtRel). */
|
||||
|
||||
struct Elf32_Rel {
|
||||
Elf32_Addr r_offset; /* Address */
|
||||
Elf32_Word r_info; /* Relocation type and symbol index */
|
||||
};
|
||||
|
||||
/* Relocation table entry with addend (in section of type ShtRela). */
|
||||
|
||||
struct Elf32_Rela {
|
||||
Elf32_Addr r_offset; /* Address */
|
||||
Elf32_Word r_info; /* Relocation type and symbol index */
|
||||
Elf32_Sword r_addend; /* Addend */
|
||||
};
|
||||
|
||||
struct Elf64_Rela {
|
||||
Elf64_Addr r_offset; /* Address */
|
||||
Elf64_Xword r_info; /* Relocation type and symbol index */
|
||||
Elf64_Sxword r_addend; /* Addend */
|
||||
};
|
||||
|
||||
/* How to extract and insert information held in the r_info field. */
|
||||
|
||||
static inline u32 Elf32RelSymIndex(Elf32_Word r_info) {
|
||||
return r_info >> 8;
|
||||
}
|
||||
static inline u8 Elf32RelType(Elf32_Word r_info) {
|
||||
return static_cast<u8>(r_info & 0xff);
|
||||
}
|
||||
static inline Elf32_Word Elf32RelInfo(u32 sym_index, u8 type) {
|
||||
return (sym_index << 8) + type;
|
||||
}
|
||||
static inline u32 Elf64RelSymIndex(Elf64_Xword r_info) {
|
||||
return static_cast<u32>(r_info >> 32);
|
||||
}
|
||||
static inline u32 Elf64RelType(Elf64_Xword r_info) {
|
||||
return r_info & 0xffffffff;
|
||||
}
|
||||
static inline Elf64_Xword Elf64RelInfo(u32 sym_index, u32 type) {
|
||||
return (static_cast<Elf64_Xword>(sym_index) << 32) + type;
|
||||
}
|
||||
|
||||
constexpr u32 ElfArmCopy = 20; /* Copy symbol at runtime */
|
||||
constexpr u32 ElfArmGlobDat = 21; /* Create GOT entry */
|
||||
constexpr u32 ElfArmJumpSlot = 22; /* Create PLT entry */
|
||||
constexpr u32 ElfArmRelative = 23; /* Adjust by program base */
|
||||
|
||||
constexpr u32 ElfAArch64Copy = 1024; /* Copy symbol at runtime */
|
||||
constexpr u32 ElfAArch64GlobDat = 1025; /* Create GOT entry */
|
||||
constexpr u32 ElfAArch64JumpSlot = 1026; /* Create PLT entry */
|
||||
constexpr u32 ElfAArch64Relative = 1027; /* Adjust by program base */
|
||||
|
||||
/* Program segment header. */
|
||||
|
||||
struct Elf32_Phdr {
|
||||
Elf32_Word p_type; /* Segment type */
|
||||
Elf32_Off p_offset; /* Segment file offset */
|
||||
Elf32_Addr p_vaddr; /* Segment virtual address */
|
||||
Elf32_Addr p_paddr; /* Segment physical address */
|
||||
Elf32_Word p_filesz; /* Segment size in file */
|
||||
Elf32_Word p_memsz; /* Segment size in memory */
|
||||
Elf32_Word p_flags; /* Segment flags */
|
||||
Elf32_Word p_align; /* Segment alignment */
|
||||
};
|
||||
|
||||
struct Elf64_Phdr {
|
||||
Elf64_Word p_type; /* Segment type */
|
||||
Elf64_Word p_flags; /* Segment flags */
|
||||
Elf64_Off p_offset; /* Segment file offset */
|
||||
Elf64_Addr p_vaddr; /* Segment virtual address */
|
||||
Elf64_Addr p_paddr; /* Segment physical address */
|
||||
Elf64_Xword p_filesz; /* Segment size in file */
|
||||
Elf64_Xword p_memsz; /* Segment size in memory */
|
||||
Elf64_Xword p_align; /* Segment alignment */
|
||||
};
|
||||
|
||||
/* Legal values for p_type (segment type). */
|
||||
|
||||
constexpr u32 ElfPtNull = 0; /* Program header table entry unused */
|
||||
constexpr u32 ElfPtLoad = 1; /* Loadable program segment */
|
||||
constexpr u32 ElfPtDynamic = 2; /* Dynamic linking information */
|
||||
constexpr u32 ElfPtInterp = 3; /* Program interpreter */
|
||||
constexpr u32 ElfPtNote = 4; /* Auxiliary information */
|
||||
constexpr u32 ElfPtPhdr = 6; /* Entry for header table itself */
|
||||
constexpr u32 ElfPtTls = 7; /* Thread-local storage segment */
|
||||
|
||||
/* Legal values for p_flags (segment flags). */
|
||||
|
||||
constexpr u32 ElfPfExec = 0; /* Segment is executable */
|
||||
constexpr u32 ElfPfWrite = 1; /* Segment is writable */
|
||||
constexpr u32 ElfPfRead = 2; /* Segment is readable */
|
||||
|
||||
/* Dynamic section entry. */
|
||||
|
||||
struct Elf32_Dyn {
|
||||
Elf32_Sword d_tag; /* Dynamic entry type */
|
||||
union {
|
||||
Elf32_Word d_val; /* Integer value */
|
||||
Elf32_Addr d_ptr; /* Address value */
|
||||
} d_un;
|
||||
};
|
||||
|
||||
struct Elf64_Dyn {
|
||||
Elf64_Sxword d_tag; /* Dynamic entry type */
|
||||
union {
|
||||
Elf64_Xword d_val; /* Integer value */
|
||||
Elf64_Addr d_ptr; /* Address value */
|
||||
} d_un;
|
||||
};
|
||||
|
||||
/* Legal values for d_tag (dynamic entry type). */
|
||||
|
||||
constexpr u32 ElfDtNull = 0; /* Marks end of dynamic section */
|
||||
constexpr u32 ElfDtNeeded = 1; /* Name of needed library */
|
||||
constexpr u32 ElfDtPltRelSz = 2; /* Size in bytes of PLT relocs */
|
||||
constexpr u32 ElfDtPltGot = 3; /* Processor defined value */
|
||||
constexpr u32 ElfDtHash = 4; /* Address of symbol hash table */
|
||||
constexpr u32 ElfDtStrtab = 5; /* Address of string table */
|
||||
constexpr u32 ElfDtSymtab = 6; /* Address of symbol table */
|
||||
constexpr u32 ElfDtRela = 7; /* Address of Rela relocs */
|
||||
constexpr u32 ElfDtRelasz = 8; /* Total size of Rela relocs */
|
||||
constexpr u32 ElfDtRelaent = 9; /* Size of one Rela reloc */
|
||||
constexpr u32 ElfDtStrsz = 10; /* Size of string table */
|
||||
constexpr u32 ElfDtSyment = 11; /* Size of one symbol table entry */
|
||||
constexpr u32 ElfDtInit = 12; /* Address of init function */
|
||||
constexpr u32 ElfDtFini = 13; /* Address of termination function */
|
||||
constexpr u32 ElfDtRel = 17; /* Address of Rel relocs */
|
||||
constexpr u32 ElfDtRelsz = 18; /* Total size of Rel relocs */
|
||||
constexpr u32 ElfDtRelent = 19; /* Size of one Rel reloc */
|
||||
constexpr u32 ElfDtPltRel = 20; /* Type of reloc in PLT */
|
||||
constexpr u32 ElfDtTextRel = 22; /* Reloc might modify .text */
|
||||
constexpr u32 ElfDtJmpRel = 23; /* Address of PLT relocs */
|
||||
constexpr u32 ElfDtBindNow = 24; /* Process relocations of object */
|
||||
constexpr u32 ElfDtInitArray = 25; /* Array with addresses of init fct */
|
||||
constexpr u32 ElfDtFiniArray = 26; /* Array with addresses of fini fct */
|
||||
constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */
|
||||
constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */
|
||||
constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */
|
||||
|
||||
} // namespace ELF
|
||||
} // namespace Common
|
||||
@@ -232,7 +232,9 @@ void SetYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) {
|
||||
fs::path GetExeDirectory() {
|
||||
wchar_t exe_path[MAX_PATH];
|
||||
|
||||
if (GetModuleFileNameW(nullptr, exe_path, MAX_PATH) == 0) {
|
||||
GetModuleFileNameW(nullptr, exe_path, MAX_PATH);
|
||||
|
||||
if (!exe_path) {
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Failed to get the path to the executable of the current process");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/hex_util.h"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -72,7 +72,6 @@ enum class PollingError {
|
||||
enum class VibrationAmplificationType {
|
||||
Linear,
|
||||
Exponential,
|
||||
Test,
|
||||
};
|
||||
|
||||
// Analog properties for calibration
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -15,9 +15,6 @@ enum class PageType : u8 {
|
||||
Unmapped,
|
||||
/// Page is mapped to regular memory. This is the only type you can get pointers to.
|
||||
Memory,
|
||||
/// Page is mapped to regular memory, but inaccessible from CPU fastmem and must use
|
||||
/// the callbacks.
|
||||
DebugMemory,
|
||||
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
||||
/// invalidation
|
||||
RasterizerCachedMemory,
|
||||
|
||||
@@ -76,7 +76,7 @@ std::string ParamPackage::Serialize() const {
|
||||
std::string ParamPackage::Get(const std::string& key, const std::string& default_value) const {
|
||||
auto pair = data.find(key);
|
||||
if (pair == data.end()) {
|
||||
LOG_TRACE(Common, "key '{}' not found", key);
|
||||
LOG_DEBUG(Common, "key '{}' not found", key);
|
||||
return default_value;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ std::string ParamPackage::Get(const std::string& key, const std::string& default
|
||||
int ParamPackage::Get(const std::string& key, int default_value) const {
|
||||
auto pair = data.find(key);
|
||||
if (pair == data.end()) {
|
||||
LOG_TRACE(Common, "key '{}' not found", key);
|
||||
LOG_DEBUG(Common, "key '{}' not found", key);
|
||||
return default_value;
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ int ParamPackage::Get(const std::string& key, int default_value) const {
|
||||
float ParamPackage::Get(const std::string& key, float default_value) const {
|
||||
auto pair = data.find(key);
|
||||
if (pair == data.end()) {
|
||||
LOG_TRACE(Common, "key {} not found", key);
|
||||
LOG_DEBUG(Common, "key {} not found", key);
|
||||
return default_value;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
namespace Settings {
|
||||
|
||||
Values values;
|
||||
Values values = {};
|
||||
static bool configuring_global = true;
|
||||
|
||||
std::string GetTimeZoneString() {
|
||||
@@ -70,7 +70,6 @@ void LogSettings() {
|
||||
log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir));
|
||||
log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir));
|
||||
log_setting("Debugging_ProgramArgs", values.program_args.GetValue());
|
||||
log_setting("Debugging_GDBStub", values.use_gdbstub.GetValue());
|
||||
log_setting("Input_EnableMotion", values.motion_enabled.GetValue());
|
||||
log_setting("Input_EnableVibration", values.vibration_enabled.GetValue());
|
||||
log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue());
|
||||
@@ -147,7 +146,7 @@ void UpdateRescalingInfo() {
|
||||
info.down_shift = 0;
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
UNREACHABLE();
|
||||
info.up_scale = 1;
|
||||
info.down_shift = 0;
|
||||
}
|
||||
|
||||
@@ -496,7 +496,7 @@ struct Values {
|
||||
|
||||
// Renderer
|
||||
RangedSetting<RendererBackend> renderer_backend{
|
||||
RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
|
||||
RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
|
||||
BasicSetting<bool> renderer_debug{false, "debug"};
|
||||
BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"};
|
||||
BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
|
||||
@@ -601,12 +601,11 @@ struct Values {
|
||||
// Debugging
|
||||
bool record_frame_times;
|
||||
BasicSetting<bool> use_gdbstub{false, "use_gdbstub"};
|
||||
BasicSetting<u16> gdbstub_port{6543, "gdbstub_port"};
|
||||
BasicSetting<u16> gdbstub_port{0, "gdbstub_port"};
|
||||
BasicSetting<std::string> program_args{std::string(), "program_args"};
|
||||
BasicSetting<bool> dump_exefs{false, "dump_exefs"};
|
||||
BasicSetting<bool> dump_nso{false, "dump_nso"};
|
||||
BasicSetting<bool> dump_shaders{false, "dump_shaders"};
|
||||
BasicSetting<bool> dump_macros{false, "dump_macros"};
|
||||
BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"};
|
||||
BasicSetting<bool> reporting_services{false, "reporting_services"};
|
||||
BasicSetting<bool> quest_flag{false, "quest_flag"};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
@@ -178,10 +178,6 @@ std::wstring UTF8ToUTF16W(const std::string& input) {
|
||||
|
||||
#endif
|
||||
|
||||
std::u16string U16StringFromBuffer(const u16* input, std::size_t length) {
|
||||
return std::u16string(reinterpret_cast<const char16_t*>(input), length);
|
||||
}
|
||||
|
||||
std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer, std::size_t max_len) {
|
||||
std::size_t len = 0;
|
||||
while (len < buffer.length() && len < max_len && buffer[len] != '\0') {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -44,8 +44,6 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
|
||||
|
||||
#endif
|
||||
|
||||
[[nodiscard]] std::u16string U16StringFromBuffer(const u16* input, std::size_t length);
|
||||
|
||||
/**
|
||||
* Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string
|
||||
* `other` for equality.
|
||||
|
||||
+13
-3
@@ -1,6 +1,16 @@
|
||||
// SPDX-FileCopyrightText: 2012 PPSSPP Project
|
||||
// SPDX-FileCopyrightText: 2012 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright (c) 2012- PPSSPP Project / Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-FileCopyrightText: 2012 PPSSPP Project
|
||||
// SPDX-FileCopyrightText: 2014 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright 2014 Citra Emulator Project / PPSSPP Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// SPDX-FileCopyrightText: 2010 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright 2010 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
+25
-3
@@ -1,10 +1,32 @@
|
||||
// SPDX-FileCopyrightText: 2002 Niels Provos <provos@citi.umich.edu>
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */
|
||||
/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
|
||||
/*-
|
||||
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,6 +1,32 @@
|
||||
// SPDX-FileCopyrightText: 2014 Tony Wasserka
|
||||
// SPDX-FileCopyrightText: 2014 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: BSD-3-Clause AND GPL-2.0-or-later
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
+6
-11
@@ -36,13 +36,6 @@ add_library(core STATIC
|
||||
crypto/ctr_encryption_layer.h
|
||||
crypto/xts_encryption_layer.cpp
|
||||
crypto/xts_encryption_layer.h
|
||||
debugger/debugger_interface.h
|
||||
debugger/debugger.cpp
|
||||
debugger/debugger.h
|
||||
debugger/gdbstub_arch.cpp
|
||||
debugger/gdbstub_arch.h
|
||||
debugger/gdbstub.cpp
|
||||
debugger/gdbstub.h
|
||||
device_memory.cpp
|
||||
device_memory.h
|
||||
file_sys/bis_factory.cpp
|
||||
@@ -222,7 +215,7 @@ add_library(core STATIC
|
||||
hle/kernel/k_page_buffer.h
|
||||
hle/kernel/k_page_heap.cpp
|
||||
hle/kernel/k_page_heap.h
|
||||
hle/kernel/k_page_group.h
|
||||
hle/kernel/k_page_linked_list.h
|
||||
hle/kernel/k_page_table.cpp
|
||||
hle/kernel/k_page_table.h
|
||||
hle/kernel/k_port.cpp
|
||||
@@ -743,11 +736,16 @@ if (MSVC)
|
||||
/we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
|
||||
/we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch
|
||||
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||
/we4456 # Declaration of 'identifier' hides previous local declaration
|
||||
/we4457 # Declaration of 'identifier' hides function parameter
|
||||
/we4458 # Declaration of 'identifier' hides class member
|
||||
/we4459 # Declaration of 'identifier' hides global declaration
|
||||
)
|
||||
else()
|
||||
target_compile_options(core PRIVATE
|
||||
-Werror=conversion
|
||||
-Werror=ignored-qualifiers
|
||||
-Werror=shadow
|
||||
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
|
||||
@@ -763,9 +761,6 @@ create_target_directory_groups(core)
|
||||
|
||||
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
|
||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus)
|
||||
if (MINGW)
|
||||
target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
|
||||
endif()
|
||||
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
|
||||
|
||||
@@ -9,9 +9,7 @@
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/symbols.h"
|
||||
#include "core/core.h"
|
||||
#include "core/debugger/debugger.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
@@ -90,89 +88,4 @@ void ARM_Interface::LogBacktrace() const {
|
||||
}
|
||||
}
|
||||
|
||||
void ARM_Interface::Run() {
|
||||
using Kernel::StepState;
|
||||
using Kernel::SuspendType;
|
||||
|
||||
while (true) {
|
||||
Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())};
|
||||
Dynarmic::HaltReason hr{};
|
||||
|
||||
// Notify the debugger and go to sleep if a step was performed
|
||||
// and this thread has been scheduled again.
|
||||
if (current_thread->GetStepState() == StepState::StepPerformed) {
|
||||
system.GetDebugger().NotifyThreadStopped(current_thread);
|
||||
current_thread->RequestSuspend(SuspendType::Debug);
|
||||
break;
|
||||
}
|
||||
|
||||
// Otherwise, run the thread.
|
||||
system.EnterDynarmicProfile();
|
||||
if (current_thread->GetStepState() == StepState::StepPending) {
|
||||
hr = StepJit();
|
||||
|
||||
if (Has(hr, step_thread)) {
|
||||
current_thread->SetStepState(StepState::StepPerformed);
|
||||
}
|
||||
} else {
|
||||
hr = RunJit();
|
||||
}
|
||||
system.ExitDynarmicProfile();
|
||||
|
||||
// Notify the debugger and go to sleep if a breakpoint was hit.
|
||||
if (Has(hr, breakpoint)) {
|
||||
RewindBreakpointInstruction();
|
||||
system.GetDebugger().NotifyThreadStopped(current_thread);
|
||||
current_thread->RequestSuspend(SuspendType::Debug);
|
||||
break;
|
||||
}
|
||||
if (Has(hr, watchpoint)) {
|
||||
RewindBreakpointInstruction();
|
||||
system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint());
|
||||
current_thread->RequestSuspend(SuspendType::Debug);
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle syscalls and scheduling (this may change the current thread)
|
||||
if (Has(hr, svc_call)) {
|
||||
Kernel::Svc::Call(system, GetSvcNumber());
|
||||
}
|
||||
if (Has(hr, break_loop) || !uses_wall_clock) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ARM_Interface::LoadWatchpointArray(const WatchpointArray& wp) {
|
||||
watchpoints = ℘
|
||||
}
|
||||
|
||||
const Kernel::DebugWatchpoint* ARM_Interface::MatchingWatchpoint(
|
||||
VAddr addr, u64 size, Kernel::DebugWatchpointType access_type) const {
|
||||
if (!watchpoints) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const VAddr start_address{addr};
|
||||
const VAddr end_address{addr + size};
|
||||
|
||||
for (size_t i = 0; i < Core::Hardware::NUM_WATCHPOINTS; i++) {
|
||||
const auto& watch{(*watchpoints)[i]};
|
||||
|
||||
if (end_address <= watch.start_address) {
|
||||
continue;
|
||||
}
|
||||
if (start_address >= watch.end_address) {
|
||||
continue;
|
||||
}
|
||||
if ((access_type & watch.type) == Kernel::DebugWatchpointType::None) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return &watch;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -5,11 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include <dynarmic/interface/halt_reason.h>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hardware_properties.h"
|
||||
@@ -20,16 +16,13 @@ struct PageTable;
|
||||
|
||||
namespace Kernel {
|
||||
enum class VMAPermission : u8;
|
||||
enum class DebugWatchpointType : u8;
|
||||
struct DebugWatchpoint;
|
||||
} // namespace Kernel
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
class CPUInterruptHandler;
|
||||
|
||||
using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>;
|
||||
using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>;
|
||||
|
||||
/// Generic ARMv8 CPU interface
|
||||
class ARM_Interface {
|
||||
@@ -71,7 +64,10 @@ public:
|
||||
static_assert(sizeof(ThreadContext64) == 0x320);
|
||||
|
||||
/// Runs the CPU until an event happens
|
||||
void Run();
|
||||
virtual void Run() = 0;
|
||||
|
||||
/// Step CPU by one instruction
|
||||
virtual void Step() = 0;
|
||||
|
||||
/// Clear all instruction cache
|
||||
virtual void ClearInstructionCache() = 0;
|
||||
@@ -174,11 +170,13 @@ public:
|
||||
virtual void SaveContext(ThreadContext64& ctx) = 0;
|
||||
virtual void LoadContext(const ThreadContext32& ctx) = 0;
|
||||
virtual void LoadContext(const ThreadContext64& ctx) = 0;
|
||||
void LoadWatchpointArray(const WatchpointArray& wp);
|
||||
|
||||
/// Clears the exclusive monitor's state.
|
||||
virtual void ClearExclusiveState() = 0;
|
||||
|
||||
/// Prepare core for thread reschedule (if needed to correctly handle state)
|
||||
virtual void PrepareReschedule() = 0;
|
||||
|
||||
/// Signal an interrupt and ask the core to halt as soon as possible.
|
||||
virtual void SignalInterrupt() = 0;
|
||||
|
||||
@@ -199,28 +197,13 @@ public:
|
||||
|
||||
void LogBacktrace() const;
|
||||
|
||||
static constexpr Dynarmic::HaltReason step_thread = Dynarmic::HaltReason::Step;
|
||||
static constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
|
||||
static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
|
||||
static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
|
||||
static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::UserDefined5;
|
||||
|
||||
protected:
|
||||
/// System context that this ARM interface is running under.
|
||||
System& system;
|
||||
CPUInterrupts& interrupt_handlers;
|
||||
const WatchpointArray* watchpoints;
|
||||
bool uses_wall_clock;
|
||||
|
||||
static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out);
|
||||
const Kernel::DebugWatchpoint* MatchingWatchpoint(
|
||||
VAddr addr, u64 size, Kernel::DebugWatchpointType access_type) const;
|
||||
|
||||
virtual Dynarmic::HaltReason RunJit() = 0;
|
||||
virtual Dynarmic::HaltReason StepJit() = 0;
|
||||
virtual u32 GetSvcNumber() const = 0;
|
||||
virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0;
|
||||
virtual void RewindBreakpointInstruction() = 0;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/debugger/debugger.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
@@ -26,65 +24,51 @@ namespace Core {
|
||||
|
||||
using namespace Common::Literals;
|
||||
|
||||
constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
|
||||
constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
|
||||
|
||||
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
|
||||
public:
|
||||
explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
|
||||
: parent{parent_},
|
||||
memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {}
|
||||
: parent{parent_}, memory(parent.system.Memory()) {}
|
||||
|
||||
u8 MemoryRead8(u32 vaddr) override {
|
||||
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
|
||||
return memory.Read8(vaddr);
|
||||
}
|
||||
u16 MemoryRead16(u32 vaddr) override {
|
||||
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
|
||||
return memory.Read16(vaddr);
|
||||
}
|
||||
u32 MemoryRead32(u32 vaddr) override {
|
||||
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
|
||||
return memory.Read32(vaddr);
|
||||
}
|
||||
u64 MemoryRead64(u32 vaddr) override {
|
||||
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
|
||||
return memory.Read64(vaddr);
|
||||
}
|
||||
|
||||
void MemoryWrite8(u32 vaddr, u8 value) override {
|
||||
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
|
||||
memory.Write8(vaddr, value);
|
||||
}
|
||||
memory.Write8(vaddr, value);
|
||||
}
|
||||
void MemoryWrite16(u32 vaddr, u16 value) override {
|
||||
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
|
||||
memory.Write16(vaddr, value);
|
||||
}
|
||||
memory.Write16(vaddr, value);
|
||||
}
|
||||
void MemoryWrite32(u32 vaddr, u32 value) override {
|
||||
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
|
||||
memory.Write32(vaddr, value);
|
||||
}
|
||||
memory.Write32(vaddr, value);
|
||||
}
|
||||
void MemoryWrite64(u32 vaddr, u64 value) override {
|
||||
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
|
||||
memory.Write64(vaddr, value);
|
||||
}
|
||||
memory.Write64(vaddr, value);
|
||||
}
|
||||
|
||||
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override {
|
||||
return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
|
||||
memory.WriteExclusive8(vaddr, value, expected);
|
||||
return memory.WriteExclusive8(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override {
|
||||
return CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write) &&
|
||||
memory.WriteExclusive16(vaddr, value, expected);
|
||||
return memory.WriteExclusive16(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override {
|
||||
return CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write) &&
|
||||
memory.WriteExclusive32(vaddr, value, expected);
|
||||
return memory.WriteExclusive32(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override {
|
||||
return CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write) &&
|
||||
memory.WriteExclusive64(vaddr, value, expected);
|
||||
return memory.WriteExclusive64(vaddr, value, expected);
|
||||
}
|
||||
|
||||
void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
|
||||
@@ -94,21 +78,16 @@ public:
|
||||
}
|
||||
|
||||
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
|
||||
if (debugger_enabled) {
|
||||
parent.SaveContext(parent.breakpoint_context);
|
||||
parent.jit.load()->HaltExecution(ARM_Interface::breakpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
parent.LogBacktrace();
|
||||
LOG_CRITICAL(Core_ARM,
|
||||
"ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
|
||||
exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void CallSVC(u32 swi) override {
|
||||
parent.svc_swi = swi;
|
||||
parent.jit.load()->HaltExecution(ARM_Interface::svc_call);
|
||||
parent.jit.load()->HaltExecution(svc_call);
|
||||
}
|
||||
|
||||
void AddTicks(u64 ticks) override {
|
||||
@@ -134,26 +113,9 @@ public:
|
||||
return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
|
||||
}
|
||||
|
||||
bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) {
|
||||
if (!debugger_enabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto match{parent.MatchingWatchpoint(addr, size, type)};
|
||||
if (match) {
|
||||
parent.SaveContext(parent.breakpoint_context);
|
||||
parent.jit.load()->HaltExecution(ARM_Interface::watchpoint);
|
||||
parent.halted_watchpoint = match;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ARM_Dynarmic_32& parent;
|
||||
Core::Memory::Memory& memory;
|
||||
std::size_t num_interpreted_instructions{};
|
||||
bool debugger_enabled{};
|
||||
static constexpr u64 minimum_run_cycles = 1000U;
|
||||
};
|
||||
|
||||
@@ -188,11 +150,6 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
||||
config.code_cache_size = 512_MiB;
|
||||
config.far_code_offset = 400_MiB;
|
||||
|
||||
// Allow memory fault handling to work
|
||||
if (system.DebuggerEnabled()) {
|
||||
config.check_halt_on_memory_access = true;
|
||||
}
|
||||
|
||||
// null_jit
|
||||
if (!page_table) {
|
||||
// Don't waste too much memory on null_jit
|
||||
@@ -275,24 +232,20 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
||||
return std::make_unique<Dynarmic::A32::Jit>(config);
|
||||
}
|
||||
|
||||
Dynarmic::HaltReason ARM_Dynarmic_32::RunJit() {
|
||||
return jit.load()->Run();
|
||||
void ARM_Dynarmic_32::Run() {
|
||||
while (true) {
|
||||
const auto hr = jit.load()->Run();
|
||||
if (Has(hr, svc_call)) {
|
||||
Kernel::Svc::Call(system, svc_swi);
|
||||
}
|
||||
if (Has(hr, break_loop) || !uses_wall_clock) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Dynarmic::HaltReason ARM_Dynarmic_32::StepJit() {
|
||||
return jit.load()->Step();
|
||||
}
|
||||
|
||||
u32 ARM_Dynarmic_32::GetSvcNumber() const {
|
||||
return svc_swi;
|
||||
}
|
||||
|
||||
const Kernel::DebugWatchpoint* ARM_Dynarmic_32::HaltedWatchpoint() const {
|
||||
return halted_watchpoint;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::RewindBreakpointInstruction() {
|
||||
LoadContext(breakpoint_context);
|
||||
void ARM_Dynarmic_32::Step() {
|
||||
jit.load()->Step();
|
||||
}
|
||||
|
||||
ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_,
|
||||
@@ -374,6 +327,10 @@ void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
|
||||
jit.load()->LoadContext(context);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::PrepareReschedule() {
|
||||
jit.load()->HaltExecution(break_loop);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::SignalInterrupt() {
|
||||
jit.load()->HaltExecution(break_loop);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@ public:
|
||||
void SetVectorReg(int index, u128 value) override;
|
||||
u32 GetPSTATE() const override;
|
||||
void SetPSTATE(u32 pstate) override;
|
||||
void Run() override;
|
||||
void Step() override;
|
||||
VAddr GetTlsAddress() const override;
|
||||
void SetTlsAddress(VAddr address) override;
|
||||
void SetTPIDR_EL0(u64 value) override;
|
||||
@@ -55,6 +57,7 @@ public:
|
||||
void LoadContext(const ThreadContext32& ctx) override;
|
||||
void LoadContext(const ThreadContext64& ctx) override {}
|
||||
|
||||
void PrepareReschedule() override;
|
||||
void SignalInterrupt() override;
|
||||
void ClearExclusiveState() override;
|
||||
|
||||
@@ -68,13 +71,6 @@ public:
|
||||
|
||||
std::vector<BacktraceEntry> GetBacktrace() const override;
|
||||
|
||||
protected:
|
||||
Dynarmic::HaltReason RunJit() override;
|
||||
Dynarmic::HaltReason StepJit() override;
|
||||
u32 GetSvcNumber() const override;
|
||||
const Kernel::DebugWatchpoint* HaltedWatchpoint() const override;
|
||||
void RewindBreakpointInstruction() override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const;
|
||||
|
||||
@@ -100,10 +96,6 @@ private:
|
||||
|
||||
// SVC callback
|
||||
u32 svc_swi{};
|
||||
|
||||
// Watchpoint info
|
||||
const Kernel::DebugWatchpoint* halted_watchpoint;
|
||||
ThreadContext32 breakpoint_context;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/debugger/debugger.h"
|
||||
#include "core/hardware_properties.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
@@ -26,79 +25,61 @@ namespace Core {
|
||||
using Vector = Dynarmic::A64::Vector;
|
||||
using namespace Common::Literals;
|
||||
|
||||
constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
|
||||
constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
|
||||
|
||||
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
|
||||
public:
|
||||
explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
|
||||
: parent{parent_},
|
||||
memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {}
|
||||
: parent{parent_}, memory(parent.system.Memory()) {}
|
||||
|
||||
u8 MemoryRead8(u64 vaddr) override {
|
||||
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
|
||||
return memory.Read8(vaddr);
|
||||
}
|
||||
u16 MemoryRead16(u64 vaddr) override {
|
||||
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
|
||||
return memory.Read16(vaddr);
|
||||
}
|
||||
u32 MemoryRead32(u64 vaddr) override {
|
||||
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
|
||||
return memory.Read32(vaddr);
|
||||
}
|
||||
u64 MemoryRead64(u64 vaddr) override {
|
||||
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
|
||||
return memory.Read64(vaddr);
|
||||
}
|
||||
Vector MemoryRead128(u64 vaddr) override {
|
||||
CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
|
||||
return {memory.Read64(vaddr), memory.Read64(vaddr + 8)};
|
||||
}
|
||||
|
||||
void MemoryWrite8(u64 vaddr, u8 value) override {
|
||||
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
|
||||
memory.Write8(vaddr, value);
|
||||
}
|
||||
memory.Write8(vaddr, value);
|
||||
}
|
||||
void MemoryWrite16(u64 vaddr, u16 value) override {
|
||||
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
|
||||
memory.Write16(vaddr, value);
|
||||
}
|
||||
memory.Write16(vaddr, value);
|
||||
}
|
||||
void MemoryWrite32(u64 vaddr, u32 value) override {
|
||||
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
|
||||
memory.Write32(vaddr, value);
|
||||
}
|
||||
memory.Write32(vaddr, value);
|
||||
}
|
||||
void MemoryWrite64(u64 vaddr, u64 value) override {
|
||||
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
|
||||
memory.Write64(vaddr, value);
|
||||
}
|
||||
memory.Write64(vaddr, value);
|
||||
}
|
||||
void MemoryWrite128(u64 vaddr, Vector value) override {
|
||||
if (CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Write)) {
|
||||
memory.Write64(vaddr, value[0]);
|
||||
memory.Write64(vaddr + 8, value[1]);
|
||||
}
|
||||
memory.Write64(vaddr, value[0]);
|
||||
memory.Write64(vaddr + 8, value[1]);
|
||||
}
|
||||
|
||||
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override {
|
||||
return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
|
||||
memory.WriteExclusive8(vaddr, value, expected);
|
||||
return memory.WriteExclusive8(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override {
|
||||
return CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write) &&
|
||||
memory.WriteExclusive16(vaddr, value, expected);
|
||||
return memory.WriteExclusive16(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, std::uint32_t expected) override {
|
||||
return CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write) &&
|
||||
memory.WriteExclusive32(vaddr, value, expected);
|
||||
return memory.WriteExclusive32(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, std::uint64_t expected) override {
|
||||
return CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write) &&
|
||||
memory.WriteExclusive64(vaddr, value, expected);
|
||||
return memory.WriteExclusive64(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive128(u64 vaddr, Vector value, Vector expected) override {
|
||||
return CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Write) &&
|
||||
memory.WriteExclusive128(vaddr, value, expected);
|
||||
return memory.WriteExclusive128(vaddr, value, expected);
|
||||
}
|
||||
|
||||
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
|
||||
@@ -138,13 +119,8 @@ public:
|
||||
case Dynarmic::A64::Exception::SendEventLocal:
|
||||
case Dynarmic::A64::Exception::Yield:
|
||||
return;
|
||||
case Dynarmic::A64::Exception::Breakpoint:
|
||||
default:
|
||||
if (debugger_enabled) {
|
||||
parent.SaveContext(parent.breakpoint_context);
|
||||
parent.jit.load()->HaltExecution(ARM_Interface::breakpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
parent.LogBacktrace();
|
||||
ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
|
||||
static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
|
||||
@@ -153,7 +129,7 @@ public:
|
||||
|
||||
void CallSVC(u32 swi) override {
|
||||
parent.svc_swi = swi;
|
||||
parent.jit.load()->HaltExecution(ARM_Interface::svc_call);
|
||||
parent.jit.load()->HaltExecution(svc_call);
|
||||
}
|
||||
|
||||
void AddTicks(u64 ticks) override {
|
||||
@@ -181,27 +157,10 @@ public:
|
||||
return parent.system.CoreTiming().GetClockTicks();
|
||||
}
|
||||
|
||||
bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) {
|
||||
if (!debugger_enabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto match{parent.MatchingWatchpoint(addr, size, type)};
|
||||
if (match) {
|
||||
parent.SaveContext(parent.breakpoint_context);
|
||||
parent.jit.load()->HaltExecution(ARM_Interface::watchpoint);
|
||||
parent.halted_watchpoint = match;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ARM_Dynarmic_64& parent;
|
||||
Core::Memory::Memory& memory;
|
||||
u64 tpidrro_el0 = 0;
|
||||
u64 tpidr_el0 = 0;
|
||||
bool debugger_enabled{};
|
||||
static constexpr u64 minimum_run_cycles = 1000U;
|
||||
};
|
||||
|
||||
@@ -252,11 +211,6 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
||||
config.code_cache_size = 512_MiB;
|
||||
config.far_code_offset = 400_MiB;
|
||||
|
||||
// Allow memory fault handling to work
|
||||
if (system.DebuggerEnabled()) {
|
||||
config.check_halt_on_memory_access = true;
|
||||
}
|
||||
|
||||
// null_jit
|
||||
if (!page_table) {
|
||||
// Don't waste too much memory on null_jit
|
||||
@@ -339,24 +293,20 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
||||
return std::make_shared<Dynarmic::A64::Jit>(config);
|
||||
}
|
||||
|
||||
Dynarmic::HaltReason ARM_Dynarmic_64::RunJit() {
|
||||
return jit.load()->Run();
|
||||
void ARM_Dynarmic_64::Run() {
|
||||
while (true) {
|
||||
const auto hr = jit.load()->Run();
|
||||
if (Has(hr, svc_call)) {
|
||||
Kernel::Svc::Call(system, svc_swi);
|
||||
}
|
||||
if (Has(hr, break_loop) || !uses_wall_clock) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Dynarmic::HaltReason ARM_Dynarmic_64::StepJit() {
|
||||
return jit.load()->Step();
|
||||
}
|
||||
|
||||
u32 ARM_Dynarmic_64::GetSvcNumber() const {
|
||||
return svc_swi;
|
||||
}
|
||||
|
||||
const Kernel::DebugWatchpoint* ARM_Dynarmic_64::HaltedWatchpoint() const {
|
||||
return halted_watchpoint;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::RewindBreakpointInstruction() {
|
||||
LoadContext(breakpoint_context);
|
||||
void ARM_Dynarmic_64::Step() {
|
||||
jit.load()->Step();
|
||||
}
|
||||
|
||||
ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_,
|
||||
@@ -445,6 +395,10 @@ void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
|
||||
SetTPIDR_EL0(ctx.tpidr);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::PrepareReschedule() {
|
||||
jit.load()->HaltExecution(break_loop);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::SignalInterrupt() {
|
||||
jit.load()->HaltExecution(break_loop);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ public:
|
||||
void SetVectorReg(int index, u128 value) override;
|
||||
u32 GetPSTATE() const override;
|
||||
void SetPSTATE(u32 pstate) override;
|
||||
void Run() override;
|
||||
void Step() override;
|
||||
VAddr GetTlsAddress() const override;
|
||||
void SetTlsAddress(VAddr address) override;
|
||||
void SetTPIDR_EL0(u64 value) override;
|
||||
@@ -49,6 +51,7 @@ public:
|
||||
void LoadContext(const ThreadContext32& ctx) override {}
|
||||
void LoadContext(const ThreadContext64& ctx) override;
|
||||
|
||||
void PrepareReschedule() override;
|
||||
void SignalInterrupt() override;
|
||||
void ClearExclusiveState() override;
|
||||
|
||||
@@ -62,13 +65,6 @@ public:
|
||||
|
||||
std::vector<BacktraceEntry> GetBacktrace() const override;
|
||||
|
||||
protected:
|
||||
Dynarmic::HaltReason RunJit() override;
|
||||
Dynarmic::HaltReason StepJit() override;
|
||||
u32 GetSvcNumber() const override;
|
||||
const Kernel::DebugWatchpoint* HaltedWatchpoint() const override;
|
||||
void RewindBreakpointInstruction() override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table,
|
||||
std::size_t address_space_bits) const;
|
||||
@@ -93,10 +89,6 @@ private:
|
||||
|
||||
// SVC callback
|
||||
u32 svc_swi{};
|
||||
|
||||
// Breakpoint info
|
||||
const Kernel::DebugWatchpoint* halted_watchpoint;
|
||||
ThreadContext64 breakpoint_context;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -20,7 +20,7 @@ struct fmt::formatter<Dynarmic::A32::CoprocReg> {
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Dynarmic::A32::CoprocReg& reg, FormatContext& ctx) {
|
||||
return fmt::format_to(ctx.out(), "cp{}", static_cast<size_t>(reg));
|
||||
return format_to(ctx.out(), "cp{}", static_cast<size_t>(reg));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+72
-13
@@ -3,14 +3,73 @@
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/elf.h"
|
||||
#include "core/arm/symbols.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
using namespace Common::ELF;
|
||||
|
||||
namespace Core {
|
||||
namespace {
|
||||
|
||||
constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
|
||||
constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
|
||||
constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
|
||||
constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
|
||||
|
||||
enum class ELFSymbolType : u8 {
|
||||
None = 0,
|
||||
Object = 1,
|
||||
Function = 2,
|
||||
Section = 3,
|
||||
File = 4,
|
||||
Common = 5,
|
||||
TLS = 6,
|
||||
};
|
||||
|
||||
enum class ELFSymbolBinding : u8 {
|
||||
Local = 0,
|
||||
Global = 1,
|
||||
Weak = 2,
|
||||
};
|
||||
|
||||
enum class ELFSymbolVisibility : u8 {
|
||||
Default = 0,
|
||||
Internal = 1,
|
||||
Hidden = 2,
|
||||
Protected = 3,
|
||||
};
|
||||
|
||||
struct ELF64Symbol {
|
||||
u32 name_index;
|
||||
union {
|
||||
u8 info;
|
||||
|
||||
BitField<0, 4, ELFSymbolType> type;
|
||||
BitField<4, 4, ELFSymbolBinding> binding;
|
||||
};
|
||||
ELFSymbolVisibility visibility;
|
||||
u16 sh_index;
|
||||
u64 value;
|
||||
u64 size;
|
||||
};
|
||||
static_assert(sizeof(ELF64Symbol) == 0x18, "ELF64Symbol has incorrect size.");
|
||||
|
||||
struct ELF32Symbol {
|
||||
u32 name_index;
|
||||
u32 value;
|
||||
u32 size;
|
||||
union {
|
||||
u8 info;
|
||||
|
||||
BitField<0, 4, ELFSymbolType> type;
|
||||
BitField<4, 4, ELFSymbolBinding> binding;
|
||||
};
|
||||
ELFSymbolVisibility visibility;
|
||||
u16 sh_index;
|
||||
};
|
||||
static_assert(sizeof(ELF32Symbol) == 0x10, "ELF32Symbol has incorrect size.");
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
namespace Symbols {
|
||||
|
||||
template <typename Word, typename ELFSymbol, typename ByteReader>
|
||||
@@ -51,15 +110,15 @@ static Symbols GetSymbols(ByteReader ReadBytes) {
|
||||
const Word value = ReadWord(dynamic_index + sizeof(Word));
|
||||
dynamic_index += 2 * sizeof(Word);
|
||||
|
||||
if (tag == ElfDtNull) {
|
||||
if (tag == ELF_DYNAMIC_TAG_NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (tag == ElfDtStrtab) {
|
||||
if (tag == ELF_DYNAMIC_TAG_STRTAB) {
|
||||
string_table_offset = value;
|
||||
} else if (tag == ElfDtSymtab) {
|
||||
} else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
|
||||
symbol_table_offset = value;
|
||||
} else if (tag == ElfDtSyment) {
|
||||
} else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
|
||||
symbol_entry_size = value;
|
||||
}
|
||||
}
|
||||
@@ -75,14 +134,14 @@ static Symbols GetSymbols(ByteReader ReadBytes) {
|
||||
ELFSymbol symbol{};
|
||||
ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol));
|
||||
|
||||
VAddr string_offset = string_table_offset + symbol.st_name;
|
||||
VAddr string_offset = string_table_offset + symbol.name_index;
|
||||
std::string name;
|
||||
for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) {
|
||||
name += static_cast<char>(c);
|
||||
}
|
||||
|
||||
symbol_index += symbol_entry_size;
|
||||
out[name] = std::make_pair(symbol.st_value, symbol.st_size);
|
||||
out[name] = std::make_pair(symbol.value, symbol.size);
|
||||
}
|
||||
|
||||
return out;
|
||||
@@ -93,9 +152,9 @@ Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) {
|
||||
[&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }};
|
||||
|
||||
if (is_64) {
|
||||
return GetSymbols<u64, Elf64_Sym>(ReadBytes);
|
||||
return GetSymbols<u64, ELF64Symbol>(ReadBytes);
|
||||
} else {
|
||||
return GetSymbols<u32, Elf32_Sym>(ReadBytes);
|
||||
return GetSymbols<u32, ELF32Symbol>(ReadBytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,9 +164,9 @@ Symbols GetSymbols(std::span<const u8> data, bool is_64) {
|
||||
}};
|
||||
|
||||
if (is_64) {
|
||||
return GetSymbols<u64, Elf64_Sym>(ReadBytes);
|
||||
return GetSymbols<u64, ELF64Symbol>(ReadBytes);
|
||||
} else {
|
||||
return GetSymbols<u32, Elf32_Sym>(ReadBytes);
|
||||
return GetSymbols<u32, ELF32Symbol>(ReadBytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+18
-39
@@ -17,7 +17,6 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/debugger/debugger.h"
|
||||
#include "core/device_memory.h"
|
||||
#include "core/file_sys/bis_factory.h"
|
||||
#include "core/file_sys/mode.h"
|
||||
@@ -138,6 +137,7 @@ struct System::Impl {
|
||||
|
||||
kernel.Suspend(false);
|
||||
core_timing.SyncPause(false);
|
||||
cpu_manager.Pause(false);
|
||||
is_paused = false;
|
||||
|
||||
return status;
|
||||
@@ -149,29 +149,28 @@ struct System::Impl {
|
||||
|
||||
core_timing.SyncPause(true);
|
||||
kernel.Suspend(true);
|
||||
cpu_manager.Pause(true);
|
||||
is_paused = true;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> StallProcesses() {
|
||||
std::unique_lock<std::mutex> StallCPU() {
|
||||
std::unique_lock<std::mutex> lk(suspend_guard);
|
||||
kernel.Suspend(true);
|
||||
core_timing.SyncPause(true);
|
||||
cpu_manager.Pause(true);
|
||||
return lk;
|
||||
}
|
||||
|
||||
void UnstallProcesses() {
|
||||
void UnstallCPU() {
|
||||
if (!is_paused) {
|
||||
core_timing.SyncPause(false);
|
||||
kernel.Suspend(false);
|
||||
cpu_manager.Pause(false);
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeDebugger(System& system, u16 port) {
|
||||
debugger = std::make_unique<Debugger>(system, port);
|
||||
}
|
||||
|
||||
SystemResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
|
||||
LOG_DEBUG(Core, "initialized OK");
|
||||
|
||||
@@ -330,9 +329,6 @@ struct System::Impl {
|
||||
gpu_core->NotifyShutdown();
|
||||
}
|
||||
|
||||
kernel.ShutdownCores();
|
||||
cpu_manager.Shutdown();
|
||||
debugger.reset();
|
||||
services.reset();
|
||||
service_manager.reset();
|
||||
cheat_engine.reset();
|
||||
@@ -440,9 +436,6 @@ struct System::Impl {
|
||||
/// Network instance
|
||||
Network::NetworkInstance network_instance;
|
||||
|
||||
/// Debugger
|
||||
std::unique_ptr<Core::Debugger> debugger;
|
||||
|
||||
SystemResultStatus status = SystemResultStatus::Success;
|
||||
std::string status_details = "";
|
||||
|
||||
@@ -479,6 +472,10 @@ SystemResultStatus System::Pause() {
|
||||
return impl->Pause();
|
||||
}
|
||||
|
||||
SystemResultStatus System::SingleStep() {
|
||||
return SystemResultStatus::Success;
|
||||
}
|
||||
|
||||
void System::InvalidateCpuInstructionCaches() {
|
||||
impl->kernel.InvalidateAllInstructionCaches();
|
||||
}
|
||||
@@ -491,22 +488,12 @@ void System::Shutdown() {
|
||||
impl->Shutdown();
|
||||
}
|
||||
|
||||
void System::DetachDebugger() {
|
||||
if (impl->debugger) {
|
||||
impl->debugger->NotifyShutdown();
|
||||
}
|
||||
std::unique_lock<std::mutex> System::StallCPU() {
|
||||
return impl->StallCPU();
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> System::StallProcesses() {
|
||||
return impl->StallProcesses();
|
||||
}
|
||||
|
||||
void System::UnstallProcesses() {
|
||||
impl->UnstallProcesses();
|
||||
}
|
||||
|
||||
void System::InitializeDebugger() {
|
||||
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
|
||||
void System::UnstallCPU() {
|
||||
impl->UnstallCPU();
|
||||
}
|
||||
|
||||
SystemResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
|
||||
@@ -518,6 +505,10 @@ bool System::IsPoweredOn() const {
|
||||
return impl->is_powered_on.load(std::memory_order::relaxed);
|
||||
}
|
||||
|
||||
void System::PrepareReschedule() {
|
||||
// Deprecated, does nothing, kept for backward compatibility.
|
||||
}
|
||||
|
||||
void System::PrepareReschedule(const u32 core_index) {
|
||||
impl->kernel.PrepareReschedule(core_index);
|
||||
}
|
||||
@@ -822,18 +813,6 @@ bool System::IsMulticore() const {
|
||||
return impl->is_multicore;
|
||||
}
|
||||
|
||||
bool System::DebuggerEnabled() const {
|
||||
return Settings::values.use_gdbstub.GetValue();
|
||||
}
|
||||
|
||||
Core::Debugger& System::GetDebugger() {
|
||||
return *impl->debugger;
|
||||
}
|
||||
|
||||
const Core::Debugger& System::GetDebugger() const {
|
||||
return *impl->debugger;
|
||||
}
|
||||
|
||||
void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) {
|
||||
impl->execute_program_callback = std::move(callback);
|
||||
}
|
||||
|
||||
+11
-17
@@ -97,7 +97,6 @@ namespace Core {
|
||||
|
||||
class ARM_Interface;
|
||||
class CpuManager;
|
||||
class Debugger;
|
||||
class DeviceMemory;
|
||||
class ExclusiveMonitor;
|
||||
class SpeedLimiter;
|
||||
@@ -148,6 +147,12 @@ public:
|
||||
*/
|
||||
[[nodiscard]] SystemResultStatus Pause();
|
||||
|
||||
/**
|
||||
* Step the CPU one instruction
|
||||
* @return Result status, indicating whether or not the operation succeeded.
|
||||
*/
|
||||
[[nodiscard]] SystemResultStatus SingleStep();
|
||||
|
||||
/**
|
||||
* Invalidate the CPU instruction caches
|
||||
* This function should only be used by GDB Stub to support breakpoints, memory updates and
|
||||
@@ -160,16 +165,8 @@ public:
|
||||
/// Shutdown the emulated system.
|
||||
void Shutdown();
|
||||
|
||||
/// Forcibly detach the debugger if it is running.
|
||||
void DetachDebugger();
|
||||
|
||||
std::unique_lock<std::mutex> StallProcesses();
|
||||
void UnstallProcesses();
|
||||
|
||||
/**
|
||||
* Initialize the debugger.
|
||||
*/
|
||||
void InitializeDebugger();
|
||||
std::unique_lock<std::mutex> StallCPU();
|
||||
void UnstallCPU();
|
||||
|
||||
/**
|
||||
* Load an executable application.
|
||||
@@ -196,6 +193,9 @@ public:
|
||||
/// Gets a reference to the telemetry session for this emulation session.
|
||||
[[nodiscard]] const Core::TelemetrySession& TelemetrySession() const;
|
||||
|
||||
/// Prepare the core emulation for a reschedule
|
||||
void PrepareReschedule();
|
||||
|
||||
/// Prepare the core emulation for a reschedule
|
||||
void PrepareReschedule(u32 core_index);
|
||||
|
||||
@@ -357,9 +357,6 @@ public:
|
||||
[[nodiscard]] Service::Time::TimeManager& GetTimeManager();
|
||||
[[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
|
||||
|
||||
[[nodiscard]] Core::Debugger& GetDebugger();
|
||||
[[nodiscard]] const Core::Debugger& GetDebugger() const;
|
||||
|
||||
void SetExitLock(bool locked);
|
||||
[[nodiscard]] bool GetExitLock() const;
|
||||
|
||||
@@ -381,9 +378,6 @@ public:
|
||||
/// Tells if system is running on multicore.
|
||||
[[nodiscard]] bool IsMulticore() const;
|
||||
|
||||
/// Tells if the system debugger is enabled.
|
||||
[[nodiscard]] bool DebuggerEnabled() const;
|
||||
|
||||
/// Type used for the frontend to designate a callback for System to re-launch the application
|
||||
/// using a specified program index.
|
||||
using ExecuteProgramCallback = std::function<void(std::size_t)>;
|
||||
|
||||
+160
-39
@@ -21,24 +21,23 @@ CpuManager::~CpuManager() = default;
|
||||
|
||||
void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager,
|
||||
std::size_t core) {
|
||||
cpu_manager.RunThread(core);
|
||||
cpu_manager.RunThread(stop_token, core);
|
||||
}
|
||||
|
||||
void CpuManager::Initialize() {
|
||||
num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1;
|
||||
gpu_barrier = std::make_unique<Common::Barrier>(num_cores + 1);
|
||||
|
||||
for (std::size_t core = 0; core < num_cores; core++) {
|
||||
core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core);
|
||||
running_mode = true;
|
||||
if (is_multicore) {
|
||||
for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
|
||||
core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core);
|
||||
}
|
||||
} else {
|
||||
core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::Shutdown() {
|
||||
for (std::size_t core = 0; core < num_cores; core++) {
|
||||
if (core_data[core].host_thread.joinable()) {
|
||||
core_data[core].host_thread.join();
|
||||
}
|
||||
}
|
||||
running_mode = false;
|
||||
Pause(false);
|
||||
}
|
||||
|
||||
std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() {
|
||||
@@ -49,8 +48,8 @@ std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() {
|
||||
return IdleThreadFunction;
|
||||
}
|
||||
|
||||
std::function<void(void*)> CpuManager::GetShutdownThreadStartFunc() {
|
||||
return ShutdownThreadFunction;
|
||||
std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() {
|
||||
return SuspendThreadFunction;
|
||||
}
|
||||
|
||||
void CpuManager::GuestThreadFunction(void* cpu_manager_) {
|
||||
@@ -80,12 +79,17 @@ void CpuManager::IdleThreadFunction(void* cpu_manager_) {
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::ShutdownThreadFunction(void* cpu_manager) {
|
||||
static_cast<CpuManager*>(cpu_manager)->ShutdownThread();
|
||||
void CpuManager::SuspendThreadFunction(void* cpu_manager_) {
|
||||
CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
|
||||
if (cpu_manager->is_multicore) {
|
||||
cpu_manager->MultiCoreRunSuspendThread();
|
||||
} else {
|
||||
cpu_manager->SingleCoreRunSuspendThread();
|
||||
}
|
||||
}
|
||||
|
||||
void* CpuManager::GetStartFuncParameter() {
|
||||
return this;
|
||||
void* CpuManager::GetStartFuncParamater() {
|
||||
return static_cast<void*>(this);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -95,7 +99,7 @@ void* CpuManager::GetStartFuncParameter() {
|
||||
void CpuManager::MultiCoreRunGuestThread() {
|
||||
auto& kernel = system.Kernel();
|
||||
kernel.CurrentScheduler()->OnThreadStart();
|
||||
auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread();
|
||||
auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
auto& host_context = thread->GetHostContext();
|
||||
host_context->SetRewindPoint(GuestRewindFunction, this);
|
||||
MultiCoreRunGuestLoop();
|
||||
@@ -106,10 +110,12 @@ void CpuManager::MultiCoreRunGuestLoop() {
|
||||
|
||||
while (true) {
|
||||
auto* physical_core = &kernel.CurrentPhysicalCore();
|
||||
system.EnterDynarmicProfile();
|
||||
while (!physical_core->IsInterrupted()) {
|
||||
physical_core->Run();
|
||||
physical_core = &kernel.CurrentPhysicalCore();
|
||||
}
|
||||
system.ExitDynarmicProfile();
|
||||
{
|
||||
Kernel::KScopedDisableDispatch dd(kernel);
|
||||
physical_core->ArmInterface().ClearExclusiveState();
|
||||
@@ -125,6 +131,58 @@ void CpuManager::MultiCoreRunIdleThread() {
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::MultiCoreRunSuspendThread() {
|
||||
auto& kernel = system.Kernel();
|
||||
kernel.CurrentScheduler()->OnThreadStart();
|
||||
while (true) {
|
||||
auto core = kernel.CurrentPhysicalCoreIndex();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
|
||||
ASSERT(scheduler.ContextSwitchPending());
|
||||
ASSERT(core == kernel.CurrentPhysicalCoreIndex());
|
||||
scheduler.RescheduleCurrentCore();
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::MultiCorePause(bool paused) {
|
||||
if (!paused) {
|
||||
bool all_not_barrier = false;
|
||||
while (!all_not_barrier) {
|
||||
all_not_barrier = true;
|
||||
for (const auto& data : core_data) {
|
||||
all_not_barrier &= !data.is_running.load() && data.initialized.load();
|
||||
}
|
||||
}
|
||||
for (auto& data : core_data) {
|
||||
data.enter_barrier->Set();
|
||||
}
|
||||
if (paused_state.load()) {
|
||||
bool all_barrier = false;
|
||||
while (!all_barrier) {
|
||||
all_barrier = true;
|
||||
for (const auto& data : core_data) {
|
||||
all_barrier &= data.is_paused.load() && data.initialized.load();
|
||||
}
|
||||
}
|
||||
for (auto& data : core_data) {
|
||||
data.exit_barrier->Set();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/// Wait until all cores are paused.
|
||||
bool all_barrier = false;
|
||||
while (!all_barrier) {
|
||||
all_barrier = true;
|
||||
for (const auto& data : core_data) {
|
||||
all_barrier &= data.is_paused.load() && data.initialized.load();
|
||||
}
|
||||
}
|
||||
/// Don't release the barrier
|
||||
}
|
||||
paused_state = paused;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// SingleCore ///
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -132,7 +190,7 @@ void CpuManager::MultiCoreRunIdleThread() {
|
||||
void CpuManager::SingleCoreRunGuestThread() {
|
||||
auto& kernel = system.Kernel();
|
||||
kernel.CurrentScheduler()->OnThreadStart();
|
||||
auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread();
|
||||
auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
auto& host_context = thread->GetHostContext();
|
||||
host_context->SetRewindPoint(GuestRewindFunction, this);
|
||||
SingleCoreRunGuestLoop();
|
||||
@@ -142,10 +200,12 @@ void CpuManager::SingleCoreRunGuestLoop() {
|
||||
auto& kernel = system.Kernel();
|
||||
while (true) {
|
||||
auto* physical_core = &kernel.CurrentPhysicalCore();
|
||||
system.EnterDynarmicProfile();
|
||||
if (!physical_core->IsInterrupted()) {
|
||||
physical_core->Run();
|
||||
physical_core = &kernel.CurrentPhysicalCore();
|
||||
}
|
||||
system.ExitDynarmicProfile();
|
||||
kernel.SetIsPhantomModeForSingleCore(true);
|
||||
system.CoreTiming().Advance();
|
||||
kernel.SetIsPhantomModeForSingleCore(false);
|
||||
@@ -168,11 +228,25 @@ void CpuManager::SingleCoreRunIdleThread() {
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::SingleCoreRunSuspendThread() {
|
||||
auto& kernel = system.Kernel();
|
||||
kernel.CurrentScheduler()->OnThreadStart();
|
||||
while (true) {
|
||||
auto core = kernel.GetCurrentHostThreadID();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context);
|
||||
ASSERT(scheduler.ContextSwitchPending());
|
||||
ASSERT(core == kernel.GetCurrentHostThreadID());
|
||||
scheduler.RescheduleCurrentCore();
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
|
||||
{
|
||||
auto& kernel = system.Kernel();
|
||||
auto& scheduler = kernel.Scheduler(current_core);
|
||||
Kernel::KThread* current_thread = scheduler.GetSchedulerCurrentThread();
|
||||
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
|
||||
if (idle_count >= 4 || from_running_enviroment) {
|
||||
if (!from_running_enviroment) {
|
||||
system.CoreTiming().Idle();
|
||||
@@ -184,7 +258,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
|
||||
}
|
||||
current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
|
||||
system.CoreTiming().ResetTicks();
|
||||
scheduler.Unload(scheduler.GetSchedulerCurrentThread());
|
||||
scheduler.Unload(scheduler.GetCurrentThread());
|
||||
|
||||
auto& next_scheduler = kernel.Scheduler(current_core);
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), *next_scheduler.ControlContext());
|
||||
@@ -193,21 +267,47 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
|
||||
// May have changed scheduler
|
||||
{
|
||||
auto& scheduler = system.Kernel().Scheduler(current_core);
|
||||
scheduler.Reload(scheduler.GetSchedulerCurrentThread());
|
||||
idle_count = 0;
|
||||
scheduler.Reload(scheduler.GetCurrentThread());
|
||||
if (!scheduler.IsIdle()) {
|
||||
idle_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::ShutdownThread() {
|
||||
auto& kernel = system.Kernel();
|
||||
auto core = is_multicore ? kernel.CurrentPhysicalCoreIndex() : 0;
|
||||
auto* current_thread = kernel.GetCurrentEmuThread();
|
||||
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
|
||||
UNREACHABLE();
|
||||
void CpuManager::SingleCorePause(bool paused) {
|
||||
if (!paused) {
|
||||
bool all_not_barrier = false;
|
||||
while (!all_not_barrier) {
|
||||
all_not_barrier = !core_data[0].is_running.load() && core_data[0].initialized.load();
|
||||
}
|
||||
core_data[0].enter_barrier->Set();
|
||||
if (paused_state.load()) {
|
||||
bool all_barrier = false;
|
||||
while (!all_barrier) {
|
||||
all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
|
||||
}
|
||||
core_data[0].exit_barrier->Set();
|
||||
}
|
||||
} else {
|
||||
/// Wait until all cores are paused.
|
||||
bool all_barrier = false;
|
||||
while (!all_barrier) {
|
||||
all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
|
||||
}
|
||||
/// Don't release the barrier
|
||||
}
|
||||
paused_state = paused;
|
||||
}
|
||||
|
||||
void CpuManager::RunThread(std::size_t core) {
|
||||
void CpuManager::Pause(bool paused) {
|
||||
if (is_multicore) {
|
||||
MultiCorePause(paused);
|
||||
} else {
|
||||
SingleCorePause(paused);
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
|
||||
/// Initialization
|
||||
system.RegisterCoreThread(core);
|
||||
std::string name;
|
||||
@@ -220,24 +320,45 @@ void CpuManager::RunThread(std::size_t core) {
|
||||
Common::SetCurrentThreadName(name.c_str());
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
|
||||
auto& data = core_data[core];
|
||||
data.enter_barrier = std::make_unique<Common::Event>();
|
||||
data.exit_barrier = std::make_unique<Common::Event>();
|
||||
data.host_context = Common::Fiber::ThreadToFiber();
|
||||
data.is_running = false;
|
||||
data.initialized = true;
|
||||
const bool sc_sync = !is_async_gpu && !is_multicore;
|
||||
bool sc_sync_first_use = sc_sync;
|
||||
|
||||
// Cleanup
|
||||
SCOPE_EXIT({
|
||||
data.host_context->Exit();
|
||||
data.enter_barrier.reset();
|
||||
data.exit_barrier.reset();
|
||||
data.initialized = false;
|
||||
MicroProfileOnThreadExit();
|
||||
});
|
||||
|
||||
// Running
|
||||
gpu_barrier->Sync();
|
||||
/// Running
|
||||
while (running_mode) {
|
||||
data.is_running = false;
|
||||
data.enter_barrier->Wait();
|
||||
if (sc_sync_first_use) {
|
||||
system.GPU().ObtainContext();
|
||||
sc_sync_first_use = false;
|
||||
}
|
||||
|
||||
if (!is_async_gpu && !is_multicore) {
|
||||
system.GPU().ObtainContext();
|
||||
// Emulation was stopped
|
||||
if (stop_token.stop_requested()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
|
||||
data.is_running = true;
|
||||
Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
|
||||
data.is_running = false;
|
||||
data.is_paused = true;
|
||||
data.exit_barrier->Wait();
|
||||
data.is_paused = false;
|
||||
}
|
||||
|
||||
auto* current_thread = system.Kernel().CurrentScheduler()->GetIdleThread();
|
||||
Kernel::SetCurrentThread(system.Kernel(), current_thread);
|
||||
Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
+18
-11
@@ -43,17 +43,15 @@ public:
|
||||
is_async_gpu = is_async;
|
||||
}
|
||||
|
||||
void OnGpuReady() {
|
||||
gpu_barrier->Sync();
|
||||
}
|
||||
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
|
||||
void Pause(bool paused);
|
||||
|
||||
static std::function<void(void*)> GetGuestThreadStartFunc();
|
||||
static std::function<void(void*)> GetIdleThreadStartFunc();
|
||||
static std::function<void(void*)> GetShutdownThreadStartFunc();
|
||||
void* GetStartFuncParameter();
|
||||
static std::function<void(void*)> GetSuspendThreadStartFunc();
|
||||
void* GetStartFuncParamater();
|
||||
|
||||
void PreemptSingleCore(bool from_running_enviroment = true);
|
||||
|
||||
@@ -65,34 +63,43 @@ private:
|
||||
static void GuestThreadFunction(void* cpu_manager);
|
||||
static void GuestRewindFunction(void* cpu_manager);
|
||||
static void IdleThreadFunction(void* cpu_manager);
|
||||
static void ShutdownThreadFunction(void* cpu_manager);
|
||||
static void SuspendThreadFunction(void* cpu_manager);
|
||||
|
||||
void MultiCoreRunGuestThread();
|
||||
void MultiCoreRunGuestLoop();
|
||||
void MultiCoreRunIdleThread();
|
||||
void MultiCoreRunSuspendThread();
|
||||
void MultiCorePause(bool paused);
|
||||
|
||||
void SingleCoreRunGuestThread();
|
||||
void SingleCoreRunGuestLoop();
|
||||
void SingleCoreRunIdleThread();
|
||||
void SingleCoreRunSuspendThread();
|
||||
void SingleCorePause(bool paused);
|
||||
|
||||
static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core);
|
||||
|
||||
void ShutdownThread();
|
||||
void RunThread(std::size_t core);
|
||||
void RunThread(std::stop_token stop_token, std::size_t core);
|
||||
|
||||
struct CoreData {
|
||||
std::shared_ptr<Common::Fiber> host_context;
|
||||
std::unique_ptr<Common::Event> enter_barrier;
|
||||
std::unique_ptr<Common::Event> exit_barrier;
|
||||
std::atomic<bool> is_running;
|
||||
std::atomic<bool> is_paused;
|
||||
std::atomic<bool> initialized;
|
||||
std::jthread host_thread;
|
||||
};
|
||||
|
||||
std::unique_ptr<Common::Barrier> gpu_barrier{};
|
||||
std::atomic<bool> running_mode{};
|
||||
std::atomic<bool> paused_state{};
|
||||
|
||||
std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{};
|
||||
|
||||
bool is_async_gpu{};
|
||||
bool is_multicore{};
|
||||
std::atomic<std::size_t> current_core{};
|
||||
std::size_t idle_count{};
|
||||
std::size_t num_cores{};
|
||||
static constexpr std::size_t max_cycle_runs = 5;
|
||||
|
||||
System& system;
|
||||
|
||||
@@ -140,6 +140,7 @@ u64 GetSignatureTypeDataSize(SignatureType type) {
|
||||
return 0x3C;
|
||||
}
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 GetSignatureTypePaddingSize(SignatureType type) {
|
||||
@@ -154,6 +155,7 @@ u64 GetSignatureTypePaddingSize(SignatureType type) {
|
||||
return 0x40;
|
||||
}
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
SignatureType Ticket::GetSignatureType() const {
|
||||
|
||||
@@ -1,315 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/process/async_pipe.hpp>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
#include "core/debugger/debugger.h"
|
||||
#include "core/debugger/debugger_interface.h"
|
||||
#include "core/debugger/gdbstub.h"
|
||||
#include "core/hle/kernel/global_scheduler_context.h"
|
||||
|
||||
template <typename Readable, typename Buffer, typename Callback>
|
||||
static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
|
||||
static_assert(std::is_trivial_v<Buffer>);
|
||||
auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))};
|
||||
r.async_read_some(
|
||||
boost_buffer, [&, c](const boost::system::error_code& error, size_t bytes_read) {
|
||||
if (!error.failed()) {
|
||||
const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
|
||||
std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
|
||||
c(received_data);
|
||||
}
|
||||
|
||||
AsyncReceiveInto(r, buffer, c);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Readable, typename Buffer>
|
||||
static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
|
||||
static_assert(std::is_trivial_v<Buffer>);
|
||||
auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))};
|
||||
size_t bytes_read = r.read_some(boost_buffer);
|
||||
const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
|
||||
std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
|
||||
return received_data;
|
||||
}
|
||||
|
||||
enum class SignalType {
|
||||
Stopped,
|
||||
Watchpoint,
|
||||
ShuttingDown,
|
||||
};
|
||||
|
||||
struct SignalInfo {
|
||||
SignalType type;
|
||||
Kernel::KThread* thread;
|
||||
const Kernel::DebugWatchpoint* watchpoint;
|
||||
};
|
||||
|
||||
namespace Core {
|
||||
|
||||
class DebuggerImpl : public DebuggerBackend {
|
||||
public:
|
||||
explicit DebuggerImpl(Core::System& system_, u16 port)
|
||||
: system{system_}, signal_pipe{io_context}, client_socket{io_context} {
|
||||
frontend = std::make_unique<GDBStub>(*this, system);
|
||||
InitializeServer(port);
|
||||
}
|
||||
|
||||
~DebuggerImpl() override {
|
||||
ShutdownServer();
|
||||
}
|
||||
|
||||
bool SignalDebugger(SignalInfo signal_info) {
|
||||
{
|
||||
std::scoped_lock lk{connection_lock};
|
||||
|
||||
if (stopped) {
|
||||
// Do not notify the debugger about another event.
|
||||
// It should be ignored.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set up the state.
|
||||
stopped = true;
|
||||
info = signal_info;
|
||||
}
|
||||
|
||||
// Write a single byte into the pipe to wake up the debug interface.
|
||||
boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::span<const u8> ReadFromClient() override {
|
||||
return ReceiveInto(client_socket, client_data);
|
||||
}
|
||||
|
||||
void WriteToClient(std::span<const u8> data) override {
|
||||
boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes()));
|
||||
}
|
||||
|
||||
void SetActiveThread(Kernel::KThread* thread) override {
|
||||
active_thread = thread;
|
||||
}
|
||||
|
||||
Kernel::KThread* GetActiveThread() override {
|
||||
return active_thread;
|
||||
}
|
||||
|
||||
private:
|
||||
void InitializeServer(u16 port) {
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
LOG_INFO(Debug_GDBStub, "Starting server on port {}...", port);
|
||||
|
||||
// Run the connection thread.
|
||||
connection_thread = std::jthread([&, port](std::stop_token stop_token) {
|
||||
try {
|
||||
// Initialize the listening socket and accept a new client.
|
||||
tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port};
|
||||
tcp::acceptor acceptor{io_context, endpoint};
|
||||
|
||||
acceptor.async_accept(client_socket, [](const auto&) {});
|
||||
io_context.run_one();
|
||||
io_context.restart();
|
||||
|
||||
if (stop_token.stop_requested()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ThreadLoop(stop_token);
|
||||
} catch (const std::exception& ex) {
|
||||
LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ShutdownServer() {
|
||||
connection_thread.request_stop();
|
||||
io_context.stop();
|
||||
connection_thread.join();
|
||||
}
|
||||
|
||||
void ThreadLoop(std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("yuzu:Debugger");
|
||||
|
||||
// Set up the client signals for new data.
|
||||
AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); });
|
||||
AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); });
|
||||
|
||||
// Set the active thread.
|
||||
UpdateActiveThread();
|
||||
|
||||
// Set up the frontend.
|
||||
frontend->Connected();
|
||||
|
||||
// Main event loop.
|
||||
while (!stop_token.stop_requested() && io_context.run()) {
|
||||
}
|
||||
}
|
||||
|
||||
void PipeData(std::span<const u8> data) {
|
||||
switch (info.type) {
|
||||
case SignalType::Stopped:
|
||||
case SignalType::Watchpoint:
|
||||
// Stop emulation.
|
||||
PauseEmulation();
|
||||
|
||||
// Notify the client.
|
||||
active_thread = info.thread;
|
||||
UpdateActiveThread();
|
||||
|
||||
if (info.type == SignalType::Watchpoint) {
|
||||
frontend->Watchpoint(active_thread, *info.watchpoint);
|
||||
} else {
|
||||
frontend->Stopped(active_thread);
|
||||
}
|
||||
|
||||
break;
|
||||
case SignalType::ShuttingDown:
|
||||
frontend->ShuttingDown();
|
||||
|
||||
// Wait for emulation to shut down gracefully now.
|
||||
signal_pipe.close();
|
||||
client_socket.shutdown(boost::asio::socket_base::shutdown_both);
|
||||
LOG_INFO(Debug_GDBStub, "Shut down server");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ClientData(std::span<const u8> data) {
|
||||
const auto actions{frontend->ClientData(data)};
|
||||
for (const auto action : actions) {
|
||||
switch (action) {
|
||||
case DebuggerAction::Interrupt: {
|
||||
{
|
||||
std::scoped_lock lk{connection_lock};
|
||||
stopped = true;
|
||||
}
|
||||
PauseEmulation();
|
||||
UpdateActiveThread();
|
||||
frontend->Stopped(active_thread);
|
||||
break;
|
||||
}
|
||||
case DebuggerAction::Continue:
|
||||
MarkResumed([&] { ResumeEmulation(); });
|
||||
break;
|
||||
case DebuggerAction::StepThreadUnlocked:
|
||||
MarkResumed([&] {
|
||||
active_thread->SetStepState(Kernel::StepState::StepPending);
|
||||
active_thread->Resume(Kernel::SuspendType::Debug);
|
||||
ResumeEmulation(active_thread);
|
||||
});
|
||||
break;
|
||||
case DebuggerAction::StepThreadLocked: {
|
||||
MarkResumed([&] {
|
||||
active_thread->SetStepState(Kernel::StepState::StepPending);
|
||||
active_thread->Resume(Kernel::SuspendType::Debug);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case DebuggerAction::ShutdownEmulation: {
|
||||
// Spawn another thread that will exit after shutdown,
|
||||
// to avoid a deadlock
|
||||
Core::System* system_ref{&system};
|
||||
std::thread t([system_ref] { system_ref->Exit(); });
|
||||
t.detach();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PauseEmulation() {
|
||||
// Put all threads to sleep on next scheduler round.
|
||||
for (auto* thread : ThreadList()) {
|
||||
thread->RequestSuspend(Kernel::SuspendType::Debug);
|
||||
}
|
||||
|
||||
// Signal an interrupt so that scheduler will fire.
|
||||
system.Kernel().InterruptAllPhysicalCores();
|
||||
}
|
||||
|
||||
void ResumeEmulation(Kernel::KThread* except = nullptr) {
|
||||
// Wake up all threads.
|
||||
for (auto* thread : ThreadList()) {
|
||||
if (thread == except) {
|
||||
continue;
|
||||
}
|
||||
|
||||
thread->SetStepState(Kernel::StepState::NotStepping);
|
||||
thread->Resume(Kernel::SuspendType::Debug);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
void MarkResumed(Callback&& cb) {
|
||||
std::scoped_lock lk{connection_lock};
|
||||
stopped = false;
|
||||
cb();
|
||||
}
|
||||
|
||||
void UpdateActiveThread() {
|
||||
const auto& threads{ThreadList()};
|
||||
if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) {
|
||||
active_thread = threads[0];
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<Kernel::KThread*>& ThreadList() {
|
||||
return system.GlobalSchedulerContext().GetThreadList();
|
||||
}
|
||||
|
||||
private:
|
||||
System& system;
|
||||
std::unique_ptr<DebuggerFrontend> frontend;
|
||||
|
||||
std::jthread connection_thread;
|
||||
std::mutex connection_lock;
|
||||
boost::asio::io_context io_context;
|
||||
boost::process::async_pipe signal_pipe;
|
||||
boost::asio::ip::tcp::socket client_socket;
|
||||
|
||||
SignalInfo info;
|
||||
Kernel::KThread* active_thread;
|
||||
bool pipe_data;
|
||||
bool stopped;
|
||||
|
||||
std::array<u8, 4096> client_data;
|
||||
};
|
||||
|
||||
Debugger::Debugger(Core::System& system, u16 port) {
|
||||
try {
|
||||
impl = std::make_unique<DebuggerImpl>(system, port);
|
||||
} catch (const std::exception& ex) {
|
||||
LOG_CRITICAL(Debug_GDBStub, "Failed to initialize debugger: {}", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
Debugger::~Debugger() = default;
|
||||
|
||||
bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) {
|
||||
return impl && impl->SignalDebugger(SignalInfo{SignalType::Stopped, thread, nullptr});
|
||||
}
|
||||
|
||||
bool Debugger::NotifyThreadWatchpoint(Kernel::KThread* thread,
|
||||
const Kernel::DebugWatchpoint& watch) {
|
||||
return impl && impl->SignalDebugger(SignalInfo{SignalType::Watchpoint, thread, &watch});
|
||||
}
|
||||
|
||||
void Debugger::NotifyShutdown() {
|
||||
if (impl) {
|
||||
impl->SignalDebugger(SignalInfo{SignalType::ShuttingDown, nullptr, nullptr});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user