forked from suyu/suyu
Compare commits
3 commits
dev
...
niansa-ci-
Author | SHA1 | Date | |
---|---|---|---|
|
9ba74561b5 | ||
|
3a2020430a | ||
|
f4949ecb42 |
78 changed files with 1376 additions and 843 deletions
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/bash -ex
|
#!/bin/bash -ex
|
||||||
# SPDX-FileCopyrightText: 2019 yuzu Emulator Project
|
|
||||||
# SPDX-FileCopyrightText: 2024 suyu Emulator Project
|
# SPDX-FileCopyrightText: 2019 yuzu Emulator Project & 2024 suyu Emulator Project
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
# Exit on error, rather than continuing with the rest of the script.
|
# Exit on error, rather than continuing with the rest of the script.
|
||||||
|
@ -12,8 +12,6 @@ mkdir build || true && cd build
|
||||||
cmake .. \
|
cmake .. \
|
||||||
-DBoost_USE_STATIC_LIBS=ON \
|
-DBoost_USE_STATIC_LIBS=ON \
|
||||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||||
-DSUYU_USE_PRECOMPILED_HEADERS=OFF \
|
|
||||||
-DDYNARMIC_USE_PRECOMPILED_HEADERS=OFF \
|
|
||||||
-DCMAKE_CXX_FLAGS="-march=x86-64-v2" \
|
-DCMAKE_CXX_FLAGS="-march=x86-64-v2" \
|
||||||
-DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \
|
-DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \
|
||||||
-DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \
|
-DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \
|
||||||
|
@ -26,7 +24,6 @@ cmake .. \
|
||||||
-DSUYU_USE_BUNDLED_FFMPEG=ON \
|
-DSUYU_USE_BUNDLED_FFMPEG=ON \
|
||||||
-DSUYU_ENABLE_LTO=ON \
|
-DSUYU_ENABLE_LTO=ON \
|
||||||
-DSUYU_CRASH_DUMPS=ON \
|
-DSUYU_CRASH_DUMPS=ON \
|
||||||
-DSUYU_USE_FASTER_LD=ON \
|
|
||||||
-GNinja
|
-GNinja
|
||||||
|
|
||||||
ninja
|
ninja
|
||||||
|
|
|
@ -7,55 +7,40 @@ set -e
|
||||||
|
|
||||||
#cd /suyu
|
#cd /suyu
|
||||||
|
|
||||||
ccache -sv
|
#wine ccache.exe -sv
|
||||||
|
|
||||||
mkdir -p build && cd build
|
mkdir -p build && cd build
|
||||||
cmake .. \
|
wine cmake.exe .. \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_TOOLCHAIN_FILE="${PWD}/../CMakeModules/MinGWCross.cmake" \
|
-DCMAKE_INSTALL_PREFIX=C:/windows-libs/mingw64/ \
|
||||||
-DDISPLAY_VERSION="$1" \
|
-DDISPLAY_VERSION="$1" \
|
||||||
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=OFF \
|
||||||
-DENABLE_QT_TRANSLATION=ON \
|
-DENABLE_QT_TRANSLATION=OFF \
|
||||||
-DUSE_CCACHE=ON \
|
# -DUSE_CCACHE=ON \
|
||||||
-DSUYU_USE_BUNDLED_SDL2=OFF \
|
-DENABLE_LIBUSB=NO \
|
||||||
-DSUYU_USE_EXTERNAL_SDL2=OFF \
|
|
||||||
-DSUYU_TESTS=OFF \
|
-DSUYU_TESTS=OFF \
|
||||||
-GNinja
|
-GNinja
|
||||||
ninja suyu suyu-cmd
|
wine ninja.exe suyu suyu-cmd
|
||||||
|
|
||||||
ccache -sv
|
#wine ccache.exe -sv
|
||||||
|
|
||||||
echo "Tests skipped"
|
echo "Tests skipped"
|
||||||
|
# TODO: actually run the tests
|
||||||
#ctest -VV -C Release
|
#ctest -VV -C Release
|
||||||
|
|
||||||
echo 'Prepare binaries...'
|
echo 'Prepare binaries...'
|
||||||
cd ..
|
cd ..
|
||||||
mkdir package
|
mkdir package
|
||||||
|
|
||||||
if [ -d "/usr/x86_64-w64-mingw32/lib/qt5/plugins/platforms/" ]; then
|
|
||||||
QT_PLUGINS_PATH='/usr/x86_64-w64-mingw32/lib/qt5/plugins'
|
|
||||||
else
|
|
||||||
#fallback to qt
|
|
||||||
QT_PLUGINS_PATH='/usr/x86_64-w64-mingw32/lib/qt/plugins'
|
|
||||||
fi
|
|
||||||
|
|
||||||
find build/ -name "suyu*.exe" -exec cp {} 'package' \;
|
find build/ -name "suyu*.exe" -exec cp {} 'package' \;
|
||||||
|
|
||||||
# copy Qt plugins
|
|
||||||
mkdir package/platforms
|
|
||||||
cp -v "${QT_PLUGINS_PATH}/platforms/qwindows.dll" package/platforms/
|
|
||||||
cp -rv "${QT_PLUGINS_PATH}/mediaservice/" package/
|
|
||||||
cp -rv "${QT_PLUGINS_PATH}/imageformats/" package/
|
|
||||||
cp -rv "${QT_PLUGINS_PATH}/styles/" package/
|
|
||||||
rm -f package/mediaservice/*d.dll
|
|
||||||
|
|
||||||
for i in package/*.exe; do
|
for i in package/*.exe; do
|
||||||
# we need to process pdb here, however, cv2pdb
|
# we need to process pdb here, however, cv2pdb
|
||||||
# does not work here, so we just simply strip all the debug symbols
|
# does not work here, so we just simply strip all the debug symbols
|
||||||
x86_64-w64-mingw32-strip "${i}"
|
x86_64-w64-mingw32-strip "${i}"
|
||||||
done
|
done
|
||||||
|
|
||||||
python3 .ci/scripts/windows/scan_dll.py package/*.exe package/imageformats/*.dll "package/"
|
python3 .ci/scripts/windows/scan_dll.py package/*.exe "package/"
|
||||||
|
|
||||||
# copy FFmpeg libraries
|
# copy FFmpeg libraries
|
||||||
EXTERNALS_PATH="$(pwd)/build/externals"
|
EXTERNALS_PATH="$(pwd)/build/externals"
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -4,7 +4,6 @@
|
||||||
# Build directory
|
# Build directory
|
||||||
[Bb]uild*/
|
[Bb]uild*/
|
||||||
doc-build/
|
doc-build/
|
||||||
cmake-build*/
|
|
||||||
|
|
||||||
# Generated source files
|
# Generated source files
|
||||||
src/common/scm_rev.cpp
|
src/common/scm_rev.cpp
|
||||||
|
@ -38,5 +37,3 @@ CMakeSettings.json
|
||||||
# Windows global filetypes
|
# Windows global filetypes
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
# Local Gitlab CI Runner
|
|
||||||
.gitlab-ci-local/
|
|
||||||
|
|
70
.gitlab-ci.yml
Executable file → Normal file
70
.gitlab-ci.yml
Executable file → Normal file
|
@ -2,82 +2,40 @@ stages:
|
||||||
- format
|
- format
|
||||||
- build
|
- build
|
||||||
|
|
||||||
variables:
|
|
||||||
# https://docs.gitlab.com/ee/ci/runners/configure_runners.html
|
|
||||||
TRANSFER_METER_FREQUENCY: "2s"
|
|
||||||
ARTIFACT_COMPRESSION_LEVEL: "fast"
|
|
||||||
CACHE_COMPRESSION_LEVEL: "fastest"
|
|
||||||
CACHE_REQUEST_TIMEOUT: 5
|
|
||||||
# Use FASTZIP for faster compression in cache and artifacts (boolean)
|
|
||||||
# https://docs.gitlab.com/runner/configuration/feature-flags.html#available-feature-flags
|
|
||||||
FF_USE_FASTZIP: 1
|
|
||||||
|
|
||||||
# Our Variables
|
|
||||||
CACHE_DIR: "$CI_PROJECT_DIR/ccache"
|
|
||||||
CCACHE_DIR: $CACHE_DIR
|
|
||||||
|
|
||||||
|
|
||||||
#CLANG FORMAT - CHECKS CODE FOR FORMATTING ISSUES
|
|
||||||
clang-format:
|
clang-format:
|
||||||
stage: format
|
stage: format
|
||||||
image: suyuemu/cibuild:clangformat
|
image: suyuemu/cibuild:linux-x64
|
||||||
#THIS HAS TO BE FALSE. IT KEEPS RESOURCES AVAILABLE - EG RUNNERS WONT TRY BUILDING IF CODEBASE IS WRONG
|
allow_failure: true
|
||||||
#MR's NEED TO BE CORRECTLY CLANG FORMATTED
|
variables:
|
||||||
allow_failure: false
|
RELEASE_NAME: mainline
|
||||||
script:
|
script:
|
||||||
- git submodule update --init --depth 1 --recursive
|
- git submodule update --init --depth 1 --recursive
|
||||||
- bash .ci/scripts/format/script.sh
|
- bash .ci/scripts/format/script.sh
|
||||||
tags:
|
|
||||||
# - Linux
|
|
||||||
# - Windows
|
|
||||||
- Parallelized
|
|
||||||
- Format
|
|
||||||
|
|
||||||
#LINUX BUILD - BUILDS LINUX APPIMAGE
|
|
||||||
build-linux:
|
build-linux:
|
||||||
stage: build
|
stage: build
|
||||||
image: suyuemu/cibuild:linux-x64
|
image: suyuemu/cibuild:linux-x64
|
||||||
resource_group: linux-ci
|
resource_group: linux-ci
|
||||||
cache:
|
|
||||||
key: "$CI_COMMIT_REF_NAME-ccache"
|
|
||||||
paths:
|
|
||||||
- $CACHE_DIR
|
|
||||||
before_script:
|
|
||||||
- mkdir -p $CACHE_DIR
|
|
||||||
- chmod -R 777 $CACHE_DIR
|
|
||||||
- ls -la $CACHE_DIR
|
|
||||||
variables:
|
variables:
|
||||||
GIT_SUBMODULE_STRATEGY: recursive
|
RELEASE_NAME: mainline
|
||||||
GIT_SUBMODULE_DEPTH: 1
|
|
||||||
RELEASE_NAME: mainline
|
|
||||||
script:
|
script:
|
||||||
|
- git submodule update --init --depth 1 --recursive
|
||||||
- bash .ci/scripts/linux/docker.sh
|
- bash .ci/scripts/linux/docker.sh
|
||||||
- bash .ci/scripts/linux/upload.sh
|
- bash .ci/scripts/linux/upload.sh
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- artifacts/*
|
- artifacts/*
|
||||||
tags:
|
|
||||||
- Linux
|
|
||||||
- Parallelized
|
|
||||||
|
|
||||||
#ANDROID BUILD - BUILDS APK
|
build-windows:
|
||||||
android:
|
|
||||||
stage: build
|
stage: build
|
||||||
image: suyuemu/cibuild:android-x64
|
image: suyuemu/cibuild:windows-x64
|
||||||
|
resource_group: windows-ci
|
||||||
|
variables:
|
||||||
|
RELEASE_NAME: mainline
|
||||||
script:
|
script:
|
||||||
- apt-get update -y
|
- git submodule update --init --depth 1 --recursive
|
||||||
- git submodule update --init --recursive
|
- bash .ci/scripts/windows/docker.sh
|
||||||
- cd externals/vcpkg
|
- bash .ci/scripts/windows/upload.sh
|
||||||
- git fetch --unshallow || true
|
|
||||||
- cd ../..
|
|
||||||
- export ANDROID_HOME="/usr/lib/android-sdk/"
|
|
||||||
- echo y | sdkmanager --sdk_root=/usr/lib/android-sdk --licenses
|
|
||||||
- bash ./.ci/scripts/android/build.sh
|
|
||||||
- bash ./.ci/scripts/android/upload.sh
|
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- artifacts/*
|
- artifacts/*
|
||||||
tags:
|
|
||||||
- Android
|
|
||||||
- Parallelized
|
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,6 @@
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.22)
|
||||||
|
|
||||||
set(CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT OFF)
|
|
||||||
set(CMAKE_XCODE_EMIT_RELATIVE_PATH YES)
|
|
||||||
|
|
||||||
project(suyu)
|
project(suyu)
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
|
||||||
|
@ -73,8 +70,6 @@ CMAKE_DEPENDENT_OPTION(SUYU_USE_FASTER_LD "Check if a faster linker is available
|
||||||
|
|
||||||
CMAKE_DEPENDENT_OPTION(USE_SYSTEM_MOLTENVK "Use the system MoltenVK lib (instead of the bundled one)" OFF "APPLE" OFF)
|
CMAKE_DEPENDENT_OPTION(USE_SYSTEM_MOLTENVK "Use the system MoltenVK lib (instead of the bundled one)" OFF "APPLE" OFF)
|
||||||
|
|
||||||
option(USE_CCACHE "Use CCache for faster building" ON)
|
|
||||||
|
|
||||||
set(DEFAULT_ENABLE_OPENSSL ON)
|
set(DEFAULT_ENABLE_OPENSSL ON)
|
||||||
if (ANDROID OR WIN32 OR APPLE)
|
if (ANDROID OR WIN32 OR APPLE)
|
||||||
# - Windows defaults to the Schannel backend.
|
# - Windows defaults to the Schannel backend.
|
||||||
|
@ -738,17 +733,3 @@ if(ENABLE_QT AND UNIX AND NOT APPLE)
|
||||||
install(FILES "dist/org.suyu_emu.suyu.metainfo.xml"
|
install(FILES "dist/org.suyu_emu.suyu.metainfo.xml"
|
||||||
DESTINATION "share/metainfo")
|
DESTINATION "share/metainfo")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Enable CCACHE. Linux only for now.
|
|
||||||
if (USE_CCACHE)
|
|
||||||
find_program(CCACHE_PROGRAM ccache)
|
|
||||||
if(CCACHE_PROGRAM)
|
|
||||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
|
|
||||||
if (SUYU_USE_PRECOMPILED_HEADERS)
|
|
||||||
message(NOTICE "To make use of CCache, set SUYU_USE_PRECOMPILED_HEADERS to OFF or else, it will barely be functional")
|
|
||||||
endif()
|
|
||||||
message(STATUS "CCache found.")
|
|
||||||
else()
|
|
||||||
message(STATUS "CCache has not been found and it will not be used. Install CCache or set USE_CCACHE to OFF")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# SPDX-FileCopyrightText: 2017 yuzu Emulator Project & 2024 suyu Emulator Project
|
# SPDX-FileCopyrightText: 2017 yuzu Emulator Project
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
# This function downloads a binary library package from our external repo.
|
# This function downloads a binary library package from our external repo.
|
||||||
|
@ -17,9 +17,6 @@ if (WIN32)
|
||||||
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
set(package_repo "ext-linux-bin/-/raw/main/")
|
set(package_repo "ext-linux-bin/-/raw/main/")
|
||||||
set(package_extension ".tar.xz")
|
set(package_extension ".tar.xz")
|
||||||
# elseif (APPLE)
|
|
||||||
# set(package_repo "ext-osx-bin/-/raw/main/")
|
|
||||||
# set(package_extension ".dmg")
|
|
||||||
elseif (ANDROID)
|
elseif (ANDROID)
|
||||||
set(package_repo "ext-android-bin/-/raw/main/")
|
set(package_repo "ext-android-bin/-/raw/main/")
|
||||||
set(package_extension ".tar.xz")
|
set(package_extension ".tar.xz")
|
||||||
|
|
|
@ -5,5 +5,5 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
Please check out the
|
Please check out the
|
||||||
|
|
||||||
* [Contributors's guide](https://gitlab.com/suyu-emu/suyu/-/wikis/Contributing).
|
* [Conributors's guide](https://gitlab.com/suyu-emu/suyu/-/wikis/Contributing).
|
||||||
* [Merge request guidelines](https://gitlab.com/suyu-emu/suyu/-/wikis/Merge-requests)
|
* [Merge request guidelines](https://gitlab.com/suyu-emu/suyu/-/wikis/Merge-requests)
|
||||||
|
|
12
README.md
12
README.md
|
@ -18,9 +18,9 @@ This repo is based on Yuzu EA 4176.
|
||||||
<br>
|
<br>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<h4 align="center"><b>suyu</b> is the continuation of the world's most popular, open-source, Nintendo Switch emulator, yuzu.
|
<h4 align="center"><b>suyu</b>, pronounced "sue-you" (wink wink) is the continuation of the world's most popular, open-source, Nintendo Switch emulator, yuzu.
|
||||||
<br>
|
<br>
|
||||||
It is written in C++ with portability in mind, and we're actively working on builds for Windows, Linux and Android.
|
It is written in C++ with portability in mind, and we actively maintain builds for Windows, Linux and Android.
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
@ -51,12 +51,10 @@ You can also contact any of the developers on Discord to learn more about the cu
|
||||||
|
|
||||||
## Downloads
|
## Downloads
|
||||||
|
|
||||||
* __Windows__: [Releases](https://gitlab.com/suyu-emu/suyu/-/releases)
|
* __Windows__: WIP
|
||||||
* __Linux__: [Releases](https://gitlab.com/suyu-emu/suyu/-/releases)
|
* __Linux__: WIP
|
||||||
* __macOS__: [Releases](https://gitlab.com/suyu-emu/suyu/-/releases)
|
|
||||||
* __Android__: [Releases](https://gitlab.com/suyu-emu/suyu/-/releases)
|
|
||||||
|
|
||||||
We have official builds [here.](https://gitlab.com/suyu-emu/suyu/-/releases) If any website or person is claiming to have a build for suyu, take that with a grain of salt.
|
We don't have any official builds yet! If any website or person is claiming to have a build for suyu, take that with a grain of salt, because it might contain malware. Until we do have an official build, it might be a better idea to keep using the last version of yuzu.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
|
84
dist/icns_generator.sh
vendored
84
dist/icns_generator.sh
vendored
|
@ -1,72 +1,14 @@
|
||||||
#!/bin/bash
|
mkdir suyu.iconset
|
||||||
# icns_generator.sh GNU GPLv3 License
|
convert -background none -resize 16x16 suyu.svg suyu.iconset/icon_16x16.png;
|
||||||
# Run this script when a new logo is made and the svg file inside.
|
convert -background none -resize 32x32 suyu.svg suyu.iconset/icon_16x16@2x.png;
|
||||||
# You should install Imagemagick to make the conversions: $brew install imagemagick
|
convert -background none -resize 32x32 suyu.svg suyu.iconset/icon_32x32.png;
|
||||||
|
convert -background none -resize 64x64 suyu.svg suyu.iconset/icon_32x32@2x.png;
|
||||||
|
convert -background none -resize 128x128 suyu.svg suyu.iconset/icon_128x128.png;
|
||||||
|
convert -background none -resize 256x256 suyu.svg suyu.iconset/icon_256x256.png;
|
||||||
|
convert -background none -resize 256x256 suyu.svg suyu.iconset/icon_128x128@2x.png;
|
||||||
|
convert -background none -resize 512x512 suyu.svg suyu.iconset/icon_256x256@2x.png;
|
||||||
|
convert -background none -resize 512x512 suyu.svg suyu.iconset/icon_512x512.png;
|
||||||
|
convert -background none -resize 1024x1024 suyu.svg suyu.iconset/icon_512x512@2x.png;
|
||||||
|
|
||||||
# Change working dir to where this script is located.
|
iconutil -c icns suyu.iconset
|
||||||
cd "${0%/*}"
|
rm -rf suyu.iconset
|
||||||
|
|
||||||
if [ -z $1 ]; then
|
|
||||||
echo "icns_generator.sh GNU GPLv3 License"
|
|
||||||
echo "Run this script when a new logo is made and the svg file inside."
|
|
||||||
echo ""
|
|
||||||
echo "Syntax: ./icns_generator <input.svg>"
|
|
||||||
echo ""
|
|
||||||
echo "Don't forget to install imagemagick: "
|
|
||||||
echo "$ brew install imagemagick"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Error Handling Stuff:
|
|
||||||
## Check command availability
|
|
||||||
check_command() {
|
|
||||||
if ! command -v "$1" &> /dev/null; then
|
|
||||||
read -s -n 1 -p "Error: '$1' command not found. Please install $2."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
## Convert image with error handling
|
|
||||||
convert_image() {
|
|
||||||
convert -background none -resize "$2" "$1" "$3" || {
|
|
||||||
read -s -n 1 -p "Error: Conversion failed for $1"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check required commands
|
|
||||||
check_command "convert" "ImageMagick"
|
|
||||||
check_command "iconutil" "macOS"
|
|
||||||
|
|
||||||
# Create the iconset directory
|
|
||||||
mkdir suyu.iconset || {
|
|
||||||
read -s -n 1 -p "Error: Unable to create suyu.iconset directory."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Convert images
|
|
||||||
convert_image "$1" 16x16 suyu.iconset/icon_16x16.png
|
|
||||||
convert_image "$1" 32x32 suyu.iconset/icon_16x16@2x.png
|
|
||||||
convert_image "$1" 32x32 suyu.iconset/icon_32x32.png
|
|
||||||
convert_image "$1" 64x64 suyu.iconset/icon_32x32@2x.png
|
|
||||||
convert_image "$1" 128x128 suyu.iconset/icon_128x128.png
|
|
||||||
convert_image "$1" 256x256 suyu.iconset/icon_256x256.png
|
|
||||||
convert_image "$1" 256x256 suyu.iconset/icon_128x128@2x.png
|
|
||||||
convert_image "$1" 512x512 suyu.iconset/icon_256x256@2x.png
|
|
||||||
convert_image "$1" 512x512 suyu.iconset/icon_512x512.png
|
|
||||||
convert_image "$1" 1024x1024 suyu.iconset/icon_512x512@2x.png
|
|
||||||
|
|
||||||
# Create the ICNS file
|
|
||||||
iconutil -c icns suyu.iconset || {
|
|
||||||
read -s -n 1 -p "Error: Failed to create ICNS file."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Remove the temporary iconset directory
|
|
||||||
rm -rf suyu.iconset || {
|
|
||||||
read -s -n 1 -p "Error: Unable to remove suyu.iconset directory."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
echo -s -n 1 -p "Icon generation completed successfully."
|
|
||||||
echo ""
|
|
||||||
|
|
BIN
dist/suyu.icns
vendored
BIN
dist/suyu.icns
vendored
Binary file not shown.
2
externals/xbyak
vendored
2
externals/xbyak
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 9c0f5d3ecb06d2c93c2b59becb9b3b763213e74e
|
Subproject commit a1ac3750f9a639b5a6c6d6c7da4259b8d6790989
|
|
@ -72,12 +72,6 @@ class AppletLauncherFragment : Fragment() {
|
||||||
R.string.mii_edit_applet_description,
|
R.string.mii_edit_applet_description,
|
||||||
R.drawable.ic_mii,
|
R.drawable.ic_mii,
|
||||||
AppletInfo.MiiEdit
|
AppletInfo.MiiEdit
|
||||||
),
|
|
||||||
Applet(
|
|
||||||
R.string.qlaunch_applet,
|
|
||||||
R.string.qlaunch_description,
|
|
||||||
R.drawable.ic_home,
|
|
||||||
AppletInfo.QLaunch
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ enum class AppletInfo(val appletId: Int, val entryId: Long = 0) {
|
||||||
None(0x00),
|
None(0x00),
|
||||||
Application(0x01),
|
Application(0x01),
|
||||||
OverlayDisplay(0x02),
|
OverlayDisplay(0x02),
|
||||||
QLaunch(0x03, 0x0100000000001000),
|
QLaunch(0x03),
|
||||||
Starter(0x04),
|
Starter(0x04),
|
||||||
Auth(0x0A),
|
Auth(0x0A),
|
||||||
Cabinet(0x0B, 0x0100000000001002),
|
Cabinet(0x0B, 0x0100000000001002),
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
<vector android:alpha="1"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="?attr/colorControlNormal"
|
|
||||||
android:pathData="M21.59,11.31 L12.41,2.9a0.55,0.55 0,0 0,-0.75 0L2.47,11.31a0.54,0.54 0,0 0,0.38 0.93H4.41a0.35,0.35 0,0 1,0.35 0.35V20.32a0.54,0.54 0,0 0,0.54 0.54H18.77a0.54,0.54 0,0 0,0.54 -0.54V12.58a0.35,0.35 0,0 1,0.35 -0.35H21.21A0.54,0.54 0,0 0,21.59 11.31ZM15,16.65a0.43,0.43 0,0 1,-0.43 0.43H9.5a0.43,0.43 0,0 1,-0.43 -0.43V12.66a0.43,0.43 0,0 1,0.43 -0.43H14.56a0.43,0.43 0,0 1,0.43 0.43Z"
|
|
||||||
/>
|
|
||||||
</vector>
|
|
|
@ -145,8 +145,6 @@
|
||||||
<string name="keys_missing_help">https://suyu-emu.org/help/quickstart/#dumping-decryption-keys</string>
|
<string name="keys_missing_help">https://suyu-emu.org/help/quickstart/#dumping-decryption-keys</string>
|
||||||
|
|
||||||
<!-- Applet launcher strings -->
|
<!-- Applet launcher strings -->
|
||||||
<string name="qlaunch_applet">Qlaunch</string>
|
|
||||||
<string name="qlaunch_description">Launch applications from the system home screen</string>
|
|
||||||
<string name="applets">Applet launcher</string>
|
<string name="applets">Applet launcher</string>
|
||||||
<string name="applets_description">Launch system applets using installed firmware</string>
|
<string name="applets_description">Launch system applets using installed firmware</string>
|
||||||
<string name="applets_error_firmware">Firmware not installed</string>
|
<string name="applets_error_firmware">Firmware not installed</string>
|
||||||
|
|
|
@ -136,6 +136,8 @@ add_library(common STATIC
|
||||||
string_util.cpp
|
string_util.cpp
|
||||||
string_util.h
|
string_util.h
|
||||||
swap.h
|
swap.h
|
||||||
|
telemetry.cpp
|
||||||
|
telemetry.h
|
||||||
thread.cpp
|
thread.cpp
|
||||||
thread.h
|
thread.h
|
||||||
thread_queue_list.h
|
thread_queue_list.h
|
||||||
|
|
|
@ -530,9 +530,9 @@ struct Values {
|
||||||
Setting<bool> mouse_enabled{linkage, false, "mouse_enabled", Category::Controls};
|
Setting<bool> mouse_enabled{linkage, false, "mouse_enabled", Category::Controls};
|
||||||
|
|
||||||
Setting<u8, true> mouse_panning_x_sensitivity{
|
Setting<u8, true> mouse_panning_x_sensitivity{
|
||||||
linkage, 50, 1, 200, "mouse_panning_x_sensitivity", Category::Controls};
|
linkage, 50, 1, 100, "mouse_panning_x_sensitivity", Category::Controls};
|
||||||
Setting<u8, true> mouse_panning_y_sensitivity{
|
Setting<u8, true> mouse_panning_y_sensitivity{
|
||||||
linkage, 50, 1, 200, "mouse_panning_y_sensitivity", Category::Controls};
|
linkage, 50, 1, 100, "mouse_panning_y_sensitivity", Category::Controls};
|
||||||
Setting<u8, true> mouse_panning_deadzone_counterweight{
|
Setting<u8, true> mouse_panning_deadzone_counterweight{
|
||||||
linkage, 20, 0, 100, "mouse_panning_deadzone_counterweight", Category::Controls};
|
linkage, 20, 0, 100, "mouse_panning_deadzone_counterweight", Category::Controls};
|
||||||
Setting<u8, true> mouse_panning_decay_strength{
|
Setting<u8, true> mouse_panning_decay_strength{
|
||||||
|
@ -611,7 +611,8 @@ struct Values {
|
||||||
Category::Network};
|
Category::Network};
|
||||||
|
|
||||||
// WebService
|
// WebService
|
||||||
Setting<std::string> web_api_url{linkage, "https://suyu.dev", "web_api_url",
|
Setting<bool> enable_telemetry{linkage, false, "enable_telemetry", Category::WebService};
|
||||||
|
Setting<std::string> web_api_url{linkage, "http://74.113.97.71:3000", "web_api_url",
|
||||||
Category::WebService};
|
Category::WebService};
|
||||||
Setting<std::string> suyu_username{linkage, std::string(), "suyu_username",
|
Setting<std::string> suyu_username{linkage, std::string(), "suyu_username",
|
||||||
Category::WebService};
|
Category::WebService};
|
||||||
|
|
119
src/common/telemetry.cpp
Normal file
119
src/common/telemetry.cpp
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
|
#include "common/scm_rev.h"
|
||||||
|
#include "common/telemetry.h"
|
||||||
|
|
||||||
|
#ifdef ARCHITECTURE_x86_64
|
||||||
|
#include "common/x64/cpu_detect.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Common::Telemetry {
|
||||||
|
|
||||||
|
void FieldCollection::Accept(VisitorInterface& visitor) const {
|
||||||
|
for (const auto& field : fields) {
|
||||||
|
field.second->Accept(visitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FieldCollection::AddField(std::unique_ptr<FieldInterface> field) {
|
||||||
|
fields[field->GetName()] = std::move(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void Field<T>::Accept(VisitorInterface& visitor) const {
|
||||||
|
visitor.Visit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
template class Field<bool>;
|
||||||
|
template class Field<double>;
|
||||||
|
template class Field<float>;
|
||||||
|
template class Field<u8>;
|
||||||
|
template class Field<u16>;
|
||||||
|
template class Field<u32>;
|
||||||
|
template class Field<u64>;
|
||||||
|
template class Field<s8>;
|
||||||
|
template class Field<s16>;
|
||||||
|
template class Field<s32>;
|
||||||
|
template class Field<s64>;
|
||||||
|
template class Field<std::string>;
|
||||||
|
template class Field<const char*>;
|
||||||
|
template class Field<std::chrono::microseconds>;
|
||||||
|
|
||||||
|
void AppendBuildInfo(FieldCollection& fc) {
|
||||||
|
const bool is_git_dirty{std::strstr(Common::g_scm_desc, "dirty") != nullptr};
|
||||||
|
fc.AddField(FieldType::App, "Git_IsDirty", is_git_dirty);
|
||||||
|
fc.AddField(FieldType::App, "Git_Branch", Common::g_scm_branch);
|
||||||
|
fc.AddField(FieldType::App, "Git_Revision", Common::g_scm_rev);
|
||||||
|
fc.AddField(FieldType::App, "BuildDate", Common::g_build_date);
|
||||||
|
fc.AddField(FieldType::App, "BuildName", Common::g_build_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppendCPUInfo(FieldCollection& fc) {
|
||||||
|
#ifdef ARCHITECTURE_x86_64
|
||||||
|
|
||||||
|
const auto& caps = Common::GetCPUCaps();
|
||||||
|
const auto add_field = [&fc](std::string_view field_name, const auto& field_value) {
|
||||||
|
fc.AddField(FieldType::UserSystem, field_name, field_value);
|
||||||
|
};
|
||||||
|
add_field("CPU_Model", caps.cpu_string);
|
||||||
|
add_field("CPU_BrandString", caps.brand_string);
|
||||||
|
|
||||||
|
add_field("CPU_Extension_x64_SSE", caps.sse);
|
||||||
|
add_field("CPU_Extension_x64_SSE2", caps.sse2);
|
||||||
|
add_field("CPU_Extension_x64_SSE3", caps.sse3);
|
||||||
|
add_field("CPU_Extension_x64_SSSE3", caps.ssse3);
|
||||||
|
add_field("CPU_Extension_x64_SSE41", caps.sse4_1);
|
||||||
|
add_field("CPU_Extension_x64_SSE42", caps.sse4_2);
|
||||||
|
|
||||||
|
add_field("CPU_Extension_x64_AVX", caps.avx);
|
||||||
|
add_field("CPU_Extension_x64_AVX_VNNI", caps.avx_vnni);
|
||||||
|
add_field("CPU_Extension_x64_AVX2", caps.avx2);
|
||||||
|
|
||||||
|
// Skylake-X/SP level AVX512, for compatibility with the previous telemetry field
|
||||||
|
add_field("CPU_Extension_x64_AVX512",
|
||||||
|
caps.avx512f && caps.avx512cd && caps.avx512vl && caps.avx512dq && caps.avx512bw);
|
||||||
|
|
||||||
|
add_field("CPU_Extension_x64_AVX512F", caps.avx512f);
|
||||||
|
add_field("CPU_Extension_x64_AVX512CD", caps.avx512cd);
|
||||||
|
add_field("CPU_Extension_x64_AVX512VL", caps.avx512vl);
|
||||||
|
add_field("CPU_Extension_x64_AVX512DQ", caps.avx512dq);
|
||||||
|
add_field("CPU_Extension_x64_AVX512BW", caps.avx512bw);
|
||||||
|
add_field("CPU_Extension_x64_AVX512BITALG", caps.avx512bitalg);
|
||||||
|
add_field("CPU_Extension_x64_AVX512VBMI", caps.avx512vbmi);
|
||||||
|
|
||||||
|
add_field("CPU_Extension_x64_AES", caps.aes);
|
||||||
|
add_field("CPU_Extension_x64_BMI1", caps.bmi1);
|
||||||
|
add_field("CPU_Extension_x64_BMI2", caps.bmi2);
|
||||||
|
add_field("CPU_Extension_x64_F16C", caps.f16c);
|
||||||
|
add_field("CPU_Extension_x64_FMA", caps.fma);
|
||||||
|
add_field("CPU_Extension_x64_FMA4", caps.fma4);
|
||||||
|
add_field("CPU_Extension_x64_GFNI", caps.gfni);
|
||||||
|
add_field("CPU_Extension_x64_INVARIANT_TSC", caps.invariant_tsc);
|
||||||
|
add_field("CPU_Extension_x64_LZCNT", caps.lzcnt);
|
||||||
|
add_field("CPU_Extension_x64_MONITORX", caps.monitorx);
|
||||||
|
add_field("CPU_Extension_x64_MOVBE", caps.movbe);
|
||||||
|
add_field("CPU_Extension_x64_PCLMULQDQ", caps.pclmulqdq);
|
||||||
|
add_field("CPU_Extension_x64_POPCNT", caps.popcnt);
|
||||||
|
add_field("CPU_Extension_x64_SHA", caps.sha);
|
||||||
|
add_field("CPU_Extension_x64_WAITPKG", caps.waitpkg);
|
||||||
|
#else
|
||||||
|
fc.AddField(FieldType::UserSystem, "CPU_Model", "Other");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppendOSInfo(FieldCollection& fc) {
|
||||||
|
#ifdef __APPLE__
|
||||||
|
fc.AddField(FieldType::UserSystem, "OsPlatform", "Apple");
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
fc.AddField(FieldType::UserSystem, "OsPlatform", "Windows");
|
||||||
|
#elif defined(__linux__) || defined(linux) || defined(__linux)
|
||||||
|
fc.AddField(FieldType::UserSystem, "OsPlatform", "Linux");
|
||||||
|
#else
|
||||||
|
fc.AddField(FieldType::UserSystem, "OsPlatform", "Unknown");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Common::Telemetry
|
209
src/common/telemetry.h
Normal file
209
src/common/telemetry.h
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
// SPDX-FileCopyrightText: 2017 Citra Emulator Project & 2024 suyu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include "common/common_funcs.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace Common::Telemetry {
|
||||||
|
|
||||||
|
/// Field type, used for grouping fields together in the final submitted telemetry log
|
||||||
|
enum class FieldType : u8 {
|
||||||
|
None = 0, ///< No specified field group
|
||||||
|
App, ///< suyu application fields (e.g. version, branch, etc.)
|
||||||
|
Session, ///< Emulated session fields (e.g. title ID, log, etc.)
|
||||||
|
Performance, ///< Emulated performance (e.g. fps, emulated CPU speed, etc.)
|
||||||
|
UserFeedback, ///< User submitted feedback (e.g. star rating, user notes, etc.)
|
||||||
|
UserConfig, ///< User configuration fields (e.g. emulated CPU core, renderer, etc.)
|
||||||
|
UserSystem, ///< User system information (e.g. host CPU type, RAM, etc.)
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VisitorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface class for telemetry data fields.
|
||||||
|
*/
|
||||||
|
class FieldInterface {
|
||||||
|
public:
|
||||||
|
virtual ~FieldInterface() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept method for the visitor pattern.
|
||||||
|
* @param visitor Reference to the visitor that will visit this field.
|
||||||
|
*/
|
||||||
|
virtual void Accept(VisitorInterface& visitor) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the name of this field.
|
||||||
|
* @returns Name of this field as a string.
|
||||||
|
*/
|
||||||
|
virtual const std::string& GetName() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a telemetry data field, i.e. a unit of data that gets logged and submitted to our
|
||||||
|
* telemetry web service.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
class Field : public FieldInterface {
|
||||||
|
public:
|
||||||
|
SUYU_NON_COPYABLE(Field);
|
||||||
|
|
||||||
|
Field(FieldType type_, std::string_view name_, T value_)
|
||||||
|
: name(name_), type(type_), value(std::move(value_)) {}
|
||||||
|
|
||||||
|
~Field() override = default;
|
||||||
|
|
||||||
|
Field(Field&&) noexcept = default;
|
||||||
|
Field& operator=(Field&& other) noexcept = default;
|
||||||
|
|
||||||
|
void Accept(VisitorInterface& visitor) const override;
|
||||||
|
|
||||||
|
[[nodiscard]] const std::string& GetName() const override {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the type of the field.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] FieldType GetType() const {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of the field.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] const T& GetValue() const {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool operator==(const Field& other) const {
|
||||||
|
return (type == other.type) && (name == other.name) && (value == other.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool operator!=(const Field& other) const {
|
||||||
|
return !operator==(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string name; ///< Field name, must be unique
|
||||||
|
FieldType type{}; ///< Field type, used for grouping fields together
|
||||||
|
T value; ///< Field value
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection of data fields that have been logged.
|
||||||
|
*/
|
||||||
|
class FieldCollection final {
|
||||||
|
public:
|
||||||
|
SUYU_NON_COPYABLE(FieldCollection);
|
||||||
|
|
||||||
|
FieldCollection() = default;
|
||||||
|
~FieldCollection() = default;
|
||||||
|
|
||||||
|
FieldCollection(FieldCollection&&) noexcept = default;
|
||||||
|
FieldCollection& operator=(FieldCollection&&) noexcept = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept method for the visitor pattern, visits each field in the collection.
|
||||||
|
* @param visitor Reference to the visitor that will visit each field.
|
||||||
|
*/
|
||||||
|
void Accept(VisitorInterface& visitor) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new field and adds it to the field collection.
|
||||||
|
* @param type Type of the field to add.
|
||||||
|
* @param name Name of the field to add.
|
||||||
|
* @param value Value for the field to add.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
void AddField(FieldType type, std::string_view name, T value) {
|
||||||
|
return AddField(std::make_unique<Field<T>>(type, name, std::move(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new field to the field collection.
|
||||||
|
* @param field Field to add to the field collection.
|
||||||
|
*/
|
||||||
|
void AddField(std::unique_ptr<FieldInterface> field);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<std::string, std::unique_ptr<FieldInterface>> fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Telemetry fields visitor interface class. A backend to log to a web service should implement
|
||||||
|
* this interface.
|
||||||
|
*/
|
||||||
|
struct VisitorInterface {
|
||||||
|
virtual ~VisitorInterface() = default;
|
||||||
|
|
||||||
|
virtual void Visit(const Field<bool>& field) = 0;
|
||||||
|
virtual void Visit(const Field<double>& field) = 0;
|
||||||
|
virtual void Visit(const Field<float>& field) = 0;
|
||||||
|
virtual void Visit(const Field<u8>& field) = 0;
|
||||||
|
virtual void Visit(const Field<u16>& field) = 0;
|
||||||
|
virtual void Visit(const Field<u32>& field) = 0;
|
||||||
|
virtual void Visit(const Field<u64>& field) = 0;
|
||||||
|
virtual void Visit(const Field<s8>& field) = 0;
|
||||||
|
virtual void Visit(const Field<s16>& field) = 0;
|
||||||
|
virtual void Visit(const Field<s32>& field) = 0;
|
||||||
|
virtual void Visit(const Field<s64>& field) = 0;
|
||||||
|
virtual void Visit(const Field<std::string>& field) = 0;
|
||||||
|
virtual void Visit(const Field<const char*>& field) = 0;
|
||||||
|
virtual void Visit(const Field<std::chrono::microseconds>& field) = 0;
|
||||||
|
|
||||||
|
/// Completion method, called once all fields have been visited
|
||||||
|
virtual void Complete() = 0;
|
||||||
|
virtual bool SubmitTestcase() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty implementation of VisitorInterface that drops all fields. Used when a functional
|
||||||
|
* backend implementation is not available.
|
||||||
|
*/
|
||||||
|
struct NullVisitor final : public VisitorInterface {
|
||||||
|
SUYU_NON_COPYABLE(NullVisitor);
|
||||||
|
|
||||||
|
NullVisitor() = default;
|
||||||
|
~NullVisitor() override = default;
|
||||||
|
|
||||||
|
void Visit(const Field<bool>& /*field*/) override {}
|
||||||
|
void Visit(const Field<double>& /*field*/) override {}
|
||||||
|
void Visit(const Field<float>& /*field*/) override {}
|
||||||
|
void Visit(const Field<u8>& /*field*/) override {}
|
||||||
|
void Visit(const Field<u16>& /*field*/) override {}
|
||||||
|
void Visit(const Field<u32>& /*field*/) override {}
|
||||||
|
void Visit(const Field<u64>& /*field*/) override {}
|
||||||
|
void Visit(const Field<s8>& /*field*/) override {}
|
||||||
|
void Visit(const Field<s16>& /*field*/) override {}
|
||||||
|
void Visit(const Field<s32>& /*field*/) override {}
|
||||||
|
void Visit(const Field<s64>& /*field*/) override {}
|
||||||
|
void Visit(const Field<std::string>& /*field*/) override {}
|
||||||
|
void Visit(const Field<const char*>& /*field*/) override {}
|
||||||
|
void Visit(const Field<std::chrono::microseconds>& /*field*/) override {}
|
||||||
|
|
||||||
|
void Complete() override {}
|
||||||
|
bool SubmitTestcase() override {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Appends build-specific information to the given FieldCollection,
|
||||||
|
/// such as branch name, revision hash, etc.
|
||||||
|
void AppendBuildInfo(FieldCollection& fc);
|
||||||
|
|
||||||
|
/// Appends CPU-specific information to the given FieldCollection,
|
||||||
|
/// such as instruction set extensions, etc.
|
||||||
|
void AppendCPUInfo(FieldCollection& fc);
|
||||||
|
|
||||||
|
/// Appends OS-specific information to the given FieldCollection,
|
||||||
|
/// such as platform name, etc.
|
||||||
|
void AppendOSInfo(FieldCollection& fc);
|
||||||
|
|
||||||
|
} // namespace Common::Telemetry
|
|
@ -1136,6 +1136,8 @@ add_library(core STATIC
|
||||||
precompiled_headers.h
|
precompiled_headers.h
|
||||||
reporter.cpp
|
reporter.cpp
|
||||||
reporter.h
|
reporter.h
|
||||||
|
telemetry_session.cpp
|
||||||
|
telemetry_session.h
|
||||||
tools/freezer.cpp
|
tools/freezer.cpp
|
||||||
tools/freezer.h
|
tools/freezer.h
|
||||||
tools/renderdoc.cpp
|
tools/renderdoc.cpp
|
||||||
|
|
|
@ -11,8 +11,6 @@
|
||||||
#include <dynarmic/frontend/A64/decoder/a64.h>
|
#include <dynarmic/frontend/A64/decoder/a64.h>
|
||||||
#include <dynarmic/frontend/imm.h>
|
#include <dynarmic/frontend/imm.h>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
|
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
#include "core/memory/cheat_engine.h"
|
#include "core/memory/cheat_engine.h"
|
||||||
#include "core/perf_stats.h"
|
#include "core/perf_stats.h"
|
||||||
#include "core/reporter.h"
|
#include "core/reporter.h"
|
||||||
|
#include "core/telemetry_session.h"
|
||||||
#include "core/tools/freezer.h"
|
#include "core/tools/freezer.h"
|
||||||
#include "core/tools/renderdoc.h"
|
#include "core/tools/renderdoc.h"
|
||||||
#include "hid_core/hid_core.h"
|
#include "hid_core/hid_core.h"
|
||||||
|
@ -271,6 +272,8 @@ struct System::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) {
|
SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) {
|
||||||
|
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
||||||
|
|
||||||
host1x_core = std::make_unique<Tegra::Host1x::Host1x>(system);
|
host1x_core = std::make_unique<Tegra::Host1x::Host1x>(system);
|
||||||
gpu_core = VideoCore::CreateGPU(emu_window, system);
|
gpu_core = VideoCore::CreateGPU(emu_window, system);
|
||||||
if (!gpu_core) {
|
if (!gpu_core) {
|
||||||
|
@ -351,6 +354,8 @@ struct System::Impl {
|
||||||
return init_result;
|
return init_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
|
||||||
|
|
||||||
// Initialize cheat engine
|
// Initialize cheat engine
|
||||||
if (cheat_engine) {
|
if (cheat_engine) {
|
||||||
cheat_engine->Initialize();
|
cheat_engine->Initialize();
|
||||||
|
@ -396,6 +401,21 @@ struct System::Impl {
|
||||||
void ShutdownMainProcess() {
|
void ShutdownMainProcess() {
|
||||||
SetShuttingDown(true);
|
SetShuttingDown(true);
|
||||||
|
|
||||||
|
// Log last frame performance stats if game was loaded
|
||||||
|
if (perf_stats) {
|
||||||
|
const auto perf_results = GetAndResetPerfStats();
|
||||||
|
constexpr auto performance = Common::Telemetry::FieldType::Performance;
|
||||||
|
|
||||||
|
telemetry_session->AddField(performance, "Shutdown_EmulationSpeed",
|
||||||
|
perf_results.emulation_speed * 100.0);
|
||||||
|
telemetry_session->AddField(performance, "Shutdown_Framerate",
|
||||||
|
perf_results.average_game_fps);
|
||||||
|
telemetry_session->AddField(performance, "Shutdown_Frametime",
|
||||||
|
perf_results.frametime * 1000.0);
|
||||||
|
telemetry_session->AddField(performance, "Mean_Frametime_MS",
|
||||||
|
perf_stats->GetMeanFrametime());
|
||||||
|
}
|
||||||
|
|
||||||
is_powered_on = false;
|
is_powered_on = false;
|
||||||
exit_locked = false;
|
exit_locked = false;
|
||||||
exit_requested = false;
|
exit_requested = false;
|
||||||
|
@ -414,6 +434,7 @@ struct System::Impl {
|
||||||
service_manager.reset();
|
service_manager.reset();
|
||||||
fs_controller.Reset();
|
fs_controller.Reset();
|
||||||
cheat_engine.reset();
|
cheat_engine.reset();
|
||||||
|
telemetry_session.reset();
|
||||||
core_timing.ClearPendingEvents();
|
core_timing.ClearPendingEvents();
|
||||||
app_loader.reset();
|
app_loader.reset();
|
||||||
audio_core.reset();
|
audio_core.reset();
|
||||||
|
@ -513,6 +534,9 @@ struct System::Impl {
|
||||||
/// Services
|
/// Services
|
||||||
std::unique_ptr<Service::Services> services;
|
std::unique_ptr<Service::Services> services;
|
||||||
|
|
||||||
|
/// Telemetry session for this emulation session
|
||||||
|
std::unique_ptr<Core::TelemetrySession> telemetry_session;
|
||||||
|
|
||||||
/// Network instance
|
/// Network instance
|
||||||
Network::NetworkInstance network_instance;
|
Network::NetworkInstance network_instance;
|
||||||
|
|
||||||
|
@ -639,6 +663,14 @@ PerfStatsResults System::GetAndResetPerfStats() {
|
||||||
return impl->GetAndResetPerfStats();
|
return impl->GetAndResetPerfStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TelemetrySession& System::TelemetrySession() {
|
||||||
|
return *impl->telemetry_session;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TelemetrySession& System::TelemetrySession() const {
|
||||||
|
return *impl->telemetry_session;
|
||||||
|
}
|
||||||
|
|
||||||
Kernel::PhysicalCore& System::CurrentPhysicalCore() {
|
Kernel::PhysicalCore& System::CurrentPhysicalCore() {
|
||||||
return impl->kernel.CurrentPhysicalCore();
|
return impl->kernel.CurrentPhysicalCore();
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,6 +122,7 @@ class GPUDirtyMemoryManager;
|
||||||
class PerfStats;
|
class PerfStats;
|
||||||
class Reporter;
|
class Reporter;
|
||||||
class SpeedLimiter;
|
class SpeedLimiter;
|
||||||
|
class TelemetrySession;
|
||||||
|
|
||||||
struct PerfStatsResults;
|
struct PerfStatsResults;
|
||||||
|
|
||||||
|
@ -217,6 +218,12 @@ public:
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] bool IsPoweredOn() const;
|
[[nodiscard]] bool IsPoweredOn() const;
|
||||||
|
|
||||||
|
/// Gets a reference to the telemetry session for this emulation session.
|
||||||
|
[[nodiscard]] Core::TelemetrySession& TelemetrySession();
|
||||||
|
|
||||||
|
/// Gets a reference to the telemetry session for this emulation session.
|
||||||
|
[[nodiscard]] const Core::TelemetrySession& TelemetrySession() const;
|
||||||
|
|
||||||
/// Prepare the core emulation for a reschedule
|
/// Prepare the core emulation for a reschedule
|
||||||
void PrepareReschedule(u32 core_index);
|
void PrepareReschedule(u32 core_index);
|
||||||
|
|
||||||
|
|
|
@ -643,7 +643,7 @@ void KeyManager::ReloadKeys() {
|
||||||
const auto suyu_keys_dir = Common::FS::GetSuyuPath(Common::FS::SuyuPath::KeysDir);
|
const auto suyu_keys_dir = Common::FS::GetSuyuPath(Common::FS::SuyuPath::KeysDir);
|
||||||
|
|
||||||
if (!Common::FS::CreateDir(suyu_keys_dir)) {
|
if (!Common::FS::CreateDir(suyu_keys_dir)) {
|
||||||
LOG_ERROR(Crypto, "Failed to create the keys directory.");
|
LOG_ERROR(Core, "Failed to create the keys directory.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::values.use_dev_keys) {
|
if (Settings::values.use_dev_keys) {
|
||||||
|
@ -668,8 +668,6 @@ static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_
|
||||||
|
|
||||||
void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_title_keys) {
|
void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_title_keys) {
|
||||||
if (!Common::FS::Exists(file_path)) {
|
if (!Common::FS::Exists(file_path)) {
|
||||||
LOG_ERROR(Crypto, "Cannot handle key file '{}': File not found",
|
|
||||||
file_path.generic_string());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -677,12 +675,9 @@ void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_ti
|
||||||
Common::FS::OpenFileStream(file, file_path, std::ios_base::in);
|
Common::FS::OpenFileStream(file, file_path, std::ios_base::in);
|
||||||
|
|
||||||
if (!file.is_open()) {
|
if (!file.is_open()) {
|
||||||
LOG_ERROR(Crypto, "Failed to load key file at '{}': Can't open file",
|
|
||||||
file_path.generic_string());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO(Crypto, "Loading key file at '{}'", file_path.generic_string());
|
|
||||||
std::string line;
|
std::string line;
|
||||||
while (std::getline(file, line)) {
|
while (std::getline(file, line)) {
|
||||||
std::vector<std::string> out;
|
std::vector<std::string> out;
|
||||||
|
@ -708,8 +703,6 @@ void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_ti
|
||||||
u128 rights_id{};
|
u128 rights_id{};
|
||||||
std::memcpy(rights_id.data(), rights_id_raw.data(), rights_id_raw.size());
|
std::memcpy(rights_id.data(), rights_id_raw.data(), rights_id_raw.size());
|
||||||
Key128 key = Common::HexStringToArray<16>(out[1]);
|
Key128 key = Common::HexStringToArray<16>(out[1]);
|
||||||
|
|
||||||
LOG_INFO(Crypto, "Successfully loaded title key");
|
|
||||||
s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
|
s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
|
||||||
} else {
|
} else {
|
||||||
out[0] = Common::ToLower(out[0]);
|
out[0] = Common::ToLower(out[0]);
|
||||||
|
@ -792,20 +785,6 @@ bool KeyManager::BaseDeriveNecessary() const {
|
||||||
return !HasKey(key_type, index1, index2);
|
return !HasKey(key_type, index1, index2);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Ensure the files exists
|
|
||||||
const auto suyu_keys_dir = Common::FS::GetSuyuPath(Common::FS::SuyuPath::KeysDir);
|
|
||||||
|
|
||||||
if (!Common::FS::Exists(suyu_keys_dir /
|
|
||||||
(Settings::values.use_dev_keys ? "dev.keys" : "prod.keys"))) {
|
|
||||||
LOG_ERROR(Crypto, "No {} found",
|
|
||||||
(Settings::values.use_dev_keys ? "dev.keys" : "prod.keys"));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Common::FS::Exists(suyu_keys_dir / "title.keys")) {
|
|
||||||
LOG_WARNING(Crypto, "Could not locate a title.keys file");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (check_key_existence(S256KeyType::Header)) {
|
if (check_key_existence(S256KeyType::Header)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
|
||||||
{30, nullptr, "GetHomeButtonReaderLockAccessor"},
|
{30, nullptr, "GetHomeButtonReaderLockAccessor"},
|
||||||
{31, D<&ICommonStateGetter::GetReaderLockAccessorEx>, "GetReaderLockAccessorEx"},
|
{31, D<&ICommonStateGetter::GetReaderLockAccessorEx>, "GetReaderLockAccessorEx"},
|
||||||
{32, D<&ICommonStateGetter::GetWriterLockAccessorEx>, "GetWriterLockAccessorEx"},
|
{32, D<&ICommonStateGetter::GetWriterLockAccessorEx>, "GetWriterLockAccessorEx"},
|
||||||
{40, D<&ICommonStateGetter::GetCradleFwVersion>, "GetCradleFwVersion"},
|
{40, nullptr, "GetCradleFwVersion"},
|
||||||
{50, D<&ICommonStateGetter::IsVrModeEnabled>, "IsVrModeEnabled"},
|
{50, D<&ICommonStateGetter::IsVrModeEnabled>, "IsVrModeEnabled"},
|
||||||
{51, D<&ICommonStateGetter::SetVrModeEnabled>, "SetVrModeEnabled"},
|
{51, D<&ICommonStateGetter::SetVrModeEnabled>, "SetVrModeEnabled"},
|
||||||
{52, D<&ICommonStateGetter::SetLcdBacklighOffEnabled>, "SetLcdBacklighOffEnabled"},
|
{52, D<&ICommonStateGetter::SetLcdBacklighOffEnabled>, "SetLcdBacklighOffEnabled"},
|
||||||
|
@ -159,17 +159,6 @@ Result ICommonStateGetter::GetBootMode(Out<PM::SystemBootMode> out_boot_mode) {
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ICommonStateGetter::GetCradleFwVersion(OutArray<uint32_t, 4> out_version) {
|
|
||||||
LOG_DEBUG(Service_AM, "(STUBBED) called");
|
|
||||||
|
|
||||||
out_version[0] = 0;
|
|
||||||
out_version[1] = 0;
|
|
||||||
out_version[2] = 0;
|
|
||||||
out_version[3] = 0;
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ICommonStateGetter::IsVrModeEnabled(Out<bool> out_is_vr_mode_enabled) {
|
Result ICommonStateGetter::IsVrModeEnabled(Out<bool> out_is_vr_mode_enabled) {
|
||||||
LOG_DEBUG(Service_AM, "called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,6 @@ private:
|
||||||
Result GetOperationMode(Out<OperationMode> out_operation_mode);
|
Result GetOperationMode(Out<OperationMode> out_operation_mode);
|
||||||
Result GetPerformanceMode(Out<APM::PerformanceMode> out_performance_mode);
|
Result GetPerformanceMode(Out<APM::PerformanceMode> out_performance_mode);
|
||||||
Result GetBootMode(Out<PM::SystemBootMode> out_boot_mode);
|
Result GetBootMode(Out<PM::SystemBootMode> out_boot_mode);
|
||||||
Result GetCradleFwVersion(OutArray<uint32_t, 4> out_version);
|
|
||||||
Result IsVrModeEnabled(Out<bool> out_is_vr_mode_enabled);
|
Result IsVrModeEnabled(Out<bool> out_is_vr_mode_enabled);
|
||||||
Result SetVrModeEnabled(bool is_vr_mode_enabled);
|
Result SetVrModeEnabled(bool is_vr_mode_enabled);
|
||||||
Result SetLcdBacklighOffEnabled(bool is_lcd_backlight_off_enabled);
|
Result SetLcdBacklighOffEnabled(bool is_lcd_backlight_off_enabled);
|
||||||
|
|
|
@ -14,7 +14,7 @@ IGlobalStateController::IGlobalStateController(Core::System& system_)
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, nullptr, "RequestToEnterSleep"},
|
{0, nullptr, "RequestToEnterSleep"},
|
||||||
{1, nullptr, "EnterSleep"},
|
{1, nullptr, "EnterSleep"},
|
||||||
{2, D<&IGlobalStateController::StartSleepSequence>, "StartSleepSequence"},
|
{2, nullptr, "StartSleepSequence"},
|
||||||
{3, D<&IGlobalStateController::StartShutdownSequence>, "StartShutdownSequence"},
|
{3, D<&IGlobalStateController::StartShutdownSequence>, "StartShutdownSequence"},
|
||||||
{4, D<&IGlobalStateController::StartRebootSequence>, "StartRebootSequence"},
|
{4, D<&IGlobalStateController::StartRebootSequence>, "StartRebootSequence"},
|
||||||
{9, nullptr, "IsAutoPowerDownRequested"},
|
{9, nullptr, "IsAutoPowerDownRequested"},
|
||||||
|
@ -31,13 +31,6 @@ IGlobalStateController::IGlobalStateController(Core::System& system_)
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
}
|
}
|
||||||
|
|
||||||
IGlobalStateController::~IGlobalStateController() = default;
|
|
||||||
|
|
||||||
Result IGlobalStateController::StartSleepSequence(u8 a) {
|
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called, a={}", a);
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result IGlobalStateController::StartShutdownSequence() {
|
Result IGlobalStateController::StartShutdownSequence() {
|
||||||
LOG_INFO(Service_AM, "called");
|
LOG_INFO(Service_AM, "called");
|
||||||
system.Exit();
|
system.Exit();
|
||||||
|
@ -50,6 +43,8 @@ Result IGlobalStateController::StartRebootSequence() {
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IGlobalStateController::~IGlobalStateController() = default;
|
||||||
|
|
||||||
Result IGlobalStateController::LoadAndApplyIdlePolicySettings() {
|
Result IGlobalStateController::LoadAndApplyIdlePolicySettings() {
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
|
@ -18,7 +18,6 @@ public:
|
||||||
~IGlobalStateController() override;
|
~IGlobalStateController() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Result StartSleepSequence(u8 a);
|
|
||||||
Result StartShutdownSequence();
|
Result StartShutdownSequence();
|
||||||
Result StartRebootSequence();
|
Result StartRebootSequence();
|
||||||
Result LoadAndApplyIdlePolicySettings();
|
Result LoadAndApplyIdlePolicySettings();
|
||||||
|
|
|
@ -23,7 +23,7 @@ IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_, std::shared_ptr<Ap
|
||||||
{21, D<&IHomeMenuFunctions::GetPopFromGeneralChannelEvent>, "GetPopFromGeneralChannelEvent"},
|
{21, D<&IHomeMenuFunctions::GetPopFromGeneralChannelEvent>, "GetPopFromGeneralChannelEvent"},
|
||||||
{30, nullptr, "GetHomeButtonWriterLockAccessor"},
|
{30, nullptr, "GetHomeButtonWriterLockAccessor"},
|
||||||
{31, nullptr, "GetWriterLockAccessorEx"},
|
{31, nullptr, "GetWriterLockAccessorEx"},
|
||||||
{40, D<&IHomeMenuFunctions::IsSleepEnabled>, "IsSleepEnabled"},
|
{40, nullptr, "IsSleepEnabled"},
|
||||||
{41, D<&IHomeMenuFunctions::IsRebootEnabled>, "IsRebootEnabled"},
|
{41, D<&IHomeMenuFunctions::IsRebootEnabled>, "IsRebootEnabled"},
|
||||||
{50, nullptr, "LaunchSystemApplet"},
|
{50, nullptr, "LaunchSystemApplet"},
|
||||||
{51, nullptr, "LaunchStarter"},
|
{51, nullptr, "LaunchStarter"},
|
||||||
|
@ -64,15 +64,9 @@ Result IHomeMenuFunctions::GetPopFromGeneralChannelEvent(
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IHomeMenuFunctions::IsSleepEnabled(Out<bool> out_is_sleep_enabled) {
|
Result IHomeMenuFunctions::IsRebootEnabled(Out<bool> out_is_reboot_enbaled) {
|
||||||
LOG_INFO(Service_AM, "called");
|
LOG_INFO(Service_AM, "called");
|
||||||
*out_is_sleep_enabled = true;
|
*out_is_reboot_enbaled = true;
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result IHomeMenuFunctions::IsRebootEnabled(Out<bool> out_is_reboot_enabled) {
|
|
||||||
LOG_INFO(Service_AM, "called");
|
|
||||||
*out_is_reboot_enabled = true;
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,7 @@ private:
|
||||||
Result LockForeground();
|
Result LockForeground();
|
||||||
Result UnlockForeground();
|
Result UnlockForeground();
|
||||||
Result GetPopFromGeneralChannelEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
Result GetPopFromGeneralChannelEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||||
Result IsSleepEnabled(Out<bool> out_is_sleep_enabled);
|
Result IsRebootEnabled(Out<bool> out_is_reboot_enbaled);
|
||||||
Result IsRebootEnabled(Out<bool> out_is_reboot_enabled);
|
|
||||||
Result IsForceTerminateApplicationDisabledForDebug(
|
Result IsForceTerminateApplicationDisabledForDebug(
|
||||||
Out<bool> out_is_force_terminate_application_disabled_for_debug);
|
Out<bool> out_is_force_terminate_application_disabled_for_debug);
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,7 @@ FSP_SRV::FSP_SRV(Core::System& system_)
|
||||||
{24, nullptr, "RegisterSaveDataFileSystemAtomicDeletion"},
|
{24, nullptr, "RegisterSaveDataFileSystemAtomicDeletion"},
|
||||||
{25, nullptr, "DeleteSaveDataFileSystemBySaveDataSpaceId"},
|
{25, nullptr, "DeleteSaveDataFileSystemBySaveDataSpaceId"},
|
||||||
{26, nullptr, "FormatSdCardDryRun"},
|
{26, nullptr, "FormatSdCardDryRun"},
|
||||||
{27, D<&FSP_SRV::IsExFatSupported>, "IsExFatSupported"},
|
{27, nullptr, "IsExFatSupported"},
|
||||||
{28, nullptr, "DeleteSaveDataFileSystemBySaveDataAttribute"},
|
{28, nullptr, "DeleteSaveDataFileSystemBySaveDataAttribute"},
|
||||||
{30, nullptr, "OpenGameCardStorage"},
|
{30, nullptr, "OpenGameCardStorage"},
|
||||||
{31, nullptr, "OpenGameCardFileSystem"},
|
{31, nullptr, "OpenGameCardFileSystem"},
|
||||||
|
@ -235,14 +235,6 @@ Result FSP_SRV::CreateSaveDataFileSystem(FileSys::SaveDataCreationInfo save_crea
|
||||||
save_struct));
|
save_struct));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result FSP_SRV::IsExFatSupported(Out<bool> out_is_supported) {
|
|
||||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
|
||||||
|
|
||||||
*out_is_supported = true;
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result FSP_SRV::CreateSaveDataFileSystemBySystemSaveDataId(
|
Result FSP_SRV::CreateSaveDataFileSystemBySystemSaveDataId(
|
||||||
FileSys::SaveDataAttribute save_struct, FileSys::SaveDataCreationInfo save_create_struct) {
|
FileSys::SaveDataAttribute save_struct, FileSys::SaveDataCreationInfo save_create_struct) {
|
||||||
LOG_DEBUG(Service_FS, "called save_struct = {}", save_struct.DebugInfo());
|
LOG_DEBUG(Service_FS, "called save_struct = {}", save_struct.DebugInfo());
|
||||||
|
|
|
@ -53,7 +53,6 @@ private:
|
||||||
Result OpenSdCardFileSystem(OutInterface<IFileSystem> out_interface);
|
Result OpenSdCardFileSystem(OutInterface<IFileSystem> out_interface);
|
||||||
Result CreateSaveDataFileSystem(FileSys::SaveDataCreationInfo save_create_struct,
|
Result CreateSaveDataFileSystem(FileSys::SaveDataCreationInfo save_create_struct,
|
||||||
FileSys::SaveDataAttribute save_struct, u128 uid);
|
FileSys::SaveDataAttribute save_struct, u128 uid);
|
||||||
Result IsExFatSupported(Out<bool> out_is_supported);
|
|
||||||
Result CreateSaveDataFileSystemBySystemSaveDataId(
|
Result CreateSaveDataFileSystemBySystemSaveDataId(
|
||||||
FileSys::SaveDataAttribute save_struct, FileSys::SaveDataCreationInfo save_create_struct);
|
FileSys::SaveDataAttribute save_struct, FileSys::SaveDataCreationInfo save_create_struct);
|
||||||
Result OpenSaveDataFileSystem(OutInterface<IFileSystem> out_interface,
|
Result OpenSaveDataFileSystem(OutInterface<IFileSystem> out_interface,
|
||||||
|
|
|
@ -30,10 +30,10 @@ Result ISfMonitorService::Initialize(Out<u32> out_value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ISfMonitorService::GetGroupInfo(
|
Result ISfMonitorService::GetGroupInfo(
|
||||||
GroupInfo in_group_info, OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info) {
|
OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info) {
|
||||||
LOG_WARNING(Service_LDN, "(STUBBED) called");
|
LOG_WARNING(Service_LDN, "(STUBBED) called");
|
||||||
|
|
||||||
memcpy(out_group_info, &in_group_info, sizeof(GroupInfo));
|
*out_group_info = GroupInfo{};
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Result Initialize(Out<u32> out_value);
|
Result Initialize(Out<u32> out_value);
|
||||||
Result GetGroupInfo(GroupInfo in_group_info,
|
Result GetGroupInfo(OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info);
|
||||||
OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::LDN
|
} // namespace Service::LDN
|
||||||
|
|
|
@ -40,10 +40,10 @@ Result ISfServiceMonitor::Initialize(Out<u32> out_value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ISfServiceMonitor::GetGroupInfo(
|
Result ISfServiceMonitor::GetGroupInfo(
|
||||||
GroupInfo in_group_info, OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info) {
|
OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info) {
|
||||||
LOG_WARNING(Service_LDN, "(STUBBED) called");
|
LOG_WARNING(Service_LDN, "(STUBBED) called");
|
||||||
|
|
||||||
memcpy(out_group_info, &in_group_info, sizeof(GroupInfo));
|
*out_group_info = GroupInfo{};
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Result Initialize(Out<u32> out_value);
|
Result Initialize(Out<u32> out_value);
|
||||||
Result GetGroupInfo(GroupInfo in_group_info,
|
Result GetGroupInfo(OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info);
|
||||||
OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::LDN
|
} // namespace Service::LDN
|
||||||
|
|
|
@ -507,7 +507,7 @@ void IGeneralService::GetCurrentIpConfigInfo(HLERequestContext& ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void IGeneralService::IsWirelessCommunicationEnabled(HLERequestContext& ctx) {
|
void IGeneralService::IsWirelessCommunicationEnabled(HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_NIFM, "called");
|
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <boost/container/small_vector.hpp>
|
#include <boost/container/small_vector.hpp>
|
||||||
|
|
||||||
#include "common/microprofile.h"
|
#include "common/microprofile.h"
|
||||||
|
|
|
@ -509,13 +509,4 @@ struct TvSettings {
|
||||||
};
|
};
|
||||||
static_assert(sizeof(TvSettings) == 0x20, "TvSettings is an invalid size");
|
static_assert(sizeof(TvSettings) == 0x20, "TvSettings is an invalid size");
|
||||||
|
|
||||||
/// This is nn::settings::system::RebootlessSystemUpdateVersion
|
|
||||||
struct RebootlessSystemUpdateVersion {
|
|
||||||
u32 version;
|
|
||||||
u8 reserved[0x1c];
|
|
||||||
char display_version[0x20];
|
|
||||||
};
|
|
||||||
static_assert(sizeof(RebootlessSystemUpdateVersion) == 0x40,
|
|
||||||
"RebootlessSystemUpdateVersion is an invalid size");
|
|
||||||
|
|
||||||
} // namespace Service::Set
|
} // namespace Service::Set
|
||||||
|
|
|
@ -238,7 +238,7 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
|
||||||
{146, nullptr, "SetConsoleSixAxisSensorAngularVelocityTimeBias"},
|
{146, nullptr, "SetConsoleSixAxisSensorAngularVelocityTimeBias"},
|
||||||
{147, nullptr, "GetConsoleSixAxisSensorAngularAcceleration"},
|
{147, nullptr, "GetConsoleSixAxisSensorAngularAcceleration"},
|
||||||
{148, nullptr, "SetConsoleSixAxisSensorAngularAcceleration"},
|
{148, nullptr, "SetConsoleSixAxisSensorAngularAcceleration"},
|
||||||
{149, C<&ISystemSettingsServer::GetRebootlessSystemUpdateVersion>, "GetRebootlessSystemUpdateVersion"},
|
{149, nullptr, "GetRebootlessSystemUpdateVersion"},
|
||||||
{150, C<&ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime>, "GetDeviceTimeZoneLocationUpdatedTime"},
|
{150, C<&ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime>, "GetDeviceTimeZoneLocationUpdatedTime"},
|
||||||
{151, C<&ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime>, "SetDeviceTimeZoneLocationUpdatedTime"},
|
{151, C<&ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime>, "SetDeviceTimeZoneLocationUpdatedTime"},
|
||||||
{152, C<&ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime>, "GetUserSystemClockAutomaticCorrectionUpdatedTime"},
|
{152, C<&ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime>, "GetUserSystemClockAutomaticCorrectionUpdatedTime"},
|
||||||
|
@ -1194,16 +1194,6 @@ Result ISystemSettingsServer::SetKeyboardLayout(KeyboardLayout keyboard_layout)
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ISystemSettingsServer::GetRebootlessSystemUpdateVersion(
|
|
||||||
Out<RebootlessSystemUpdateVersion> out_rebootless_system_update) {
|
|
||||||
LOG_INFO(Service_SET, "(STUBBED) called");
|
|
||||||
|
|
||||||
out_rebootless_system_update->version = 0;
|
|
||||||
strcpy(out_rebootless_system_update->display_version, "0.0.0");
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(
|
Result ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(
|
||||||
Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point) {
|
Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point) {
|
||||||
LOG_INFO(Service_SET, "called");
|
LOG_INFO(Service_SET, "called");
|
||||||
|
|
|
@ -136,8 +136,6 @@ public:
|
||||||
Result SetAppletLaunchFlags(u32 applet_launch_flag);
|
Result SetAppletLaunchFlags(u32 applet_launch_flag);
|
||||||
Result GetKeyboardLayout(Out<KeyboardLayout> out_keyboard_layout);
|
Result GetKeyboardLayout(Out<KeyboardLayout> out_keyboard_layout);
|
||||||
Result SetKeyboardLayout(KeyboardLayout keyboard_layout);
|
Result SetKeyboardLayout(KeyboardLayout keyboard_layout);
|
||||||
Result GetRebootlessSystemUpdateVersion(
|
|
||||||
Out<RebootlessSystemUpdateVersion> out_rebootless_system_update);
|
|
||||||
Result GetDeviceTimeZoneLocationUpdatedTime(
|
Result GetDeviceTimeZoneLocationUpdatedTime(
|
||||||
Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point);
|
Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point);
|
||||||
Result SetDeviceTimeZoneLocationUpdatedTime(
|
Result SetDeviceTimeZoneLocationUpdatedTime(
|
||||||
|
|
294
src/core/telemetry_session.cpp
Normal file
294
src/core/telemetry_session.cpp
Normal file
|
@ -0,0 +1,294 @@
|
||||||
|
// SPDX-FileCopyrightText: 2017 Citra Emulator Project & 2024 suyu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include <mbedtls/ctr_drbg.h>
|
||||||
|
#include <mbedtls/entropy.h>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "common/fs/file.h"
|
||||||
|
#include "common/fs/fs.h"
|
||||||
|
#include "common/fs/path_util.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
|
||||||
|
#include "common/settings.h"
|
||||||
|
#include "common/settings_enums.h"
|
||||||
|
#include "core/file_sys/control_metadata.h"
|
||||||
|
#include "core/file_sys/patch_manager.h"
|
||||||
|
#include "core/loader/loader.h"
|
||||||
|
#include "core/telemetry_session.h"
|
||||||
|
|
||||||
|
#ifdef ENABLE_WEB_SERVICE
|
||||||
|
#include "web_service/telemetry_json.h"
|
||||||
|
#include "web_service/verify_login.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
namespace Telemetry = Common::Telemetry;
|
||||||
|
|
||||||
|
static u64 GenerateTelemetryId() {
|
||||||
|
u64 telemetry_id{};
|
||||||
|
|
||||||
|
mbedtls_entropy_context entropy;
|
||||||
|
mbedtls_entropy_init(&entropy);
|
||||||
|
mbedtls_ctr_drbg_context ctr_drbg;
|
||||||
|
static constexpr std::array<char, 18> personalization{{"suyu Telemetry ID"}};
|
||||||
|
|
||||||
|
mbedtls_ctr_drbg_init(&ctr_drbg);
|
||||||
|
ASSERT(mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
|
||||||
|
reinterpret_cast<const unsigned char*>(personalization.data()),
|
||||||
|
personalization.size()) == 0);
|
||||||
|
ASSERT(mbedtls_ctr_drbg_random(&ctr_drbg, reinterpret_cast<unsigned char*>(&telemetry_id),
|
||||||
|
sizeof(u64)) == 0);
|
||||||
|
|
||||||
|
mbedtls_ctr_drbg_free(&ctr_drbg);
|
||||||
|
mbedtls_entropy_free(&entropy);
|
||||||
|
|
||||||
|
return telemetry_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* TranslateRenderer(Settings::RendererBackend backend) {
|
||||||
|
switch (backend) {
|
||||||
|
case Settings::RendererBackend::OpenGL:
|
||||||
|
return "OpenGL";
|
||||||
|
case Settings::RendererBackend::Vulkan:
|
||||||
|
return "Vulkan";
|
||||||
|
case Settings::RendererBackend::Null:
|
||||||
|
return "Null";
|
||||||
|
}
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* TranslateGPUAccuracyLevel(Settings::GpuAccuracy backend) {
|
||||||
|
switch (backend) {
|
||||||
|
case Settings::GpuAccuracy::Normal:
|
||||||
|
return "Normal";
|
||||||
|
case Settings::GpuAccuracy::High:
|
||||||
|
return "High";
|
||||||
|
case Settings::GpuAccuracy::Extreme:
|
||||||
|
return "Extreme";
|
||||||
|
}
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* TranslateNvdecEmulation(Settings::NvdecEmulation backend) {
|
||||||
|
switch (backend) {
|
||||||
|
case Settings::NvdecEmulation::Off:
|
||||||
|
return "Off";
|
||||||
|
case Settings::NvdecEmulation::Cpu:
|
||||||
|
return "CPU";
|
||||||
|
case Settings::NvdecEmulation::Gpu:
|
||||||
|
return "GPU";
|
||||||
|
}
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr const char* TranslateVSyncMode(Settings::VSyncMode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case Settings::VSyncMode::Immediate:
|
||||||
|
return "Immediate";
|
||||||
|
case Settings::VSyncMode::Mailbox:
|
||||||
|
return "Mailbox";
|
||||||
|
case Settings::VSyncMode::Fifo:
|
||||||
|
return "FIFO";
|
||||||
|
case Settings::VSyncMode::FifoRelaxed:
|
||||||
|
return "FIFO Relaxed";
|
||||||
|
}
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr const char* TranslateASTCDecodeMode(Settings::AstcDecodeMode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case Settings::AstcDecodeMode::Cpu:
|
||||||
|
return "CPU";
|
||||||
|
case Settings::AstcDecodeMode::Gpu:
|
||||||
|
return "GPU";
|
||||||
|
case Settings::AstcDecodeMode::CpuAsynchronous:
|
||||||
|
return "CPU Asynchronous";
|
||||||
|
}
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 GetTelemetryId() {
|
||||||
|
u64 telemetry_id{};
|
||||||
|
const auto filename = Common::FS::GetSuyuPath(Common::FS::SuyuPath::ConfigDir) / "telemetry_id";
|
||||||
|
|
||||||
|
bool generate_new_id = !Common::FS::Exists(filename);
|
||||||
|
|
||||||
|
if (!generate_new_id) {
|
||||||
|
Common::FS::IOFile file{filename, Common::FS::FileAccessMode::Read,
|
||||||
|
Common::FS::FileType::BinaryFile};
|
||||||
|
|
||||||
|
if (!file.IsOpen()) {
|
||||||
|
LOG_ERROR(Core, "failed to open telemetry_id: {}",
|
||||||
|
Common::FS::PathToUTF8String(filename));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.ReadObject(telemetry_id) || telemetry_id == 0) {
|
||||||
|
LOG_ERROR(Frontend, "telemetry_id is 0. Generating a new one.", telemetry_id);
|
||||||
|
generate_new_id = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generate_new_id) {
|
||||||
|
Common::FS::IOFile file{filename, Common::FS::FileAccessMode::Write,
|
||||||
|
Common::FS::FileType::BinaryFile};
|
||||||
|
|
||||||
|
if (!file.IsOpen()) {
|
||||||
|
LOG_ERROR(Core, "failed to open telemetry_id: {}",
|
||||||
|
Common::FS::PathToUTF8String(filename));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
telemetry_id = GenerateTelemetryId();
|
||||||
|
|
||||||
|
if (!file.WriteObject(telemetry_id)) {
|
||||||
|
LOG_ERROR(Core, "Failed to write telemetry_id to file.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return telemetry_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 RegenerateTelemetryId() {
|
||||||
|
const u64 new_telemetry_id{GenerateTelemetryId()};
|
||||||
|
const auto filename = Common::FS::GetSuyuPath(Common::FS::SuyuPath::ConfigDir) / "telemetry_id";
|
||||||
|
|
||||||
|
Common::FS::IOFile file{filename, Common::FS::FileAccessMode::Write,
|
||||||
|
Common::FS::FileType::BinaryFile};
|
||||||
|
|
||||||
|
if (!file.IsOpen()) {
|
||||||
|
LOG_ERROR(Core, "failed to open telemetry_id: {}", Common::FS::PathToUTF8String(filename));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.WriteObject(new_telemetry_id)) {
|
||||||
|
LOG_ERROR(Core, "Failed to write telemetry_id to file.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_telemetry_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VerifyLogin(const std::string& username, const std::string& token) {
|
||||||
|
#ifdef ENABLE_WEB_SERVICE
|
||||||
|
return WebService::VerifyLogin(Settings::values.web_api_url.GetValue(), username, token);
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TelemetrySession::TelemetrySession() = default;
|
||||||
|
|
||||||
|
TelemetrySession::~TelemetrySession() {
|
||||||
|
// Log one-time session end information
|
||||||
|
const s64 shutdown_time{std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
std::chrono::system_clock::now().time_since_epoch())
|
||||||
|
.count()};
|
||||||
|
AddField(Telemetry::FieldType::Session, "Shutdown_Time", shutdown_time);
|
||||||
|
|
||||||
|
#ifdef ENABLE_WEB_SERVICE
|
||||||
|
auto backend = std::make_unique<WebService::TelemetryJson>(
|
||||||
|
Settings::values.web_api_url.GetValue(), Settings::values.suyu_username.GetValue(),
|
||||||
|
Settings::values.suyu_token.GetValue());
|
||||||
|
#else
|
||||||
|
auto backend = std::make_unique<Telemetry::NullVisitor>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Complete the session, submitting to the web service backend if necessary
|
||||||
|
field_collection.Accept(*backend);
|
||||||
|
if (Settings::values.enable_telemetry) {
|
||||||
|
backend->Complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
|
||||||
|
const Service::FileSystem::FileSystemController& fsc,
|
||||||
|
const FileSys::ContentProvider& content_provider) {
|
||||||
|
// Log one-time top-level information
|
||||||
|
AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId());
|
||||||
|
|
||||||
|
// Log one-time session start information
|
||||||
|
const s64 init_time{std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
std::chrono::system_clock::now().time_since_epoch())
|
||||||
|
.count()};
|
||||||
|
AddField(Telemetry::FieldType::Session, "Init_Time", init_time);
|
||||||
|
|
||||||
|
u64 program_id{};
|
||||||
|
const Loader::ResultStatus res{app_loader.ReadProgramId(program_id)};
|
||||||
|
if (res == Loader::ResultStatus::Success) {
|
||||||
|
const std::string formatted_program_id{fmt::format("{:016X}", program_id)};
|
||||||
|
AddField(Telemetry::FieldType::Session, "ProgramId", formatted_program_id);
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
app_loader.ReadTitle(name);
|
||||||
|
|
||||||
|
if (name.empty()) {
|
||||||
|
const auto metadata = [&content_provider, &fsc, program_id] {
|
||||||
|
const FileSys::PatchManager pm{program_id, fsc, content_provider};
|
||||||
|
return pm.GetControlMetadata();
|
||||||
|
}();
|
||||||
|
if (metadata.first != nullptr) {
|
||||||
|
name = metadata.first->GetApplicationName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!name.empty()) {
|
||||||
|
AddField(Telemetry::FieldType::Session, "ProgramName", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddField(Telemetry::FieldType::Session, "ProgramFormat",
|
||||||
|
static_cast<u8>(app_loader.GetFileType()));
|
||||||
|
|
||||||
|
// Log application information
|
||||||
|
Telemetry::AppendBuildInfo(field_collection);
|
||||||
|
|
||||||
|
// Log user system information
|
||||||
|
Telemetry::AppendCPUInfo(field_collection);
|
||||||
|
Telemetry::AppendOSInfo(field_collection);
|
||||||
|
|
||||||
|
// Log user configuration information
|
||||||
|
constexpr auto field_type = Telemetry::FieldType::UserConfig;
|
||||||
|
AddField(field_type, "Audio_SinkId",
|
||||||
|
Settings::CanonicalizeEnum(Settings::values.sink_id.GetValue()));
|
||||||
|
AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
|
||||||
|
AddField(field_type, "Renderer_Backend",
|
||||||
|
TranslateRenderer(Settings::values.renderer_backend.GetValue()));
|
||||||
|
AddField(field_type, "Renderer_UseSpeedLimit", Settings::values.use_speed_limit.GetValue());
|
||||||
|
AddField(field_type, "Renderer_SpeedLimit", Settings::values.speed_limit.GetValue());
|
||||||
|
AddField(field_type, "Renderer_UseDiskShaderCache",
|
||||||
|
Settings::values.use_disk_shader_cache.GetValue());
|
||||||
|
AddField(field_type, "Renderer_GPUAccuracyLevel",
|
||||||
|
TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy.GetValue()));
|
||||||
|
AddField(field_type, "Renderer_UseAsynchronousGpuEmulation",
|
||||||
|
Settings::values.use_asynchronous_gpu_emulation.GetValue());
|
||||||
|
AddField(field_type, "Renderer_NvdecEmulation",
|
||||||
|
TranslateNvdecEmulation(Settings::values.nvdec_emulation.GetValue()));
|
||||||
|
AddField(field_type, "Renderer_AccelerateASTC",
|
||||||
|
TranslateASTCDecodeMode(Settings::values.accelerate_astc.GetValue()));
|
||||||
|
AddField(field_type, "Renderer_UseVsync",
|
||||||
|
TranslateVSyncMode(Settings::values.vsync_mode.GetValue()));
|
||||||
|
AddField(field_type, "Renderer_ShaderBackend",
|
||||||
|
static_cast<u32>(Settings::values.shader_backend.GetValue()));
|
||||||
|
AddField(field_type, "Renderer_UseAsynchronousShaders",
|
||||||
|
Settings::values.use_asynchronous_shaders.GetValue());
|
||||||
|
AddField(field_type, "System_UseDockedMode", Settings::IsDockedMode());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TelemetrySession::SubmitTestcase() {
|
||||||
|
#ifdef ENABLE_WEB_SERVICE
|
||||||
|
auto backend = std::make_unique<WebService::TelemetryJson>(
|
||||||
|
Settings::values.web_api_url.GetValue(), Settings::values.suyu_username.GetValue(),
|
||||||
|
Settings::values.suyu_token.GetValue());
|
||||||
|
field_collection.Accept(*backend);
|
||||||
|
return backend->SubmitTestcase();
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Core
|
101
src/core/telemetry_session.h
Normal file
101
src/core/telemetry_session.h
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "common/telemetry.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
class ContentProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Loader {
|
||||||
|
class AppLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Service::FileSystem {
|
||||||
|
class FileSystemController;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instruments telemetry for this emulation session. Creates a new set of telemetry fields on each
|
||||||
|
* session, logging any one-time fields. Interfaces with the telemetry backend used for submitting
|
||||||
|
* data to the web service. Submits session data on close.
|
||||||
|
*/
|
||||||
|
class TelemetrySession {
|
||||||
|
public:
|
||||||
|
explicit TelemetrySession();
|
||||||
|
~TelemetrySession();
|
||||||
|
|
||||||
|
TelemetrySession(const TelemetrySession&) = delete;
|
||||||
|
TelemetrySession& operator=(const TelemetrySession&) = delete;
|
||||||
|
|
||||||
|
TelemetrySession(TelemetrySession&&) = delete;
|
||||||
|
TelemetrySession& operator=(TelemetrySession&&) = delete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the initial telemetry info necessary when starting up a title.
|
||||||
|
*
|
||||||
|
* This includes information such as:
|
||||||
|
* - Telemetry ID
|
||||||
|
* - Initialization time
|
||||||
|
* - Title ID
|
||||||
|
* - Title name
|
||||||
|
* - Title file format
|
||||||
|
* - Miscellaneous settings values.
|
||||||
|
*
|
||||||
|
* @param app_loader The application loader to use to retrieve
|
||||||
|
* title-specific information.
|
||||||
|
* @param fsc Filesystem controller to use to retrieve info.
|
||||||
|
* @param content_provider Content provider to use to retrieve info.
|
||||||
|
*/
|
||||||
|
void AddInitialInfo(Loader::AppLoader& app_loader,
|
||||||
|
const Service::FileSystem::FileSystemController& fsc,
|
||||||
|
const FileSys::ContentProvider& content_provider);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around the Telemetry::FieldCollection::AddField method.
|
||||||
|
* @param type Type of the field to add.
|
||||||
|
* @param name Name of the field to add.
|
||||||
|
* @param value Value for the field to add.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
void AddField(Common::Telemetry::FieldType type, const char* name, T value) {
|
||||||
|
field_collection.AddField(type, name, std::move(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submits a Testcase.
|
||||||
|
* @returns A bool indicating whether the submission succeeded
|
||||||
|
*/
|
||||||
|
bool SubmitTestcase();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Tracks all added fields for the session
|
||||||
|
Common::Telemetry::FieldCollection field_collection;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets TelemetryId, a unique identifier used for the user's telemetry sessions.
|
||||||
|
* @returns The current TelemetryId for the session.
|
||||||
|
*/
|
||||||
|
u64 GetTelemetryId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regenerates TelemetryId, a unique identifier used for the user's telemetry sessions.
|
||||||
|
* @returns The new TelemetryId that was generated.
|
||||||
|
*/
|
||||||
|
u64 RegenerateTelemetryId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies the username and token.
|
||||||
|
* @param username suyu username to use for authentication.
|
||||||
|
* @param token suyu token to use for authentication.
|
||||||
|
* @returns Future with bool indicating whether the verification succeeded
|
||||||
|
*/
|
||||||
|
bool VerifyLogin(const std::string& username, const std::string& token);
|
||||||
|
|
||||||
|
} // namespace Core
|
|
@ -762,8 +762,6 @@ void EmulatedController::StartMotionCalibration() {
|
||||||
|
|
||||||
void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
|
void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
|
||||||
Common::UUID uuid) {
|
Common::UUID uuid) {
|
||||||
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
|
|
||||||
const auto& player = Settings::values.players.GetValue()[player_index];
|
|
||||||
if (index >= controller.button_values.size()) {
|
if (index >= controller.button_values.size()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -919,8 +917,13 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
|
||||||
|
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
if (player.connected) {
|
if (!is_connected) {
|
||||||
Connect();
|
if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) {
|
||||||
|
Connect();
|
||||||
|
}
|
||||||
|
if (npad_id_type == NpadIdType::Handheld && npad_type == NpadStyleIndex::Handheld) {
|
||||||
|
Connect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
TriggerOnChange(ControllerTriggerType::Button, true);
|
TriggerOnChange(ControllerTriggerType::Button, true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -357,7 +357,7 @@ if (APPLE)
|
||||||
|
|
||||||
if (NOT USE_SYSTEM_MOLTENVK)
|
if (NOT USE_SYSTEM_MOLTENVK)
|
||||||
set(MOLTENVK_PLATFORM "macOS")
|
set(MOLTENVK_PLATFORM "macOS")
|
||||||
set(MOLTENVK_VERSION "v1.2.8")
|
set(MOLTENVK_VERSION "v1.2.7")
|
||||||
download_moltenvk_external(${MOLTENVK_PLATFORM} ${MOLTENVK_VERSION})
|
download_moltenvk_external(${MOLTENVK_PLATFORM} ${MOLTENVK_VERSION})
|
||||||
endif()
|
endif()
|
||||||
find_library(MOLTENVK_LIBRARY MoltenVK REQUIRED)
|
find_library(MOLTENVK_LIBRARY MoltenVK REQUIRED)
|
||||||
|
|
|
@ -6,12 +6,14 @@
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QtConcurrent/qtconcurrentrun.h>
|
#include <QtConcurrent/qtconcurrentrun.h>
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/telemetry.h"
|
||||||
|
#include "core/telemetry_session.h"
|
||||||
#include "suyu/compatdb.h"
|
#include "suyu/compatdb.h"
|
||||||
#include "ui_compatdb.h"
|
#include "ui_compatdb.h"
|
||||||
|
|
||||||
CompatDB::CompatDB(QWidget* parent)
|
CompatDB::CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent)
|
||||||
: QWizard(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),
|
: QWizard(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),
|
||||||
ui{std::make_unique<Ui::CompatDB>()} {
|
ui{std::make_unique<Ui::CompatDB>()}, telemetry_session{telemetry_session_} {
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
connect(ui->radioButton_GameBoot_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext);
|
connect(ui->radioButton_GameBoot_Yes, &QRadioButton::clicked, this, &CompatDB::EnableNext);
|
||||||
|
@ -112,10 +114,15 @@ void CompatDB::Submit() {
|
||||||
case CompatDBPage::Final:
|
case CompatDBPage::Final:
|
||||||
back();
|
back();
|
||||||
LOG_INFO(Frontend, "Compatibility Rating: {}", compatibility);
|
LOG_INFO(Frontend, "Compatibility Rating: {}", compatibility);
|
||||||
|
telemetry_session.AddField(Common::Telemetry::FieldType::UserFeedback, "Compatibility",
|
||||||
|
compatibility);
|
||||||
|
|
||||||
button(NextButton)->setEnabled(false);
|
button(NextButton)->setEnabled(false);
|
||||||
button(NextButton)->setText(tr("Submitting"));
|
button(NextButton)->setText(tr("Submitting"));
|
||||||
button(CancelButton)->setVisible(false);
|
button(CancelButton)->setVisible(false);
|
||||||
|
|
||||||
|
testcase_watcher.setFuture(
|
||||||
|
QtConcurrent::run([this] { return telemetry_session.SubmitTestcase(); }));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_ERROR(Frontend, "Unexpected page: {}", currentId());
|
LOG_ERROR(Frontend, "Unexpected page: {}", currentId());
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
#include <QWizard>
|
#include <QWizard>
|
||||||
|
#include "core/telemetry_session.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class CompatDB;
|
class CompatDB;
|
||||||
|
@ -24,7 +25,7 @@ class CompatDB : public QWizard {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CompatDB(QWidget* parent = nullptr);
|
explicit CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent = nullptr);
|
||||||
~CompatDB();
|
~CompatDB();
|
||||||
int nextId() const override;
|
int nextId() const override;
|
||||||
|
|
||||||
|
@ -37,4 +38,6 @@ private:
|
||||||
CompatibilityStatus CalculateCompatibility() const;
|
CompatibilityStatus CalculateCompatibility() const;
|
||||||
void OnTestcaseSubmitted();
|
void OnTestcaseSubmitted();
|
||||||
void EnableNext();
|
void EnableNext();
|
||||||
|
|
||||||
|
Core::TelemetrySession& telemetry_session;
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
<number>1</number>
|
<number>1</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>200</number>
|
<number>100</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="value">
|
<property name="value">
|
||||||
<number>50</number>
|
<number>50</number>
|
||||||
|
@ -69,7 +69,7 @@
|
||||||
<number>1</number>
|
<number>1</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>200</number>
|
<number>100</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="value">
|
<property name="value">
|
||||||
<number>50</number>
|
<number>50</number>
|
||||||
|
|
|
@ -17,17 +17,14 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include "common/fs/fs_util.h"
|
#include "common/fs/fs_util.h"
|
||||||
#include "common/hex_util.h"
|
|
||||||
#include "common/settings_enums.h"
|
#include "common/settings_enums.h"
|
||||||
#include "common/settings_input.h"
|
#include "common/settings_input.h"
|
||||||
#include "configuration/shared_widget.h"
|
#include "configuration/shared_widget.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/file_sys/control_metadata.h"
|
#include "core/file_sys/control_metadata.h"
|
||||||
#include "core/file_sys/ips_layer.h"
|
|
||||||
#include "core/file_sys/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
#include "core/file_sys/xts_archive.h"
|
#include "core/file_sys/xts_archive.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
#include "core/loader/nso.h"
|
|
||||||
#include "frontend_common/config.h"
|
#include "frontend_common/config.h"
|
||||||
#include "suyu/configuration/configuration_shared.h"
|
#include "suyu/configuration/configuration_shared.h"
|
||||||
#include "suyu/configuration/configure_audio.h"
|
#include "suyu/configuration/configure_audio.h"
|
||||||
|
@ -48,12 +45,9 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
|
||||||
std::vector<VkDeviceInfo::Record>& vk_device_records,
|
std::vector<VkDeviceInfo::Record>& vk_device_records,
|
||||||
Core::System& system_)
|
Core::System& system_)
|
||||||
: QDialog(parent),
|
: QDialog(parent),
|
||||||
ui(std::make_unique<Ui::ConfigurePerGame>()), pm{title_id_, system_.GetFileSystemController(),
|
ui(std::make_unique<Ui::ConfigurePerGame>()), title_id{title_id_}, system{system_},
|
||||||
system_.GetContentProvider()},
|
builder{std::make_unique<ConfigurationShared::Builder>(this, !system_.IsPoweredOn())},
|
||||||
title_id{title_id_}, system{system_}, builder{std::make_unique<ConfigurationShared::Builder>(
|
|
||||||
this, !system_.IsPoweredOn())},
|
|
||||||
tab_group{std::make_shared<std::vector<ConfigurationShared::Tab*>>()} {
|
tab_group{std::make_shared<std::vector<ConfigurationShared::Tab*>>()} {
|
||||||
|
|
||||||
const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name));
|
const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name));
|
||||||
const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename())
|
const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename())
|
||||||
: fmt::format("{:016X}", title_id);
|
: fmt::format("{:016X}", title_id);
|
||||||
|
@ -147,14 +141,6 @@ void ConfigurePerGame::LoadFromFile(FileSys::VirtualFile file_) {
|
||||||
LoadConfiguration();
|
LoadConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ConfigurePerGame::GetBuildID() {
|
|
||||||
LOG_INFO(Core, "{}", file->GetExtension());
|
|
||||||
|
|
||||||
// https://github.com/Ryujinx/Ryujinx/blob/master/src/Ryujinx.UI.Common/App/ApplicationData.cs#L71
|
|
||||||
|
|
||||||
return "Invalid File";
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConfigurePerGame::LoadConfiguration() {
|
void ConfigurePerGame::LoadConfiguration() {
|
||||||
if (file == nullptr) {
|
if (file == nullptr) {
|
||||||
return;
|
return;
|
||||||
|
@ -162,14 +148,13 @@ void ConfigurePerGame::LoadConfiguration() {
|
||||||
|
|
||||||
addons_tab->LoadFromFile(file);
|
addons_tab->LoadFromFile(file);
|
||||||
|
|
||||||
const auto control = pm.GetControlMetadata();
|
|
||||||
const auto loader = Loader::GetLoader(system, file);
|
|
||||||
|
|
||||||
ui->display_title_id->setText(
|
ui->display_title_id->setText(
|
||||||
QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper());
|
QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper());
|
||||||
|
|
||||||
// TODO: Should get proper build id for UI
|
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
|
||||||
// ui->display_build_id->setText(QString::fromStdString(GetBuildID()));
|
system.GetContentProvider()};
|
||||||
|
const auto control = pm.GetControlMetadata();
|
||||||
|
const auto loader = Loader::GetLoader(system, file);
|
||||||
|
|
||||||
if (control.first != nullptr) {
|
if (control.first != nullptr) {
|
||||||
ui->display_version->setText(QString::fromStdString(control.first->GetVersionString()));
|
ui->display_version->setText(QString::fromStdString(control.first->GetVersionString()));
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include <QList>
|
#include <QList>
|
||||||
|
|
||||||
#include "configuration/shared_widget.h"
|
#include "configuration/shared_widget.h"
|
||||||
#include "core/file_sys/patch_manager.h"
|
|
||||||
#include "core/file_sys/vfs/vfs_types.h"
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
#include "frontend_common/config.h"
|
#include "frontend_common/config.h"
|
||||||
#include "suyu/configuration/configuration_shared.h"
|
#include "suyu/configuration/configuration_shared.h"
|
||||||
|
@ -69,11 +68,8 @@ private:
|
||||||
|
|
||||||
void LoadConfiguration();
|
void LoadConfiguration();
|
||||||
|
|
||||||
std::string GetBuildID();
|
|
||||||
|
|
||||||
std::unique_ptr<Ui::ConfigurePerGame> ui;
|
std::unique_ptr<Ui::ConfigurePerGame> ui;
|
||||||
FileSys::VirtualFile file;
|
FileSys::VirtualFile file;
|
||||||
FileSys::PatchManager pm;
|
|
||||||
u64 title_id;
|
u64 title_id;
|
||||||
|
|
||||||
QGraphicsScene* scene;
|
QGraphicsScene* scene;
|
||||||
|
|
|
@ -67,18 +67,8 @@
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QLineEdit" name="display_developer">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="readOnly">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="6" column="1">
|
<item row="6" column="1">
|
||||||
<widget class="QLineEdit" name="display_format">
|
<widget class="QLineEdit" name="display_size">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
|
@ -87,58 +77,6 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="0">
|
|
||||||
<widget class="QLabel" name="label_6">
|
|
||||||
<property name="text">
|
|
||||||
<string>Size</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="8" column="0">
|
|
||||||
<widget class="QLabel" name="label_7">
|
|
||||||
<property name="text">
|
|
||||||
<string>Filename</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0">
|
|
||||||
<widget class="QLabel" name="label_4">
|
|
||||||
<property name="text">
|
|
||||||
<string>Title ID</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="text">
|
|
||||||
<string>Name</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="1">
|
|
||||||
<widget class="QLineEdit" name="display_title_id">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="readOnly">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="label_2">
|
|
||||||
<property name="text">
|
|
||||||
<string>Developer</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="6" column="0">
|
|
||||||
<widget class="QLabel" name="label_5">
|
|
||||||
<property name="text">
|
|
||||||
<string>Format</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="QLineEdit" name="display_version">
|
<widget class="QLineEdit" name="display_version">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
|
@ -149,14 +87,31 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="label_3">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Version</string>
|
<string>Name</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="8" column="1">
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Title ID</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QLineEdit" name="display_title_id">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
<widget class="QLineEdit" name="display_filename">
|
<widget class="QLineEdit" name="display_filename">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
@ -166,6 +121,23 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QLineEdit" name="display_format">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>Filename</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QLineEdit" name="display_name">
|
<widget class="QLineEdit" name="display_name">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
|
@ -176,8 +148,8 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="QLineEdit" name="display_size">
|
<widget class="QLineEdit" name="display_developer">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
|
@ -185,22 +157,35 @@
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<!-- TODO: Gotta implement proper working build id -->
|
<item row="5" column="0">
|
||||||
<!--item row="5" column="1">
|
<widget class="QLabel" name="label_5">
|
||||||
<widget class="QLineEdit" name="display_build_id">
|
<property name="text">
|
||||||
<property name="readOnly">
|
<string>Format</string>
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0">
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="label_9">
|
<widget class="QLabel" name="label_3">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Build ID</string>
|
<string>Version</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item-->
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>Size</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Developer</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2016 Citra Emulator Project & 2024 suyu Emulator Project
|
||||||
// SPDX-FileCopyrightText: 2024 suyu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -25,7 +24,6 @@
|
||||||
|
|
||||||
#include "common/fs/fs.h"
|
#include "common/fs/fs.h"
|
||||||
#include "common/fs/path_util.h"
|
#include "common/fs/path_util.h"
|
||||||
#include "common/logging/log.h"
|
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/file_sys/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QtConcurrent/QtConcurrentRun>
|
#include <QtConcurrent/QtConcurrentRun>
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
#include "core/telemetry_session.h"
|
||||||
#include "suyu/configuration/configure_web.h"
|
#include "suyu/configuration/configure_web.h"
|
||||||
#include "suyu/uisettings.h"
|
#include "suyu/uisettings.h"
|
||||||
#include "ui_configure_web.h"
|
#include "ui_configure_web.h"
|
||||||
#include "web_service/verify_login.h"
|
|
||||||
|
|
||||||
static constexpr char token_delimiter{':'};
|
static constexpr char token_delimiter{':'};
|
||||||
|
|
||||||
|
@ -38,6 +38,8 @@ static std::string TokenFromDisplayToken(const std::string& display_token) {
|
||||||
ConfigureWeb::ConfigureWeb(QWidget* parent)
|
ConfigureWeb::ConfigureWeb(QWidget* parent)
|
||||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureWeb>()) {
|
: QWidget(parent), ui(std::make_unique<Ui::ConfigureWeb>()) {
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
connect(ui->button_regenerate_telemetry_id, &QPushButton::clicked, this,
|
||||||
|
&ConfigureWeb::RefreshTelemetryID);
|
||||||
connect(ui->button_verify_login, &QPushButton::clicked, this, &ConfigureWeb::VerifyLogin);
|
connect(ui->button_verify_login, &QPushButton::clicked, this, &ConfigureWeb::VerifyLogin);
|
||||||
connect(&verify_watcher, &QFutureWatcher<bool>::finished, this, &ConfigureWeb::OnLoginVerified);
|
connect(&verify_watcher, &QFutureWatcher<bool>::finished, this, &ConfigureWeb::OnLoginVerified);
|
||||||
|
|
||||||
|
@ -62,18 +64,26 @@ void ConfigureWeb::changeEvent(QEvent* event) {
|
||||||
void ConfigureWeb::RetranslateUI() {
|
void ConfigureWeb::RetranslateUI() {
|
||||||
ui->retranslateUi(this);
|
ui->retranslateUi(this);
|
||||||
|
|
||||||
|
ui->telemetry_learn_more->setText(
|
||||||
|
tr("<a href='https://suyu.dev/help/feature/telemetry/'><span style=\"text-decoration: "
|
||||||
|
"underline; color:#039be5;\">Learn more</span></a>"));
|
||||||
|
|
||||||
ui->web_signup_link->setText(
|
ui->web_signup_link->setText(
|
||||||
tr("<a href='https://suyu.dev/signup'><span style=\"text-decoration: underline; "
|
tr("<a href='https://profile.suyu.dev/'><span style=\"text-decoration: underline; "
|
||||||
"color:#039be5;\">Sign up</span></a>"));
|
"color:#039be5;\">Sign up</span></a>"));
|
||||||
|
|
||||||
ui->web_token_info_link->setText(
|
ui->web_token_info_link->setText(
|
||||||
tr("<a href='https://suyu.dev/account'><span style=\"text-decoration: "
|
tr("<a href='https://suyu.dev/wiki/suyu-web-service/'><span style=\"text-decoration: "
|
||||||
"underline; color:#039be5;\">What is my token?</span></a>"));
|
"underline; color:#039be5;\">What is my token?</span></a>"));
|
||||||
|
|
||||||
|
ui->label_telemetry_id->setText(
|
||||||
|
tr("Telemetry ID: 0x%1").arg(QString::number(Core::GetTelemetryId(), 16).toUpper()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureWeb::SetConfiguration() {
|
void ConfigureWeb::SetConfiguration() {
|
||||||
ui->web_credentials_disclaimer->setWordWrap(true);
|
ui->web_credentials_disclaimer->setWordWrap(true);
|
||||||
|
|
||||||
|
ui->telemetry_learn_more->setOpenExternalLinks(true);
|
||||||
ui->web_signup_link->setOpenExternalLinks(true);
|
ui->web_signup_link->setOpenExternalLinks(true);
|
||||||
ui->web_token_info_link->setOpenExternalLinks(true);
|
ui->web_token_info_link->setOpenExternalLinks(true);
|
||||||
|
|
||||||
|
@ -83,6 +93,7 @@ void ConfigureWeb::SetConfiguration() {
|
||||||
ui->username->setText(QString::fromStdString(Settings::values.suyu_username.GetValue()));
|
ui->username->setText(QString::fromStdString(Settings::values.suyu_username.GetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry.GetValue());
|
||||||
ui->edit_token->setText(QString::fromStdString(GenerateDisplayToken(
|
ui->edit_token->setText(QString::fromStdString(GenerateDisplayToken(
|
||||||
Settings::values.suyu_username.GetValue(), Settings::values.suyu_token.GetValue())));
|
Settings::values.suyu_username.GetValue(), Settings::values.suyu_token.GetValue())));
|
||||||
|
|
||||||
|
@ -95,6 +106,7 @@ void ConfigureWeb::SetConfiguration() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureWeb::ApplyConfiguration() {
|
void ConfigureWeb::ApplyConfiguration() {
|
||||||
|
Settings::values.enable_telemetry = ui->toggle_telemetry->isChecked();
|
||||||
UISettings::values.enable_discord_presence = ui->toggle_discordrpc->isChecked();
|
UISettings::values.enable_discord_presence = ui->toggle_discordrpc->isChecked();
|
||||||
if (user_verified) {
|
if (user_verified) {
|
||||||
Settings::values.suyu_username =
|
Settings::values.suyu_username =
|
||||||
|
@ -107,6 +119,12 @@ void ConfigureWeb::ApplyConfiguration() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConfigureWeb::RefreshTelemetryID() {
|
||||||
|
const u64 new_telemetry_id{Core::RegenerateTelemetryId()};
|
||||||
|
ui->label_telemetry_id->setText(
|
||||||
|
tr("Telemetry ID: 0x%1").arg(QString::number(new_telemetry_id, 16).toUpper()));
|
||||||
|
}
|
||||||
|
|
||||||
void ConfigureWeb::OnLoginChanged() {
|
void ConfigureWeb::OnLoginChanged() {
|
||||||
if (ui->edit_token->text().isEmpty()) {
|
if (ui->edit_token->text().isEmpty()) {
|
||||||
user_verified = true;
|
user_verified = true;
|
||||||
|
@ -132,12 +150,7 @@ void ConfigureWeb::VerifyLogin() {
|
||||||
verify_watcher.setFuture(QtConcurrent::run(
|
verify_watcher.setFuture(QtConcurrent::run(
|
||||||
[username = UsernameFromDisplayToken(ui->edit_token->text().toStdString()),
|
[username = UsernameFromDisplayToken(ui->edit_token->text().toStdString()),
|
||||||
token = TokenFromDisplayToken(ui->edit_token->text().toStdString())] {
|
token = TokenFromDisplayToken(ui->edit_token->text().toStdString())] {
|
||||||
#ifdef ENABLE_WEB_SERVICE
|
return Core::VerifyLogin(username, token);
|
||||||
return WebService::VerifyLogin(Settings::values.web_api_url.GetValue(), username,
|
|
||||||
token);
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ private:
|
||||||
void changeEvent(QEvent* event) override;
|
void changeEvent(QEvent* event) override;
|
||||||
void RetranslateUI();
|
void RetranslateUI();
|
||||||
|
|
||||||
|
void RefreshTelemetryID();
|
||||||
void OnLoginChanged();
|
void OnLoginChanged();
|
||||||
void VerifyLogin();
|
void VerifyLogin();
|
||||||
void OnLoginVerified();
|
void OnLoginVerified();
|
||||||
|
|
|
@ -125,6 +125,56 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Telemetry</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="toggle_telemetry">
|
||||||
|
<property name="text">
|
||||||
|
<string>Share anonymous usage data with the suyu team</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="telemetry_learn_more">
|
||||||
|
<property name="text">
|
||||||
|
<string>Learn more</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QGridLayout" name="gridLayoutTelemetryId">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_telemetry_id">
|
||||||
|
<property name="text">
|
||||||
|
<string>Telemetry ID:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QPushButton" name="button_regenerate_telemetry_id">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="layoutDirection">
|
||||||
|
<enum>Qt::RightToLeft</enum>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Regenerate</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
// Modified by palfaiate on <2024/03/07>
|
// Modified by palfaiate on <2024/03/07>
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
@ -383,17 +384,6 @@ GameList::~GameList() {
|
||||||
UnloadController();
|
UnloadController();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameList::ClearList() {
|
|
||||||
// Clear all items
|
|
||||||
item_model->setRowCount(0);
|
|
||||||
|
|
||||||
// Notify a reload is pending
|
|
||||||
UISettings::values.is_game_list_reload_pending.exchange(true);
|
|
||||||
UISettings::values.is_game_list_reload_pending.notify_all();
|
|
||||||
|
|
||||||
// Load stuff back up
|
|
||||||
}
|
|
||||||
|
|
||||||
void GameList::SetFilterFocus() {
|
void GameList::SetFilterFocus() {
|
||||||
if (tree_view->model()->rowCount() > 0) {
|
if (tree_view->model()->rowCount() > 0) {
|
||||||
search_field->setFocus();
|
search_field->setFocus();
|
||||||
|
@ -423,10 +413,6 @@ void GameList::AddEntry(const QList<QStandardItem*>& entry_items, GameListDir* p
|
||||||
parent->appendRow(entry_items);
|
parent->appendRow(entry_items);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameList::AddRootEntry(const QList<QStandardItem*>& entry_items) {
|
|
||||||
item_model->invisibleRootItem()->appendRow(entry_items);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GameList::ValidateEntry(const QModelIndex& item) {
|
void GameList::ValidateEntry(const QModelIndex& item) {
|
||||||
const auto selected = item.sibling(item.row(), 0);
|
const auto selected = item.sibling(item.row(), 0);
|
||||||
|
|
||||||
|
@ -482,9 +468,7 @@ bool GameList::IsEmpty() const {
|
||||||
void GameList::DonePopulating(const QStringList& watch_list) {
|
void GameList::DonePopulating(const QStringList& watch_list) {
|
||||||
emit ShowList(!IsEmpty());
|
emit ShowList(!IsEmpty());
|
||||||
|
|
||||||
if (UISettings::values.show_folders_in_list) {
|
item_model->invisibleRootItem()->appendRow(new GameListAddDir());
|
||||||
item_model->invisibleRootItem()->appendRow(new GameListAddDir());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add favorites row
|
// Add favorites row
|
||||||
item_model->invisibleRootItem()->insertRow(0, new GameListFavorites());
|
item_model->invisibleRootItem()->insertRow(0, new GameListFavorites());
|
||||||
|
@ -501,7 +485,6 @@ void GameList::DonePopulating(const QStringList& watch_list) {
|
||||||
if (!watch_dirs.isEmpty()) {
|
if (!watch_dirs.isEmpty()) {
|
||||||
watcher->removePaths(watch_dirs);
|
watcher->removePaths(watch_dirs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workaround: Add the watch paths in chunks to allow the gui to refresh
|
// Workaround: Add the watch paths in chunks to allow the gui to refresh
|
||||||
// This prevents the UI from stalling when a large number of watch paths are added
|
// This prevents the UI from stalling when a large number of watch paths are added
|
||||||
// Also artificially caps the watcher to a certain number of directories
|
// Also artificially caps the watcher to a certain number of directories
|
||||||
|
@ -903,43 +886,22 @@ void GameList::ToggleFavorite(u64 program_id) {
|
||||||
void GameList::AddFavorite(u64 program_id) {
|
void GameList::AddFavorite(u64 program_id) {
|
||||||
auto* favorites_row = item_model->item(0);
|
auto* favorites_row = item_model->item(0);
|
||||||
|
|
||||||
if (UISettings::values.show_folders_in_list) {
|
for (int i = 1; i < item_model->rowCount() - 1; i++) {
|
||||||
for (int i = 0; i < item_model->rowCount(); i++) {
|
const auto* folder = item_model->item(i);
|
||||||
const auto* folder = item_model->item(i);
|
for (int j = 0; j < folder->rowCount(); j++) {
|
||||||
for (int j = 0; j < folder->rowCount(); j++) {
|
if (folder->child(j)->data(GameListItemPath::ProgramIdRole).toULongLong() ==
|
||||||
if (folder->child(j)->data(GameListItemPath::ProgramIdRole).toULongLong() ==
|
program_id) {
|
||||||
program_id) {
|
QList<QStandardItem*> list;
|
||||||
QList<QStandardItem*> list;
|
for (int k = 0; k < COLUMN_COUNT; k++) {
|
||||||
for (int k = 0; k < COLUMN_COUNT; k++) {
|
list.append(folder->child(j, k)->clone());
|
||||||
list.append(folder->child(j, k)->clone());
|
|
||||||
}
|
|
||||||
list[0]->setData(folder->child(j)->data(GameListItem::SortRole),
|
|
||||||
GameListItem::SortRole);
|
|
||||||
list[0]->setText(folder->child(j)->data(Qt::DisplayRole).toString());
|
|
||||||
|
|
||||||
favorites_row->appendRow(list);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
list[0]->setData(folder->child(j)->data(GameListItem::SortRole),
|
||||||
}
|
GameListItem::SortRole);
|
||||||
return;
|
list[0]->setText(folder->child(j)->data(Qt::DisplayRole).toString());
|
||||||
} else {
|
|
||||||
for (int i = 0; i < item_model->rowCount(); i++) {
|
|
||||||
const auto* game = item_model->item(i);
|
|
||||||
if (game->data(GameListItemPath::ProgramIdRole).toULongLong() != program_id) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<QStandardItem*> list;
|
favorites_row->appendRow(list);
|
||||||
for (int j = 0; j < COLUMN_COUNT; j++) {
|
return;
|
||||||
list.append(item_model->item(i, j)->clone());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
list[0]->setData(game->data(GameListItem::SortRole), GameListItem::SortRole);
|
|
||||||
list[0]->setText(game->data(Qt::DisplayRole).toString());
|
|
||||||
|
|
||||||
favorites_row->appendRow(list);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,6 @@ public:
|
||||||
~GameList() override;
|
~GameList() override;
|
||||||
|
|
||||||
QString GetLastFilterResultItem() const;
|
QString GetLastFilterResultItem() const;
|
||||||
void ClearList();
|
|
||||||
void ClearFilter();
|
void ClearFilter();
|
||||||
void SetFilterFocus();
|
void SetFilterFocus();
|
||||||
void SetFilterVisible(bool visibility);
|
void SetFilterVisible(bool visibility);
|
||||||
|
@ -138,7 +137,6 @@ private:
|
||||||
|
|
||||||
void AddDirEntry(GameListDir* entry_items);
|
void AddDirEntry(GameListDir* entry_items);
|
||||||
void AddEntry(const QList<QStandardItem*>& entry_items, GameListDir* parent);
|
void AddEntry(const QList<QStandardItem*>& entry_items, GameListDir* parent);
|
||||||
void AddRootEntry(const QList<QStandardItem*>& entry_items);
|
|
||||||
void DonePopulating(const QStringList& watch_list);
|
void DonePopulating(const QStringList& watch_list);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -330,13 +330,7 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
|
||||||
|
|
||||||
auto entry = MakeGameListEntry(file->GetFullPath(), name, file->GetSize(), icon, *loader,
|
auto entry = MakeGameListEntry(file->GetFullPath(), name, file->GetSize(), icon, *loader,
|
||||||
program_id, compatibility_list, play_time_manager, patch);
|
program_id, compatibility_list, play_time_manager, patch);
|
||||||
RecordEvent([=](GameList* game_list) {
|
RecordEvent([=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); });
|
||||||
if (UISettings::values.show_folders_in_list) {
|
|
||||||
game_list->AddEntry(entry, parent_dir);
|
|
||||||
} else {
|
|
||||||
game_list->AddRootEntry(entry);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,7 +408,8 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
|
||||||
physical_name, name, Common::FS::GetSize(physical_name), icon, *loader,
|
physical_name, name, Common::FS::GetSize(physical_name), icon, *loader,
|
||||||
id, compatibility_list, play_time_manager, patch);
|
id, compatibility_list, play_time_manager, patch);
|
||||||
|
|
||||||
RecordEvent([=](GameList* game_list) { game_list->AddRootEntry(entry); });
|
RecordEvent(
|
||||||
|
[=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std::vector<u8> icon;
|
std::vector<u8> icon;
|
||||||
|
@ -430,13 +425,8 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
|
||||||
physical_name, name, Common::FS::GetSize(physical_name), icon, *loader,
|
physical_name, name, Common::FS::GetSize(physical_name), icon, *loader,
|
||||||
program_id, compatibility_list, play_time_manager, patch);
|
program_id, compatibility_list, play_time_manager, patch);
|
||||||
|
|
||||||
RecordEvent([=](GameList* game_list) {
|
RecordEvent(
|
||||||
if (UISettings::values.show_folders_in_list) {
|
[=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); });
|
||||||
game_list->AddEntry(entry, parent_dir);
|
|
||||||
} else {
|
|
||||||
game_list->AddRootEntry(entry);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (is_dir) {
|
} else if (is_dir) {
|
||||||
|
@ -469,32 +459,20 @@ void GameListWorker::run() {
|
||||||
|
|
||||||
if (game_dir.path == std::string("SDMC")) {
|
if (game_dir.path == std::string("SDMC")) {
|
||||||
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir);
|
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir);
|
||||||
|
DirEntryReady(game_list_dir);
|
||||||
if (UISettings::values.show_folders_in_list)
|
|
||||||
DirEntryReady(game_list_dir);
|
|
||||||
|
|
||||||
AddTitlesToGameList(game_list_dir);
|
AddTitlesToGameList(game_list_dir);
|
||||||
} else if (game_dir.path == std::string("UserNAND")) {
|
} else if (game_dir.path == std::string("UserNAND")) {
|
||||||
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir);
|
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir);
|
||||||
|
DirEntryReady(game_list_dir);
|
||||||
if (UISettings::values.show_folders_in_list)
|
|
||||||
DirEntryReady(game_list_dir);
|
|
||||||
|
|
||||||
AddTitlesToGameList(game_list_dir);
|
AddTitlesToGameList(game_list_dir);
|
||||||
} else if (game_dir.path == std::string("SysNAND")) {
|
} else if (game_dir.path == std::string("SysNAND")) {
|
||||||
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir);
|
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir);
|
||||||
|
DirEntryReady(game_list_dir);
|
||||||
if (UISettings::values.show_folders_in_list)
|
|
||||||
DirEntryReady(game_list_dir);
|
|
||||||
|
|
||||||
AddTitlesToGameList(game_list_dir);
|
AddTitlesToGameList(game_list_dir);
|
||||||
} else {
|
} else {
|
||||||
watch_list.append(QString::fromStdString(game_dir.path));
|
watch_list.append(QString::fromStdString(game_dir.path));
|
||||||
auto* const game_list_dir = new GameListDir(game_dir);
|
auto* const game_list_dir = new GameListDir(game_dir);
|
||||||
|
DirEntryReady(game_list_dir);
|
||||||
if (UISettings::values.show_folders_in_list)
|
|
||||||
DirEntryReady(game_list_dir);
|
|
||||||
|
|
||||||
ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path, game_dir.deep_scan,
|
ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path, game_dir.deep_scan,
|
||||||
game_list_dir);
|
game_list_dir);
|
||||||
ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path, game_dir.deep_scan,
|
ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path, game_dir.deep_scan,
|
||||||
|
|
|
@ -100,6 +100,7 @@
|
||||||
#include "common/x64/cpu_detect.h"
|
#include "common/x64/cpu_detect.h"
|
||||||
#endif
|
#endif
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
#include "common/telemetry.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/crypto/key_manager.h"
|
#include "core/crypto/key_manager.h"
|
||||||
|
@ -118,6 +119,7 @@
|
||||||
#include "core/hle/service/sm/sm.h"
|
#include "core/hle/service/sm/sm.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
#include "core/perf_stats.h"
|
#include "core/perf_stats.h"
|
||||||
|
#include "core/telemetry_session.h"
|
||||||
#include "frontend_common/config.h"
|
#include "frontend_common/config.h"
|
||||||
#include "input_common/drivers/tas_input.h"
|
#include "input_common/drivers/tas_input.h"
|
||||||
#include "input_common/drivers/virtual_amiibo.h"
|
#include "input_common/drivers/virtual_amiibo.h"
|
||||||
|
@ -186,9 +188,28 @@ constexpr size_t CopyBufferSize = 1_MiB;
|
||||||
* user. This is 32-bits - if we have more than 32 callouts, we should retire and recycle old ones.
|
* user. This is 32-bits - if we have more than 32 callouts, we should retire and recycle old ones.
|
||||||
*/
|
*/
|
||||||
enum class CalloutFlag : uint32_t {
|
enum class CalloutFlag : uint32_t {
|
||||||
|
Telemetry = 0x1,
|
||||||
DRDDeprecation = 0x2,
|
DRDDeprecation = 0x2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void GMainWindow::ShowTelemetryCallout() {
|
||||||
|
if (UISettings::values.callout_flags.GetValue() &
|
||||||
|
static_cast<uint32_t>(CalloutFlag::Telemetry)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UISettings::values.callout_flags =
|
||||||
|
UISettings::values.callout_flags.GetValue() | static_cast<uint32_t>(CalloutFlag::Telemetry);
|
||||||
|
const QString telemetry_message =
|
||||||
|
tr("<a href='https://suyu.dev/help/feature/telemetry/'>Anonymous "
|
||||||
|
"data is collected</a> to help improve suyu. "
|
||||||
|
"<br/><br/>Would you like to share your usage data with us?");
|
||||||
|
if (!question(this, tr("Telemetry"), telemetry_message)) {
|
||||||
|
Settings::values.enable_telemetry = false;
|
||||||
|
system->ApplySettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const int GMainWindow::max_recent_files_item;
|
const int GMainWindow::max_recent_files_item;
|
||||||
|
|
||||||
static void RemoveCachedContents() {
|
static void RemoveCachedContents() {
|
||||||
|
@ -396,6 +417,9 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk
|
||||||
game_list->LoadCompatibilityList();
|
game_list->LoadCompatibilityList();
|
||||||
game_list->PopulateAsync(UISettings::values.game_dirs);
|
game_list->PopulateAsync(UISettings::values.game_dirs);
|
||||||
|
|
||||||
|
// Show one-time "callout" messages to the user
|
||||||
|
ShowTelemetryCallout();
|
||||||
|
|
||||||
// make sure menubar has the arrow cursor instead of inheriting from this
|
// make sure menubar has the arrow cursor instead of inheriting from this
|
||||||
ui->menubar->setCursor(QCursor());
|
ui->menubar->setCursor(QCursor());
|
||||||
statusBar()->setCursor(QCursor());
|
statusBar()->setCursor(QCursor());
|
||||||
|
@ -1416,7 +1440,6 @@ void GMainWindow::RestoreUIState() {
|
||||||
game_list->SetFilterVisible(ui->action_Show_Filter_Bar->isChecked());
|
game_list->SetFilterVisible(ui->action_Show_Filter_Bar->isChecked());
|
||||||
|
|
||||||
ui->action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar.GetValue());
|
ui->action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar.GetValue());
|
||||||
ui->action_Show_Folders_In_List->setChecked(UISettings::values.show_folders_in_list.GetValue());
|
|
||||||
statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked());
|
statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked());
|
||||||
Debugger::ToggleConsole();
|
Debugger::ToggleConsole();
|
||||||
}
|
}
|
||||||
|
@ -1532,7 +1555,6 @@ void GMainWindow::ConnectMenuEvents() {
|
||||||
connect_menu(ui->action_Display_Dock_Widget_Headers, &GMainWindow::OnDisplayTitleBars);
|
connect_menu(ui->action_Display_Dock_Widget_Headers, &GMainWindow::OnDisplayTitleBars);
|
||||||
connect_menu(ui->action_Show_Filter_Bar, &GMainWindow::OnToggleFilterBar);
|
connect_menu(ui->action_Show_Filter_Bar, &GMainWindow::OnToggleFilterBar);
|
||||||
connect_menu(ui->action_Show_Status_Bar, &GMainWindow::OnToggleStatusBar);
|
connect_menu(ui->action_Show_Status_Bar, &GMainWindow::OnToggleStatusBar);
|
||||||
connect_menu(ui->action_Show_Folders_In_List, &GMainWindow::OnToggleFoldersInList);
|
|
||||||
|
|
||||||
connect_menu(ui->action_Reset_Window_Size_720, &GMainWindow::ResetWindowSize720);
|
connect_menu(ui->action_Reset_Window_Size_720, &GMainWindow::ResetWindowSize720);
|
||||||
connect_menu(ui->action_Reset_Window_Size_900, &GMainWindow::ResetWindowSize900);
|
connect_menu(ui->action_Reset_Window_Size_900, &GMainWindow::ResetWindowSize900);
|
||||||
|
@ -1752,15 +1774,6 @@ bool GMainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletPa
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ContentManager::AreKeysPresent()) {
|
|
||||||
QMessageBox::warning(this, tr("Derivation Components Missing"),
|
|
||||||
tr("Encryption keys are missing. "
|
|
||||||
"In order to use this emulator"
|
|
||||||
"you need to provide your own encryption keys"
|
|
||||||
"in order to play them."));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shutdown previous session if the emu thread is still active...
|
// Shutdown previous session if the emu thread is still active...
|
||||||
if (emu_thread != nullptr) {
|
if (emu_thread != nullptr) {
|
||||||
ShutdownGame();
|
ShutdownGame();
|
||||||
|
@ -1857,6 +1870,8 @@ bool GMainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletPa
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
current_game_path = filename;
|
current_game_path = filename;
|
||||||
|
|
||||||
|
system->TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "Qt");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3365,7 +3380,7 @@ void GMainWindow::OnMenuReportCompatibility() {
|
||||||
|
|
||||||
if (!Settings::values.suyu_token.GetValue().empty() &&
|
if (!Settings::values.suyu_token.GetValue().empty() &&
|
||||||
!Settings::values.suyu_username.GetValue().empty()) {
|
!Settings::values.suyu_username.GetValue().empty()) {
|
||||||
CompatDB compatdb{this};
|
CompatDB compatdb{system->TelemetrySession(), this};
|
||||||
compatdb.exec();
|
compatdb.exec();
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::critical(
|
QMessageBox::critical(
|
||||||
|
@ -3595,6 +3610,8 @@ void GMainWindow::OnConfigure() {
|
||||||
|
|
||||||
SetDefaultUIGeometry();
|
SetDefaultUIGeometry();
|
||||||
RestoreUIState();
|
RestoreUIState();
|
||||||
|
|
||||||
|
ShowTelemetryCallout();
|
||||||
}
|
}
|
||||||
InitializeHotkeys();
|
InitializeHotkeys();
|
||||||
|
|
||||||
|
@ -4196,14 +4213,6 @@ void GMainWindow::OnToggleStatusBar() {
|
||||||
statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked());
|
statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked());
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnToggleFoldersInList() {
|
|
||||||
UISettings::values.show_folders_in_list = ui->action_Show_Folders_In_List->isChecked();
|
|
||||||
|
|
||||||
game_list->ClearList();
|
|
||||||
game_list->LoadCompatibilityList();
|
|
||||||
game_list->PopulateAsync(UISettings::values.game_dirs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GMainWindow::OnAlbum() {
|
void GMainWindow::OnAlbum() {
|
||||||
constexpr u64 AlbumId = static_cast<u64>(Service::AM::AppletProgramId::PhotoViewer);
|
constexpr u64 AlbumId = static_cast<u64>(Service::AM::AppletProgramId::PhotoViewer);
|
||||||
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
|
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
|
||||||
|
@ -4592,7 +4601,6 @@ void GMainWindow::UpdateUISettings() {
|
||||||
UISettings::values.display_titlebar = ui->action_Display_Dock_Widget_Headers->isChecked();
|
UISettings::values.display_titlebar = ui->action_Display_Dock_Widget_Headers->isChecked();
|
||||||
UISettings::values.show_filter_bar = ui->action_Show_Filter_Bar->isChecked();
|
UISettings::values.show_filter_bar = ui->action_Show_Filter_Bar->isChecked();
|
||||||
UISettings::values.show_status_bar = ui->action_Show_Status_Bar->isChecked();
|
UISettings::values.show_status_bar = ui->action_Show_Status_Bar->isChecked();
|
||||||
UISettings::values.show_folders_in_list = ui->action_Show_Folders_In_List->isChecked();
|
|
||||||
UISettings::values.first_start = false;
|
UISettings::values.first_start = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4628,13 +4636,13 @@ void GMainWindow::OnMouseActivity() {
|
||||||
void GMainWindow::OnCheckFirmwareDecryption() {
|
void GMainWindow::OnCheckFirmwareDecryption() {
|
||||||
system->GetFileSystemController().CreateFactories(*vfs);
|
system->GetFileSystemController().CreateFactories(*vfs);
|
||||||
if (!ContentManager::AreKeysPresent()) {
|
if (!ContentManager::AreKeysPresent()) {
|
||||||
QMessageBox::warning(this, tr("Derivation Components Missing"),
|
QMessageBox::warning(
|
||||||
tr("Encryption keys are missing. "
|
this, tr("Derivation Components Missing"),
|
||||||
"In order to use this emulator"
|
tr("Encryption keys are missing. "
|
||||||
"you need to provide your own encryption keys"
|
"<br>Please follow <a href='https://suyu.dev/help/quickstart/'>the suyu "
|
||||||
"in order to play them."));
|
"quickstart guide</a> to get all your keys, firmware and "
|
||||||
|
"games."));
|
||||||
}
|
}
|
||||||
|
|
||||||
SetFirmwareVersion();
|
SetFirmwareVersion();
|
||||||
UpdateMenuState();
|
UpdateMenuState();
|
||||||
}
|
}
|
||||||
|
|
|
@ -275,6 +275,7 @@ private:
|
||||||
void BootGameFromList(const QString& filename, StartGameType with_config);
|
void BootGameFromList(const QString& filename, StartGameType with_config);
|
||||||
void ShutdownGame();
|
void ShutdownGame();
|
||||||
|
|
||||||
|
void ShowTelemetryCallout();
|
||||||
void SetDiscordEnabled(bool state);
|
void SetDiscordEnabled(bool state);
|
||||||
void LoadAmiibo(const QString& filename);
|
void LoadAmiibo(const QString& filename);
|
||||||
|
|
||||||
|
@ -383,7 +384,6 @@ private slots:
|
||||||
void OnAbout();
|
void OnAbout();
|
||||||
void OnToggleFilterBar();
|
void OnToggleFilterBar();
|
||||||
void OnToggleStatusBar();
|
void OnToggleStatusBar();
|
||||||
void OnToggleFoldersInList();
|
|
||||||
void OnDisplayTitleBars(bool);
|
void OnDisplayTitleBars(bool);
|
||||||
void InitializeHotkeys();
|
void InitializeHotkeys();
|
||||||
void ToggleFullscreen();
|
void ToggleFullscreen();
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1280</width>
|
<width>1280</width>
|
||||||
<height>21</height>
|
<height>22</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menu_File">
|
<widget class="QMenu" name="menu_File">
|
||||||
|
@ -124,7 +124,6 @@
|
||||||
<addaction name="action_Display_Dock_Widget_Headers"/>
|
<addaction name="action_Display_Dock_Widget_Headers"/>
|
||||||
<addaction name="action_Show_Filter_Bar"/>
|
<addaction name="action_Show_Filter_Bar"/>
|
||||||
<addaction name="action_Show_Status_Bar"/>
|
<addaction name="action_Show_Status_Bar"/>
|
||||||
<addaction name="action_Show_Folders_In_List" />
|
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="menu_Reset_Window_Size"/>
|
<addaction name="menu_Reset_Window_Size"/>
|
||||||
<addaction name="menu_View_Debugging"/>
|
<addaction name="menu_View_Debugging"/>
|
||||||
|
@ -289,17 +288,6 @@
|
||||||
<string>Show Status Bar</string>
|
<string>Show Status Bar</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="action_Show_Folders_In_List">
|
|
||||||
<property name="checkable">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Show Folders in List</string>
|
|
||||||
</property>
|
|
||||||
<property name="iconText">
|
|
||||||
<string>Show Status Bar</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_View_Lobby">
|
<action name="action_View_Lobby">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
|
|
@ -200,7 +200,6 @@ struct Values {
|
||||||
std::atomic_bool is_game_list_reload_pending{false};
|
std::atomic_bool is_game_list_reload_pending{false};
|
||||||
Setting<bool> cache_game_list{linkage, true, "cache_game_list", Category::UiGameList};
|
Setting<bool> cache_game_list{linkage, true, "cache_game_list", Category::UiGameList};
|
||||||
Setting<bool> favorites_expanded{linkage, true, "favorites_expanded", Category::UiGameList};
|
Setting<bool> favorites_expanded{linkage, true, "favorites_expanded", Category::UiGameList};
|
||||||
Setting<bool> show_folders_in_list{linkage, true, "show_folders_in_list", Category::UiGameList};
|
|
||||||
QVector<u64> favorited_ids;
|
QVector<u64> favorited_ids;
|
||||||
|
|
||||||
// Compatibility List
|
// Compatibility List
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
|
#include "common/telemetry.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/cpu_manager.h"
|
#include "core/cpu_manager.h"
|
||||||
|
@ -28,6 +29,7 @@
|
||||||
#include "core/hle/service/am/applet_manager.h"
|
#include "core/hle/service/am/applet_manager.h"
|
||||||
#include "core/hle/service/filesystem/filesystem.h"
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
#include "core/telemetry_session.h"
|
||||||
#include "frontend_common/config.h"
|
#include "frontend_common/config.h"
|
||||||
#include "input_common/main.h"
|
#include "input_common/main.h"
|
||||||
#include "network/network.h"
|
#include "network/network.h"
|
||||||
|
@ -401,6 +403,8 @@ int main(int argc, char** argv) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL");
|
||||||
|
|
||||||
if (use_multiplayer) {
|
if (use_multiplayer) {
|
||||||
if (auto member = system.GetRoomNetwork().GetRoomMember().lock()) {
|
if (auto member = system.GetRoomNetwork().GetRoomMember().lock()) {
|
||||||
member->BindOnChatMessageReceived(OnMessageReceived);
|
member->BindOnChatMessageReceived(OnMessageReceived);
|
||||||
|
|
|
@ -9,7 +9,7 @@ if(LIBVA_FOUND)
|
||||||
list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES})
|
list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(sources
|
add_library(video_core STATIC
|
||||||
buffer_cache/buffer_base.h
|
buffer_cache/buffer_base.h
|
||||||
buffer_cache/buffer_cache_base.h
|
buffer_cache/buffer_cache_base.h
|
||||||
buffer_cache/buffer_cache.cpp
|
buffer_cache/buffer_cache.cpp
|
||||||
|
@ -315,67 +315,6 @@ set(sources
|
||||||
vulkan_common/vulkan.h
|
vulkan_common/vulkan.h
|
||||||
)
|
)
|
||||||
|
|
||||||
if (APPLE)
|
|
||||||
list(REMOVE_ITEM sources
|
|
||||||
renderer_opengl/present/filters.cpp
|
|
||||||
renderer_opengl/present/filters.h
|
|
||||||
renderer_opengl/present/fsr.cpp
|
|
||||||
renderer_opengl/present/fsr.h
|
|
||||||
renderer_opengl/present/fxaa.cpp
|
|
||||||
renderer_opengl/present/fxaa.h
|
|
||||||
renderer_opengl/present/layer.cpp
|
|
||||||
renderer_opengl/present/layer.h
|
|
||||||
renderer_opengl/present/present_uniforms.h
|
|
||||||
renderer_opengl/present/smaa.cpp
|
|
||||||
renderer_opengl/present/smaa.h
|
|
||||||
renderer_opengl/present/util.h
|
|
||||||
renderer_opengl/present/window_adapt_pass.cpp
|
|
||||||
renderer_opengl/present/window_adapt_pass.h
|
|
||||||
renderer_opengl/blit_image.cpp
|
|
||||||
renderer_opengl/blit_image.h
|
|
||||||
renderer_opengl/gl_blit_screen.cpp
|
|
||||||
renderer_opengl/gl_blit_screen.h
|
|
||||||
renderer_opengl/gl_buffer_cache_base.cpp
|
|
||||||
renderer_opengl/gl_buffer_cache.cpp
|
|
||||||
renderer_opengl/gl_buffer_cache.h
|
|
||||||
renderer_opengl/gl_compute_pipeline.cpp
|
|
||||||
renderer_opengl/gl_compute_pipeline.h
|
|
||||||
renderer_opengl/gl_device.cpp
|
|
||||||
renderer_opengl/gl_device.h
|
|
||||||
renderer_opengl/gl_fence_manager.cpp
|
|
||||||
renderer_opengl/gl_fence_manager.h
|
|
||||||
renderer_opengl/gl_graphics_pipeline.cpp
|
|
||||||
renderer_opengl/gl_graphics_pipeline.h
|
|
||||||
renderer_opengl/gl_rasterizer.cpp
|
|
||||||
renderer_opengl/gl_rasterizer.h
|
|
||||||
renderer_opengl/gl_resource_manager.cpp
|
|
||||||
renderer_opengl/gl_resource_manager.h
|
|
||||||
renderer_opengl/gl_shader_cache.cpp
|
|
||||||
renderer_opengl/gl_shader_cache.h
|
|
||||||
renderer_opengl/gl_shader_manager.cpp
|
|
||||||
renderer_opengl/gl_shader_manager.h
|
|
||||||
renderer_opengl/gl_shader_context.h
|
|
||||||
renderer_opengl/gl_shader_util.cpp
|
|
||||||
renderer_opengl/gl_shader_util.h
|
|
||||||
renderer_opengl/gl_state_tracker.cpp
|
|
||||||
renderer_opengl/gl_state_tracker.h
|
|
||||||
renderer_opengl/gl_staging_buffer_pool.cpp
|
|
||||||
renderer_opengl/gl_staging_buffer_pool.h
|
|
||||||
renderer_opengl/gl_texture_cache.cpp
|
|
||||||
renderer_opengl/gl_texture_cache.h
|
|
||||||
renderer_opengl/gl_texture_cache_base.cpp
|
|
||||||
renderer_opengl/gl_query_cache.cpp
|
|
||||||
renderer_opengl/gl_query_cache.h
|
|
||||||
renderer_opengl/maxwell_to_gl.h
|
|
||||||
renderer_opengl/renderer_opengl.cpp
|
|
||||||
renderer_opengl/renderer_opengl.h
|
|
||||||
renderer_opengl/util_shaders.cpp
|
|
||||||
renderer_opengl/util_shaders.h
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_library(video_core STATIC ${sources})
|
|
||||||
|
|
||||||
target_link_libraries(video_core PUBLIC common core)
|
target_link_libraries(video_core PUBLIC common core)
|
||||||
target_link_libraries(video_core PUBLIC glad shader_recompiler stb bc_decoder)
|
target_link_libraries(video_core PUBLIC glad shader_recompiler stb bc_decoder)
|
||||||
|
|
||||||
|
@ -408,7 +347,7 @@ if (MSVC)
|
||||||
/we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
|
/we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
if (APPLE OR ARCHITECTURE_arm64)
|
if (APPLE)
|
||||||
# error: declaration shadows a typedef in 'interval_base_set<SubType, DomainT, Compare, Interval, Alloc>'
|
# error: declaration shadows a typedef in 'interval_base_set<SubType, DomainT, Compare, Interval, Alloc>'
|
||||||
# error: implicit conversion loses integer precision: 'int' to 'boost::icl::bound_type' (aka 'unsigned char')
|
# error: implicit conversion loses integer precision: 'int' to 'boost::icl::bound_type' (aka 'unsigned char')
|
||||||
target_compile_options(video_core PRIVATE -Wno-shadow -Wno-unused-local-typedef)
|
target_compile_options(video_core PRIVATE -Wno-shadow -Wno-unused-local-typedef)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project & 2024 suyu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
@ -217,166 +217,7 @@ bool DecoderContext::OpenContext(const Decoder& decoder) {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#ifndef ANDROID
|
|
||||||
// Nasty but allows linux builds to pass.
|
|
||||||
// Requires double checks when FFMPEG gets updated.
|
|
||||||
// Hopefully a future FFMPEG update will all and expose a solution in the public API.
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
typedef struct FFCodecDefault {
|
|
||||||
const char* key;
|
|
||||||
const char* value;
|
|
||||||
} FFCodecDefault;
|
|
||||||
|
|
||||||
typedef struct FFCodec {
|
|
||||||
/**
|
|
||||||
* The public AVCodec. See codec.h for it.
|
|
||||||
*/
|
|
||||||
AVCodec p;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal codec capabilities FF_CODEC_CAP_*.
|
|
||||||
*/
|
|
||||||
unsigned caps_internal : 29;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This field determines the type of the codec (decoder/encoder)
|
|
||||||
* and also the exact callback cb implemented by the codec.
|
|
||||||
* cb_type uses enum FFCodecType values.
|
|
||||||
*/
|
|
||||||
unsigned cb_type : 3;
|
|
||||||
|
|
||||||
int priv_data_size;
|
|
||||||
/**
|
|
||||||
* @name Frame-level threading support functions
|
|
||||||
* @{
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Copy necessary context variables from a previous thread context to the current one.
|
|
||||||
* If not defined, the next thread will start automatically; otherwise, the codec
|
|
||||||
* must call ff_thread_finish_setup().
|
|
||||||
*
|
|
||||||
* dst and src will (rarely) point to the same context, in which case memcpy should be skipped.
|
|
||||||
*/
|
|
||||||
int (*update_thread_context)(struct AVCodecContext* dst, const struct AVCodecContext* src);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copy variables back to the user-facing context
|
|
||||||
*/
|
|
||||||
int (*update_thread_context_for_user)(struct AVCodecContext* dst,
|
|
||||||
const struct AVCodecContext* src);
|
|
||||||
/** @} */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Private codec-specific defaults.
|
|
||||||
*/
|
|
||||||
const FFCodecDefault* defaults;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize codec static data, called from av_codec_iterate().
|
|
||||||
*
|
|
||||||
* This is not intended for time consuming operations as it is
|
|
||||||
* run for every codec regardless of that codec being used.
|
|
||||||
*/
|
|
||||||
void (*init_static_data)(struct FFCodec* codec);
|
|
||||||
|
|
||||||
int (*init)(struct AVCodecContext*);
|
|
||||||
|
|
||||||
union {
|
|
||||||
/**
|
|
||||||
* Decode to an AVFrame.
|
|
||||||
* cb is in this state if cb_type is FF_CODEC_CB_TYPE_DECODE.
|
|
||||||
*
|
|
||||||
* @param avctx codec context
|
|
||||||
* @param[out] frame AVFrame for output
|
|
||||||
* @param[out] got_frame_ptr decoder sets to 0 or 1 to indicate that
|
|
||||||
* a non-empty frame was returned in frame.
|
|
||||||
* @param[in] avpkt AVPacket containing the data to be decoded
|
|
||||||
* @return amount of bytes read from the packet on success,
|
|
||||||
* negative error code on failure
|
|
||||||
*/
|
|
||||||
int (*decode)(struct AVCodecContext* avctx, struct AVFrame* frame, int* got_frame_ptr,
|
|
||||||
struct AVPacket* avpkt);
|
|
||||||
/**
|
|
||||||
* Decode subtitle data to an AVSubtitle.
|
|
||||||
* cb is in this state if cb_type is FF_CODEC_CB_TYPE_DECODE_SUB.
|
|
||||||
*
|
|
||||||
* Apart from that this is like the decode callback.
|
|
||||||
*/
|
|
||||||
int (*decode_sub)(struct AVCodecContext* avctx, struct AVSubtitle* sub, int* got_frame_ptr,
|
|
||||||
const struct AVPacket* avpkt);
|
|
||||||
/**
|
|
||||||
* Decode API with decoupled packet/frame dataflow.
|
|
||||||
* cb is in this state if cb_type is FF_CODEC_CB_TYPE_RECEIVE_FRAME.
|
|
||||||
*
|
|
||||||
* This function is called to get one output frame. It should call
|
|
||||||
* ff_decode_get_packet() to obtain input data.
|
|
||||||
*/
|
|
||||||
int (*receive_frame)(struct AVCodecContext* avctx, struct AVFrame* frame);
|
|
||||||
/**
|
|
||||||
* Encode data to an AVPacket.
|
|
||||||
* cb is in this state if cb_type is FF_CODEC_CB_TYPE_ENCODE
|
|
||||||
*
|
|
||||||
* @param avctx codec context
|
|
||||||
* @param[out] avpkt output AVPacket
|
|
||||||
* @param[in] frame AVFrame containing the input to be encoded
|
|
||||||
* @param[out] got_packet_ptr encoder sets to 0 or 1 to indicate that a
|
|
||||||
* non-empty packet was returned in avpkt.
|
|
||||||
* @return 0 on success, negative error code on failure
|
|
||||||
*/
|
|
||||||
int (*encode)(struct AVCodecContext* avctx, struct AVPacket* avpkt,
|
|
||||||
const struct AVFrame* frame, int* got_packet_ptr);
|
|
||||||
/**
|
|
||||||
* Encode subtitles to a raw buffer.
|
|
||||||
* cb is in this state if cb_type is FF_CODEC_CB_TYPE_ENCODE_SUB.
|
|
||||||
*/
|
|
||||||
int (*encode_sub)(struct AVCodecContext* avctx, uint8_t* buf, int buf_size,
|
|
||||||
const struct AVSubtitle* sub);
|
|
||||||
/**
|
|
||||||
* Encode API with decoupled frame/packet dataflow.
|
|
||||||
* cb is in this state if cb_type is FF_CODEC_CB_TYPE_RECEIVE_PACKET.
|
|
||||||
*
|
|
||||||
* This function is called to get one output packet.
|
|
||||||
* It should call ff_encode_get_frame() to obtain input data.
|
|
||||||
*/
|
|
||||||
int (*receive_packet)(struct AVCodecContext* avctx, struct AVPacket* avpkt);
|
|
||||||
} cb;
|
|
||||||
|
|
||||||
int (*close)(struct AVCodecContext*);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flush buffers.
|
|
||||||
* Will be called when seeking
|
|
||||||
*/
|
|
||||||
void (*flush)(struct AVCodecContext*);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decoding only, a comma-separated list of bitstream filters to apply to
|
|
||||||
* packets before decoding.
|
|
||||||
*/
|
|
||||||
const char* bsfs;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Array of pointers to hardware configurations supported by the codec,
|
|
||||||
* or NULL if no hardware supported. The array is terminated by a NULL
|
|
||||||
* pointer.
|
|
||||||
*
|
|
||||||
* The user can only access this field via avcodec_get_hw_config().
|
|
||||||
*/
|
|
||||||
const struct AVCodecHWConfigInternal* const* hw_configs;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of supported codec_tags, terminated by FF_CODEC_TAGS_END.
|
|
||||||
*/
|
|
||||||
const uint32_t* codec_tags;
|
|
||||||
} FFCodec;
|
|
||||||
|
|
||||||
static av_always_inline const FFCodec* ffcodec(const AVCodec* codec) {
|
|
||||||
return (const FFCodec*)codec;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
#endif
|
|
||||||
bool DecoderContext::SendPacket(const Packet& packet) {
|
bool DecoderContext::SendPacket(const Packet& packet) {
|
||||||
m_temp_frame = std::make_shared<Frame>();
|
m_temp_frame = std::make_shared<Frame>();
|
||||||
m_got_frame = 0;
|
m_got_frame = 0;
|
||||||
|
@ -386,10 +227,8 @@ bool DecoderContext::SendPacket(const Packet& packet) {
|
||||||
#ifndef ANDROID
|
#ifndef ANDROID
|
||||||
if (!m_codec_context->hw_device_ctx && m_codec_context->codec_id == AV_CODEC_ID_H264) {
|
if (!m_codec_context->hw_device_ctx && m_codec_context->codec_id == AV_CODEC_ID_H264) {
|
||||||
m_decode_order = true;
|
m_decode_order = true;
|
||||||
auto* codec{ffcodec(m_decoder.GetCodec())};
|
const int ret = avcodec_send_frame(m_codec_context, m_temp_frame->GetFrame());
|
||||||
if (const int ret = codec->cb.decode(m_codec_context, m_temp_frame->GetFrame(),
|
if (ret < 0) {
|
||||||
&m_got_frame, packet.GetPacket());
|
|
||||||
ret < 0) {
|
|
||||||
LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", AVError(ret));
|
LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", AVError(ret));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -411,7 +250,6 @@ std::shared_ptr<Frame> DecoderContext::ReceiveFrame() {
|
||||||
#ifndef ANDROID
|
#ifndef ANDROID
|
||||||
if (!m_codec_context->hw_device_ctx && m_codec_context->codec_id == AV_CODEC_ID_H264) {
|
if (!m_codec_context->hw_device_ctx && m_codec_context->codec_id == AV_CODEC_ID_H264) {
|
||||||
m_decode_order = true;
|
m_decode_order = true;
|
||||||
auto* codec{ffcodec(m_decoder.GetCodec())};
|
|
||||||
int ret{0};
|
int ret{0};
|
||||||
|
|
||||||
if (m_got_frame == 0) {
|
if (m_got_frame == 0) {
|
||||||
|
@ -419,7 +257,7 @@ std::shared_ptr<Frame> DecoderContext::ReceiveFrame() {
|
||||||
auto* pkt = packet.GetPacket();
|
auto* pkt = packet.GetPacket();
|
||||||
pkt->data = nullptr;
|
pkt->data = nullptr;
|
||||||
pkt->size = 0;
|
pkt->size = 0;
|
||||||
ret = codec->cb.decode(m_codec_context, m_temp_frame->GetFrame(), &m_got_frame, pkt);
|
ret = avcodec_receive_packet(m_codec_context, pkt);
|
||||||
m_codec_context->has_b_frames = 0;
|
m_codec_context->has_b_frames = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,27 @@
|
||||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdlib>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/microprofile.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
#include "common/telemetry.h"
|
||||||
|
#include "core/core_timing.h"
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
|
#include "core/telemetry_session.h"
|
||||||
#include "video_core/capture.h"
|
#include "video_core/capture.h"
|
||||||
#include "video_core/present.h"
|
#include "video_core/present.h"
|
||||||
#include "video_core/renderer_opengl/gl_blit_screen.h"
|
#include "video_core/renderer_opengl/gl_blit_screen.h"
|
||||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||||
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
||||||
|
#include "video_core/renderer_opengl/gl_shader_util.h"
|
||||||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
#include "video_core/renderer_opengl/renderer_opengl.h"
|
||||||
#include "video_core/textures/decoders.h"
|
#include "video_core/textures/decoders.h"
|
||||||
|
|
||||||
|
@ -82,18 +90,20 @@ void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severit
|
||||||
}
|
}
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window_,
|
RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
|
||||||
|
Core::Frontend::EmuWindow& emu_window_,
|
||||||
Tegra::MaxwellDeviceMemoryManager& device_memory_, Tegra::GPU& gpu_,
|
Tegra::MaxwellDeviceMemoryManager& device_memory_, Tegra::GPU& gpu_,
|
||||||
std::unique_ptr<Core::Frontend::GraphicsContext> context_)
|
std::unique_ptr<Core::Frontend::GraphicsContext> context_)
|
||||||
: RendererBase{emu_window_, std::move(context_)}, emu_window{emu_window_},
|
: RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_},
|
||||||
device_memory{device_memory_}, gpu{gpu_}, device{emu_window_}, state_tracker{},
|
emu_window{emu_window_}, device_memory{device_memory_}, gpu{gpu_}, device{emu_window_},
|
||||||
program_manager{device},
|
state_tracker{}, program_manager{device},
|
||||||
rasterizer(emu_window, gpu, device_memory, device, program_manager, state_tracker) {
|
rasterizer(emu_window, gpu, device_memory, device, program_manager, state_tracker) {
|
||||||
if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) {
|
if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) {
|
||||||
glEnable(GL_DEBUG_OUTPUT);
|
glEnable(GL_DEBUG_OUTPUT);
|
||||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||||
glDebugMessageCallback(DebugHandler, nullptr);
|
glDebugMessageCallback(DebugHandler, nullptr);
|
||||||
}
|
}
|
||||||
|
AddTelemetryFields();
|
||||||
|
|
||||||
// Initialize default attributes to match hardware's disabled attributes
|
// Initialize default attributes to match hardware's disabled attributes
|
||||||
GLint max_attribs{};
|
GLint max_attribs{};
|
||||||
|
@ -145,6 +155,21 @@ void RendererOpenGL::Composite(std::span<const Tegra::FramebufferConfig> framebu
|
||||||
render_window.OnFrameDisplayed();
|
render_window.OnFrameDisplayed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RendererOpenGL::AddTelemetryFields() {
|
||||||
|
const char* const gl_version{reinterpret_cast<char const*>(glGetString(GL_VERSION))};
|
||||||
|
const char* const gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))};
|
||||||
|
const char* const gpu_model{reinterpret_cast<char const*>(glGetString(GL_RENDERER))};
|
||||||
|
|
||||||
|
LOG_INFO(Render_OpenGL, "GL_VERSION: {}", gl_version);
|
||||||
|
LOG_INFO(Render_OpenGL, "GL_VENDOR: {}", gpu_vendor);
|
||||||
|
LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model);
|
||||||
|
|
||||||
|
constexpr auto user_system = Common::Telemetry::FieldType::UserSystem;
|
||||||
|
telemetry_session.AddField(user_system, "GPU_Vendor", std::string(gpu_vendor));
|
||||||
|
telemetry_session.AddField(user_system, "GPU_Model", std::string(gpu_model));
|
||||||
|
telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version));
|
||||||
|
}
|
||||||
|
|
||||||
void RendererOpenGL::RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
|
void RendererOpenGL::RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
|
||||||
const Layout::FramebufferLayout& layout, void* dst) {
|
const Layout::FramebufferLayout& layout, void* dst) {
|
||||||
GLint old_read_fb;
|
GLint old_read_fb;
|
||||||
|
|
|
@ -34,7 +34,8 @@ class BlitScreen;
|
||||||
|
|
||||||
class RendererOpenGL final : public VideoCore::RendererBase {
|
class RendererOpenGL final : public VideoCore::RendererBase {
|
||||||
public:
|
public:
|
||||||
explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window_,
|
explicit RendererOpenGL(Core::TelemetrySession& telemetry_session_,
|
||||||
|
Core::Frontend::EmuWindow& emu_window_,
|
||||||
Tegra::MaxwellDeviceMemoryManager& device_memory_, Tegra::GPU& gpu_,
|
Tegra::MaxwellDeviceMemoryManager& device_memory_, Tegra::GPU& gpu_,
|
||||||
std::unique_ptr<Core::Frontend::GraphicsContext> context_);
|
std::unique_ptr<Core::Frontend::GraphicsContext> context_);
|
||||||
~RendererOpenGL() override;
|
~RendererOpenGL() override;
|
||||||
|
@ -52,11 +53,14 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void AddTelemetryFields();
|
||||||
|
|
||||||
void RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
|
void RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
|
||||||
const Layout::FramebufferLayout& layout, void* dst);
|
const Layout::FramebufferLayout& layout, void* dst);
|
||||||
void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
|
void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
|
||||||
void RenderAppletCaptureLayer(std::span<const Tegra::FramebufferConfig> framebuffers);
|
void RenderAppletCaptureLayer(std::span<const Tegra::FramebufferConfig> framebuffers);
|
||||||
|
|
||||||
|
Core::TelemetrySession& telemetry_session;
|
||||||
Core::Frontend::EmuWindow& emu_window;
|
Core::Frontend::EmuWindow& emu_window;
|
||||||
Tegra::MaxwellDeviceMemoryManager& device_memory;
|
Tegra::MaxwellDeviceMemoryManager& device_memory;
|
||||||
Tegra::GPU& gpu;
|
Tegra::GPU& gpu;
|
||||||
|
|
|
@ -1,17 +1,24 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/polyfill_ranges.h"
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
#include "common/telemetry.h"
|
||||||
|
#include "core/core_timing.h"
|
||||||
#include "core/frontend/graphics_context.h"
|
#include "core/frontend/graphics_context.h"
|
||||||
|
#include "core/telemetry_session.h"
|
||||||
#include "video_core/capture.h"
|
#include "video_core/capture.h"
|
||||||
#include "video_core/gpu.h"
|
#include "video_core/gpu.h"
|
||||||
#include "video_core/present.h"
|
#include "video_core/present.h"
|
||||||
|
@ -46,6 +53,37 @@ constexpr VkExtent3D CaptureImageExtent{
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr VkFormat CaptureFormat = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
constexpr VkFormat CaptureFormat = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||||
|
|
||||||
|
std::string GetReadableVersion(u32 version) {
|
||||||
|
return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version),
|
||||||
|
VK_VERSION_PATCH(version));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetDriverVersion(const Device& device) {
|
||||||
|
// Extracted from
|
||||||
|
// https://github.com/SaschaWillems/vulkan.gpuinfo.org/blob/5dddea46ea1120b0df14eef8f15ff8e318e35462/functions.php#L308-L314
|
||||||
|
const u32 version = device.GetDriverVersion();
|
||||||
|
|
||||||
|
if (device.GetDriverID() == VK_DRIVER_ID_NVIDIA_PROPRIETARY) {
|
||||||
|
const u32 major = (version >> 22) & 0x3ff;
|
||||||
|
const u32 minor = (version >> 14) & 0x0ff;
|
||||||
|
const u32 secondary = (version >> 6) & 0x0ff;
|
||||||
|
const u32 tertiary = version & 0x003f;
|
||||||
|
return fmt::format("{}.{}.{}.{}", major, minor, secondary, tertiary);
|
||||||
|
}
|
||||||
|
if (device.GetDriverID() == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) {
|
||||||
|
const u32 major = version >> 14;
|
||||||
|
const u32 minor = version & 0x3fff;
|
||||||
|
return fmt::format("{}.{}", major, minor);
|
||||||
|
}
|
||||||
|
return GetReadableVersion(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string BuildCommaSeparatedExtensions(
|
||||||
|
const std::set<std::string, std::less<>>& available_extensions) {
|
||||||
|
return fmt::format("{}", fmt::join(available_extensions, ","));
|
||||||
|
}
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld,
|
Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld,
|
||||||
|
@ -60,11 +98,12 @@ Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dl
|
||||||
return Device(*instance, physical_device, surface, dld);
|
return Device(*instance, physical_device, surface, dld);
|
||||||
}
|
}
|
||||||
|
|
||||||
RendererVulkan::RendererVulkan(Core::Frontend::EmuWindow& emu_window,
|
RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
|
||||||
|
Core::Frontend::EmuWindow& emu_window,
|
||||||
Tegra::MaxwellDeviceMemoryManager& device_memory_, Tegra::GPU& gpu_,
|
Tegra::MaxwellDeviceMemoryManager& device_memory_, Tegra::GPU& gpu_,
|
||||||
std::unique_ptr<Core::Frontend::GraphicsContext> context_) try
|
std::unique_ptr<Core::Frontend::GraphicsContext> context_) try
|
||||||
: RendererBase(emu_window, std::move(context_)), device_memory(device_memory_), gpu(gpu_),
|
: RendererBase(emu_window, std::move(context_)), telemetry_session(telemetry_session_),
|
||||||
library(OpenLibrary(context.get())),
|
device_memory(device_memory_), gpu(gpu_), library(OpenLibrary(context.get())),
|
||||||
instance(CreateInstance(*library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type,
|
instance(CreateInstance(*library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type,
|
||||||
Settings::values.renderer_debug.GetValue())),
|
Settings::values.renderer_debug.GetValue())),
|
||||||
debug_messenger(Settings::values.renderer_debug ? CreateDebugUtilsCallback(instance)
|
debug_messenger(Settings::values.renderer_debug ? CreateDebugUtilsCallback(instance)
|
||||||
|
@ -89,6 +128,7 @@ RendererVulkan::RendererVulkan(Core::Frontend::EmuWindow& emu_window,
|
||||||
turbo_mode.emplace(instance, dld);
|
turbo_mode.emplace(instance, dld);
|
||||||
scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); });
|
scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); });
|
||||||
}
|
}
|
||||||
|
Report();
|
||||||
} catch (const vk::Exception& exception) {
|
} catch (const vk::Exception& exception) {
|
||||||
LOG_ERROR(Render_Vulkan, "Vulkan initialization failed with error: {}", exception.what());
|
LOG_ERROR(Render_Vulkan, "Vulkan initialization failed with error: {}", exception.what());
|
||||||
throw std::runtime_error{fmt::format("Vulkan initialization error {}", exception.what())};
|
throw std::runtime_error{fmt::format("Vulkan initialization error {}", exception.what())};
|
||||||
|
@ -126,6 +166,32 @@ void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebu
|
||||||
rasterizer.TickFrame();
|
rasterizer.TickFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RendererVulkan::Report() const {
|
||||||
|
using namespace Common::Literals;
|
||||||
|
const std::string vendor_name{device.GetVendorName()};
|
||||||
|
const std::string model_name{device.GetModelName()};
|
||||||
|
const std::string driver_version = GetDriverVersion(device);
|
||||||
|
const std::string driver_name = fmt::format("{} {}", vendor_name, driver_version);
|
||||||
|
|
||||||
|
const std::string api_version = GetReadableVersion(device.ApiVersion());
|
||||||
|
|
||||||
|
const std::string extensions = BuildCommaSeparatedExtensions(device.GetAvailableExtensions());
|
||||||
|
|
||||||
|
const auto available_vram = static_cast<f64>(device.GetDeviceLocalMemory()) / f64{1_GiB};
|
||||||
|
|
||||||
|
LOG_INFO(Render_Vulkan, "Driver: {}", driver_name);
|
||||||
|
LOG_INFO(Render_Vulkan, "Device: {}", model_name);
|
||||||
|
LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version);
|
||||||
|
LOG_INFO(Render_Vulkan, "Available VRAM: {:.2f} GiB", available_vram);
|
||||||
|
|
||||||
|
static constexpr auto field = Common::Telemetry::FieldType::UserSystem;
|
||||||
|
telemetry_session.AddField(field, "GPU_Vendor", vendor_name);
|
||||||
|
telemetry_session.AddField(field, "GPU_Model", model_name);
|
||||||
|
telemetry_session.AddField(field, "GPU_Vulkan_Driver", driver_name);
|
||||||
|
telemetry_session.AddField(field, "GPU_Vulkan_Version", api_version);
|
||||||
|
telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
|
||||||
|
}
|
||||||
|
|
||||||
vk::Buffer RendererVulkan::RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
|
vk::Buffer RendererVulkan::RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
|
||||||
const Layout::FramebufferLayout& layout, VkFormat format,
|
const Layout::FramebufferLayout& layout, VkFormat format,
|
||||||
VkDeviceSize buffer_size) {
|
VkDeviceSize buffer_size) {
|
||||||
|
|
|
@ -40,7 +40,8 @@ Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dl
|
||||||
|
|
||||||
class RendererVulkan final : public VideoCore::RendererBase {
|
class RendererVulkan final : public VideoCore::RendererBase {
|
||||||
public:
|
public:
|
||||||
explicit RendererVulkan(Core::Frontend::EmuWindow& emu_window,
|
explicit RendererVulkan(Core::TelemetrySession& telemtry_session,
|
||||||
|
Core::Frontend::EmuWindow& emu_window,
|
||||||
Tegra::MaxwellDeviceMemoryManager& device_memory_, Tegra::GPU& gpu_,
|
Tegra::MaxwellDeviceMemoryManager& device_memory_, Tegra::GPU& gpu_,
|
||||||
std::unique_ptr<Core::Frontend::GraphicsContext> context_);
|
std::unique_ptr<Core::Frontend::GraphicsContext> context_);
|
||||||
~RendererVulkan() override;
|
~RendererVulkan() override;
|
||||||
|
@ -58,12 +59,15 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void Report() const;
|
||||||
|
|
||||||
vk::Buffer RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
|
vk::Buffer RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
|
||||||
const Layout::FramebufferLayout& layout, VkFormat format,
|
const Layout::FramebufferLayout& layout, VkFormat format,
|
||||||
VkDeviceSize buffer_size);
|
VkDeviceSize buffer_size);
|
||||||
void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
|
void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
|
||||||
void RenderAppletCaptureLayer(std::span<const Tegra::FramebufferConfig> framebuffers);
|
void RenderAppletCaptureLayer(std::span<const Tegra::FramebufferConfig> framebuffers);
|
||||||
|
|
||||||
|
Core::TelemetrySession& telemetry_session;
|
||||||
Tegra::MaxwellDeviceMemoryManager& device_memory;
|
Tegra::MaxwellDeviceMemoryManager& device_memory;
|
||||||
Tegra::GPU& gpu;
|
Tegra::GPU& gpu;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project & 2024 suyu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -136,10 +136,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||||
auto usage = ImageUsageFlags(format_info, info.format);
|
auto usage = ImageUsageFlags(format_info, info.format);
|
||||||
if (is_3d) {
|
if (is_3d) {
|
||||||
flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
|
flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
|
||||||
// Force usage to be VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT only on MoltenVK
|
usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||||
if (device.IsMoltenVK()) {
|
|
||||||
usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(info.num_samples);
|
const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(info.num_samples);
|
||||||
return VkImageCreateInfo{
|
return VkImageCreateInfo{
|
||||||
|
@ -167,7 +164,8 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||||
|
|
||||||
[[nodiscard]] vk::Image MakeImage(const Device& device, const MemoryAllocator& allocator,
|
[[nodiscard]] vk::Image MakeImage(const Device& device, const MemoryAllocator& allocator,
|
||||||
const ImageInfo& info, std::span<const VkFormat> view_formats) {
|
const ImageInfo& info, std::span<const VkFormat> view_formats) {
|
||||||
if (info.type == ImageType::Buffer) {
|
const bool is_buffer = (info.type == ImageType::Buffer);
|
||||||
|
if (is_buffer) {
|
||||||
return vk::Image{};
|
return vk::Image{};
|
||||||
}
|
}
|
||||||
VkImageCreateInfo image_ci = MakeImageCreateInfo(device, info);
|
VkImageCreateInfo image_ci = MakeImageCreateInfo(device, info);
|
||||||
|
|
|
@ -47,7 +47,7 @@ bool SurfaceTargetIsLayered(SurfaceTarget target) {
|
||||||
case SurfaceTarget::TextureCubeArray:
|
case SurfaceTarget::TextureCubeArray:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
LOG_CRITICAL(HW_GPU, "Unimplemented layered surface_target={}", target);
|
LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", target);
|
||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ bool SurfaceTargetIsArray(SurfaceTarget target) {
|
||||||
case SurfaceTarget::TextureCubeArray:
|
case SurfaceTarget::TextureCubeArray:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
LOG_CRITICAL(HW_GPU, "Unimplemented array surface_target={}", target);
|
LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", target);
|
||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) {
|
||||||
case Tegra::DepthFormat::X8Z24_UNORM:
|
case Tegra::DepthFormat::X8Z24_UNORM:
|
||||||
return PixelFormat::X8_D24_UNORM;
|
return PixelFormat::X8_D24_UNORM;
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented depth format={}", format);
|
UNIMPLEMENTED_MSG("Unimplemented format={}", format);
|
||||||
return PixelFormat::S8_UINT_D24_UNORM;
|
return PixelFormat::S8_UINT_D24_UNORM;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,7 +197,7 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format)
|
||||||
case Tegra::RenderTargetFormat::R8_UINT:
|
case Tegra::RenderTargetFormat::R8_UINT:
|
||||||
return PixelFormat::R8_UINT;
|
return PixelFormat::R8_UINT;
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented render target format={}", format);
|
UNIMPLEMENTED_MSG("Unimplemented format={}", format);
|
||||||
return PixelFormat::A8B8G8R8_UNORM;
|
return PixelFormat::A8B8G8R8_UNORM;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,7 +212,7 @@ PixelFormat PixelFormatFromGPUPixelFormat(Service::android::PixelFormat format)
|
||||||
case Service::android::PixelFormat::Bgra8888:
|
case Service::android::PixelFormat::Bgra8888:
|
||||||
return PixelFormat::B8G8R8A8_UNORM;
|
return PixelFormat::B8G8R8A8_UNORM;
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented android pixel format={}", format);
|
UNIMPLEMENTED_MSG("Unimplemented format={}", format);
|
||||||
return PixelFormat::A8B8G8R8_UNORM;
|
return PixelFormat::A8B8G8R8_UNORM;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
#include "video_core/host1x/gpu_device_memory_manager.h"
|
||||||
#include "video_core/host1x/host1x.h"
|
#include "video_core/host1x/host1x.h"
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
#include "video_core/renderer_null/renderer_null.h"
|
#include "video_core/renderer_null/renderer_null.h"
|
||||||
|
@ -18,21 +19,16 @@ namespace {
|
||||||
std::unique_ptr<VideoCore::RendererBase> CreateRenderer(
|
std::unique_ptr<VideoCore::RendererBase> CreateRenderer(
|
||||||
Core::System& system, Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu,
|
Core::System& system, Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu,
|
||||||
std::unique_ptr<Core::Frontend::GraphicsContext> context) {
|
std::unique_ptr<Core::Frontend::GraphicsContext> context) {
|
||||||
|
auto& telemetry_session = system.TelemetrySession();
|
||||||
auto& device_memory = system.Host1x().MemoryManager();
|
auto& device_memory = system.Host1x().MemoryManager();
|
||||||
|
|
||||||
switch (Settings::values.renderer_backend.GetValue()) {
|
switch (Settings::values.renderer_backend.GetValue()) {
|
||||||
#ifdef __APPLE__
|
|
||||||
// do nothing for now, include metal in here at later date.
|
|
||||||
#else
|
|
||||||
// openGL, not supported on Apple so not bothering to include if macos
|
|
||||||
case Settings::RendererBackend::OpenGL:
|
case Settings::RendererBackend::OpenGL:
|
||||||
return std::make_unique<OpenGL::RendererOpenGL>(emu_window, device_memory, gpu,
|
return std::make_unique<OpenGL::RendererOpenGL>(telemetry_session, emu_window,
|
||||||
std::move(context));
|
device_memory, gpu, std::move(context));
|
||||||
#endif
|
|
||||||
// common renderers
|
|
||||||
case Settings::RendererBackend::Vulkan:
|
case Settings::RendererBackend::Vulkan:
|
||||||
return std::make_unique<Vulkan::RendererVulkan>(emu_window, device_memory, gpu,
|
return std::make_unique<Vulkan::RendererVulkan>(telemetry_session, emu_window,
|
||||||
std::move(context));
|
device_memory, gpu, std::move(context));
|
||||||
case Settings::RendererBackend::Null:
|
case Settings::RendererBackend::Null:
|
||||||
return std::make_unique<Null::RendererNull>(emu_window, gpu, std::move(context));
|
return std::make_unique<Null::RendererNull>(emu_window, gpu, std::move(context));
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -15,11 +15,7 @@
|
||||||
#define VK_USE_PLATFORM_WAYLAND_KHR
|
#define VK_USE_PLATFORM_WAYLAND_KHR
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#include <MoltenVK/mvk_vulkan.h>
|
|
||||||
#else
|
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
// Sanitize macros
|
// Sanitize macros
|
||||||
#undef CreateEvent
|
#undef CreateEvent
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project & 2024 suyu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -580,7 +580,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||||
VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
|
VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Mesa RadV drivers still have broken extendedDynamicState3ColorBlendEquation support.
|
|
||||||
if (extensions.extended_dynamic_state3 && is_radv) {
|
if (extensions.extended_dynamic_state3 && is_radv) {
|
||||||
LOG_WARNING(Render_Vulkan, "RADV has broken extendedDynamicState3ColorBlendEquation");
|
LOG_WARNING(Render_Vulkan, "RADV has broken extendedDynamicState3ColorBlendEquation");
|
||||||
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false;
|
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false;
|
||||||
|
@ -595,8 +594,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||||
dynamic_state3_enables = false;
|
dynamic_state3_enables = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// AMD still has broken extendedDynamicState3ColorBlendEquation on RDNA3.
|
|
||||||
// TODO: distinguis RDNA3 from other uArchs.
|
|
||||||
if (extensions.extended_dynamic_state3 && is_amd_driver) {
|
if (extensions.extended_dynamic_state3 && is_amd_driver) {
|
||||||
LOG_WARNING(Render_Vulkan,
|
LOG_WARNING(Render_Vulkan,
|
||||||
"AMD drivers have broken extendedDynamicState3ColorBlendEquation");
|
"AMD drivers have broken extendedDynamicState3ColorBlendEquation");
|
||||||
|
@ -1364,7 +1361,6 @@ void Device::CollectToolingInfo() {
|
||||||
LOG_INFO(Render_Vulkan, "Attached debugging tool: {}", name);
|
LOG_INFO(Render_Vulkan, "Attached debugging tool: {}", name);
|
||||||
has_renderdoc = has_renderdoc || name == "RenderDoc";
|
has_renderdoc = has_renderdoc || name == "RenderDoc";
|
||||||
has_nsight_graphics = has_nsight_graphics || name == "NVIDIA Nsight Graphics";
|
has_nsight_graphics = has_nsight_graphics || name == "NVIDIA Nsight Graphics";
|
||||||
has_radeon_gpu_profiler = has_radeon_gpu_profiler || name == "Radeon GPU Profiler";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -592,7 +592,7 @@ public:
|
||||||
|
|
||||||
/// Returns true when a known debugging tool is attached.
|
/// Returns true when a known debugging tool is attached.
|
||||||
bool HasDebuggingToolAttached() const {
|
bool HasDebuggingToolAttached() const {
|
||||||
return has_renderdoc || has_nsight_graphics || has_radeon_gpu_profiler;
|
return has_renderdoc || has_nsight_graphics;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @returns True if compute pipelines can cause crashing.
|
/// @returns True if compute pipelines can cause crashing.
|
||||||
|
@ -702,11 +702,6 @@ public:
|
||||||
return properties.driver.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY;
|
return properties.driver.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if we are runing MolvenVK.
|
|
||||||
bool IsMoltenVK() const noexcept {
|
|
||||||
return properties.driver.driverID == VK_DRIVER_ID_MOLTENVK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NvidiaArchitecture GetNvidiaArch() const noexcept {
|
NvidiaArchitecture GetNvidiaArch() const noexcept {
|
||||||
return nvidia_arch;
|
return nvidia_arch;
|
||||||
}
|
}
|
||||||
|
@ -821,7 +816,6 @@ private:
|
||||||
bool has_broken_parallel_compiling{}; ///< Has broken parallel shader compiling.
|
bool has_broken_parallel_compiling{}; ///< Has broken parallel shader compiling.
|
||||||
bool has_renderdoc{}; ///< Has RenderDoc attached
|
bool has_renderdoc{}; ///< Has RenderDoc attached
|
||||||
bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
|
bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
|
||||||
bool has_radeon_gpu_profiler{}; ///< Has Radeon GPU Profiler attached.
|
|
||||||
bool supports_d24_depth{}; ///< Supports D24 depth buffers.
|
bool supports_d24_depth{}; ///< Supports D24 depth buffers.
|
||||||
bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
|
bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
|
||||||
bool must_emulate_scaled_formats{}; ///< Requires scaled vertex format emulation
|
bool must_emulate_scaled_formats{}; ///< Requires scaled vertex format emulation
|
||||||
|
|
|
@ -5,6 +5,8 @@ add_library(web_service STATIC
|
||||||
announce_room_json.cpp
|
announce_room_json.cpp
|
||||||
announce_room_json.h
|
announce_room_json.h
|
||||||
precompiled_headers.h
|
precompiled_headers.h
|
||||||
|
telemetry_json.cpp
|
||||||
|
telemetry_json.h
|
||||||
verify_login.cpp
|
verify_login.cpp
|
||||||
verify_login.h
|
verify_login.h
|
||||||
verify_user_jwt.cpp
|
verify_user_jwt.cpp
|
||||||
|
|
130
src/web_service/telemetry_json.cpp
Normal file
130
src/web_service/telemetry_json.cpp
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include "common/detached_tasks.h"
|
||||||
|
#include "web_service/telemetry_json.h"
|
||||||
|
#include "web_service/web_backend.h"
|
||||||
|
#include "web_service/web_result.h"
|
||||||
|
|
||||||
|
namespace WebService {
|
||||||
|
|
||||||
|
namespace Telemetry = Common::Telemetry;
|
||||||
|
|
||||||
|
struct TelemetryJson::Impl {
|
||||||
|
Impl(std::string host_, std::string username_, std::string token_)
|
||||||
|
: host{std::move(host_)}, username{std::move(username_)}, token{std::move(token_)} {}
|
||||||
|
|
||||||
|
nlohmann::json& TopSection() {
|
||||||
|
return sections[static_cast<u8>(Telemetry::FieldType::None)];
|
||||||
|
}
|
||||||
|
|
||||||
|
const nlohmann::json& TopSection() const {
|
||||||
|
return sections[static_cast<u8>(Telemetry::FieldType::None)];
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void Serialize(Telemetry::FieldType type, const std::string& name, T value) {
|
||||||
|
sections[static_cast<u8>(type)][name] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerializeSection(Telemetry::FieldType type, const std::string& name) {
|
||||||
|
TopSection()[name] = sections[static_cast<unsigned>(type)];
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json output;
|
||||||
|
std::array<nlohmann::json, 7> sections;
|
||||||
|
std::string host;
|
||||||
|
std::string username;
|
||||||
|
std::string token;
|
||||||
|
};
|
||||||
|
|
||||||
|
TelemetryJson::TelemetryJson(std::string host, std::string username, std::string token)
|
||||||
|
: impl{std::make_unique<Impl>(std::move(host), std::move(username), std::move(token))} {}
|
||||||
|
TelemetryJson::~TelemetryJson() = default;
|
||||||
|
|
||||||
|
void TelemetryJson::Visit(const Telemetry::Field<bool>& field) {
|
||||||
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TelemetryJson::Visit(const Telemetry::Field<double>& field) {
|
||||||
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TelemetryJson::Visit(const Telemetry::Field<float>& field) {
|
||||||
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TelemetryJson::Visit(const Telemetry::Field<u8>& field) {
|
||||||
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TelemetryJson::Visit(const Telemetry::Field<u16>& field) {
|
||||||
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TelemetryJson::Visit(const Telemetry::Field<u32>& field) {
|
||||||
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TelemetryJson::Visit(const Telemetry::Field<u64>& field) {
|
||||||
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TelemetryJson::Visit(const Telemetry::Field<s8>& field) {
|
||||||
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TelemetryJson::Visit(const Telemetry::Field<s16>& field) {
|
||||||
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TelemetryJson::Visit(const Telemetry::Field<s32>& field) {
|
||||||
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TelemetryJson::Visit(const Telemetry::Field<s64>& field) {
|
||||||
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TelemetryJson::Visit(const Telemetry::Field<std::string>& field) {
|
||||||
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TelemetryJson::Visit(const Telemetry::Field<const char*>& field) {
|
||||||
|
impl->Serialize(field.GetType(), field.GetName(), std::string(field.GetValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TelemetryJson::Visit(const Telemetry::Field<std::chrono::microseconds>& field) {
|
||||||
|
impl->Serialize(field.GetType(), field.GetName(), field.GetValue().count());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TelemetryJson::Complete() {
|
||||||
|
impl->SerializeSection(Telemetry::FieldType::App, "App");
|
||||||
|
impl->SerializeSection(Telemetry::FieldType::Session, "Session");
|
||||||
|
impl->SerializeSection(Telemetry::FieldType::Performance, "Performance");
|
||||||
|
impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
|
||||||
|
impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
|
||||||
|
|
||||||
|
auto content = impl->TopSection().dump();
|
||||||
|
// Send the telemetry async but don't handle the errors since they were written to the log
|
||||||
|
Common::DetachedTasks::AddTask([host{impl->host}, content]() {
|
||||||
|
Client{host, "", ""}.PostJson("/telemetry", content, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TelemetryJson::SubmitTestcase() {
|
||||||
|
impl->SerializeSection(Telemetry::FieldType::App, "App");
|
||||||
|
impl->SerializeSection(Telemetry::FieldType::Session, "Session");
|
||||||
|
impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
|
||||||
|
impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
|
||||||
|
impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
|
||||||
|
|
||||||
|
auto content = impl->TopSection().dump();
|
||||||
|
Client client(impl->host, impl->username, impl->token);
|
||||||
|
auto value = client.PostJson("/gamedb/testcase", content, false);
|
||||||
|
|
||||||
|
return value.result_code == WebResult::Code::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace WebService
|
44
src/web_service/telemetry_json.h
Normal file
44
src/web_service/telemetry_json.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
// SPDX-FileCopyrightText: 2017 Citra Emulator Project & 2024 suyu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <string>
|
||||||
|
#include "common/telemetry.h"
|
||||||
|
|
||||||
|
namespace WebService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of VisitorInterface that serialized telemetry into JSON, and submits it to the
|
||||||
|
* suyu web service
|
||||||
|
*/
|
||||||
|
class TelemetryJson : public Common::Telemetry::VisitorInterface {
|
||||||
|
public:
|
||||||
|
TelemetryJson(std::string host, std::string username, std::string token);
|
||||||
|
~TelemetryJson() override;
|
||||||
|
|
||||||
|
void Visit(const Common::Telemetry::Field<bool>& field) override;
|
||||||
|
void Visit(const Common::Telemetry::Field<double>& field) override;
|
||||||
|
void Visit(const Common::Telemetry::Field<float>& field) override;
|
||||||
|
void Visit(const Common::Telemetry::Field<u8>& field) override;
|
||||||
|
void Visit(const Common::Telemetry::Field<u16>& field) override;
|
||||||
|
void Visit(const Common::Telemetry::Field<u32>& field) override;
|
||||||
|
void Visit(const Common::Telemetry::Field<u64>& field) override;
|
||||||
|
void Visit(const Common::Telemetry::Field<s8>& field) override;
|
||||||
|
void Visit(const Common::Telemetry::Field<s16>& field) override;
|
||||||
|
void Visit(const Common::Telemetry::Field<s32>& field) override;
|
||||||
|
void Visit(const Common::Telemetry::Field<s64>& field) override;
|
||||||
|
void Visit(const Common::Telemetry::Field<std::string>& field) override;
|
||||||
|
void Visit(const Common::Telemetry::Field<const char*>& field) override;
|
||||||
|
void Visit(const Common::Telemetry::Field<std::chrono::microseconds>& field) override;
|
||||||
|
|
||||||
|
void Complete() override;
|
||||||
|
bool SubmitTestcase() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Impl;
|
||||||
|
std::unique_ptr<Impl> impl;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace WebService
|
Loading…
Reference in a new issue