Compare commits
120 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be5c149d37 | ||
|
|
361285add9 | ||
|
|
a4e840181c | ||
|
|
fab2607c6b | ||
|
|
4414640285 | ||
|
|
78f977c980 | ||
|
|
5135b74179 | ||
|
|
624a0f7f3f | ||
|
|
c332c66eb2 | ||
|
|
0d6d8129c4 | ||
|
|
ae0e481677 | ||
|
|
028b2718ed | ||
|
|
7bd447355f | ||
|
|
4cbb363d3f | ||
|
|
287d5921cf | ||
|
|
f2c61bbe13 | ||
|
|
f846e3d6d0 | ||
|
|
8a76f816a4 | ||
|
|
5b989f189f | ||
|
|
3813af2f3c | ||
|
|
c83bf7cd1e | ||
|
|
a5bb1ac6e3 | ||
|
|
5619d24377 | ||
|
|
4af569ee47 | ||
|
|
b9e3f5eb36 | ||
|
|
4a3026b16b | ||
|
|
5770418fb3 | ||
|
|
91d35559e5 | ||
|
|
e976d0e924 | ||
|
|
1e76655f83 | ||
|
|
0f3ac9cfeb | ||
|
|
3dc585d011 | ||
|
|
218ee18417 | ||
|
|
a3916588b6 | ||
|
|
51c9e98677 | ||
|
|
aea978e037 | ||
|
|
27efcc15e9 | ||
|
|
16dcfacbfc | ||
|
|
1e16023d60 | ||
|
|
486c6a5316 | ||
|
|
af93909c9c | ||
|
|
38d3a48873 | ||
|
|
cf27b59493 | ||
|
|
7be65c6a68 | ||
|
|
6d55b14cc0 | ||
|
|
e41da22c8d | ||
|
|
ec983a2451 | ||
|
|
6ddffa010a | ||
|
|
54747d60bc | ||
|
|
2a63b3bdb9 | ||
|
|
de918ebeb0 | ||
|
|
485c21eac3 | ||
|
|
1eb4a95d2b | ||
|
|
253aa52351 | ||
|
|
f4a25f854c | ||
|
|
abb33d4aec | ||
|
|
d53cf05513 | ||
|
|
da0aa4da6b | ||
|
|
ae8d4b6c0c | ||
|
|
a7d6bd1ef1 | ||
|
|
c053269017 | ||
|
|
15a753b9a5 | ||
|
|
e438079b50 | ||
|
|
8b26b4228b | ||
|
|
8825b88a45 | ||
|
|
8a23c32cf0 | ||
|
|
67b8ecc73e | ||
|
|
b52297767e | ||
|
|
65b1b05e05 | ||
|
|
e09c1fbc1f | ||
|
|
844e4a297b | ||
|
|
a87c85eba2 | ||
|
|
3d2c44848b | ||
|
|
3d9fff82c0 | ||
|
|
eac075692b | ||
|
|
0471eb6dc7 | ||
|
|
3d51153611 | ||
|
|
ccda77c8c4 | ||
|
|
035ec7d9de | ||
|
|
285705b5f4 | ||
|
|
2b650543c6 | ||
|
|
e3ea583893 | ||
|
|
f27b21077d | ||
|
|
8db8631d81 | ||
|
|
15513f0801 | ||
|
|
f845df8651 | ||
|
|
2df9a2dcaf | ||
|
|
6d0d79109b | ||
|
|
8fc49a83b6 | ||
|
|
c0ee0aa1a8 | ||
|
|
af89723fa3 | ||
|
|
84a158c977 | ||
|
|
271a3264f3 | ||
|
|
900b2e5cae | ||
|
|
1d2ba3cc97 | ||
|
|
1a66cde175 | ||
|
|
e9faa1617c | ||
|
|
22c6b9fab2 | ||
|
|
30e365e4fc | ||
|
|
f564eaebed | ||
|
|
48e16c4c49 | ||
|
|
34f8881d3e | ||
|
|
c8db7d1399 | ||
|
|
7ffb672f61 | ||
|
|
425a254fa2 | ||
|
|
6edadef96d | ||
|
|
233ed96a5c | ||
|
|
d30cf51d7d | ||
|
|
0b5b93053d | ||
|
|
ecbfa416f0 | ||
|
|
9ad6327fbd | ||
|
|
6233b1db08 | ||
|
|
f2458106e6 | ||
|
|
67b8265bd6 | ||
|
|
e6a0a30334 | ||
|
|
dee7844443 | ||
|
|
3a44faff11 | ||
|
|
75cc501d52 | ||
|
|
056f049b26 | ||
|
|
4589582eaf |
@@ -1,39 +0,0 @@
|
||||
# Set-up Visual Studio Command Prompt environment for PowerShell
|
||||
pushd "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\"
|
||||
cmd /c "VsDevCmd.bat -arch=x64 & set" | foreach {
|
||||
if ($_ -match "=") {
|
||||
$v = $_.split("="); Set-Item -Force -Path "ENV:\$($v[0])" -Value "$($v[1])"
|
||||
}
|
||||
}
|
||||
popd
|
||||
|
||||
function Which ($search_path, $name) {
|
||||
($search_path).Split(";") | Get-ChildItem -Filter $name | Select -First 1 -Exp FullName
|
||||
}
|
||||
|
||||
function GetDeps ($search_path, $binary) {
|
||||
((dumpbin /dependents $binary).Where({ $_ -match "dependencies:"}, "SkipUntil") | Select-String "[^ ]*\.dll").Matches | foreach {
|
||||
Which $search_path $_.Value
|
||||
}
|
||||
}
|
||||
|
||||
function RecursivelyGetDeps ($search_path, $binary) {
|
||||
$final_deps = @()
|
||||
$deps_to_process = GetDeps $search_path $binary
|
||||
while ($deps_to_process.Count -gt 0) {
|
||||
$current, $deps_to_process = $deps_to_process
|
||||
if ($final_deps -contains $current) { continue }
|
||||
|
||||
# Is this a system dll file?
|
||||
# We use the same algorithm that cmake uses to determine this.
|
||||
if ($current -match "$([regex]::Escape($env:SystemRoot))\\sys") { continue }
|
||||
if ($current -match "$([regex]::Escape($env:WinDir))\\sys") { continue }
|
||||
if ($current -match "\\msvc[^\\]+dll") { continue }
|
||||
if ($current -match "\\api-ms-win-[^\\]+dll") { continue }
|
||||
|
||||
$final_deps += $current
|
||||
$new_deps = GetDeps $search_path $current
|
||||
$deps_to_process += ($new_deps | ?{-not ($final_deps -contains $_)})
|
||||
}
|
||||
return $final_deps
|
||||
}
|
||||
178
appveyor.yml
178
appveyor.yml
@@ -1,178 +0,0 @@
|
||||
# shallow clone
|
||||
clone_depth: 10
|
||||
|
||||
cache:
|
||||
- C:\ProgramData\chocolatey\bin -> appveyor.yml
|
||||
- C:\ProgramData\chocolatey\lib -> appveyor.yml
|
||||
|
||||
os: Visual Studio 2017
|
||||
|
||||
environment:
|
||||
# Tell msys2 to add mingw64 to the path
|
||||
MSYSTEM: MINGW64
|
||||
# Tell msys2 to inherit the current directory when starting the shell
|
||||
CHERE_INVOKING: 1
|
||||
matrix:
|
||||
- BUILD_TYPE: msvc
|
||||
- BUILD_TYPE: mingw
|
||||
|
||||
platform:
|
||||
- x64
|
||||
|
||||
configuration:
|
||||
- Release
|
||||
|
||||
install:
|
||||
- git submodule update --init --recursive
|
||||
- ps: |
|
||||
if ($env:BUILD_TYPE -eq 'mingw') {
|
||||
$dependencies = "mingw64/mingw-w64-x86_64-cmake",
|
||||
"mingw64/mingw-w64-x86_64-qt5",
|
||||
"mingw64/mingw-w64-x86_64-SDL2"
|
||||
# redirect err to null to prevent warnings from becoming errors
|
||||
# workaround to prevent pacman from failing due to cyclical dependencies
|
||||
C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw64/mingw-w64-x86_64-freetype mingw64/mingw-w64-x86_64-fontconfig" 2> $null
|
||||
C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S $dependencies" 2> $null
|
||||
}
|
||||
|
||||
before_build:
|
||||
- mkdir %BUILD_TYPE%_build
|
||||
- cd %BUILD_TYPE%_build
|
||||
- ps: |
|
||||
$COMPAT = if ($env:ENABLE_COMPATIBILITY_REPORTING -eq $null) {0} else {$env:ENABLE_COMPATIBILITY_REPORTING}
|
||||
if ($env:BUILD_TYPE -eq 'msvc') {
|
||||
# redirect stderr and change the exit code to prevent powershell from cancelling the build if cmake prints a warning
|
||||
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON .. 2>&1 && exit 0'
|
||||
} else {
|
||||
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON .. 2>&1"
|
||||
}
|
||||
- cd ..
|
||||
|
||||
build_script:
|
||||
- ps: |
|
||||
if ($env:BUILD_TYPE -eq 'msvc') {
|
||||
# https://www.appveyor.com/docs/build-phase
|
||||
msbuild msvc_build/yuzu.sln /maxcpucount /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||
} else {
|
||||
C:\msys64\usr\bin\bash.exe -lc 'mingw32-make -C mingw_build/ 2>&1'
|
||||
}
|
||||
|
||||
after_build:
|
||||
- ps: |
|
||||
$GITDATE = $(git show -s --date=short --format='%ad') -replace "-",""
|
||||
$GITREV = $(git show -s --format='%h')
|
||||
|
||||
# Find out which kind of release we are producing by tag name
|
||||
if ($env:APPVEYOR_REPO_TAG_NAME) {
|
||||
$RELEASE_DIST, $RELEASE_VERSION = $env:APPVEYOR_REPO_TAG_NAME.split('-')
|
||||
} else {
|
||||
# There is no repo tag - make assumptions
|
||||
$RELEASE_DIST = "head"
|
||||
}
|
||||
|
||||
if ($env:BUILD_TYPE -eq 'msvc') {
|
||||
# Where are these spaces coming from? Regardless, let's remove them
|
||||
$MSVC_BUILD_ZIP = "yuzu-windows-msvc-$GITDATE-$GITREV.zip" -replace " ", ""
|
||||
$MSVC_BUILD_PDB = "yuzu-windows-msvc-$GITDATE-$GITREV-debugsymbols.zip" -replace " ", ""
|
||||
$MSVC_SEVENZIP = "yuzu-windows-msvc-$GITDATE-$GITREV.7z" -replace " ", ""
|
||||
|
||||
# set the build names as env vars so the artifacts can upload them
|
||||
$env:BUILD_ZIP = $MSVC_BUILD_ZIP
|
||||
$env:BUILD_SYMBOLS = $MSVC_BUILD_PDB
|
||||
$env:BUILD_UPDATE = $MSVC_SEVENZIP
|
||||
|
||||
$BUILD_DIR = ".\msvc_build\bin\Release"
|
||||
|
||||
# Make a debug symbol upload
|
||||
mkdir pdb
|
||||
Get-ChildItem "$BUILD_DIR\" -Recurse -Filter "*.pdb" | Copy-Item -destination .\pdb
|
||||
7z a -tzip $MSVC_BUILD_PDB .\pdb\*.pdb
|
||||
rm "$BUILD_DIR\*.pdb"
|
||||
|
||||
mkdir $RELEASE_DIST
|
||||
# get rid of extra exes by copying everything over, then deleting all the exes, then copying just the exes we want
|
||||
Copy-Item "$BUILD_DIR\*" -Destination $RELEASE_DIST -Recurse
|
||||
rm "$RELEASE_DIST\*.exe"
|
||||
Get-ChildItem "$BUILD_DIR" -Recurse -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST
|
||||
Get-ChildItem "$BUILD_DIR" -Recurse -Filter "QtWebEngineProcess*.exe" | Copy-Item -destination $RELEASE_DIST
|
||||
Copy-Item .\license.txt -Destination $RELEASE_DIST
|
||||
Copy-Item .\README.md -Destination $RELEASE_DIST
|
||||
7z a -tzip $MSVC_BUILD_ZIP $RELEASE_DIST\*
|
||||
7z a $MSVC_SEVENZIP $RELEASE_DIST
|
||||
} else {
|
||||
$MINGW_BUILD_ZIP = "yuzu-windows-mingw-$GITDATE-$GITREV.zip" -replace " ", ""
|
||||
$MINGW_SEVENZIP = "yuzu-windows-mingw-$GITDATE-$GITREV.7z" -replace " ", ""
|
||||
# not going to bother adding separate debug symbols for mingw, so just upload a README for it
|
||||
# if someone wants to add them, change mingw to compile with -g and use objdump and strip to separate the symbols from the binary
|
||||
$MINGW_NO_DEBUG_SYMBOLS = "README_No_Debug_Symbols.txt"
|
||||
Set-Content -Path $MINGW_NO_DEBUG_SYMBOLS -Value "This is a workaround for Appveyor since msvc has debug symbols but mingw doesnt" -Force
|
||||
|
||||
# store the build information in env vars so we can use them as artifacts
|
||||
$env:BUILD_ZIP = $MINGW_BUILD_ZIP
|
||||
$env:BUILD_SYMBOLS = $MINGW_NO_DEBUG_SYMBOLS
|
||||
$env:BUILD_UPDATE = $MINGW_SEVENZIP
|
||||
|
||||
$CMAKE_SOURCE_DIR = "$env:APPVEYOR_BUILD_FOLDER"
|
||||
$CMAKE_BINARY_DIR = "$CMAKE_SOURCE_DIR/mingw_build/bin"
|
||||
$RELEASE_DIST = $RELEASE_DIST + "-mingw"
|
||||
|
||||
mkdir $RELEASE_DIST
|
||||
mkdir $RELEASE_DIST/platforms
|
||||
mkdir $RELEASE_DIST/styles
|
||||
mkdir $RELEASE_DIST/imageformats
|
||||
|
||||
# copy the compiled binaries and other release files to the release folder
|
||||
Get-ChildItem "$CMAKE_BINARY_DIR" -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST
|
||||
Copy-Item -path "$CMAKE_SOURCE_DIR/license.txt" -destination $RELEASE_DIST
|
||||
Copy-Item -path "$CMAKE_SOURCE_DIR/README.md" -destination $RELEASE_DIST
|
||||
|
||||
# copy the qt windows plugin dll to platforms
|
||||
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/platforms/qwindows.dll" -force -destination "$RELEASE_DIST/platforms"
|
||||
|
||||
# copy the qt windows vista style dll to platforms
|
||||
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/styles/qwindowsvistastyle.dll" -force -destination "$RELEASE_DIST/styles"
|
||||
|
||||
# copy the qt jpeg imageformat dll to platforms
|
||||
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/imageformats/qjpeg.dll" -force -destination "$RELEASE_DIST/imageformats"
|
||||
|
||||
# copy all the dll dependencies to the release folder
|
||||
. "./.appveyor/UtilityFunctions.ps1"
|
||||
$DLLSearchPath = "C:\msys64\mingw64\bin;$env:PATH"
|
||||
$MingwDLLs = RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu.exe"
|
||||
$MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu_cmd.exe"
|
||||
$MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\imageformats\qjpeg.dll"
|
||||
Write-Host "Detected the following dependencies:"
|
||||
Write-Host $MingwDLLs
|
||||
foreach ($file in $MingwDLLs) {
|
||||
Copy-Item -path "$file" -force -destination "$RELEASE_DIST"
|
||||
}
|
||||
|
||||
7z a -tzip $MINGW_BUILD_ZIP $RELEASE_DIST\*
|
||||
7z a $MINGW_SEVENZIP $RELEASE_DIST
|
||||
}
|
||||
|
||||
test_script:
|
||||
- cd %BUILD_TYPE%_build
|
||||
- ps: |
|
||||
if ($env:BUILD_TYPE -eq 'msvc') {
|
||||
ctest -VV -C Release
|
||||
} else {
|
||||
C:\msys64\usr\bin\bash.exe -lc "ctest -VV -C Release"
|
||||
}
|
||||
- cd ..
|
||||
|
||||
artifacts:
|
||||
- path: $(BUILD_ZIP)
|
||||
name: build
|
||||
type: zip
|
||||
|
||||
deploy:
|
||||
provider: GitHub
|
||||
release: $(appveyor_repo_tag_name)
|
||||
auth_token:
|
||||
secure: QqePPnXbkzmXct5c8hZ2X5AbsthbI6cS1Sr+VBzcD8oUOIjfWJJKXVAQGUbQAbb0
|
||||
artifact: update,build
|
||||
draft: false
|
||||
prerelease: false
|
||||
on:
|
||||
appveyor_repo_tag: true
|
||||
2
externals/boost
vendored
2
externals/boost
vendored
Submodule externals/boost updated: 0b920df1c9...5e8300b76a
2
externals/sirit
vendored
2
externals/sirit
vendored
Submodule externals/sirit updated: f7c4b07a7e...9f4d057aa2
@@ -46,9 +46,16 @@ public:
|
||||
ElementPtr* new_ptr = new ElementPtr();
|
||||
write_ptr->next.store(new_ptr, std::memory_order_release);
|
||||
write_ptr = new_ptr;
|
||||
cv.notify_one();
|
||||
|
||||
++size;
|
||||
const size_t previous_size{size++};
|
||||
|
||||
// Acquire the mutex and then immediately release it as a fence.
|
||||
// TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported.
|
||||
// See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details.
|
||||
if (previous_size == 0) {
|
||||
std::lock_guard lock{cv_mutex};
|
||||
}
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void Pop() {
|
||||
|
||||
@@ -96,6 +96,8 @@ add_library(core STATIC
|
||||
file_sys/system_archive/system_archive.h
|
||||
file_sys/system_archive/system_version.cpp
|
||||
file_sys/system_archive/system_version.h
|
||||
file_sys/system_archive/time_zone_binary.cpp
|
||||
file_sys/system_archive/time_zone_binary.h
|
||||
file_sys/vfs.cpp
|
||||
file_sys/vfs.h
|
||||
file_sys/vfs_concat.cpp
|
||||
@@ -461,12 +463,40 @@ add_library(core STATIC
|
||||
hle/service/spl/spl.h
|
||||
hle/service/ssl/ssl.cpp
|
||||
hle/service/ssl/ssl.h
|
||||
hle/service/time/clock_types.h
|
||||
hle/service/time/ephemeral_network_system_clock_context_writer.h
|
||||
hle/service/time/ephemeral_network_system_clock_core.h
|
||||
hle/service/time/errors.h
|
||||
hle/service/time/interface.cpp
|
||||
hle/service/time/interface.h
|
||||
hle/service/time/local_system_clock_context_writer.h
|
||||
hle/service/time/network_system_clock_context_writer.h
|
||||
hle/service/time/standard_local_system_clock_core.h
|
||||
hle/service/time/standard_network_system_clock_core.h
|
||||
hle/service/time/standard_steady_clock_core.cpp
|
||||
hle/service/time/standard_steady_clock_core.h
|
||||
hle/service/time/standard_user_system_clock_core.cpp
|
||||
hle/service/time/standard_user_system_clock_core.h
|
||||
hle/service/time/steady_clock_core.h
|
||||
hle/service/time/system_clock_context_update_callback.cpp
|
||||
hle/service/time/system_clock_context_update_callback.h
|
||||
hle/service/time/system_clock_core.cpp
|
||||
hle/service/time/system_clock_core.h
|
||||
hle/service/time/tick_based_steady_clock_core.cpp
|
||||
hle/service/time/tick_based_steady_clock_core.h
|
||||
hle/service/time/time.cpp
|
||||
hle/service/time/time.h
|
||||
hle/service/time/time_manager.cpp
|
||||
hle/service/time/time_manager.h
|
||||
hle/service/time/time_sharedmemory.cpp
|
||||
hle/service/time/time_sharedmemory.h
|
||||
hle/service/time/time_zone_content_manager.cpp
|
||||
hle/service/time/time_zone_content_manager.h
|
||||
hle/service/time/time_zone_manager.cpp
|
||||
hle/service/time/time_zone_manager.h
|
||||
hle/service/time/time_zone_service.cpp
|
||||
hle/service/time/time_zone_service.h
|
||||
hle/service/time/time_zone_types.h
|
||||
hle/service/usb/usb.cpp
|
||||
hle/service/usb/usb.h
|
||||
hle/service/vi/display/vi_display.cpp
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
#include "core/settings.h"
|
||||
#include "core/telemetry_session.h"
|
||||
#include "core/tools/freezer.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
@@ -341,7 +340,6 @@ struct System::Impl {
|
||||
std::unique_ptr<Loader::AppLoader> app_loader;
|
||||
std::unique_ptr<VideoCore::RendererBase> renderer;
|
||||
std::unique_ptr<Tegra::GPU> gpu_core;
|
||||
std::shared_ptr<Tegra::DebugContext> debug_context;
|
||||
std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
|
||||
Memory::Memory memory;
|
||||
CpuCoreManager cpu_core_manager;
|
||||
@@ -580,14 +578,6 @@ Loader::AppLoader& System::GetAppLoader() const {
|
||||
return *impl->app_loader;
|
||||
}
|
||||
|
||||
void System::SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) {
|
||||
impl->debug_context = std::move(context);
|
||||
}
|
||||
|
||||
Tegra::DebugContext* System::GetGPUDebugContext() const {
|
||||
return impl->debug_context.get();
|
||||
}
|
||||
|
||||
void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
|
||||
impl->virtual_filesystem = std::move(vfs);
|
||||
}
|
||||
|
||||
@@ -307,10 +307,6 @@ public:
|
||||
Service::SM::ServiceManager& ServiceManager();
|
||||
const Service::SM::ServiceManager& ServiceManager() const;
|
||||
|
||||
void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context);
|
||||
|
||||
Tegra::DebugContext* GetGPUDebugContext() const;
|
||||
|
||||
void SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs);
|
||||
|
||||
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "core/file_sys/system_archive/shared_font.h"
|
||||
#include "core/file_sys/system_archive/system_archive.h"
|
||||
#include "core/file_sys/system_archive/system_version.h"
|
||||
#include "core/file_sys/system_archive/time_zone_binary.h"
|
||||
|
||||
namespace FileSys::SystemArchive {
|
||||
|
||||
@@ -38,7 +39,7 @@ constexpr std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHI
|
||||
{0x010000000000080B, "LocalNews", nullptr},
|
||||
{0x010000000000080C, "Eula", nullptr},
|
||||
{0x010000000000080D, "UrlBlackList", nullptr},
|
||||
{0x010000000000080E, "TimeZoneBinary", nullptr},
|
||||
{0x010000000000080E, "TimeZoneBinary", &TimeZoneBinary},
|
||||
{0x010000000000080F, "CertStoreCruiser", nullptr},
|
||||
{0x0100000000000810, "FontNintendoExtension", &FontNintendoExtension},
|
||||
{0x0100000000000811, "FontStandard", &FontStandard},
|
||||
|
||||
657
src/core/file_sys/system_archive/time_zone_binary.cpp
Normal file
657
src/core/file_sys/system_archive/time_zone_binary.cpp
Normal file
@@ -0,0 +1,657 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/system_archive/time_zone_binary.h"
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
#include "core/hle/service/time/time_zone_types.h"
|
||||
|
||||
namespace FileSys::SystemArchive {
|
||||
|
||||
static constexpr std::array<u8, 9633> LOCATION_NAMES{
|
||||
0x43, 0x45, 0x54, 0x0d, 0x0a, 0x43, 0x53, 0x54, 0x36, 0x43, 0x44, 0x54, 0x0d, 0x0a, 0x43, 0x75,
|
||||
0x62, 0x61, 0x0d, 0x0a, 0x45, 0x45, 0x54, 0x0d, 0x0a, 0x45, 0x67, 0x79, 0x70, 0x74, 0x0d, 0x0a,
|
||||
0x45, 0x69, 0x72, 0x65, 0x0d, 0x0a, 0x45, 0x53, 0x54, 0x0d, 0x0a, 0x45, 0x53, 0x54, 0x35, 0x45,
|
||||
0x44, 0x54, 0x0d, 0x0a, 0x47, 0x42, 0x0d, 0x0a, 0x47, 0x42, 0x2d, 0x45, 0x69, 0x72, 0x65, 0x0d,
|
||||
0x0a, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x47, 0x4d, 0x54, 0x2b, 0x30, 0x0d, 0x0a, 0x47, 0x4d, 0x54,
|
||||
0x2d, 0x30, 0x0d, 0x0a, 0x47, 0x4d, 0x54, 0x30, 0x0d, 0x0a, 0x47, 0x72, 0x65, 0x65, 0x6e, 0x77,
|
||||
0x69, 0x63, 0x68, 0x0d, 0x0a, 0x48, 0x6f, 0x6e, 0x67, 0x6b, 0x6f, 0x6e, 0x67, 0x0d, 0x0a, 0x48,
|
||||
0x53, 0x54, 0x0d, 0x0a, 0x49, 0x63, 0x65, 0x6c, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x49, 0x72, 0x61,
|
||||
0x6e, 0x0d, 0x0a, 0x49, 0x73, 0x72, 0x61, 0x65, 0x6c, 0x0d, 0x0a, 0x4a, 0x61, 0x6d, 0x61, 0x69,
|
||||
0x63, 0x61, 0x0d, 0x0a, 0x4a, 0x61, 0x70, 0x61, 0x6e, 0x0d, 0x0a, 0x4b, 0x77, 0x61, 0x6a, 0x61,
|
||||
0x6c, 0x65, 0x69, 0x6e, 0x0d, 0x0a, 0x4c, 0x69, 0x62, 0x79, 0x61, 0x0d, 0x0a, 0x4d, 0x45, 0x54,
|
||||
0x0d, 0x0a, 0x4d, 0x53, 0x54, 0x0d, 0x0a, 0x4d, 0x53, 0x54, 0x37, 0x4d, 0x44, 0x54, 0x0d, 0x0a,
|
||||
0x4e, 0x61, 0x76, 0x61, 0x6a, 0x6f, 0x0d, 0x0a, 0x4e, 0x5a, 0x0d, 0x0a, 0x4e, 0x5a, 0x2d, 0x43,
|
||||
0x48, 0x41, 0x54, 0x0d, 0x0a, 0x50, 0x6f, 0x6c, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x50, 0x6f, 0x72,
|
||||
0x74, 0x75, 0x67, 0x61, 0x6c, 0x0d, 0x0a, 0x50, 0x52, 0x43, 0x0d, 0x0a, 0x50, 0x53, 0x54, 0x38,
|
||||
0x50, 0x44, 0x54, 0x0d, 0x0a, 0x52, 0x4f, 0x43, 0x0d, 0x0a, 0x52, 0x4f, 0x4b, 0x0d, 0x0a, 0x53,
|
||||
0x69, 0x6e, 0x67, 0x61, 0x70, 0x6f, 0x72, 0x65, 0x0d, 0x0a, 0x54, 0x75, 0x72, 0x6b, 0x65, 0x79,
|
||||
0x0d, 0x0a, 0x55, 0x43, 0x54, 0x0d, 0x0a, 0x55, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c,
|
||||
0x0d, 0x0a, 0x55, 0x54, 0x43, 0x0d, 0x0a, 0x57, 0x2d, 0x53, 0x55, 0x0d, 0x0a, 0x57, 0x45, 0x54,
|
||||
0x0d, 0x0a, 0x5a, 0x75, 0x6c, 0x75, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41,
|
||||
0x62, 0x69, 0x64, 0x6a, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41,
|
||||
0x63, 0x63, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x64, 0x64,
|
||||
0x69, 0x73, 0x5f, 0x41, 0x62, 0x61, 0x62, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61,
|
||||
0x2f, 0x41, 0x6c, 0x67, 0x69, 0x65, 0x72, 0x73, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61,
|
||||
0x2f, 0x41, 0x73, 0x6d, 0x61, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f,
|
||||
0x41, 0x73, 0x6d, 0x65, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42,
|
||||
0x61, 0x6d, 0x61, 0x6b, 0x6f, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x61,
|
||||
0x6e, 0x67, 0x75, 0x69, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x61, 0x6e,
|
||||
0x6a, 0x75, 0x6c, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x69, 0x73, 0x73,
|
||||
0x61, 0x75, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x6c, 0x61, 0x6e, 0x74,
|
||||
0x79, 0x72, 0x65, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x72, 0x61, 0x7a,
|
||||
0x7a, 0x61, 0x76, 0x69, 0x6c, 0x6c, 0x65, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f,
|
||||
0x42, 0x75, 0x6a, 0x75, 0x6d, 0x62, 0x75, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63,
|
||||
0x61, 0x2f, 0x43, 0x61, 0x69, 0x72, 0x6f, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f,
|
||||
0x43, 0x61, 0x73, 0x61, 0x62, 0x6c, 0x61, 0x6e, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69,
|
||||
0x63, 0x61, 0x2f, 0x43, 0x65, 0x75, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61,
|
||||
0x2f, 0x43, 0x6f, 0x6e, 0x61, 0x6b, 0x72, 0x79, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61,
|
||||
0x2f, 0x44, 0x61, 0x6b, 0x61, 0x72, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44,
|
||||
0x61, 0x72, 0x5f, 0x65, 0x73, 0x5f, 0x53, 0x61, 0x6c, 0x61, 0x61, 0x6d, 0x0d, 0x0a, 0x41, 0x66,
|
||||
0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x6a, 0x69, 0x62, 0x6f, 0x75, 0x74, 0x69, 0x0d, 0x0a, 0x41,
|
||||
0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x6f, 0x75, 0x61, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x66,
|
||||
0x72, 0x69, 0x63, 0x61, 0x2f, 0x45, 0x6c, 0x5f, 0x41, 0x61, 0x69, 0x75, 0x6e, 0x0d, 0x0a, 0x41,
|
||||
0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x46, 0x72, 0x65, 0x65, 0x74, 0x6f, 0x77, 0x6e, 0x0d, 0x0a,
|
||||
0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x47, 0x61, 0x62, 0x6f, 0x72, 0x6f, 0x6e, 0x65, 0x0d,
|
||||
0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x48, 0x61, 0x72, 0x61, 0x72, 0x65, 0x0d, 0x0a,
|
||||
0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4a, 0x6f, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x73, 0x62,
|
||||
0x75, 0x72, 0x67, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4a, 0x75, 0x62, 0x61,
|
||||
0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x61, 0x6d, 0x70, 0x61, 0x6c, 0x61,
|
||||
0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x68, 0x61, 0x72, 0x74, 0x6f, 0x75,
|
||||
0x6d, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x69, 0x67, 0x61, 0x6c, 0x69,
|
||||
0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x69, 0x6e, 0x73, 0x68, 0x61, 0x73,
|
||||
0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x61, 0x67, 0x6f, 0x73, 0x0d,
|
||||
0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x69, 0x62, 0x72, 0x65, 0x76, 0x69, 0x6c,
|
||||
0x6c, 0x65, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x6f, 0x6d, 0x65, 0x0d,
|
||||
0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x75, 0x61, 0x6e, 0x64, 0x61, 0x0d, 0x0a,
|
||||
0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x75, 0x62, 0x75, 0x6d, 0x62, 0x61, 0x73, 0x68,
|
||||
0x69, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x75, 0x73, 0x61, 0x6b, 0x61,
|
||||
0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x6c, 0x61, 0x62, 0x6f, 0x0d,
|
||||
0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x70, 0x75, 0x74, 0x6f, 0x0d, 0x0a,
|
||||
0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x73, 0x65, 0x72, 0x75, 0x0d, 0x0a, 0x41,
|
||||
0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x62, 0x61, 0x62, 0x61, 0x6e, 0x65, 0x0d, 0x0a, 0x41,
|
||||
0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x67, 0x61, 0x64, 0x69, 0x73, 0x68, 0x75, 0x0d,
|
||||
0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x6e, 0x72, 0x6f, 0x76, 0x69, 0x61,
|
||||
0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x61, 0x69, 0x72, 0x6f, 0x62, 0x69,
|
||||
0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x64, 0x6a, 0x61, 0x6d, 0x65, 0x6e,
|
||||
0x61, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x69, 0x61, 0x6d, 0x65, 0x79,
|
||||
0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x6f, 0x75, 0x61, 0x6b, 0x63, 0x68,
|
||||
0x6f, 0x74, 0x74, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4f, 0x75, 0x61, 0x67,
|
||||
0x61, 0x64, 0x6f, 0x75, 0x67, 0x6f, 0x75, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69, 0x63, 0x61, 0x2f,
|
||||
0x50, 0x6f, 0x72, 0x74, 0x6f, 0x2d, 0x4e, 0x6f, 0x76, 0x6f, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69,
|
||||
0x63, 0x61, 0x2f, 0x53, 0x61, 0x6f, 0x5f, 0x54, 0x6f, 0x6d, 0x65, 0x0d, 0x0a, 0x41, 0x66, 0x72,
|
||||
0x69, 0x63, 0x61, 0x2f, 0x54, 0x69, 0x6d, 0x62, 0x75, 0x6b, 0x74, 0x75, 0x0d, 0x0a, 0x41, 0x66,
|
||||
0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x72, 0x69, 0x70, 0x6f, 0x6c, 0x69, 0x0d, 0x0a, 0x41, 0x66,
|
||||
0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x75, 0x6e, 0x69, 0x73, 0x0d, 0x0a, 0x41, 0x66, 0x72, 0x69,
|
||||
0x63, 0x61, 0x2f, 0x57, 0x69, 0x6e, 0x64, 0x68, 0x6f, 0x65, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
|
||||
0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x64, 0x61, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
|
||||
0x63, 0x61, 0x2f, 0x41, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x0d, 0x0a, 0x41, 0x6d,
|
||||
0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x6e, 0x67, 0x75, 0x69, 0x6c, 0x6c, 0x61, 0x0d, 0x0a,
|
||||
0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x6e, 0x74, 0x69, 0x67, 0x75, 0x61, 0x0d,
|
||||
0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x61, 0x67, 0x75, 0x61, 0x69,
|
||||
0x6e, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x75, 0x62,
|
||||
0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x73, 0x75, 0x6e, 0x63,
|
||||
0x69, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x74, 0x69,
|
||||
0x6b, 0x6f, 0x6b, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41,
|
||||
0x74, 0x6b, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x61, 0x68,
|
||||
0x69, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x61, 0x68, 0x69,
|
||||
0x61, 0x5f, 0x42, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x61, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
|
||||
0x69, 0x63, 0x61, 0x2f, 0x42, 0x61, 0x72, 0x62, 0x61, 0x64, 0x6f, 0x73, 0x0d, 0x0a, 0x41, 0x6d,
|
||||
0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x65, 0x6c, 0x65, 0x6d, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
|
||||
0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x65, 0x6c, 0x69, 0x7a, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
|
||||
0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x6c, 0x61, 0x6e, 0x63, 0x2d, 0x53, 0x61, 0x62, 0x6c, 0x6f,
|
||||
0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x6f, 0x61, 0x5f, 0x56,
|
||||
0x69, 0x73, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x6f,
|
||||
0x67, 0x6f, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x6f,
|
||||
0x69, 0x73, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x42, 0x75, 0x65,
|
||||
0x6e, 0x6f, 0x73, 0x5f, 0x41, 0x69, 0x72, 0x65, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
|
||||
0x63, 0x61, 0x2f, 0x43, 0x61, 0x6d, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x5f, 0x42, 0x61, 0x79,
|
||||
0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x61, 0x6d, 0x70, 0x6f, 0x5f,
|
||||
0x47, 0x72, 0x61, 0x6e, 0x64, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
|
||||
0x43, 0x61, 0x6e, 0x63, 0x75, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
|
||||
0x43, 0x61, 0x72, 0x61, 0x63, 0x61, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
|
||||
0x2f, 0x43, 0x61, 0x74, 0x61, 0x6d, 0x61, 0x72, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
|
||||
0x69, 0x63, 0x61, 0x2f, 0x43, 0x61, 0x79, 0x65, 0x6e, 0x6e, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
|
||||
0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x61, 0x79, 0x6d, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
|
||||
0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x68, 0x69, 0x63, 0x61, 0x67, 0x6f, 0x0d, 0x0a, 0x41, 0x6d,
|
||||
0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x68, 0x69, 0x68, 0x75, 0x61, 0x68, 0x75, 0x61, 0x0d,
|
||||
0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x6f, 0x72, 0x61, 0x6c, 0x5f, 0x48,
|
||||
0x61, 0x72, 0x62, 0x6f, 0x75, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
|
||||
0x43, 0x6f, 0x72, 0x64, 0x6f, 0x62, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
|
||||
0x2f, 0x43, 0x6f, 0x73, 0x74, 0x61, 0x5f, 0x52, 0x69, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
|
||||
0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d,
|
||||
0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x75, 0x69, 0x61, 0x62, 0x61, 0x0d, 0x0a, 0x41, 0x6d,
|
||||
0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x43, 0x75, 0x72, 0x61, 0x63, 0x61, 0x6f, 0x0d, 0x0a, 0x41,
|
||||
0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x61, 0x6e, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x68,
|
||||
0x61, 0x76, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x61, 0x77,
|
||||
0x73, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x61, 0x77,
|
||||
0x73, 0x6f, 0x6e, 0x5f, 0x43, 0x72, 0x65, 0x65, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
|
||||
0x63, 0x61, 0x2f, 0x44, 0x65, 0x6e, 0x76, 0x65, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
|
||||
0x63, 0x61, 0x2f, 0x44, 0x65, 0x74, 0x72, 0x6f, 0x69, 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
|
||||
0x69, 0x63, 0x61, 0x2f, 0x44, 0x6f, 0x6d, 0x69, 0x6e, 0x69, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x6d,
|
||||
0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x45, 0x64, 0x6d, 0x6f, 0x6e, 0x74, 0x6f, 0x6e, 0x0d, 0x0a,
|
||||
0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x45, 0x69, 0x72, 0x75, 0x6e, 0x65, 0x70, 0x65,
|
||||
0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x45, 0x6c, 0x5f, 0x53, 0x61, 0x6c,
|
||||
0x76, 0x61, 0x64, 0x6f, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x45,
|
||||
0x6e, 0x73, 0x65, 0x6e, 0x61, 0x64, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
|
||||
0x2f, 0x46, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x65, 0x7a, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
|
||||
0x69, 0x63, 0x61, 0x2f, 0x46, 0x6f, 0x72, 0x74, 0x5f, 0x4e, 0x65, 0x6c, 0x73, 0x6f, 0x6e, 0x0d,
|
||||
0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x46, 0x6f, 0x72, 0x74, 0x5f, 0x57, 0x61,
|
||||
0x79, 0x6e, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x47, 0x6c, 0x61,
|
||||
0x63, 0x65, 0x5f, 0x42, 0x61, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
|
||||
0x47, 0x6f, 0x64, 0x74, 0x68, 0x61, 0x62, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
|
||||
0x2f, 0x47, 0x6f, 0x6f, 0x73, 0x65, 0x5f, 0x42, 0x61, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
|
||||
0x69, 0x63, 0x61, 0x2f, 0x47, 0x72, 0x61, 0x6e, 0x64, 0x5f, 0x54, 0x75, 0x72, 0x6b, 0x0d, 0x0a,
|
||||
0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x47, 0x72, 0x65, 0x6e, 0x61, 0x64, 0x61, 0x0d,
|
||||
0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x47, 0x75, 0x61, 0x64, 0x65, 0x6c, 0x6f,
|
||||
0x75, 0x70, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x47, 0x75, 0x61,
|
||||
0x74, 0x65, 0x6d, 0x61, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
|
||||
0x47, 0x75, 0x61, 0x79, 0x61, 0x71, 0x75, 0x69, 0x6c, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
|
||||
0x63, 0x61, 0x2f, 0x47, 0x75, 0x79, 0x61, 0x6e, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
|
||||
0x63, 0x61, 0x2f, 0x48, 0x61, 0x6c, 0x69, 0x66, 0x61, 0x78, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
|
||||
0x69, 0x63, 0x61, 0x2f, 0x48, 0x61, 0x76, 0x61, 0x6e, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
|
||||
0x69, 0x63, 0x61, 0x2f, 0x48, 0x65, 0x72, 0x6d, 0x6f, 0x73, 0x69, 0x6c, 0x6c, 0x6f, 0x0d, 0x0a,
|
||||
0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x70,
|
||||
0x6f, 0x6c, 0x69, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e,
|
||||
0x75, 0x76, 0x69, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x71,
|
||||
0x61, 0x6c, 0x75, 0x69, 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4a,
|
||||
0x61, 0x6d, 0x61, 0x69, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
|
||||
0x4a, 0x75, 0x6a, 0x75, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4a,
|
||||
0x75, 0x6e, 0x65, 0x61, 0x75, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b,
|
||||
0x6e, 0x6f, 0x78, 0x5f, 0x49, 0x4e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
|
||||
0x4b, 0x72, 0x61, 0x6c, 0x65, 0x6e, 0x64, 0x69, 0x6a, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
|
||||
0x69, 0x63, 0x61, 0x2f, 0x4c, 0x61, 0x5f, 0x50, 0x61, 0x7a, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
|
||||
0x69, 0x63, 0x61, 0x2f, 0x4c, 0x69, 0x6d, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
|
||||
0x61, 0x2f, 0x4c, 0x6f, 0x73, 0x5f, 0x41, 0x6e, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x0d, 0x0a, 0x41,
|
||||
0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x6f, 0x75, 0x69, 0x73, 0x76, 0x69, 0x6c, 0x6c,
|
||||
0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4c, 0x6f, 0x77, 0x65, 0x72,
|
||||
0x5f, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x65, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
|
||||
0x61, 0x2f, 0x4d, 0x61, 0x63, 0x65, 0x69, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
|
||||
0x61, 0x2f, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x75, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
|
||||
0x63, 0x61, 0x2f, 0x4d, 0x61, 0x6e, 0x61, 0x75, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
|
||||
0x63, 0x61, 0x2f, 0x4d, 0x61, 0x72, 0x69, 0x67, 0x6f, 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
|
||||
0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x72, 0x74, 0x69, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x0d, 0x0a,
|
||||
0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x74, 0x61, 0x6d, 0x6f, 0x72, 0x6f,
|
||||
0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x7a, 0x61, 0x74,
|
||||
0x6c, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x65, 0x6e,
|
||||
0x64, 0x6f, 0x7a, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x65,
|
||||
0x6e, 0x6f, 0x6d, 0x69, 0x6e, 0x65, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
|
||||
0x2f, 0x4d, 0x65, 0x72, 0x69, 0x64, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
|
||||
0x2f, 0x4d, 0x65, 0x74, 0x6c, 0x61, 0x6b, 0x61, 0x74, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
|
||||
0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x65, 0x78, 0x69, 0x63, 0x6f, 0x5f, 0x43, 0x69, 0x74, 0x79,
|
||||
0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x69, 0x71, 0x75, 0x65, 0x6c,
|
||||
0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x6e, 0x63,
|
||||
0x74, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x6e,
|
||||
0x74, 0x65, 0x72, 0x72, 0x65, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
|
||||
0x4d, 0x6f, 0x6e, 0x74, 0x65, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
|
||||
0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x6e, 0x74, 0x72, 0x65, 0x61, 0x6c, 0x0d, 0x0a, 0x41, 0x6d,
|
||||
0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x6f, 0x6e, 0x74, 0x73, 0x65, 0x72, 0x72, 0x61, 0x74,
|
||||
0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x61, 0x73, 0x73, 0x61, 0x75,
|
||||
0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x65, 0x77, 0x5f, 0x59, 0x6f,
|
||||
0x72, 0x6b, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x69, 0x70, 0x69,
|
||||
0x67, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x6f, 0x6d,
|
||||
0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x6f, 0x72, 0x6f, 0x6e,
|
||||
0x68, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4f, 0x6a, 0x69, 0x6e,
|
||||
0x61, 0x67, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, 0x61, 0x6e,
|
||||
0x61, 0x6d, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, 0x61, 0x6e,
|
||||
0x67, 0x6e, 0x69, 0x72, 0x74, 0x75, 0x6e, 0x67, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
|
||||
0x61, 0x2f, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x61, 0x72, 0x69, 0x62, 0x6f, 0x0d, 0x0a, 0x41, 0x6d,
|
||||
0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, 0x68, 0x6f, 0x65, 0x6e, 0x69, 0x78, 0x0d, 0x0a, 0x41,
|
||||
0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, 0x6f, 0x72, 0x74, 0x2d, 0x61, 0x75, 0x2d, 0x50,
|
||||
0x72, 0x69, 0x6e, 0x63, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50,
|
||||
0x6f, 0x72, 0x74, 0x6f, 0x5f, 0x41, 0x63, 0x72, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
|
||||
0x63, 0x61, 0x2f, 0x50, 0x6f, 0x72, 0x74, 0x6f, 0x5f, 0x56, 0x65, 0x6c, 0x68, 0x6f, 0x0d, 0x0a,
|
||||
0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50, 0x6f, 0x72, 0x74, 0x5f, 0x6f, 0x66, 0x5f,
|
||||
0x53, 0x70, 0x61, 0x69, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x50,
|
||||
0x75, 0x65, 0x72, 0x74, 0x6f, 0x5f, 0x52, 0x69, 0x63, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
|
||||
0x69, 0x63, 0x61, 0x2f, 0x50, 0x75, 0x6e, 0x74, 0x61, 0x5f, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x73,
|
||||
0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x61, 0x69, 0x6e, 0x79, 0x5f,
|
||||
0x52, 0x69, 0x76, 0x65, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52,
|
||||
0x61, 0x6e, 0x6b, 0x69, 0x6e, 0x5f, 0x49, 0x6e, 0x6c, 0x65, 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
|
||||
0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x65, 0x63, 0x69, 0x66, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
|
||||
0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x65, 0x67, 0x69, 0x6e, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
|
||||
0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x0d, 0x0a, 0x41,
|
||||
0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x69, 0x6f, 0x5f, 0x42, 0x72, 0x61, 0x6e, 0x63,
|
||||
0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x52, 0x6f, 0x73, 0x61, 0x72,
|
||||
0x69, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x61, 0x6e, 0x74,
|
||||
0x61, 0x72, 0x65, 0x6d, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x61,
|
||||
0x6e, 0x74, 0x61, 0x5f, 0x49, 0x73, 0x61, 0x62, 0x65, 0x6c, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
|
||||
0x69, 0x63, 0x61, 0x2f, 0x53, 0x61, 0x6e, 0x74, 0x69, 0x61, 0x67, 0x6f, 0x0d, 0x0a, 0x41, 0x6d,
|
||||
0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x61, 0x6e, 0x74, 0x6f, 0x5f, 0x44, 0x6f, 0x6d, 0x69,
|
||||
0x6e, 0x67, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x61, 0x6f,
|
||||
0x5f, 0x50, 0x61, 0x75, 0x6c, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
|
||||
0x53, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x62, 0x79, 0x73, 0x75, 0x6e, 0x64, 0x0d, 0x0a, 0x41, 0x6d,
|
||||
0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x68, 0x69, 0x70, 0x72, 0x6f, 0x63, 0x6b, 0x0d, 0x0a,
|
||||
0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x69, 0x74, 0x6b, 0x61, 0x0d, 0x0a, 0x41,
|
||||
0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x74, 0x5f, 0x42, 0x61, 0x72, 0x74, 0x68, 0x65,
|
||||
0x6c, 0x65, 0x6d, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x74,
|
||||
0x5f, 0x4a, 0x6f, 0x68, 0x6e, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
|
||||
0x53, 0x74, 0x5f, 0x4b, 0x69, 0x74, 0x74, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
|
||||
0x61, 0x2f, 0x53, 0x74, 0x5f, 0x4c, 0x75, 0x63, 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
|
||||
0x69, 0x63, 0x61, 0x2f, 0x53, 0x74, 0x5f, 0x54, 0x68, 0x6f, 0x6d, 0x61, 0x73, 0x0d, 0x0a, 0x41,
|
||||
0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x74, 0x5f, 0x56, 0x69, 0x6e, 0x63, 0x65, 0x6e,
|
||||
0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x77, 0x69, 0x66, 0x74,
|
||||
0x5f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
|
||||
0x61, 0x2f, 0x54, 0x65, 0x67, 0x75, 0x63, 0x69, 0x67, 0x61, 0x6c, 0x70, 0x61, 0x0d, 0x0a, 0x41,
|
||||
0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x68, 0x75, 0x6c, 0x65, 0x0d, 0x0a, 0x41, 0x6d,
|
||||
0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x68, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x42, 0x61,
|
||||
0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x69, 0x6a, 0x75, 0x61,
|
||||
0x6e, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x6f, 0x72, 0x6f,
|
||||
0x6e, 0x74, 0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x54, 0x6f, 0x72,
|
||||
0x74, 0x6f, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x56, 0x61,
|
||||
0x6e, 0x63, 0x6f, 0x75, 0x76, 0x65, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
|
||||
0x2f, 0x56, 0x69, 0x72, 0x67, 0x69, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
|
||||
0x2f, 0x57, 0x68, 0x69, 0x74, 0x65, 0x68, 0x6f, 0x72, 0x73, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65,
|
||||
0x72, 0x69, 0x63, 0x61, 0x2f, 0x57, 0x69, 0x6e, 0x6e, 0x69, 0x70, 0x65, 0x67, 0x0d, 0x0a, 0x41,
|
||||
0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x59, 0x61, 0x6b, 0x75, 0x74, 0x61, 0x74, 0x0d, 0x0a,
|
||||
0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x59, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6b, 0x6e,
|
||||
0x69, 0x66, 0x65, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67,
|
||||
0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x42, 0x75, 0x65, 0x6e, 0x6f, 0x73, 0x5f, 0x41, 0x69,
|
||||
0x72, 0x65, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67,
|
||||
0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x43, 0x61, 0x74, 0x61, 0x6d, 0x61, 0x72, 0x63, 0x61,
|
||||
0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74,
|
||||
0x69, 0x6e, 0x61, 0x2f, 0x43, 0x6f, 0x6d, 0x6f, 0x64, 0x52, 0x69, 0x76, 0x61, 0x64, 0x61, 0x76,
|
||||
0x69, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65,
|
||||
0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x43, 0x6f, 0x72, 0x64, 0x6f, 0x62, 0x61, 0x0d, 0x0a, 0x41,
|
||||
0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61,
|
||||
0x2f, 0x4a, 0x75, 0x6a, 0x75, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
|
||||
0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x4c, 0x61, 0x5f, 0x52, 0x69, 0x6f,
|
||||
0x6a, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65,
|
||||
0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x4d, 0x65, 0x6e, 0x64, 0x6f, 0x7a, 0x61, 0x0d, 0x0a, 0x41,
|
||||
0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61,
|
||||
0x2f, 0x52, 0x69, 0x6f, 0x5f, 0x47, 0x61, 0x6c, 0x6c, 0x65, 0x67, 0x6f, 0x73, 0x0d, 0x0a, 0x41,
|
||||
0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61,
|
||||
0x2f, 0x53, 0x61, 0x6c, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f,
|
||||
0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x53, 0x61, 0x6e, 0x5f, 0x4a, 0x75,
|
||||
0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65,
|
||||
0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x53, 0x61, 0x6e, 0x5f, 0x4c, 0x75, 0x69, 0x73, 0x0d, 0x0a,
|
||||
0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e,
|
||||
0x61, 0x2f, 0x54, 0x75, 0x63, 0x75, 0x6d, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69,
|
||||
0x63, 0x61, 0x2f, 0x41, 0x72, 0x67, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x61, 0x2f, 0x55, 0x73, 0x68,
|
||||
0x75, 0x61, 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e,
|
||||
0x64, 0x69, 0x61, 0x6e, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x70, 0x6f, 0x6c,
|
||||
0x69, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69,
|
||||
0x61, 0x6e, 0x61, 0x2f, 0x4b, 0x6e, 0x6f, 0x78, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
|
||||
0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2f, 0x4d, 0x61, 0x72, 0x65, 0x6e, 0x67,
|
||||
0x6f, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61,
|
||||
0x6e, 0x61, 0x2f, 0x50, 0x65, 0x74, 0x65, 0x72, 0x73, 0x62, 0x75, 0x72, 0x67, 0x0d, 0x0a, 0x41,
|
||||
0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2f, 0x54,
|
||||
0x65, 0x6c, 0x6c, 0x5f, 0x43, 0x69, 0x74, 0x79, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63,
|
||||
0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2f, 0x56, 0x65, 0x76, 0x61, 0x79, 0x0d,
|
||||
0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61,
|
||||
0x2f, 0x56, 0x69, 0x6e, 0x63, 0x65, 0x6e, 0x6e, 0x65, 0x73, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
|
||||
0x69, 0x63, 0x61, 0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2f, 0x57, 0x69, 0x6e, 0x61,
|
||||
0x6d, 0x61, 0x63, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x65, 0x6e,
|
||||
0x74, 0x75, 0x63, 0x6b, 0x79, 0x2f, 0x4c, 0x6f, 0x75, 0x69, 0x73, 0x76, 0x69, 0x6c, 0x6c, 0x65,
|
||||
0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4b, 0x65, 0x6e, 0x74, 0x75, 0x63,
|
||||
0x6b, 0x79, 0x2f, 0x4d, 0x6f, 0x6e, 0x74, 0x69, 0x63, 0x65, 0x6c, 0x6c, 0x6f, 0x0d, 0x0a, 0x41,
|
||||
0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x2f, 0x4e, 0x6f, 0x72, 0x74, 0x68, 0x5f, 0x44, 0x61, 0x6b,
|
||||
0x6f, 0x74, 0x61, 0x2f, 0x42, 0x65, 0x75, 0x6c, 0x61, 0x68, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72,
|
||||
0x69, 0x63, 0x61, 0x2f, 0x4e, 0x6f, 0x72, 0x74, 0x68, 0x5f, 0x44, 0x61, 0x6b, 0x6f, 0x74, 0x61,
|
||||
0x2f, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x0d, 0x0a, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61,
|
||||
0x2f, 0x4e, 0x6f, 0x72, 0x74, 0x68, 0x5f, 0x44, 0x61, 0x6b, 0x6f, 0x74, 0x61, 0x2f, 0x4e, 0x65,
|
||||
0x77, 0x5f, 0x53, 0x61, 0x6c, 0x65, 0x6d, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74,
|
||||
0x69, 0x63, 0x61, 0x2f, 0x43, 0x61, 0x73, 0x65, 0x79, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72,
|
||||
0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x61, 0x76, 0x69, 0x73, 0x0d, 0x0a, 0x41, 0x6e, 0x74,
|
||||
0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x44, 0x75, 0x6d, 0x6f, 0x6e, 0x74, 0x44, 0x55,
|
||||
0x72, 0x76, 0x69, 0x6c, 0x6c, 0x65, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69,
|
||||
0x63, 0x61, 0x2f, 0x4d, 0x61, 0x63, 0x71, 0x75, 0x61, 0x72, 0x69, 0x65, 0x0d, 0x0a, 0x41, 0x6e,
|
||||
0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x61, 0x77, 0x73, 0x6f, 0x6e, 0x0d,
|
||||
0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x4d, 0x63, 0x4d, 0x75,
|
||||
0x72, 0x64, 0x6f, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f,
|
||||
0x50, 0x61, 0x6c, 0x6d, 0x65, 0x72, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69,
|
||||
0x63, 0x61, 0x2f, 0x52, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61,
|
||||
0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x6f, 0x75, 0x74, 0x68, 0x5f, 0x50, 0x6f, 0x6c,
|
||||
0x65, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f, 0x53, 0x79,
|
||||
0x6f, 0x77, 0x61, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63, 0x61, 0x2f,
|
||||
0x54, 0x72, 0x6f, 0x6c, 0x6c, 0x0d, 0x0a, 0x41, 0x6e, 0x74, 0x61, 0x72, 0x63, 0x74, 0x69, 0x63,
|
||||
0x61, 0x2f, 0x56, 0x6f, 0x73, 0x74, 0x6f, 0x6b, 0x0d, 0x0a, 0x41, 0x72, 0x63, 0x74, 0x69, 0x63,
|
||||
0x2f, 0x4c, 0x6f, 0x6e, 0x67, 0x79, 0x65, 0x61, 0x72, 0x62, 0x79, 0x65, 0x6e, 0x0d, 0x0a, 0x41,
|
||||
0x73, 0x69, 0x61, 0x2f, 0x41, 0x64, 0x65, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41,
|
||||
0x6c, 0x6d, 0x61, 0x74, 0x79, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x6d, 0x6d, 0x61,
|
||||
0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x6e, 0x61, 0x64, 0x79, 0x72, 0x0d, 0x0a,
|
||||
0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x71, 0x74, 0x61, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
|
||||
0x2f, 0x41, 0x71, 0x74, 0x6f, 0x62, 0x65, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x73,
|
||||
0x68, 0x67, 0x61, 0x62, 0x61, 0x74, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x73, 0x68,
|
||||
0x6b, 0x68, 0x61, 0x62, 0x61, 0x64, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x41, 0x74, 0x79,
|
||||
0x72, 0x61, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x61, 0x67, 0x68, 0x64, 0x61,
|
||||
0x64, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x61, 0x68, 0x72, 0x61, 0x69, 0x6e, 0x0d,
|
||||
0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x61, 0x6b, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
|
||||
0x2f, 0x42, 0x61, 0x6e, 0x67, 0x6b, 0x6f, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42,
|
||||
0x61, 0x72, 0x6e, 0x61, 0x75, 0x6c, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x65, 0x69,
|
||||
0x72, 0x75, 0x74, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x69, 0x73, 0x68, 0x6b, 0x65,
|
||||
0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x42, 0x72, 0x75, 0x6e, 0x65, 0x69, 0x0d, 0x0a,
|
||||
0x41, 0x73, 0x69, 0x61, 0x2f, 0x43, 0x61, 0x6c, 0x63, 0x75, 0x74, 0x74, 0x61, 0x0d, 0x0a, 0x41,
|
||||
0x73, 0x69, 0x61, 0x2f, 0x43, 0x68, 0x69, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f,
|
||||
0x43, 0x68, 0x6f, 0x69, 0x62, 0x61, 0x6c, 0x73, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
|
||||
0x2f, 0x43, 0x68, 0x6f, 0x6e, 0x67, 0x71, 0x69, 0x6e, 0x67, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
|
||||
0x2f, 0x43, 0x68, 0x75, 0x6e, 0x67, 0x6b, 0x69, 0x6e, 0x67, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
|
||||
0x2f, 0x43, 0x6f, 0x6c, 0x6f, 0x6d, 0x62, 0x6f, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x44,
|
||||
0x61, 0x63, 0x63, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x44, 0x61, 0x6d, 0x61, 0x73,
|
||||
0x63, 0x75, 0x73, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x44, 0x68, 0x61, 0x6b, 0x61, 0x0d,
|
||||
0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x44, 0x69, 0x6c, 0x69, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
|
||||
0x2f, 0x44, 0x75, 0x62, 0x61, 0x69, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x44, 0x75, 0x73,
|
||||
0x68, 0x61, 0x6e, 0x62, 0x65, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x46, 0x61, 0x6d, 0x61,
|
||||
0x67, 0x75, 0x73, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x47, 0x61, 0x7a, 0x61,
|
||||
0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x48, 0x61, 0x72, 0x62, 0x69, 0x6e, 0x0d, 0x0a, 0x41,
|
||||
0x73, 0x69, 0x61, 0x2f, 0x48, 0x65, 0x62, 0x72, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
|
||||
0x2f, 0x48, 0x6f, 0x6e, 0x67, 0x5f, 0x4b, 0x6f, 0x6e, 0x67, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
|
||||
0x2f, 0x48, 0x6f, 0x76, 0x64, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x48, 0x6f, 0x5f, 0x43,
|
||||
0x68, 0x69, 0x5f, 0x4d, 0x69, 0x6e, 0x68, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x49, 0x72,
|
||||
0x6b, 0x75, 0x74, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x49, 0x73, 0x74, 0x61,
|
||||
0x6e, 0x62, 0x75, 0x6c, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4a, 0x61, 0x6b, 0x61, 0x72,
|
||||
0x74, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4a, 0x61, 0x79, 0x61, 0x70, 0x75, 0x72,
|
||||
0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4a, 0x65, 0x72, 0x75, 0x73, 0x61, 0x6c, 0x65,
|
||||
0x6d, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x61, 0x62, 0x75, 0x6c, 0x0d, 0x0a, 0x41,
|
||||
0x73, 0x69, 0x61, 0x2f, 0x4b, 0x61, 0x6d, 0x63, 0x68, 0x61, 0x74, 0x6b, 0x61, 0x0d, 0x0a, 0x41,
|
||||
0x73, 0x69, 0x61, 0x2f, 0x4b, 0x61, 0x72, 0x61, 0x63, 0x68, 0x69, 0x0d, 0x0a, 0x41, 0x73, 0x69,
|
||||
0x61, 0x2f, 0x4b, 0x61, 0x73, 0x68, 0x67, 0x61, 0x72, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f,
|
||||
0x4b, 0x61, 0x74, 0x68, 0x6d, 0x61, 0x6e, 0x64, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f,
|
||||
0x4b, 0x61, 0x74, 0x6d, 0x61, 0x6e, 0x64, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b,
|
||||
0x68, 0x61, 0x6e, 0x64, 0x79, 0x67, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x6f,
|
||||
0x6c, 0x6b, 0x61, 0x74, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x72, 0x61, 0x73,
|
||||
0x6e, 0x6f, 0x79, 0x61, 0x72, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b, 0x75,
|
||||
0x61, 0x6c, 0x61, 0x5f, 0x4c, 0x75, 0x6d, 0x70, 0x75, 0x72, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
|
||||
0x2f, 0x4b, 0x75, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4b,
|
||||
0x75, 0x77, 0x61, 0x69, 0x74, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4d, 0x61, 0x63, 0x61,
|
||||
0x6f, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4d, 0x61, 0x63, 0x61, 0x75, 0x0d, 0x0a, 0x41,
|
||||
0x73, 0x69, 0x61, 0x2f, 0x4d, 0x61, 0x67, 0x61, 0x64, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69,
|
||||
0x61, 0x2f, 0x4d, 0x61, 0x6b, 0x61, 0x73, 0x73, 0x61, 0x72, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
|
||||
0x2f, 0x4d, 0x61, 0x6e, 0x69, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4d, 0x75,
|
||||
0x73, 0x63, 0x61, 0x74, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4e, 0x69, 0x63, 0x6f, 0x73,
|
||||
0x69, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4e, 0x6f, 0x76, 0x6f, 0x6b, 0x75, 0x7a,
|
||||
0x6e, 0x65, 0x74, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4e, 0x6f, 0x76, 0x6f,
|
||||
0x73, 0x69, 0x62, 0x69, 0x72, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4f, 0x6d,
|
||||
0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x4f, 0x72, 0x61, 0x6c, 0x0d, 0x0a, 0x41,
|
||||
0x73, 0x69, 0x61, 0x2f, 0x50, 0x68, 0x6e, 0x6f, 0x6d, 0x5f, 0x50, 0x65, 0x6e, 0x68, 0x0d, 0x0a,
|
||||
0x41, 0x73, 0x69, 0x61, 0x2f, 0x50, 0x6f, 0x6e, 0x74, 0x69, 0x61, 0x6e, 0x61, 0x6b, 0x0d, 0x0a,
|
||||
0x41, 0x73, 0x69, 0x61, 0x2f, 0x50, 0x79, 0x6f, 0x6e, 0x67, 0x79, 0x61, 0x6e, 0x67, 0x0d, 0x0a,
|
||||
0x41, 0x73, 0x69, 0x61, 0x2f, 0x51, 0x61, 0x74, 0x61, 0x72, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
|
||||
0x2f, 0x51, 0x79, 0x7a, 0x79, 0x6c, 0x6f, 0x72, 0x64, 0x61, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
|
||||
0x2f, 0x52, 0x61, 0x6e, 0x67, 0x6f, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x52,
|
||||
0x69, 0x79, 0x61, 0x64, 0x68, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x53, 0x61, 0x69, 0x67,
|
||||
0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x53, 0x61, 0x6b, 0x68, 0x61, 0x6c, 0x69,
|
||||
0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x53, 0x61, 0x6d, 0x61, 0x72, 0x6b, 0x61, 0x6e,
|
||||
0x64, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x53, 0x65, 0x6f, 0x75, 0x6c, 0x0d, 0x0a, 0x41,
|
||||
0x73, 0x69, 0x61, 0x2f, 0x53, 0x68, 0x61, 0x6e, 0x67, 0x68, 0x61, 0x69, 0x0d, 0x0a, 0x41, 0x73,
|
||||
0x69, 0x61, 0x2f, 0x53, 0x69, 0x6e, 0x67, 0x61, 0x70, 0x6f, 0x72, 0x65, 0x0d, 0x0a, 0x41, 0x73,
|
||||
0x69, 0x61, 0x2f, 0x53, 0x72, 0x65, 0x64, 0x6e, 0x65, 0x6b, 0x6f, 0x6c, 0x79, 0x6d, 0x73, 0x6b,
|
||||
0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x54, 0x61, 0x69, 0x70, 0x65, 0x69, 0x0d, 0x0a, 0x41,
|
||||
0x73, 0x69, 0x61, 0x2f, 0x54, 0x61, 0x73, 0x68, 0x6b, 0x65, 0x6e, 0x74, 0x0d, 0x0a, 0x41, 0x73,
|
||||
0x69, 0x61, 0x2f, 0x54, 0x62, 0x69, 0x6c, 0x69, 0x73, 0x69, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61,
|
||||
0x2f, 0x54, 0x65, 0x68, 0x72, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x54, 0x65,
|
||||
0x6c, 0x5f, 0x41, 0x76, 0x69, 0x76, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x54, 0x68, 0x69,
|
||||
0x6d, 0x62, 0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x54, 0x68, 0x69, 0x6d, 0x70, 0x68,
|
||||
0x75, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x54, 0x6f, 0x6b, 0x79, 0x6f, 0x0d, 0x0a, 0x41,
|
||||
0x73, 0x69, 0x61, 0x2f, 0x54, 0x6f, 0x6d, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f,
|
||||
0x55, 0x6a, 0x75, 0x6e, 0x67, 0x5f, 0x50, 0x61, 0x6e, 0x64, 0x61, 0x6e, 0x67, 0x0d, 0x0a, 0x41,
|
||||
0x73, 0x69, 0x61, 0x2f, 0x55, 0x6c, 0x61, 0x61, 0x6e, 0x62, 0x61, 0x61, 0x74, 0x61, 0x72, 0x0d,
|
||||
0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x55, 0x6c, 0x61, 0x6e, 0x5f, 0x42, 0x61, 0x74, 0x6f, 0x72,
|
||||
0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f, 0x55, 0x72, 0x75, 0x6d, 0x71, 0x69, 0x0d, 0x0a, 0x41,
|
||||
0x73, 0x69, 0x61, 0x2f, 0x55, 0x73, 0x74, 0x2d, 0x4e, 0x65, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x73,
|
||||
0x69, 0x61, 0x2f, 0x56, 0x69, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6e, 0x65, 0x0d, 0x0a, 0x41, 0x73,
|
||||
0x69, 0x61, 0x2f, 0x56, 0x6c, 0x61, 0x64, 0x69, 0x76, 0x6f, 0x73, 0x74, 0x6f, 0x6b, 0x0d, 0x0a,
|
||||
0x41, 0x73, 0x69, 0x61, 0x2f, 0x59, 0x61, 0x6b, 0x75, 0x74, 0x73, 0x6b, 0x0d, 0x0a, 0x41, 0x73,
|
||||
0x69, 0x61, 0x2f, 0x59, 0x61, 0x6e, 0x67, 0x6f, 0x6e, 0x0d, 0x0a, 0x41, 0x73, 0x69, 0x61, 0x2f,
|
||||
0x59, 0x65, 0x6b, 0x61, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x62, 0x75, 0x72, 0x67, 0x0d, 0x0a, 0x41,
|
||||
0x73, 0x69, 0x61, 0x2f, 0x59, 0x65, 0x72, 0x65, 0x76, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x74, 0x6c,
|
||||
0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x41, 0x7a, 0x6f, 0x72, 0x65, 0x73, 0x0d, 0x0a, 0x41, 0x74,
|
||||
0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x42, 0x65, 0x72, 0x6d, 0x75, 0x64, 0x61, 0x0d, 0x0a,
|
||||
0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x0d,
|
||||
0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x43, 0x61, 0x70, 0x65, 0x5f, 0x56,
|
||||
0x65, 0x72, 0x64, 0x65, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x46,
|
||||
0x61, 0x65, 0x72, 0x6f, 0x65, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f,
|
||||
0x46, 0x61, 0x72, 0x6f, 0x65, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f,
|
||||
0x4a, 0x61, 0x6e, 0x5f, 0x4d, 0x61, 0x79, 0x65, 0x6e, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e,
|
||||
0x74, 0x69, 0x63, 0x2f, 0x4d, 0x61, 0x64, 0x65, 0x69, 0x72, 0x61, 0x0d, 0x0a, 0x41, 0x74, 0x6c,
|
||||
0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x52, 0x65, 0x79, 0x6b, 0x6a, 0x61, 0x76, 0x69, 0x6b, 0x0d,
|
||||
0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x2f, 0x53, 0x6f, 0x75, 0x74, 0x68, 0x5f,
|
||||
0x47, 0x65, 0x6f, 0x72, 0x67, 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e, 0x74, 0x69,
|
||||
0x63, 0x2f, 0x53, 0x74, 0x61, 0x6e, 0x6c, 0x65, 0x79, 0x0d, 0x0a, 0x41, 0x74, 0x6c, 0x61, 0x6e,
|
||||
0x74, 0x69, 0x63, 0x2f, 0x53, 0x74, 0x5f, 0x48, 0x65, 0x6c, 0x65, 0x6e, 0x61, 0x0d, 0x0a, 0x41,
|
||||
0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x41, 0x43, 0x54, 0x0d, 0x0a, 0x41, 0x75,
|
||||
0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x41, 0x64, 0x65, 0x6c, 0x61, 0x69, 0x64, 0x65,
|
||||
0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x42, 0x72, 0x69, 0x73,
|
||||
0x62, 0x61, 0x6e, 0x65, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f,
|
||||
0x42, 0x72, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x48, 0x69, 0x6c, 0x6c, 0x0d, 0x0a, 0x41, 0x75, 0x73,
|
||||
0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x43, 0x61, 0x6e, 0x62, 0x65, 0x72, 0x72, 0x61, 0x0d,
|
||||
0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x43, 0x75, 0x72, 0x72, 0x69,
|
||||
0x65, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x44, 0x61, 0x72,
|
||||
0x77, 0x69, 0x6e, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x45,
|
||||
0x75, 0x63, 0x6c, 0x61, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f,
|
||||
0x48, 0x6f, 0x62, 0x61, 0x72, 0x74, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69,
|
||||
0x61, 0x2f, 0x4c, 0x48, 0x49, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61,
|
||||
0x2f, 0x4c, 0x69, 0x6e, 0x64, 0x65, 0x6d, 0x61, 0x6e, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72,
|
||||
0x61, 0x6c, 0x69, 0x61, 0x2f, 0x4c, 0x6f, 0x72, 0x64, 0x5f, 0x48, 0x6f, 0x77, 0x65, 0x0d, 0x0a,
|
||||
0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x4d, 0x65, 0x6c, 0x62, 0x6f, 0x75,
|
||||
0x72, 0x6e, 0x65, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x4e,
|
||||
0x6f, 0x72, 0x74, 0x68, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f,
|
||||
0x4e, 0x53, 0x57, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x50,
|
||||
0x65, 0x72, 0x74, 0x68, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f,
|
||||
0x51, 0x75, 0x65, 0x65, 0x6e, 0x73, 0x6c, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74,
|
||||
0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x53, 0x6f, 0x75, 0x74, 0x68, 0x0d, 0x0a, 0x41, 0x75, 0x73,
|
||||
0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x53, 0x79, 0x64, 0x6e, 0x65, 0x79, 0x0d, 0x0a, 0x41,
|
||||
0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x54, 0x61, 0x73, 0x6d, 0x61, 0x6e, 0x69,
|
||||
0x61, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61, 0x2f, 0x56, 0x69, 0x63,
|
||||
0x74, 0x6f, 0x72, 0x69, 0x61, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61,
|
||||
0x2f, 0x57, 0x65, 0x73, 0x74, 0x0d, 0x0a, 0x41, 0x75, 0x73, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x61,
|
||||
0x2f, 0x59, 0x61, 0x6e, 0x63, 0x6f, 0x77, 0x69, 0x6e, 0x6e, 0x61, 0x0d, 0x0a, 0x42, 0x72, 0x61,
|
||||
0x7a, 0x69, 0x6c, 0x2f, 0x41, 0x63, 0x72, 0x65, 0x0d, 0x0a, 0x42, 0x72, 0x61, 0x7a, 0x69, 0x6c,
|
||||
0x2f, 0x44, 0x65, 0x4e, 0x6f, 0x72, 0x6f, 0x6e, 0x68, 0x61, 0x0d, 0x0a, 0x42, 0x72, 0x61, 0x7a,
|
||||
0x69, 0x6c, 0x2f, 0x45, 0x61, 0x73, 0x74, 0x0d, 0x0a, 0x42, 0x72, 0x61, 0x7a, 0x69, 0x6c, 0x2f,
|
||||
0x57, 0x65, 0x73, 0x74, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x41, 0x74, 0x6c,
|
||||
0x61, 0x6e, 0x74, 0x69, 0x63, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x43, 0x65,
|
||||
0x6e, 0x74, 0x72, 0x61, 0x6c, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x45, 0x61,
|
||||
0x73, 0x74, 0x2d, 0x53, 0x61, 0x73, 0x6b, 0x61, 0x74, 0x63, 0x68, 0x65, 0x77, 0x61, 0x6e, 0x0d,
|
||||
0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x45, 0x61, 0x73, 0x74, 0x65, 0x72, 0x6e, 0x0d,
|
||||
0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e,
|
||||
0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x4e, 0x65, 0x77, 0x66, 0x6f, 0x75, 0x6e,
|
||||
0x64, 0x6c, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x50, 0x61,
|
||||
0x63, 0x69, 0x66, 0x69, 0x63, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61, 0x64, 0x61, 0x2f, 0x53, 0x61,
|
||||
0x73, 0x6b, 0x61, 0x74, 0x63, 0x68, 0x65, 0x77, 0x61, 0x6e, 0x0d, 0x0a, 0x43, 0x61, 0x6e, 0x61,
|
||||
0x64, 0x61, 0x2f, 0x59, 0x75, 0x6b, 0x6f, 0x6e, 0x0d, 0x0a, 0x43, 0x68, 0x69, 0x6c, 0x65, 0x2f,
|
||||
0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x0d, 0x0a, 0x43, 0x68, 0x69,
|
||||
0x6c, 0x65, 0x2f, 0x45, 0x61, 0x73, 0x74, 0x65, 0x72, 0x49, 0x73, 0x6c, 0x61, 0x6e, 0x64, 0x0d,
|
||||
0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d,
|
||||
0x54, 0x2b, 0x30, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x31, 0x0d, 0x0a,
|
||||
0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x31, 0x30, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f,
|
||||
0x47, 0x4d, 0x54, 0x2b, 0x31, 0x31, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b,
|
||||
0x31, 0x32, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x32, 0x0d, 0x0a, 0x45,
|
||||
0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x33, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d,
|
||||
0x54, 0x2b, 0x34, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x35, 0x0d, 0x0a,
|
||||
0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x36, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47,
|
||||
0x4d, 0x54, 0x2b, 0x37, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x38, 0x0d,
|
||||
0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2b, 0x39, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f,
|
||||
0x47, 0x4d, 0x54, 0x2d, 0x30, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x31,
|
||||
0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x31, 0x30, 0x0d, 0x0a, 0x45, 0x74,
|
||||
0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x31, 0x31, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d,
|
||||
0x54, 0x2d, 0x31, 0x32, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x31, 0x33,
|
||||
0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x31, 0x34, 0x0d, 0x0a, 0x45, 0x74,
|
||||
0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x32, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54,
|
||||
0x2d, 0x33, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x34, 0x0d, 0x0a, 0x45,
|
||||
0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x35, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d,
|
||||
0x54, 0x2d, 0x36, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x37, 0x0d, 0x0a,
|
||||
0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x2d, 0x38, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47,
|
||||
0x4d, 0x54, 0x2d, 0x39, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x47, 0x4d, 0x54, 0x30, 0x0d, 0x0a,
|
||||
0x45, 0x74, 0x63, 0x2f, 0x47, 0x72, 0x65, 0x65, 0x6e, 0x77, 0x69, 0x63, 0x68, 0x0d, 0x0a, 0x45,
|
||||
0x74, 0x63, 0x2f, 0x55, 0x43, 0x54, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x55, 0x6e, 0x69, 0x76,
|
||||
0x65, 0x72, 0x73, 0x61, 0x6c, 0x0d, 0x0a, 0x45, 0x74, 0x63, 0x2f, 0x55, 0x54, 0x43, 0x0d, 0x0a,
|
||||
0x45, 0x74, 0x63, 0x2f, 0x5a, 0x75, 0x6c, 0x75, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65,
|
||||
0x2f, 0x41, 0x6d, 0x73, 0x74, 0x65, 0x72, 0x64, 0x61, 0x6d, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f,
|
||||
0x70, 0x65, 0x2f, 0x41, 0x6e, 0x64, 0x6f, 0x72, 0x72, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f,
|
||||
0x70, 0x65, 0x2f, 0x41, 0x73, 0x74, 0x72, 0x61, 0x6b, 0x68, 0x61, 0x6e, 0x0d, 0x0a, 0x45, 0x75,
|
||||
0x72, 0x6f, 0x70, 0x65, 0x2f, 0x41, 0x74, 0x68, 0x65, 0x6e, 0x73, 0x0d, 0x0a, 0x45, 0x75, 0x72,
|
||||
0x6f, 0x70, 0x65, 0x2f, 0x42, 0x65, 0x6c, 0x66, 0x61, 0x73, 0x74, 0x0d, 0x0a, 0x45, 0x75, 0x72,
|
||||
0x6f, 0x70, 0x65, 0x2f, 0x42, 0x65, 0x6c, 0x67, 0x72, 0x61, 0x64, 0x65, 0x0d, 0x0a, 0x45, 0x75,
|
||||
0x72, 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x65, 0x72, 0x6c, 0x69, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72,
|
||||
0x6f, 0x70, 0x65, 0x2f, 0x42, 0x72, 0x61, 0x74, 0x69, 0x73, 0x6c, 0x61, 0x76, 0x61, 0x0d, 0x0a,
|
||||
0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x72, 0x75, 0x73, 0x73, 0x65, 0x6c, 0x73, 0x0d,
|
||||
0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x75, 0x63, 0x68, 0x61, 0x72, 0x65, 0x73,
|
||||
0x74, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x75, 0x64, 0x61, 0x70, 0x65,
|
||||
0x73, 0x74, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x42, 0x75, 0x73, 0x69, 0x6e,
|
||||
0x67, 0x65, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x43, 0x68, 0x69, 0x73,
|
||||
0x69, 0x6e, 0x61, 0x75, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x43, 0x6f, 0x70,
|
||||
0x65, 0x6e, 0x68, 0x61, 0x67, 0x65, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f,
|
||||
0x44, 0x75, 0x62, 0x6c, 0x69, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x47,
|
||||
0x69, 0x62, 0x72, 0x61, 0x6c, 0x74, 0x61, 0x72, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65,
|
||||
0x2f, 0x47, 0x75, 0x65, 0x72, 0x6e, 0x73, 0x65, 0x79, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70,
|
||||
0x65, 0x2f, 0x48, 0x65, 0x6c, 0x73, 0x69, 0x6e, 0x6b, 0x69, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f,
|
||||
0x70, 0x65, 0x2f, 0x49, 0x73, 0x6c, 0x65, 0x5f, 0x6f, 0x66, 0x5f, 0x4d, 0x61, 0x6e, 0x0d, 0x0a,
|
||||
0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x49, 0x73, 0x74, 0x61, 0x6e, 0x62, 0x75, 0x6c, 0x0d,
|
||||
0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4a, 0x65, 0x72, 0x73, 0x65, 0x79, 0x0d, 0x0a,
|
||||
0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4b, 0x61, 0x6c, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x72,
|
||||
0x61, 0x64, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4b, 0x69, 0x65, 0x76, 0x0d,
|
||||
0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4b, 0x69, 0x72, 0x6f, 0x76, 0x0d, 0x0a, 0x45,
|
||||
0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4c, 0x69, 0x73, 0x62, 0x6f, 0x6e, 0x0d, 0x0a, 0x45, 0x75,
|
||||
0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4c, 0x6a, 0x75, 0x62, 0x6c, 0x6a, 0x61, 0x6e, 0x61, 0x0d, 0x0a,
|
||||
0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4c, 0x6f, 0x6e, 0x64, 0x6f, 0x6e, 0x0d, 0x0a, 0x45,
|
||||
0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4c, 0x75, 0x78, 0x65, 0x6d, 0x62, 0x6f, 0x75, 0x72, 0x67,
|
||||
0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x61, 0x64, 0x72, 0x69, 0x64, 0x0d,
|
||||
0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x61, 0x6c, 0x74, 0x61, 0x0d, 0x0a, 0x45,
|
||||
0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x61, 0x72, 0x69, 0x65, 0x68, 0x61, 0x6d, 0x6e, 0x0d,
|
||||
0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x69, 0x6e, 0x73, 0x6b, 0x0d, 0x0a, 0x45,
|
||||
0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x6f, 0x6e, 0x61, 0x63, 0x6f, 0x0d, 0x0a, 0x45, 0x75,
|
||||
0x72, 0x6f, 0x70, 0x65, 0x2f, 0x4d, 0x6f, 0x73, 0x63, 0x6f, 0x77, 0x0d, 0x0a, 0x45, 0x75, 0x72,
|
||||
0x6f, 0x70, 0x65, 0x2f, 0x4e, 0x69, 0x63, 0x6f, 0x73, 0x69, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72,
|
||||
0x6f, 0x70, 0x65, 0x2f, 0x4f, 0x73, 0x6c, 0x6f, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65,
|
||||
0x2f, 0x50, 0x61, 0x72, 0x69, 0x73, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x50,
|
||||
0x6f, 0x64, 0x67, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65,
|
||||
0x2f, 0x50, 0x72, 0x61, 0x67, 0x75, 0x65, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f,
|
||||
0x52, 0x69, 0x67, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x52, 0x6f, 0x6d,
|
||||
0x65, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x61, 0x6d, 0x61, 0x72, 0x61,
|
||||
0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x61, 0x6e, 0x5f, 0x4d, 0x61, 0x72,
|
||||
0x69, 0x6e, 0x6f, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x61, 0x72, 0x61,
|
||||
0x6a, 0x65, 0x76, 0x6f, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x61, 0x72,
|
||||
0x61, 0x74, 0x6f, 0x76, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x69, 0x6d,
|
||||
0x66, 0x65, 0x72, 0x6f, 0x70, 0x6f, 0x6c, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f,
|
||||
0x53, 0x6b, 0x6f, 0x70, 0x6a, 0x65, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53,
|
||||
0x6f, 0x66, 0x69, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x53, 0x74, 0x6f,
|
||||
0x63, 0x6b, 0x68, 0x6f, 0x6c, 0x6d, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x54,
|
||||
0x61, 0x6c, 0x6c, 0x69, 0x6e, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x54,
|
||||
0x69, 0x72, 0x61, 0x6e, 0x65, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x54, 0x69,
|
||||
0x72, 0x61, 0x73, 0x70, 0x6f, 0x6c, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x55,
|
||||
0x6c, 0x79, 0x61, 0x6e, 0x6f, 0x76, 0x73, 0x6b, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65,
|
||||
0x2f, 0x55, 0x7a, 0x68, 0x67, 0x6f, 0x72, 0x6f, 0x64, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70,
|
||||
0x65, 0x2f, 0x56, 0x61, 0x64, 0x75, 0x7a, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f,
|
||||
0x56, 0x61, 0x74, 0x69, 0x63, 0x61, 0x6e, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f,
|
||||
0x56, 0x69, 0x65, 0x6e, 0x6e, 0x61, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x56,
|
||||
0x69, 0x6c, 0x6e, 0x69, 0x75, 0x73, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x56,
|
||||
0x6f, 0x6c, 0x67, 0x6f, 0x67, 0x72, 0x61, 0x64, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65,
|
||||
0x2f, 0x57, 0x61, 0x72, 0x73, 0x61, 0x77, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f,
|
||||
0x5a, 0x61, 0x67, 0x72, 0x65, 0x62, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70, 0x65, 0x2f, 0x5a,
|
||||
0x61, 0x70, 0x6f, 0x72, 0x6f, 0x7a, 0x68, 0x79, 0x65, 0x0d, 0x0a, 0x45, 0x75, 0x72, 0x6f, 0x70,
|
||||
0x65, 0x2f, 0x5a, 0x75, 0x72, 0x69, 0x63, 0x68, 0x0d, 0x0a, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e,
|
||||
0x2f, 0x41, 0x6e, 0x74, 0x61, 0x6e, 0x61, 0x6e, 0x61, 0x72, 0x69, 0x76, 0x6f, 0x0d, 0x0a, 0x49,
|
||||
0x6e, 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x43, 0x68, 0x61, 0x67, 0x6f, 0x73, 0x0d, 0x0a, 0x49, 0x6e,
|
||||
0x64, 0x69, 0x61, 0x6e, 0x2f, 0x43, 0x68, 0x72, 0x69, 0x73, 0x74, 0x6d, 0x61, 0x73, 0x0d, 0x0a,
|
||||
0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x43, 0x6f, 0x63, 0x6f, 0x73, 0x0d, 0x0a, 0x49, 0x6e,
|
||||
0x64, 0x69, 0x61, 0x6e, 0x2f, 0x43, 0x6f, 0x6d, 0x6f, 0x72, 0x6f, 0x0d, 0x0a, 0x49, 0x6e, 0x64,
|
||||
0x69, 0x61, 0x6e, 0x2f, 0x4b, 0x65, 0x72, 0x67, 0x75, 0x65, 0x6c, 0x65, 0x6e, 0x0d, 0x0a, 0x49,
|
||||
0x6e, 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x4d, 0x61, 0x68, 0x65, 0x0d, 0x0a, 0x49, 0x6e, 0x64, 0x69,
|
||||
0x61, 0x6e, 0x2f, 0x4d, 0x61, 0x6c, 0x64, 0x69, 0x76, 0x65, 0x73, 0x0d, 0x0a, 0x49, 0x6e, 0x64,
|
||||
0x69, 0x61, 0x6e, 0x2f, 0x4d, 0x61, 0x75, 0x72, 0x69, 0x74, 0x69, 0x75, 0x73, 0x0d, 0x0a, 0x49,
|
||||
0x6e, 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x4d, 0x61, 0x79, 0x6f, 0x74, 0x74, 0x65, 0x0d, 0x0a, 0x49,
|
||||
0x6e, 0x64, 0x69, 0x61, 0x6e, 0x2f, 0x52, 0x65, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x0d, 0x0a, 0x4d,
|
||||
0x65, 0x78, 0x69, 0x63, 0x6f, 0x2f, 0x42, 0x61, 0x6a, 0x61, 0x4e, 0x6f, 0x72, 0x74, 0x65, 0x0d,
|
||||
0x0a, 0x4d, 0x65, 0x78, 0x69, 0x63, 0x6f, 0x2f, 0x42, 0x61, 0x6a, 0x61, 0x53, 0x75, 0x72, 0x0d,
|
||||
0x0a, 0x4d, 0x65, 0x78, 0x69, 0x63, 0x6f, 0x2f, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x0d,
|
||||
0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x41, 0x70, 0x69, 0x61, 0x0d, 0x0a, 0x50,
|
||||
0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x41, 0x75, 0x63, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x0d,
|
||||
0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x42, 0x6f, 0x75, 0x67, 0x61, 0x69, 0x6e,
|
||||
0x76, 0x69, 0x6c, 0x6c, 0x65, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x43,
|
||||
0x68, 0x61, 0x74, 0x68, 0x61, 0x6d, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f,
|
||||
0x43, 0x68, 0x75, 0x75, 0x6b, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x45,
|
||||
0x61, 0x73, 0x74, 0x65, 0x72, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x45,
|
||||
0x66, 0x61, 0x74, 0x65, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x45, 0x6e,
|
||||
0x64, 0x65, 0x72, 0x62, 0x75, 0x72, 0x79, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63,
|
||||
0x2f, 0x46, 0x61, 0x6b, 0x61, 0x6f, 0x66, 0x6f, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69,
|
||||
0x63, 0x2f, 0x46, 0x69, 0x6a, 0x69, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f,
|
||||
0x46, 0x75, 0x6e, 0x61, 0x66, 0x75, 0x74, 0x69, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69,
|
||||
0x63, 0x2f, 0x47, 0x61, 0x6c, 0x61, 0x70, 0x61, 0x67, 0x6f, 0x73, 0x0d, 0x0a, 0x50, 0x61, 0x63,
|
||||
0x69, 0x66, 0x69, 0x63, 0x2f, 0x47, 0x61, 0x6d, 0x62, 0x69, 0x65, 0x72, 0x0d, 0x0a, 0x50, 0x61,
|
||||
0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x47, 0x75, 0x61, 0x64, 0x61, 0x6c, 0x63, 0x61, 0x6e, 0x61,
|
||||
0x6c, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x47, 0x75, 0x61, 0x6d, 0x0d,
|
||||
0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x48, 0x6f, 0x6e, 0x6f, 0x6c, 0x75, 0x6c,
|
||||
0x75, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4a, 0x6f, 0x68, 0x6e, 0x73,
|
||||
0x74, 0x6f, 0x6e, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4b, 0x69, 0x72,
|
||||
0x69, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x69, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63,
|
||||
0x2f, 0x4b, 0x6f, 0x73, 0x72, 0x61, 0x65, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63,
|
||||
0x2f, 0x4b, 0x77, 0x61, 0x6a, 0x61, 0x6c, 0x65, 0x69, 0x6e, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69,
|
||||
0x66, 0x69, 0x63, 0x2f, 0x4d, 0x61, 0x6a, 0x75, 0x72, 0x6f, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69,
|
||||
0x66, 0x69, 0x63, 0x2f, 0x4d, 0x61, 0x72, 0x71, 0x75, 0x65, 0x73, 0x61, 0x73, 0x0d, 0x0a, 0x50,
|
||||
0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4d, 0x69, 0x64, 0x77, 0x61, 0x79, 0x0d, 0x0a, 0x50,
|
||||
0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4e, 0x61, 0x75, 0x72, 0x75, 0x0d, 0x0a, 0x50, 0x61,
|
||||
0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x4e, 0x69, 0x75, 0x65, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69,
|
||||
0x66, 0x69, 0x63, 0x2f, 0x4e, 0x6f, 0x72, 0x66, 0x6f, 0x6c, 0x6b, 0x0d, 0x0a, 0x50, 0x61, 0x63,
|
||||
0x69, 0x66, 0x69, 0x63, 0x2f, 0x4e, 0x6f, 0x75, 0x6d, 0x65, 0x61, 0x0d, 0x0a, 0x50, 0x61, 0x63,
|
||||
0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x61, 0x67, 0x6f, 0x5f, 0x50, 0x61, 0x67, 0x6f, 0x0d, 0x0a,
|
||||
0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x61, 0x6c, 0x61, 0x75, 0x0d, 0x0a, 0x50,
|
||||
0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x69, 0x74, 0x63, 0x61, 0x69, 0x72, 0x6e, 0x0d,
|
||||
0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x6f, 0x68, 0x6e, 0x70, 0x65, 0x69,
|
||||
0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x6f, 0x6e, 0x61, 0x70, 0x65,
|
||||
0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f, 0x50, 0x6f, 0x72, 0x74, 0x5f, 0x4d,
|
||||
0x6f, 0x72, 0x65, 0x73, 0x62, 0x79, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63, 0x2f,
|
||||
0x52, 0x61, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x67, 0x61, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66,
|
||||
0x69, 0x63, 0x2f, 0x53, 0x61, 0x69, 0x70, 0x61, 0x6e, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66,
|
||||
0x69, 0x63, 0x2f, 0x53, 0x61, 0x6d, 0x6f, 0x61, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69,
|
||||
0x63, 0x2f, 0x54, 0x61, 0x68, 0x69, 0x74, 0x69, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69,
|
||||
0x63, 0x2f, 0x54, 0x61, 0x72, 0x61, 0x77, 0x61, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69,
|
||||
0x63, 0x2f, 0x54, 0x6f, 0x6e, 0x67, 0x61, 0x74, 0x61, 0x70, 0x75, 0x0d, 0x0a, 0x50, 0x61, 0x63,
|
||||
0x69, 0x66, 0x69, 0x63, 0x2f, 0x54, 0x72, 0x75, 0x6b, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66,
|
||||
0x69, 0x63, 0x2f, 0x57, 0x61, 0x6b, 0x65, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63,
|
||||
0x2f, 0x57, 0x61, 0x6c, 0x6c, 0x69, 0x73, 0x0d, 0x0a, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69, 0x63,
|
||||
0x2f, 0x59, 0x61, 0x70, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x41, 0x6c, 0x61, 0x73, 0x6b, 0x61, 0x0d,
|
||||
0x0a, 0x55, 0x53, 0x2f, 0x41, 0x6c, 0x65, 0x75, 0x74, 0x69, 0x61, 0x6e, 0x0d, 0x0a, 0x55, 0x53,
|
||||
0x2f, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x43, 0x65, 0x6e,
|
||||
0x74, 0x72, 0x61, 0x6c, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x45, 0x61, 0x73, 0x74, 0x2d, 0x49, 0x6e,
|
||||
0x64, 0x69, 0x61, 0x6e, 0x61, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x45, 0x61, 0x73, 0x74, 0x65, 0x72,
|
||||
0x6e, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x48, 0x61, 0x77, 0x61, 0x69, 0x69, 0x0d, 0x0a, 0x55, 0x53,
|
||||
0x2f, 0x49, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x61, 0x2d, 0x53, 0x74, 0x61, 0x72, 0x6b, 0x65, 0x0d,
|
||||
0x0a, 0x55, 0x53, 0x2f, 0x4d, 0x69, 0x63, 0x68, 0x69, 0x67, 0x61, 0x6e, 0x0d, 0x0a, 0x55, 0x53,
|
||||
0x2f, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x50, 0x61,
|
||||
0x63, 0x69, 0x66, 0x69, 0x63, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x50, 0x61, 0x63, 0x69, 0x66, 0x69,
|
||||
0x63, 0x2d, 0x4e, 0x65, 0x77, 0x0d, 0x0a, 0x55, 0x53, 0x2f, 0x53, 0x61, 0x6d, 0x6f, 0x61, 0x0d,
|
||||
0x0a};
|
||||
|
||||
static VirtualFile GenerateDefaultTimeZoneFile() {
|
||||
struct {
|
||||
s64_be at;
|
||||
INSERT_PADDING_BYTES(7);
|
||||
std::array<char, 4> time_zone_chars;
|
||||
INSERT_PADDING_BYTES(2);
|
||||
std::array<char, 6> time_zone_name;
|
||||
} time_zone_info{};
|
||||
|
||||
const VirtualFile file{std::make_shared<VectorVfsFile>(
|
||||
std::vector<u8>(sizeof(Service::Time::TimeZone::TzifHeader) + sizeof(time_zone_info)),
|
||||
"GMT")};
|
||||
|
||||
Service::Time::TimeZone::TzifHeader header{};
|
||||
header.magic = 0x545a6966;
|
||||
header.version = 0x32;
|
||||
header.ttis_gmt_count = 0x1;
|
||||
header.ttis_std_count = 0x1;
|
||||
header.time_count = 0x1;
|
||||
header.type_count = 0x1;
|
||||
header.char_count = 0x4;
|
||||
file->WriteObject(header, 0);
|
||||
|
||||
time_zone_info.at = 0xf8;
|
||||
time_zone_info.time_zone_chars = {'G', 'M', 'T', '\0'};
|
||||
time_zone_info.time_zone_name = {'\n', 'G', 'M', 'T', '0', '\n'};
|
||||
file->WriteObject(time_zone_info, sizeof(Service::Time::TimeZone::TzifHeader));
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
VirtualDir TimeZoneBinary() {
|
||||
const std::vector<VirtualDir> root_dirs{std::make_shared<VectorVfsDirectory>(
|
||||
std::vector<VirtualFile>{GenerateDefaultTimeZoneFile()}, std::vector<VirtualDir>{},
|
||||
"zoneinfo")};
|
||||
const std::vector<VirtualFile> root_files{
|
||||
std::make_shared<ArrayVfsFile<LOCATION_NAMES.size()>>(LOCATION_NAMES, "binaryList.txt")};
|
||||
return std::make_shared<VectorVfsDirectory>(root_files, root_dirs, "data");
|
||||
}
|
||||
|
||||
} // namespace FileSys::SystemArchive
|
||||
14
src/core/file_sys/system_archive/time_zone_binary.h
Normal file
14
src/core/file_sys/system_archive/time_zone_binary.h
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
|
||||
namespace FileSys::SystemArchive {
|
||||
|
||||
VirtualDir TimeZoneBinary();
|
||||
|
||||
} // namespace FileSys::SystemArchive
|
||||
@@ -17,10 +17,10 @@
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
namespace {
|
||||
|
||||
// Wake up num_to_wake (or all) threads in a vector.
|
||||
void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s32 num_to_wake) {
|
||||
auto& system = Core::System::GetInstance();
|
||||
void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads,
|
||||
s32 num_to_wake) {
|
||||
// Only process up to 'target' threads, unless 'target' is <= 0, in which case process
|
||||
// them all.
|
||||
std::size_t last = waiting_threads.size();
|
||||
@@ -32,12 +32,12 @@ void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s3
|
||||
for (std::size_t i = 0; i < last; i++) {
|
||||
ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb);
|
||||
waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
|
||||
RemoveThread(waiting_threads[i]);
|
||||
waiting_threads[i]->SetArbiterWaitAddress(0);
|
||||
waiting_threads[i]->ResumeFromWait();
|
||||
system.PrepareReschedule(waiting_threads[i]->GetProcessorID());
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
AddressArbiter::AddressArbiter(Core::System& system) : system{system} {}
|
||||
AddressArbiter::~AddressArbiter() = default;
|
||||
@@ -184,6 +184,7 @@ ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 t
|
||||
ResultCode AddressArbiter::WaitForAddressImpl(VAddr address, s64 timeout) {
|
||||
Thread* current_thread = system.CurrentScheduler().GetCurrentThread();
|
||||
current_thread->SetArbiterWaitAddress(address);
|
||||
InsertThread(SharedFrom(current_thread));
|
||||
current_thread->SetStatus(ThreadStatus::WaitArb);
|
||||
current_thread->InvalidateWakeupCallback();
|
||||
current_thread->WakeAfterDelay(timeout);
|
||||
@@ -192,26 +193,51 @@ ResultCode AddressArbiter::WaitForAddressImpl(VAddr address, s64 timeout) {
|
||||
return RESULT_TIMEOUT;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(
|
||||
VAddr address) const {
|
||||
void AddressArbiter::HandleWakeupThread(std::shared_ptr<Thread> thread) {
|
||||
ASSERT(thread->GetStatus() == ThreadStatus::WaitArb);
|
||||
RemoveThread(thread);
|
||||
thread->SetArbiterWaitAddress(0);
|
||||
}
|
||||
|
||||
// Retrieve all threads that are waiting for this address.
|
||||
std::vector<std::shared_ptr<Thread>> threads;
|
||||
const auto& scheduler = system.GlobalScheduler();
|
||||
const auto& thread_list = scheduler.GetThreadList();
|
||||
|
||||
for (const auto& thread : thread_list) {
|
||||
if (thread->GetArbiterWaitAddress() == address) {
|
||||
threads.push_back(thread);
|
||||
void AddressArbiter::InsertThread(std::shared_ptr<Thread> thread) {
|
||||
const VAddr arb_addr = thread->GetArbiterWaitAddress();
|
||||
std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr];
|
||||
auto it = thread_list.begin();
|
||||
while (it != thread_list.end()) {
|
||||
const std::shared_ptr<Thread>& current_thread = *it;
|
||||
if (current_thread->GetPriority() >= thread->GetPriority()) {
|
||||
thread_list.insert(it, thread);
|
||||
return;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
thread_list.push_back(std::move(thread));
|
||||
}
|
||||
|
||||
// Sort them by priority, such that the highest priority ones come first.
|
||||
std::sort(threads.begin(), threads.end(),
|
||||
[](const std::shared_ptr<Thread>& lhs, const std::shared_ptr<Thread>& rhs) {
|
||||
return lhs->GetPriority() < rhs->GetPriority();
|
||||
});
|
||||
void AddressArbiter::RemoveThread(std::shared_ptr<Thread> thread) {
|
||||
const VAddr arb_addr = thread->GetArbiterWaitAddress();
|
||||
std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr];
|
||||
auto it = thread_list.begin();
|
||||
while (it != thread_list.end()) {
|
||||
const std::shared_ptr<Thread>& current_thread = *it;
|
||||
if (current_thread.get() == thread.get()) {
|
||||
thread_list.erase(it);
|
||||
return;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return threads;
|
||||
std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(VAddr address) {
|
||||
std::vector<std::shared_ptr<Thread>> result;
|
||||
std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[address];
|
||||
auto it = thread_list.begin();
|
||||
while (it != thread_list.end()) {
|
||||
std::shared_ptr<Thread> current_thread = *it;
|
||||
result.push_back(std::move(current_thread));
|
||||
++it;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
@@ -48,6 +50,9 @@ public:
|
||||
/// Waits on an address with a particular arbitration type.
|
||||
ResultCode WaitForAddress(VAddr address, ArbitrationType type, s32 value, s64 timeout_ns);
|
||||
|
||||
/// Removes a thread from the container and resets its address arbiter adress to 0
|
||||
void HandleWakeupThread(std::shared_ptr<Thread> thread);
|
||||
|
||||
private:
|
||||
/// Signals an address being waited on.
|
||||
ResultCode SignalToAddressOnly(VAddr address, s32 num_to_wake);
|
||||
@@ -71,8 +76,20 @@ private:
|
||||
// Waits on the given address with a timeout in nanoseconds
|
||||
ResultCode WaitForAddressImpl(VAddr address, s64 timeout);
|
||||
|
||||
/// Wake up num_to_wake (or all) threads in a vector.
|
||||
void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s32 num_to_wake);
|
||||
|
||||
/// Insert a thread into the address arbiter container
|
||||
void InsertThread(std::shared_ptr<Thread> thread);
|
||||
|
||||
/// Removes a thread from the address arbiter container
|
||||
void RemoveThread(std::shared_ptr<Thread> thread);
|
||||
|
||||
// Gets the threads waiting on an address.
|
||||
std::vector<std::shared_ptr<Thread>> GetThreadsWaitingOnAddress(VAddr address) const;
|
||||
std::vector<std::shared_ptr<Thread>> GetThreadsWaitingOnAddress(VAddr address);
|
||||
|
||||
/// List of threads waiting for a address arbiter
|
||||
std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> arb_threads;
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
@@ -78,9 +78,9 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
|
||||
}
|
||||
}
|
||||
|
||||
if (thread->GetArbiterWaitAddress() != 0) {
|
||||
ASSERT(thread->GetStatus() == ThreadStatus::WaitArb);
|
||||
thread->SetArbiterWaitAddress(0);
|
||||
if (thread->GetStatus() == ThreadStatus::WaitArb) {
|
||||
auto& address_arbiter = thread->GetOwnerProcess()->GetAddressArbiter();
|
||||
address_arbiter.HandleWakeupThread(thread);
|
||||
}
|
||||
|
||||
if (resume) {
|
||||
|
||||
@@ -1650,8 +1650,7 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add
|
||||
}
|
||||
|
||||
/// Signal process wide key
|
||||
static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_variable_addr,
|
||||
s32 target) {
|
||||
static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_addr, s32 target) {
|
||||
LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}",
|
||||
condition_variable_addr, target);
|
||||
|
||||
@@ -1726,8 +1725,6 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var
|
||||
system.PrepareReschedule(thread->GetProcessorID());
|
||||
}
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
// Wait for an address (via Address Arbiter)
|
||||
@@ -1781,6 +1778,17 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type,
|
||||
return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake);
|
||||
}
|
||||
|
||||
static void KernelDebug([[maybe_unused]] Core::System& system,
|
||||
[[maybe_unused]] u32 kernel_debug_type, [[maybe_unused]] u64 param1,
|
||||
[[maybe_unused]] u64 param2, [[maybe_unused]] u64 param3) {
|
||||
// Intentionally do nothing, as this does nothing in released kernel binaries.
|
||||
}
|
||||
|
||||
static void ChangeKernelTraceState([[maybe_unused]] Core::System& system,
|
||||
[[maybe_unused]] u32 trace_state) {
|
||||
// Intentionally do nothing, as this does nothing in released kernel binaries.
|
||||
}
|
||||
|
||||
/// This returns the total CPU ticks elapsed since the CPU was powered-on
|
||||
static u64 GetSystemTick(Core::System& system) {
|
||||
LOG_TRACE(Kernel_SVC, "called");
|
||||
@@ -2418,8 +2426,8 @@ static const FunctionDef SVC_Table[] = {
|
||||
{0x39, nullptr, "Unknown"},
|
||||
{0x3A, nullptr, "Unknown"},
|
||||
{0x3B, nullptr, "Unknown"},
|
||||
{0x3C, nullptr, "DumpInfo"},
|
||||
{0x3D, nullptr, "DumpInfoNew"},
|
||||
{0x3C, SvcWrap<KernelDebug>, "KernelDebug"},
|
||||
{0x3D, SvcWrap<ChangeKernelTraceState>, "ChangeKernelTraceState"},
|
||||
{0x3E, nullptr, "Unknown"},
|
||||
{0x3F, nullptr, "Unknown"},
|
||||
{0x40, nullptr, "CreateSession"},
|
||||
|
||||
@@ -112,11 +112,6 @@ void SvcWrap(Core::System& system) {
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u64, s32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), static_cast<s32>(Param(system, 1))).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u64, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1))).raw);
|
||||
@@ -311,11 +306,27 @@ void SvcWrap(Core::System& system) {
|
||||
func(system);
|
||||
}
|
||||
|
||||
template <void func(Core::System&, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
func(system, static_cast<u32>(Param(system, 0)));
|
||||
}
|
||||
|
||||
template <void func(Core::System&, u32, u64, u64, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2),
|
||||
Param(system, 3));
|
||||
}
|
||||
|
||||
template <void func(Core::System&, s64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
func(system, static_cast<s64>(Param(system, 0)));
|
||||
}
|
||||
|
||||
template <void func(Core::System&, u64, s32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
func(system, Param(system, 0), static_cast<s32>(Param(system, 1)));
|
||||
}
|
||||
|
||||
template <void func(Core::System&, u64, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
func(system, Param(system, 0), Param(system, 1));
|
||||
|
||||
@@ -211,7 +211,7 @@ protected:
|
||||
}
|
||||
|
||||
ProfileManager& profile_manager;
|
||||
Common::UUID user_id; ///< The user id this profile refers to.
|
||||
Common::UUID user_id{Common::INVALID_UUID}; ///< The user id this profile refers to.
|
||||
};
|
||||
|
||||
class IProfile final : public IProfileCommon {
|
||||
|
||||
@@ -16,17 +16,17 @@ namespace Service::Account {
|
||||
using Common::UUID;
|
||||
|
||||
struct UserRaw {
|
||||
UUID uuid;
|
||||
UUID uuid2;
|
||||
u64 timestamp;
|
||||
ProfileUsername username;
|
||||
ProfileData extra_data;
|
||||
UUID uuid{Common::INVALID_UUID};
|
||||
UUID uuid2{Common::INVALID_UUID};
|
||||
u64 timestamp{};
|
||||
ProfileUsername username{};
|
||||
ProfileData extra_data{};
|
||||
};
|
||||
static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size.");
|
||||
|
||||
struct ProfileDataRaw {
|
||||
INSERT_PADDING_BYTES(0x10);
|
||||
std::array<UserRaw, MAX_USERS> users;
|
||||
std::array<UserRaw, MAX_USERS> users{};
|
||||
};
|
||||
static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size.");
|
||||
|
||||
@@ -238,7 +238,7 @@ UserIDArray ProfileManager::GetOpenUsers() const {
|
||||
std::transform(profiles.begin(), profiles.end(), output.begin(), [](const ProfileInfo& p) {
|
||||
if (p.is_open)
|
||||
return p.user_uuid;
|
||||
return UUID{};
|
||||
return UUID{Common::INVALID_UUID};
|
||||
});
|
||||
std::stable_partition(output.begin(), output.end(), [](const UUID& uuid) { return uuid; });
|
||||
return output;
|
||||
|
||||
@@ -13,9 +13,10 @@
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Service::Account {
|
||||
constexpr std::size_t MAX_USERS = 8;
|
||||
|
||||
constexpr std::size_t profile_username_size = 32;
|
||||
constexpr std::size_t MAX_USERS{8};
|
||||
constexpr std::size_t profile_username_size{32};
|
||||
|
||||
using ProfileUsername = std::array<u8, profile_username_size>;
|
||||
using UserIDArray = std::array<Common::UUID, MAX_USERS>;
|
||||
|
||||
@@ -23,8 +24,8 @@ using UserIDArray = std::array<Common::UUID, MAX_USERS>;
|
||||
/// TODO: RE this structure
|
||||
struct ProfileData {
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u32 icon_id;
|
||||
u8 bg_color_id;
|
||||
u32 icon_id{};
|
||||
u8 bg_color_id{};
|
||||
INSERT_PADDING_BYTES(0x7);
|
||||
INSERT_PADDING_BYTES(0x10);
|
||||
INSERT_PADDING_BYTES(0x60);
|
||||
@@ -34,17 +35,17 @@ static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect
|
||||
/// This holds general information about a users profile. This is where we store all the information
|
||||
/// based on a specific user
|
||||
struct ProfileInfo {
|
||||
Common::UUID user_uuid;
|
||||
ProfileUsername username;
|
||||
u64 creation_time;
|
||||
ProfileData data; // TODO(ognik): Work out what this is
|
||||
bool is_open;
|
||||
Common::UUID user_uuid{Common::INVALID_UUID};
|
||||
ProfileUsername username{};
|
||||
u64 creation_time{};
|
||||
ProfileData data{}; // TODO(ognik): Work out what this is
|
||||
bool is_open{};
|
||||
};
|
||||
|
||||
struct ProfileBase {
|
||||
Common::UUID user_uuid;
|
||||
u64_le timestamp;
|
||||
ProfileUsername username;
|
||||
Common::UUID user_uuid{Common::INVALID_UUID};
|
||||
u64_le timestamp{};
|
||||
ProfileUsername username{};
|
||||
|
||||
// Zero out all the fields to make the profile slot considered "Empty"
|
||||
void Invalidate() {
|
||||
@@ -101,7 +102,7 @@ private:
|
||||
bool RemoveProfileAtIndex(std::size_t index);
|
||||
|
||||
std::array<ProfileInfo, MAX_USERS> profiles{};
|
||||
std::size_t user_count = 0;
|
||||
std::size_t user_count{};
|
||||
Common::UUID last_opened_user{Common::INVALID_UUID};
|
||||
};
|
||||
|
||||
|
||||
@@ -241,7 +241,7 @@ private:
|
||||
bool has_received_friend_request;
|
||||
};
|
||||
|
||||
Common::UUID uuid;
|
||||
Common::UUID uuid{Common::INVALID_UUID};
|
||||
Kernel::EventPair notification_event;
|
||||
std::queue<SizedNotificationInfo> notifications;
|
||||
States states{};
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
|
||||
namespace Service::Mii {
|
||||
|
||||
constexpr std::size_t MAX_MIIS = 100;
|
||||
constexpr u32 INVALID_INDEX = 0xFFFFFFFF;
|
||||
constexpr std::size_t MAX_MIIS{100};
|
||||
constexpr u32 INVALID_INDEX{0xFFFFFFFF};
|
||||
|
||||
struct RandomParameters {
|
||||
u32 unknown_1;
|
||||
u32 unknown_2;
|
||||
u32 unknown_3;
|
||||
u32 unknown_1{};
|
||||
u32 unknown_2{};
|
||||
u32 unknown_3{};
|
||||
};
|
||||
static_assert(sizeof(RandomParameters) == 0xC, "RandomParameters has incorrect size.");
|
||||
|
||||
@@ -30,57 +30,57 @@ enum class Source : u32 {
|
||||
std::ostream& operator<<(std::ostream& os, Source source);
|
||||
|
||||
struct MiiInfo {
|
||||
Common::UUID uuid;
|
||||
std::array<char16_t, 11> name;
|
||||
u8 font_region;
|
||||
u8 favorite_color;
|
||||
u8 gender;
|
||||
u8 height;
|
||||
u8 weight;
|
||||
u8 mii_type;
|
||||
u8 mii_region;
|
||||
u8 face_type;
|
||||
u8 face_color;
|
||||
u8 face_wrinkle;
|
||||
u8 face_makeup;
|
||||
u8 hair_type;
|
||||
u8 hair_color;
|
||||
bool hair_flip;
|
||||
u8 eye_type;
|
||||
u8 eye_color;
|
||||
u8 eye_scale;
|
||||
u8 eye_aspect_ratio;
|
||||
u8 eye_rotate;
|
||||
u8 eye_x;
|
||||
u8 eye_y;
|
||||
u8 eyebrow_type;
|
||||
u8 eyebrow_color;
|
||||
u8 eyebrow_scale;
|
||||
u8 eyebrow_aspect_ratio;
|
||||
u8 eyebrow_rotate;
|
||||
u8 eyebrow_x;
|
||||
u8 eyebrow_y;
|
||||
u8 nose_type;
|
||||
u8 nose_scale;
|
||||
u8 nose_y;
|
||||
u8 mouth_type;
|
||||
u8 mouth_color;
|
||||
u8 mouth_scale;
|
||||
u8 mouth_aspect_ratio;
|
||||
u8 mouth_y;
|
||||
u8 facial_hair_color;
|
||||
u8 beard_type;
|
||||
u8 mustache_type;
|
||||
u8 mustache_scale;
|
||||
u8 mustache_y;
|
||||
u8 glasses_type;
|
||||
u8 glasses_color;
|
||||
u8 glasses_scale;
|
||||
u8 glasses_y;
|
||||
u8 mole_type;
|
||||
u8 mole_scale;
|
||||
u8 mole_x;
|
||||
u8 mole_y;
|
||||
Common::UUID uuid{Common::INVALID_UUID};
|
||||
std::array<char16_t, 11> name{};
|
||||
u8 font_region{};
|
||||
u8 favorite_color{};
|
||||
u8 gender{};
|
||||
u8 height{};
|
||||
u8 weight{};
|
||||
u8 mii_type{};
|
||||
u8 mii_region{};
|
||||
u8 face_type{};
|
||||
u8 face_color{};
|
||||
u8 face_wrinkle{};
|
||||
u8 face_makeup{};
|
||||
u8 hair_type{};
|
||||
u8 hair_color{};
|
||||
bool hair_flip{};
|
||||
u8 eye_type{};
|
||||
u8 eye_color{};
|
||||
u8 eye_scale{};
|
||||
u8 eye_aspect_ratio{};
|
||||
u8 eye_rotate{};
|
||||
u8 eye_x{};
|
||||
u8 eye_y{};
|
||||
u8 eyebrow_type{};
|
||||
u8 eyebrow_color{};
|
||||
u8 eyebrow_scale{};
|
||||
u8 eyebrow_aspect_ratio{};
|
||||
u8 eyebrow_rotate{};
|
||||
u8 eyebrow_x{};
|
||||
u8 eyebrow_y{};
|
||||
u8 nose_type{};
|
||||
u8 nose_scale{};
|
||||
u8 nose_y{};
|
||||
u8 mouth_type{};
|
||||
u8 mouth_color{};
|
||||
u8 mouth_scale{};
|
||||
u8 mouth_aspect_ratio{};
|
||||
u8 mouth_y{};
|
||||
u8 facial_hair_color{};
|
||||
u8 beard_type{};
|
||||
u8 mustache_type{};
|
||||
u8 mustache_scale{};
|
||||
u8 mustache_y{};
|
||||
u8 glasses_type{};
|
||||
u8 glasses_color{};
|
||||
u8 glasses_scale{};
|
||||
u8 glasses_y{};
|
||||
u8 mole_type{};
|
||||
u8 mole_scale{};
|
||||
u8 mole_x{};
|
||||
u8 mole_y{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
|
||||
std::u16string Name() const;
|
||||
@@ -94,14 +94,14 @@ bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs);
|
||||
|
||||
#pragma pack(push, 4)
|
||||
struct MiiInfoElement {
|
||||
MiiInfo info;
|
||||
Source source;
|
||||
MiiInfo info{};
|
||||
Source source{};
|
||||
};
|
||||
static_assert(sizeof(MiiInfoElement) == 0x5C, "MiiInfoElement has incorrect size.");
|
||||
|
||||
struct MiiStoreBitFields {
|
||||
union {
|
||||
u32 word_0;
|
||||
u32 word_0{};
|
||||
|
||||
BitField<24, 8, u32> hair_type;
|
||||
BitField<23, 1, u32> mole_type;
|
||||
@@ -112,7 +112,7 @@ struct MiiStoreBitFields {
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_1;
|
||||
u32 word_1{};
|
||||
|
||||
BitField<31, 1, u32> gender;
|
||||
BitField<24, 7, u32> eye_color;
|
||||
@@ -122,7 +122,7 @@ struct MiiStoreBitFields {
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_2;
|
||||
u32 word_2{};
|
||||
|
||||
BitField<31, 1, u32> mii_type;
|
||||
BitField<24, 7, u32> glasses_color;
|
||||
@@ -135,7 +135,7 @@ struct MiiStoreBitFields {
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_3;
|
||||
u32 word_3{};
|
||||
|
||||
BitField<29, 3, u32> mustache_type;
|
||||
BitField<24, 5, u32> eyebrow_type;
|
||||
@@ -148,7 +148,7 @@ struct MiiStoreBitFields {
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_4;
|
||||
u32 word_4{};
|
||||
|
||||
BitField<29, 3, u32> eye_rotate;
|
||||
BitField<24, 5, u32> mustache_y;
|
||||
@@ -160,7 +160,7 @@ struct MiiStoreBitFields {
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_5;
|
||||
u32 word_5{};
|
||||
|
||||
BitField<24, 5, u32> glasses_type;
|
||||
BitField<20, 4, u32> face_type;
|
||||
@@ -172,7 +172,7 @@ struct MiiStoreBitFields {
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_6;
|
||||
u32 word_6{};
|
||||
|
||||
BitField<28, 4, u32> eyebrow_rotate;
|
||||
BitField<24, 4, u32> eyebrow_scale;
|
||||
@@ -192,30 +192,30 @@ struct MiiStoreData {
|
||||
// This corresponds to the above structure MiiStoreBitFields. I did it like this because the
|
||||
// BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
|
||||
// not suitable for our uses.
|
||||
std::array<u8, 0x1C> data;
|
||||
std::array<u8, 0x1C> data{};
|
||||
static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
|
||||
|
||||
std::array<char16_t, 10> name;
|
||||
Common::UUID uuid;
|
||||
u16 crc_1;
|
||||
u16 crc_2;
|
||||
std::array<char16_t, 10> name{};
|
||||
Common::UUID uuid{Common::INVALID_UUID};
|
||||
u16 crc_1{};
|
||||
u16 crc_2{};
|
||||
|
||||
std::u16string Name() const;
|
||||
};
|
||||
static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
|
||||
|
||||
struct MiiStoreDataElement {
|
||||
MiiStoreData data;
|
||||
Source source;
|
||||
MiiStoreData data{};
|
||||
Source source{};
|
||||
};
|
||||
static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
|
||||
|
||||
struct MiiDatabase {
|
||||
u32 magic; // 'NFDB'
|
||||
std::array<MiiStoreData, MAX_MIIS> miis;
|
||||
u32 magic{}; // 'NFDB'
|
||||
std::array<MiiStoreData, MAX_MIIS> miis{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u8 count;
|
||||
u16 crc;
|
||||
u8 count{};
|
||||
u16 crc{};
|
||||
};
|
||||
static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
|
||||
#pragma pack(pop)
|
||||
@@ -266,8 +266,8 @@ private:
|
||||
void EnsureDatabasePartition();
|
||||
|
||||
MiiDatabase database;
|
||||
bool updated_flag = false;
|
||||
bool is_test_mode_enabled = false;
|
||||
bool updated_flag{};
|
||||
bool is_test_mode_enabled{};
|
||||
};
|
||||
|
||||
}; // namespace Service::Mii
|
||||
|
||||
@@ -104,10 +104,12 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
|
||||
|
||||
ASSERT(object->status == nvmap::Object::Status::Allocated);
|
||||
|
||||
u64 size = static_cast<u64>(entry.pages) << 0x10;
|
||||
const u64 size = static_cast<u64>(entry.pages) << 0x10;
|
||||
ASSERT(size <= object->size);
|
||||
const u64 map_offset = static_cast<u64>(entry.map_offset) << 0x10;
|
||||
|
||||
GPUVAddr returned = gpu.MemoryManager().MapBufferEx(object->addr, offset, size);
|
||||
const GPUVAddr returned =
|
||||
gpu.MemoryManager().MapBufferEx(object->addr + map_offset, offset, size);
|
||||
ASSERT(returned == offset);
|
||||
}
|
||||
std::memcpy(output.data(), entries.data(), output.size());
|
||||
|
||||
@@ -62,7 +62,7 @@ private:
|
||||
u16_le flags;
|
||||
u16_le kind;
|
||||
u32_le nvmap_handle;
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u32_le map_offset;
|
||||
u32_le offset;
|
||||
u32_le pages;
|
||||
};
|
||||
|
||||
103
src/core/hle/service/time/clock_types.h
Normal file
103
src/core/hle/service/time/clock_types.h
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/uuid.h"
|
||||
#include "core/hle/service/time/errors.h"
|
||||
#include "core/hle/service/time/time_zone_types.h"
|
||||
|
||||
namespace Service::Time::Clock {
|
||||
|
||||
/// https://switchbrew.org/wiki/Glue_services#SteadyClockTimePoint
|
||||
struct SteadyClockTimePoint {
|
||||
s64 time_point;
|
||||
Common::UUID clock_source_id;
|
||||
|
||||
ResultCode GetSpanBetween(SteadyClockTimePoint other, s64& span) const {
|
||||
span = 0;
|
||||
|
||||
if (clock_source_id != other.clock_source_id) {
|
||||
return ERROR_TIME_MISMATCH;
|
||||
}
|
||||
|
||||
span = other.time_point - time_point;
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static SteadyClockTimePoint GetRandom() {
|
||||
return {0, Common::UUID::Generate()};
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");
|
||||
static_assert(std::is_trivially_copyable_v<SteadyClockTimePoint>,
|
||||
"SteadyClockTimePoint must be trivially copyable");
|
||||
|
||||
struct SteadyClockContext {
|
||||
u64 internal_offset;
|
||||
Common::UUID steady_time_point;
|
||||
};
|
||||
static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size");
|
||||
static_assert(std::is_trivially_copyable_v<SteadyClockContext>,
|
||||
"SteadyClockContext must be trivially copyable");
|
||||
|
||||
struct SystemClockContext {
|
||||
s64 offset;
|
||||
SteadyClockTimePoint steady_time_point;
|
||||
};
|
||||
static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext is incorrect size");
|
||||
static_assert(std::is_trivially_copyable_v<SystemClockContext>,
|
||||
"SystemClockContext must be trivially copyable");
|
||||
|
||||
/// https://switchbrew.org/wiki/Glue_services#TimeSpanType
|
||||
struct TimeSpanType {
|
||||
s64 nanoseconds{};
|
||||
static constexpr s64 ns_per_second{1000000000ULL};
|
||||
|
||||
s64 ToSeconds() const {
|
||||
return nanoseconds / ns_per_second;
|
||||
}
|
||||
|
||||
static TimeSpanType FromSeconds(s64 seconds) {
|
||||
return {seconds * ns_per_second};
|
||||
}
|
||||
|
||||
static TimeSpanType FromTicks(u64 ticks, u64 frequency) {
|
||||
return FromSeconds(static_cast<s64>(ticks) / static_cast<s64>(frequency));
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(TimeSpanType) == 8, "TimeSpanType is incorrect size");
|
||||
|
||||
struct ClockSnapshot {
|
||||
SystemClockContext user_context{};
|
||||
SystemClockContext network_context{};
|
||||
s64 user_time{};
|
||||
s64 network_time{};
|
||||
TimeZone::CalendarTime user_calendar_time{};
|
||||
TimeZone::CalendarTime network_calendar_time{};
|
||||
TimeZone::CalendarAdditionalInfo user_calendar_additional_time{};
|
||||
TimeZone::CalendarAdditionalInfo network_calendar_additional_time{};
|
||||
SteadyClockTimePoint steady_clock_time_point{};
|
||||
TimeZone::LocationName location_name{};
|
||||
u8 is_automatic_correction_enabled{};
|
||||
u8 type{};
|
||||
INSERT_PADDING_BYTES(0x2);
|
||||
|
||||
static ResultCode GetCurrentTime(s64& current_time,
|
||||
const SteadyClockTimePoint& steady_clock_time_point,
|
||||
const SystemClockContext& context) {
|
||||
if (steady_clock_time_point.clock_source_id != context.steady_time_point.clock_source_id) {
|
||||
current_time = 0;
|
||||
return ERROR_TIME_MISMATCH;
|
||||
}
|
||||
current_time = steady_clock_time_point.time_point + context.offset;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(ClockSnapshot) == 0xD0, "ClockSnapshot is incorrect size");
|
||||
|
||||
} // namespace Service::Time::Clock
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/time/system_clock_context_update_callback.h"
|
||||
|
||||
namespace Service::Time::Clock {
|
||||
|
||||
class EphemeralNetworkSystemClockContextWriter final : public SystemClockContextUpdateCallback {
|
||||
public:
|
||||
EphemeralNetworkSystemClockContextWriter() : SystemClockContextUpdateCallback{} {}
|
||||
};
|
||||
|
||||
} // namespace Service::Time::Clock
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/time/system_clock_core.h"
|
||||
|
||||
namespace Service::Time::Clock {
|
||||
|
||||
class EphemeralNetworkSystemClockCore final : public SystemClockCore {
|
||||
public:
|
||||
explicit EphemeralNetworkSystemClockCore(SteadyClockCore& steady_clock_core)
|
||||
: SystemClockCore{steady_clock_core} {}
|
||||
};
|
||||
|
||||
} // namespace Service::Time::Clock
|
||||
22
src/core/hle/service/time/errors.h
Normal file
22
src/core/hle/service/time/errors.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Service::Time {
|
||||
|
||||
constexpr ResultCode ERROR_PERMISSION_DENIED{ErrorModule::Time, 1};
|
||||
constexpr ResultCode ERROR_TIME_MISMATCH{ErrorModule::Time, 102};
|
||||
constexpr ResultCode ERROR_UNINITIALIZED_CLOCK{ErrorModule::Time, 103};
|
||||
constexpr ResultCode ERROR_TIME_NOT_FOUND{ErrorModule::Time, 200};
|
||||
constexpr ResultCode ERROR_OVERFLOW{ErrorModule::Time, 201};
|
||||
constexpr ResultCode ERROR_LOCATION_NAME_TOO_LONG{ErrorModule::Time, 801};
|
||||
constexpr ResultCode ERROR_OUT_OF_RANGE{ErrorModule::Time, 902};
|
||||
constexpr ResultCode ERROR_TIME_ZONE_CONVERSION_FAILED{ErrorModule::Time, 903};
|
||||
constexpr ResultCode ERROR_TIME_ZONE_NOT_FOUND{ErrorModule::Time, 989};
|
||||
constexpr ResultCode ERROR_NOT_IMPLEMENTED{ErrorModule::Time, 990};
|
||||
|
||||
} // namespace Service::Time
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
|
||||
namespace Service::Time {
|
||||
|
||||
Time::Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory,
|
||||
Core::System& system, const char* name)
|
||||
: Module::Interface(std::move(time), std::move(shared_memory), system, name) {
|
||||
Time::Time(std::shared_ptr<Module> module, Core::System& system, const char* name)
|
||||
: Module::Interface(std::move(module), system, name) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"},
|
||||
@@ -22,15 +21,15 @@ Time::Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_me
|
||||
{31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"},
|
||||
{50, nullptr, "SetStandardSteadyClockInternalOffset"},
|
||||
{51, nullptr, "GetStandardSteadyClockRtcValue"},
|
||||
{100, &Time::IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
|
||||
{101, &Time::SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
|
||||
{100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
|
||||
{101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
|
||||
{102, nullptr, "GetStandardUserSystemClockInitialYear"},
|
||||
{200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"},
|
||||
{200, &Time::IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"},
|
||||
{201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
|
||||
{300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"},
|
||||
{300, &Time::CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"},
|
||||
{400, &Time::GetClockSnapshot, "GetClockSnapshot"},
|
||||
{401, nullptr, "GetClockSnapshotFromSystemClockContext"},
|
||||
{500, &Time::CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"},
|
||||
{401, &Time::GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"},
|
||||
{500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"},
|
||||
{501, nullptr, "CalculateSpanBetween"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -6,14 +6,15 @@
|
||||
|
||||
#include "core/hle/service/time/time.h"
|
||||
|
||||
namespace Service::Time {
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
class SharedMemory;
|
||||
namespace Service::Time {
|
||||
|
||||
class Time final : public Module::Interface {
|
||||
public:
|
||||
explicit Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory,
|
||||
Core::System& system, const char* name);
|
||||
explicit Time(std::shared_ptr<Module> time, Core::System& system, const char* name);
|
||||
~Time() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/time/errors.h"
|
||||
#include "core/hle/service/time/system_clock_context_update_callback.h"
|
||||
#include "core/hle/service/time/time_sharedmemory.h"
|
||||
|
||||
namespace Service::Time::Clock {
|
||||
|
||||
class LocalSystemClockContextWriter final : public SystemClockContextUpdateCallback {
|
||||
public:
|
||||
explicit LocalSystemClockContextWriter(SharedMemory& shared_memory)
|
||||
: SystemClockContextUpdateCallback{}, shared_memory{shared_memory} {}
|
||||
|
||||
protected:
|
||||
ResultCode Update() override {
|
||||
shared_memory.UpdateLocalSystemClockContext(context);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
private:
|
||||
SharedMemory& shared_memory;
|
||||
};
|
||||
|
||||
} // namespace Service::Time::Clock
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/time/errors.h"
|
||||
#include "core/hle/service/time/system_clock_context_update_callback.h"
|
||||
#include "core/hle/service/time/time_sharedmemory.h"
|
||||
|
||||
namespace Service::Time::Clock {
|
||||
|
||||
class NetworkSystemClockContextWriter final : public SystemClockContextUpdateCallback {
|
||||
public:
|
||||
explicit NetworkSystemClockContextWriter(SharedMemory& shared_memory)
|
||||
: SystemClockContextUpdateCallback{}, shared_memory{shared_memory} {}
|
||||
|
||||
protected:
|
||||
ResultCode Update() override {
|
||||
shared_memory.UpdateNetworkSystemClockContext(context);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
private:
|
||||
SharedMemory& shared_memory;
|
||||
};
|
||||
|
||||
} // namespace Service::Time::Clock
|
||||
17
src/core/hle/service/time/standard_local_system_clock_core.h
Normal file
17
src/core/hle/service/time/standard_local_system_clock_core.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/time/system_clock_core.h"
|
||||
|
||||
namespace Service::Time::Clock {
|
||||
|
||||
class StandardLocalSystemClockCore final : public SystemClockCore {
|
||||
public:
|
||||
explicit StandardLocalSystemClockCore(SteadyClockCore& steady_clock_core)
|
||||
: SystemClockCore{steady_clock_core} {}
|
||||
};
|
||||
|
||||
} // namespace Service::Time::Clock
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/time/clock_types.h"
|
||||
#include "core/hle/service/time/steady_clock_core.h"
|
||||
#include "core/hle/service/time/system_clock_core.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Time::Clock {
|
||||
|
||||
class StandardNetworkSystemClockCore final : public SystemClockCore {
|
||||
public:
|
||||
explicit StandardNetworkSystemClockCore(SteadyClockCore& steady_clock_core)
|
||||
: SystemClockCore{steady_clock_core} {}
|
||||
|
||||
void SetStandardNetworkClockSufficientAccuracy(TimeSpanType value) {
|
||||
standard_network_clock_sufficient_accuracy = value;
|
||||
}
|
||||
|
||||
bool IsStandardNetworkSystemClockAccuracySufficient(Core::System& system) {
|
||||
SystemClockContext context{};
|
||||
if (GetClockContext(system, context) != RESULT_SUCCESS) {
|
||||
return {};
|
||||
}
|
||||
|
||||
s64 span{};
|
||||
if (context.steady_time_point.GetSpanBetween(
|
||||
GetSteadyClockCore().GetCurrentTimePoint(system), span) != RESULT_SUCCESS) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return TimeSpanType{span}.nanoseconds <
|
||||
standard_network_clock_sufficient_accuracy.nanoseconds;
|
||||
}
|
||||
|
||||
private:
|
||||
TimeSpanType standard_network_clock_sufficient_accuracy{};
|
||||
};
|
||||
|
||||
} // namespace Service::Time::Clock
|
||||
26
src/core/hle/service/time/standard_steady_clock_core.cpp
Normal file
26
src/core/hle/service/time/standard_steady_clock_core.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/hle/service/time/standard_steady_clock_core.h"
|
||||
|
||||
namespace Service::Time::Clock {
|
||||
|
||||
TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) {
|
||||
const TimeSpanType ticks_time_span{TimeSpanType::FromTicks(
|
||||
Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()),
|
||||
Core::Timing::CNTFREQ)};
|
||||
TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds};
|
||||
|
||||
if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) {
|
||||
raw_time_point.nanoseconds = cached_raw_time_point.nanoseconds;
|
||||
}
|
||||
|
||||
cached_raw_time_point = raw_time_point;
|
||||
return raw_time_point;
|
||||
}
|
||||
|
||||
} // namespace Service::Time::Clock
|
||||
42
src/core/hle/service/time/standard_steady_clock_core.h
Normal file
42
src/core/hle/service/time/standard_steady_clock_core.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/time/clock_types.h"
|
||||
#include "core/hle/service/time/steady_clock_core.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Time::Clock {
|
||||
|
||||
class StandardSteadyClockCore final : public SteadyClockCore {
|
||||
public:
|
||||
SteadyClockTimePoint GetTimePoint(Core::System& system) override {
|
||||
return {GetCurrentRawTimePoint(system).ToSeconds(), GetClockSourceId()};
|
||||
}
|
||||
|
||||
TimeSpanType GetInternalOffset() const override {
|
||||
return internal_offset;
|
||||
}
|
||||
|
||||
void SetInternalOffset(TimeSpanType value) override {
|
||||
internal_offset = value;
|
||||
}
|
||||
|
||||
TimeSpanType GetCurrentRawTimePoint(Core::System& system) override;
|
||||
|
||||
void SetSetupValue(TimeSpanType value) {
|
||||
setup_value = value;
|
||||
}
|
||||
|
||||
private:
|
||||
TimeSpanType setup_value{};
|
||||
TimeSpanType internal_offset{};
|
||||
TimeSpanType cached_raw_time_point{};
|
||||
};
|
||||
|
||||
} // namespace Service::Time::Clock
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/time/standard_local_system_clock_core.h"
|
||||
#include "core/hle/service/time/standard_network_system_clock_core.h"
|
||||
#include "core/hle/service/time/standard_user_system_clock_core.h"
|
||||
|
||||
namespace Service::Time::Clock {
|
||||
|
||||
StandardUserSystemClockCore::StandardUserSystemClockCore(
|
||||
StandardLocalSystemClockCore& local_system_clock_core,
|
||||
StandardNetworkSystemClockCore& network_system_clock_core, Core::System& system)
|
||||
: SystemClockCore(local_system_clock_core.GetSteadyClockCore()),
|
||||
local_system_clock_core{local_system_clock_core},
|
||||
network_system_clock_core{network_system_clock_core}, auto_correction_enabled{},
|
||||
auto_correction_time{SteadyClockTimePoint::GetRandom()},
|
||||
auto_correction_event{Kernel::WritableEvent::CreateEventPair(
|
||||
system.Kernel(), "StandardUserSystemClockCore:AutoCorrectionEvent")} {}
|
||||
|
||||
ResultCode StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::System& system,
|
||||
bool value) {
|
||||
if (const ResultCode result{ApplyAutomaticCorrection(system, value)};
|
||||
result != RESULT_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
auto_correction_enabled = value;
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode StandardUserSystemClockCore::GetClockContext(Core::System& system,
|
||||
SystemClockContext& context) const {
|
||||
if (const ResultCode result{ApplyAutomaticCorrection(system, false)};
|
||||
result != RESULT_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return local_system_clock_core.GetClockContext(system, context);
|
||||
}
|
||||
|
||||
ResultCode StandardUserSystemClockCore::Flush(const SystemClockContext& context) {
|
||||
UNREACHABLE();
|
||||
return ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ResultCode StandardUserSystemClockCore::SetClockContext(const SystemClockContext& context) {
|
||||
UNREACHABLE();
|
||||
return ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ResultCode StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& system,
|
||||
bool value) const {
|
||||
if (auto_correction_enabled == value) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
if (!network_system_clock_core.IsClockSetup(system)) {
|
||||
return ERROR_UNINITIALIZED_CLOCK;
|
||||
}
|
||||
|
||||
SystemClockContext context{};
|
||||
if (const ResultCode result{network_system_clock_core.GetClockContext(system, context)};
|
||||
result != RESULT_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
local_system_clock_core.SetClockContext(context);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace Service::Time::Clock
|
||||
57
src/core/hle/service/time/standard_user_system_clock_core.h
Normal file
57
src/core/hle/service/time/standard_user_system_clock_core.h
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/time/clock_types.h"
|
||||
#include "core/hle/service/time/system_clock_core.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Time::Clock {
|
||||
|
||||
class StandardLocalSystemClockCore;
|
||||
class StandardNetworkSystemClockCore;
|
||||
|
||||
class StandardUserSystemClockCore final : public SystemClockCore {
|
||||
public:
|
||||
StandardUserSystemClockCore(StandardLocalSystemClockCore& local_system_clock_core,
|
||||
StandardNetworkSystemClockCore& network_system_clock_core,
|
||||
Core::System& system);
|
||||
|
||||
ResultCode SetAutomaticCorrectionEnabled(Core::System& system, bool value);
|
||||
|
||||
ResultCode GetClockContext(Core::System& system, SystemClockContext& context) const override;
|
||||
|
||||
bool IsAutomaticCorrectionEnabled() const {
|
||||
return auto_correction_enabled;
|
||||
}
|
||||
|
||||
void SetAutomaticCorrectionUpdatedTime(SteadyClockTimePoint steady_clock_time_point) {
|
||||
auto_correction_time = steady_clock_time_point;
|
||||
}
|
||||
|
||||
protected:
|
||||
ResultCode Flush(const SystemClockContext& context) override;
|
||||
|
||||
ResultCode SetClockContext(const SystemClockContext&) override;
|
||||
|
||||
ResultCode ApplyAutomaticCorrection(Core::System& system, bool value) const;
|
||||
|
||||
const SteadyClockTimePoint& GetAutomaticCorrectionUpdatedTime() const {
|
||||
return auto_correction_time;
|
||||
}
|
||||
|
||||
private:
|
||||
StandardLocalSystemClockCore& local_system_clock_core;
|
||||
StandardNetworkSystemClockCore& network_system_clock_core;
|
||||
bool auto_correction_enabled{};
|
||||
SteadyClockTimePoint auto_correction_time;
|
||||
Kernel::EventPair auto_correction_event;
|
||||
};
|
||||
|
||||
} // namespace Service::Time::Clock
|
||||
55
src/core/hle/service/time/steady_clock_core.h
Normal file
55
src/core/hle/service/time/steady_clock_core.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/uuid.h"
|
||||
#include "core/hle/service/time/clock_types.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Time::Clock {
|
||||
|
||||
class SteadyClockCore {
|
||||
public:
|
||||
SteadyClockCore() = default;
|
||||
|
||||
const Common::UUID& GetClockSourceId() const {
|
||||
return clock_source_id;
|
||||
}
|
||||
|
||||
void SetClockSourceId(const Common::UUID& value) {
|
||||
clock_source_id = value;
|
||||
}
|
||||
|
||||
virtual TimeSpanType GetInternalOffset() const = 0;
|
||||
|
||||
virtual void SetInternalOffset(TimeSpanType internal_offset) = 0;
|
||||
|
||||
virtual SteadyClockTimePoint GetTimePoint(Core::System& system) = 0;
|
||||
|
||||
virtual TimeSpanType GetCurrentRawTimePoint(Core::System& system) = 0;
|
||||
|
||||
SteadyClockTimePoint GetCurrentTimePoint(Core::System& system) {
|
||||
SteadyClockTimePoint result{GetTimePoint(system)};
|
||||
result.time_point += GetInternalOffset().ToSeconds();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IsInitialized() const {
|
||||
return is_initialized;
|
||||
}
|
||||
|
||||
void MarkAsInitialized() {
|
||||
is_initialized = true;
|
||||
}
|
||||
|
||||
private:
|
||||
Common::UUID clock_source_id{Common::UUID::Generate()};
|
||||
bool is_initialized{};
|
||||
};
|
||||
|
||||
} // namespace Service::Time::Clock
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/time/errors.h"
|
||||
#include "core/hle/service/time/system_clock_context_update_callback.h"
|
||||
|
||||
namespace Service::Time::Clock {
|
||||
|
||||
SystemClockContextUpdateCallback::SystemClockContextUpdateCallback() = default;
|
||||
SystemClockContextUpdateCallback::~SystemClockContextUpdateCallback() = default;
|
||||
|
||||
bool SystemClockContextUpdateCallback::NeedUpdate(const SystemClockContext& value) const {
|
||||
if (has_context) {
|
||||
return context.offset != value.offset ||
|
||||
context.steady_time_point.clock_source_id != value.steady_time_point.clock_source_id;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SystemClockContextUpdateCallback::RegisterOperationEvent(
|
||||
std::shared_ptr<Kernel::WritableEvent>&& writable_event) {
|
||||
operation_event_list.emplace_back(std::move(writable_event));
|
||||
}
|
||||
|
||||
void SystemClockContextUpdateCallback::BroadcastOperationEvent() {
|
||||
for (const auto& writable_event : operation_event_list) {
|
||||
writable_event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
ResultCode SystemClockContextUpdateCallback::Update(const SystemClockContext& value) {
|
||||
ResultCode result{RESULT_SUCCESS};
|
||||
|
||||
if (NeedUpdate(value)) {
|
||||
context = value;
|
||||
has_context = true;
|
||||
|
||||
result = Update();
|
||||
|
||||
if (result == RESULT_SUCCESS) {
|
||||
BroadcastOperationEvent();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ResultCode SystemClockContextUpdateCallback::Update() {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace Service::Time::Clock
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "core/hle/service/time/clock_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
class WritableEvent;
|
||||
}
|
||||
|
||||
namespace Service::Time::Clock {
|
||||
|
||||
// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783).
|
||||
// This code was released under public domain.
|
||||
|
||||
class SystemClockContextUpdateCallback {
|
||||
public:
|
||||
SystemClockContextUpdateCallback();
|
||||
~SystemClockContextUpdateCallback();
|
||||
|
||||
bool NeedUpdate(const SystemClockContext& value) const;
|
||||
|
||||
void RegisterOperationEvent(std::shared_ptr<Kernel::WritableEvent>&& writable_event);
|
||||
|
||||
void BroadcastOperationEvent();
|
||||
|
||||
ResultCode Update(const SystemClockContext& value);
|
||||
|
||||
protected:
|
||||
virtual ResultCode Update();
|
||||
|
||||
SystemClockContext context{};
|
||||
|
||||
private:
|
||||
bool has_context{};
|
||||
std::vector<std::shared_ptr<Kernel::WritableEvent>> operation_event_list;
|
||||
};
|
||||
|
||||
} // namespace Service::Time::Clock
|
||||
72
src/core/hle/service/time/system_clock_core.cpp
Normal file
72
src/core/hle/service/time/system_clock_core.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/time/steady_clock_core.h"
|
||||
#include "core/hle/service/time/system_clock_context_update_callback.h"
|
||||
#include "core/hle/service/time/system_clock_core.h"
|
||||
|
||||
namespace Service::Time::Clock {
|
||||
|
||||
SystemClockCore::SystemClockCore(SteadyClockCore& steady_clock_core)
|
||||
: steady_clock_core{steady_clock_core}, is_initialized{} {
|
||||
context.steady_time_point.clock_source_id = steady_clock_core.GetClockSourceId();
|
||||
}
|
||||
|
||||
SystemClockCore ::~SystemClockCore() = default;
|
||||
|
||||
ResultCode SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time) const {
|
||||
posix_time = 0;
|
||||
|
||||
const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
|
||||
|
||||
SystemClockContext clock_context{};
|
||||
if (const ResultCode result{GetClockContext(system, clock_context)}; result != RESULT_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (current_time_point.clock_source_id != clock_context.steady_time_point.clock_source_id) {
|
||||
return ERROR_TIME_MISMATCH;
|
||||
}
|
||||
|
||||
posix_time = clock_context.offset + current_time_point.time_point;
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode SystemClockCore::SetCurrentTime(Core::System& system, s64 posix_time) {
|
||||
const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
|
||||
const SystemClockContext clock_context{posix_time - current_time_point.time_point,
|
||||
current_time_point};
|
||||
|
||||
if (const ResultCode result{SetClockContext(clock_context)}; result != RESULT_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
return Flush(clock_context);
|
||||
}
|
||||
|
||||
ResultCode SystemClockCore::Flush(const SystemClockContext& context) {
|
||||
if (!system_clock_context_update_callback) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
return system_clock_context_update_callback->Update(context);
|
||||
}
|
||||
|
||||
ResultCode SystemClockCore::SetSystemClockContext(const SystemClockContext& context) {
|
||||
if (const ResultCode result{SetClockContext(context)}; result != RESULT_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
return Flush(context);
|
||||
}
|
||||
|
||||
bool SystemClockCore::IsClockSetup(Core::System& system) const {
|
||||
SystemClockContext value{};
|
||||
if (GetClockContext(system, value) == RESULT_SUCCESS) {
|
||||
const SteadyClockTimePoint steady_clock_time_point{
|
||||
steady_clock_core.GetCurrentTimePoint(system)};
|
||||
return steady_clock_time_point.clock_source_id == value.steady_time_point.clock_source_id;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace Service::Time::Clock
|
||||
71
src/core/hle/service/time/system_clock_core.h
Normal file
71
src/core/hle/service/time/system_clock_core.h
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/time/clock_types.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Time::Clock {
|
||||
|
||||
class SteadyClockCore;
|
||||
class SystemClockContextUpdateCallback;
|
||||
|
||||
// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783).
|
||||
// This code was released under public domain.
|
||||
|
||||
class SystemClockCore {
|
||||
public:
|
||||
explicit SystemClockCore(SteadyClockCore& steady_clock_core);
|
||||
~SystemClockCore();
|
||||
|
||||
SteadyClockCore& GetSteadyClockCore() const {
|
||||
return steady_clock_core;
|
||||
}
|
||||
|
||||
ResultCode GetCurrentTime(Core::System& system, s64& posix_time) const;
|
||||
|
||||
ResultCode SetCurrentTime(Core::System& system, s64 posix_time);
|
||||
|
||||
virtual ResultCode GetClockContext([[maybe_unused]] Core::System& system,
|
||||
SystemClockContext& value) const {
|
||||
value = context;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
virtual ResultCode SetClockContext(const SystemClockContext& value) {
|
||||
context = value;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
virtual ResultCode Flush(const SystemClockContext& context);
|
||||
|
||||
void SetUpdateCallbackInstance(std::shared_ptr<SystemClockContextUpdateCallback> callback) {
|
||||
system_clock_context_update_callback = std::move(callback);
|
||||
}
|
||||
|
||||
ResultCode SetSystemClockContext(const SystemClockContext& context);
|
||||
|
||||
bool IsInitialized() const {
|
||||
return is_initialized;
|
||||
}
|
||||
|
||||
void MarkAsInitialized() {
|
||||
is_initialized = true;
|
||||
}
|
||||
|
||||
bool IsClockSetup(Core::System& system) const;
|
||||
|
||||
private:
|
||||
SteadyClockCore& steady_clock_core;
|
||||
SystemClockContext context{};
|
||||
bool is_initialized{};
|
||||
std::shared_ptr<SystemClockContextUpdateCallback> system_clock_context_update_callback;
|
||||
};
|
||||
|
||||
} // namespace Service::Time::Clock
|
||||
24
src/core/hle/service/time/tick_based_steady_clock_core.cpp
Normal file
24
src/core/hle/service/time/tick_based_steady_clock_core.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2020 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/hle/service/time/tick_based_steady_clock_core.h"
|
||||
|
||||
namespace Service::Time::Clock {
|
||||
|
||||
SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) {
|
||||
const TimeSpanType ticks_time_span{TimeSpanType::FromTicks(
|
||||
Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()),
|
||||
Core::Timing::CNTFREQ)};
|
||||
|
||||
return {ticks_time_span.ToSeconds(), GetClockSourceId()};
|
||||
}
|
||||
|
||||
TimeSpanType TickBasedSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) {
|
||||
return TimeSpanType::FromSeconds(GetTimePoint(system).time_point);
|
||||
}
|
||||
|
||||
} // namespace Service::Time::Clock
|
||||
29
src/core/hle/service/time/tick_based_steady_clock_core.h
Normal file
29
src/core/hle/service/time/tick_based_steady_clock_core.h
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2020 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/time/clock_types.h"
|
||||
#include "core/hle/service/time/steady_clock_core.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Time::Clock {
|
||||
|
||||
class TickBasedSteadyClockCore final : public SteadyClockCore {
|
||||
public:
|
||||
TimeSpanType GetInternalOffset() const override {
|
||||
return {};
|
||||
}
|
||||
|
||||
void SetInternalOffset(TimeSpanType internal_offset) override {}
|
||||
|
||||
SteadyClockTimePoint GetTimePoint(Core::System& system) override;
|
||||
|
||||
TimeSpanType GetCurrentRawTimePoint(Core::System& system) override;
|
||||
};
|
||||
|
||||
} // namespace Service::Time::Clock
|
||||
@@ -1,9 +1,7 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
@@ -11,429 +9,321 @@
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/service/time/interface.h"
|
||||
#include "core/hle/service/time/time.h"
|
||||
#include "core/hle/service/time/time_sharedmemory.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/hle/service/time/time_zone_service.h"
|
||||
|
||||
namespace Service::Time {
|
||||
|
||||
static std::chrono::seconds GetSecondsSinceEpoch() {
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()) +
|
||||
Settings::values.custom_rtc_differential;
|
||||
}
|
||||
|
||||
static void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time,
|
||||
CalendarAdditionalInfo& additional_info,
|
||||
[[maybe_unused]] const TimeZoneRule& /*rule*/) {
|
||||
const std::time_t time(posix_time);
|
||||
const std::tm* tm = std::localtime(&time);
|
||||
if (tm == nullptr) {
|
||||
calendar_time = {};
|
||||
additional_info = {};
|
||||
return;
|
||||
}
|
||||
calendar_time.year = static_cast<u16_le>(tm->tm_year + 1900);
|
||||
calendar_time.month = static_cast<u8>(tm->tm_mon + 1);
|
||||
calendar_time.day = static_cast<u8>(tm->tm_mday);
|
||||
calendar_time.hour = static_cast<u8>(tm->tm_hour);
|
||||
calendar_time.minute = static_cast<u8>(tm->tm_min);
|
||||
calendar_time.second = static_cast<u8>(tm->tm_sec);
|
||||
|
||||
additional_info.day_of_week = tm->tm_wday;
|
||||
additional_info.day_of_year = tm->tm_yday;
|
||||
std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC"));
|
||||
additional_info.utc_offset = 0;
|
||||
}
|
||||
|
||||
static u64 CalendarToPosix(const CalendarTime& calendar_time,
|
||||
[[maybe_unused]] const TimeZoneRule& /*rule*/) {
|
||||
std::tm time{};
|
||||
time.tm_year = calendar_time.year - 1900;
|
||||
time.tm_mon = calendar_time.month - 1;
|
||||
time.tm_mday = calendar_time.day;
|
||||
|
||||
time.tm_hour = calendar_time.hour;
|
||||
time.tm_min = calendar_time.minute;
|
||||
time.tm_sec = calendar_time.second;
|
||||
|
||||
std::time_t epoch_time = std::mktime(&time);
|
||||
return static_cast<u64>(epoch_time);
|
||||
}
|
||||
|
||||
enum class ClockContextType {
|
||||
StandardSteady,
|
||||
StandardUserSystem,
|
||||
StandardNetworkSystem,
|
||||
StandardLocalSystem,
|
||||
};
|
||||
|
||||
class ISystemClock final : public ServiceFramework<ISystemClock> {
|
||||
public:
|
||||
ISystemClock(std::shared_ptr<Service::Time::SharedMemory> shared_memory,
|
||||
ClockContextType clock_type)
|
||||
: ServiceFramework("ISystemClock"), shared_memory(shared_memory), clock_type(clock_type) {
|
||||
ISystemClock(Clock::SystemClockCore& clock_core)
|
||||
: ServiceFramework("ISystemClock"), clock_core{clock_core} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ISystemClock::GetCurrentTime, "GetCurrentTime"},
|
||||
{1, nullptr, "SetCurrentTime"},
|
||||
{2, &ISystemClock::GetSystemClockContext, "GetSystemClockContext"},
|
||||
{2, &ISystemClock::GetSystemClockContext, "GetSystemClockContext"},
|
||||
{3, nullptr, "SetSystemClockContext"},
|
||||
{4, nullptr, "GetOperationEventReadableHandle"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
UpdateSharedMemoryContext(system_clock_context);
|
||||
}
|
||||
|
||||
private:
|
||||
void GetCurrentTime(Kernel::HLERequestContext& ctx) {
|
||||
const s64 time_since_epoch{GetSecondsSinceEpoch().count()};
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
if (!clock_core.IsInitialized()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_UNINITIALIZED_CLOCK);
|
||||
return;
|
||||
}
|
||||
|
||||
s64 posix_time{};
|
||||
if (const ResultCode result{
|
||||
clock_core.GetCurrentTime(Core::System::GetInstance(), posix_time)};
|
||||
result != RESULT_SUCCESS) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(time_since_epoch);
|
||||
rb.Push<s64>(posix_time);
|
||||
}
|
||||
|
||||
void GetSystemClockContext(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Time, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
// TODO(ogniK): This should be updated periodically however since we have it stubbed we'll
|
||||
// only update when we get a new context
|
||||
UpdateSharedMemoryContext(system_clock_context);
|
||||
if (!clock_core.IsInitialized()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_UNINITIALIZED_CLOCK);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, (sizeof(SystemClockContext) / 4) + 2};
|
||||
Clock::SystemClockContext system_clock_context{};
|
||||
if (const ResultCode result{
|
||||
clock_core.GetClockContext(Core::System::GetInstance(), system_clock_context)};
|
||||
result != RESULT_SUCCESS) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, sizeof(Clock::SystemClockContext) / 4 + 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(system_clock_context);
|
||||
}
|
||||
|
||||
void UpdateSharedMemoryContext(const SystemClockContext& clock_context) {
|
||||
switch (clock_type) {
|
||||
case ClockContextType::StandardLocalSystem:
|
||||
shared_memory->SetStandardLocalSystemClockContext(clock_context);
|
||||
break;
|
||||
case ClockContextType::StandardNetworkSystem:
|
||||
shared_memory->SetStandardNetworkSystemClockContext(clock_context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SystemClockContext system_clock_context{};
|
||||
std::shared_ptr<Service::Time::SharedMemory> shared_memory;
|
||||
ClockContextType clock_type;
|
||||
Clock::SystemClockCore& clock_core;
|
||||
};
|
||||
|
||||
class ISteadyClock final : public ServiceFramework<ISteadyClock> {
|
||||
public:
|
||||
ISteadyClock(std::shared_ptr<SharedMemory> shared_memory, Core::System& system)
|
||||
: ServiceFramework("ISteadyClock"), shared_memory(shared_memory), system(system) {
|
||||
ISteadyClock(Clock::SteadyClockCore& clock_core)
|
||||
: ServiceFramework("ISteadyClock"), clock_core{clock_core} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
shared_memory->SetStandardSteadyClockTimepoint(GetCurrentTimePoint());
|
||||
}
|
||||
|
||||
private:
|
||||
void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
const auto time_point = GetCurrentTimePoint();
|
||||
// TODO(ogniK): This should be updated periodically
|
||||
shared_memory->SetStandardSteadyClockTimepoint(time_point);
|
||||
if (!clock_core.IsInitialized()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_UNINITIALIZED_CLOCK);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2};
|
||||
const Clock::SteadyClockTimePoint time_point{
|
||||
clock_core.GetCurrentTimePoint(Core::System::GetInstance())};
|
||||
IPC::ResponseBuilder rb{ctx, (sizeof(Clock::SteadyClockTimePoint) / 4) + 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(time_point);
|
||||
}
|
||||
|
||||
SteadyClockTimePoint GetCurrentTimePoint() const {
|
||||
const auto& core_timing = system.CoreTiming();
|
||||
const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
|
||||
return {static_cast<u64_le>(ms.count() / 1000), {}};
|
||||
}
|
||||
|
||||
std::shared_ptr<SharedMemory> shared_memory;
|
||||
Core::System& system;
|
||||
Clock::SteadyClockCore& clock_core;
|
||||
};
|
||||
|
||||
class ITimeZoneService final : public ServiceFramework<ITimeZoneService> {
|
||||
public:
|
||||
ITimeZoneService() : ServiceFramework("ITimeZoneService") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ITimeZoneService::GetDeviceLocationName, "GetDeviceLocationName"},
|
||||
{1, nullptr, "SetDeviceLocationName"},
|
||||
{2, &ITimeZoneService::GetTotalLocationNameCount, "GetTotalLocationNameCount"},
|
||||
{3, nullptr, "LoadLocationNameList"},
|
||||
{4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"},
|
||||
{5, nullptr, "GetTimeZoneRuleVersion"},
|
||||
{6, nullptr, "GetDeviceLocationNameAndUpdatedTime"},
|
||||
{7, nullptr, "SetDeviceLocationNameWithTimeZoneRule"},
|
||||
{8, nullptr, "ParseTimeZoneBinary"},
|
||||
{20, nullptr, "GetDeviceLocationNameOperationEventReadableHandle"},
|
||||
{100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
|
||||
{101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
|
||||
{201, &ITimeZoneService::ToPosixTime, "ToPosixTime"},
|
||||
{202, &ITimeZoneService::ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
|
||||
};
|
||||
// clang-format on
|
||||
ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
|
||||
Kernel::Thread* thread, Clock::SystemClockContext user_context,
|
||||
Clock::SystemClockContext network_context, u8 type, Clock::ClockSnapshot& clock_snapshot) {
|
||||
|
||||
RegisterHandlers(functions);
|
||||
auto& time_manager{module->GetTimeManager()};
|
||||
|
||||
clock_snapshot.is_automatic_correction_enabled =
|
||||
time_manager.GetStandardUserSystemClockCore().IsAutomaticCorrectionEnabled();
|
||||
clock_snapshot.user_context = user_context;
|
||||
clock_snapshot.network_context = network_context;
|
||||
|
||||
if (const ResultCode result{
|
||||
time_manager.GetTimeZoneContentManager().GetTimeZoneManager().GetDeviceLocationName(
|
||||
clock_snapshot.location_name)};
|
||||
result != RESULT_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
LocationName location_name{"UTC"};
|
||||
TimeZoneRule my_time_zone_rule{};
|
||||
|
||||
void GetDeviceLocationName(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, (sizeof(LocationName) / 4) + 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(location_name);
|
||||
const auto current_time_point{
|
||||
time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(Core::System::GetInstance())};
|
||||
if (const ResultCode result{Clock::ClockSnapshot::GetCurrentTime(
|
||||
clock_snapshot.user_time, current_time_point, clock_snapshot.user_context)};
|
||||
result != RESULT_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void GetTotalLocationNameCount(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Time, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(0);
|
||||
TimeZone::CalendarInfo userCalendarInfo{};
|
||||
if (const ResultCode result{
|
||||
time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules(
|
||||
clock_snapshot.user_time, userCalendarInfo)};
|
||||
result != RESULT_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void LoadTimeZoneRule(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Time, "(STUBBED) called");
|
||||
clock_snapshot.user_calendar_time = userCalendarInfo.time;
|
||||
clock_snapshot.user_calendar_additional_time = userCalendarInfo.additiona_info;
|
||||
|
||||
ctx.WriteBuffer(&my_time_zone_rule, sizeof(TimeZoneRule));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
if (Clock::ClockSnapshot::GetCurrentTime(clock_snapshot.network_time, current_time_point,
|
||||
clock_snapshot.network_context) != RESULT_SUCCESS) {
|
||||
clock_snapshot.network_time = 0;
|
||||
}
|
||||
|
||||
void ToCalendarTime(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 posix_time = rp.Pop<u64>();
|
||||
LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time);
|
||||
|
||||
TimeZoneRule time_zone_rule{};
|
||||
auto buffer = ctx.ReadBuffer();
|
||||
std::memcpy(&time_zone_rule, buffer.data(), buffer.size());
|
||||
|
||||
CalendarTime calendar_time{2018, 1, 1, 0, 0, 0};
|
||||
CalendarAdditionalInfo additional_info{};
|
||||
|
||||
PosixToCalendar(posix_time, calendar_time, additional_info, time_zone_rule);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 10};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(calendar_time);
|
||||
rb.PushRaw(additional_info);
|
||||
TimeZone::CalendarInfo networkCalendarInfo{};
|
||||
if (const ResultCode result{
|
||||
time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules(
|
||||
clock_snapshot.network_time, networkCalendarInfo)};
|
||||
result != RESULT_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 posix_time = rp.Pop<u64>();
|
||||
LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time);
|
||||
clock_snapshot.network_calendar_time = networkCalendarInfo.time;
|
||||
clock_snapshot.network_calendar_additional_time = networkCalendarInfo.additiona_info;
|
||||
clock_snapshot.type = type;
|
||||
|
||||
CalendarTime calendar_time{2018, 1, 1, 0, 0, 0};
|
||||
CalendarAdditionalInfo additional_info{};
|
||||
|
||||
PosixToCalendar(posix_time, calendar_time, additional_info, my_time_zone_rule);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 10};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(calendar_time);
|
||||
rb.PushRaw(additional_info);
|
||||
}
|
||||
|
||||
void ToPosixTime(Kernel::HLERequestContext& ctx) {
|
||||
// TODO(ogniK): Figure out how to handle multiple times
|
||||
LOG_WARNING(Service_Time, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto calendar_time = rp.PopRaw<CalendarTime>();
|
||||
auto posix_time = CalendarToPosix(calendar_time, {});
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<u32>(1); // Amount of times we're returning
|
||||
ctx.WriteBuffer(&posix_time, sizeof(u64));
|
||||
}
|
||||
|
||||
void ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Time, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto calendar_time = rp.PopRaw<CalendarTime>();
|
||||
auto posix_time = CalendarToPosix(calendar_time, {});
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<u32>(1); // Amount of times we're returning
|
||||
ctx.WriteBuffer(&posix_time, sizeof(u64));
|
||||
}
|
||||
};
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardUserSystem);
|
||||
rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardUserSystemClockCore());
|
||||
}
|
||||
|
||||
void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardNetworkSystem);
|
||||
rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardNetworkSystemClockCore());
|
||||
}
|
||||
|
||||
void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISteadyClock>(shared_memory, system);
|
||||
rb.PushIpcInterface<ISteadyClock>(module->GetTimeManager().GetStandardSteadyClockCore());
|
||||
}
|
||||
|
||||
void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ITimeZoneService>();
|
||||
rb.PushIpcInterface<ITimeZoneService>(module->GetTimeManager().GetTimeZoneContentManager());
|
||||
}
|
||||
|
||||
void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardLocalSystem);
|
||||
rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardLocalSystemClockCore());
|
||||
}
|
||||
|
||||
void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient(
|
||||
Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
auto& clock_core{module->GetTimeManager().GetStandardNetworkSystemClockCore()};
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(clock_core.IsStandardNetworkSystemClockAccuracySufficient(system));
|
||||
}
|
||||
|
||||
void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
auto& steady_clock_core{module->GetTimeManager().GetStandardSteadyClockCore()};
|
||||
if (!steady_clock_core.IsInitialized()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_UNINITIALIZED_CLOCK);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto context{rp.PopRaw<Clock::SystemClockContext>()};
|
||||
const auto current_time_point{
|
||||
steady_clock_core.GetCurrentTimePoint(Core::System::GetInstance())};
|
||||
|
||||
if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) {
|
||||
const auto ticks{Clock::TimeSpanType::FromTicks(
|
||||
Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()),
|
||||
Core::Timing::CNTFREQ)};
|
||||
const s64 base_time_point{context.offset + current_time_point.time_point -
|
||||
ticks.ToSeconds()};
|
||||
IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(base_time_point);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_TIME_MISMATCH);
|
||||
}
|
||||
|
||||
void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto initial_type = rp.PopRaw<u8>();
|
||||
const auto type{rp.PopRaw<u8>()};
|
||||
|
||||
const s64 time_since_epoch{GetSecondsSinceEpoch().count()};
|
||||
const std::time_t time(time_since_epoch);
|
||||
const std::tm* tm = std::localtime(&time);
|
||||
if (tm == nullptr) {
|
||||
LOG_ERROR(Service_Time, "tm is a nullptr");
|
||||
Clock::SystemClockContext user_context{};
|
||||
if (const ResultCode result{
|
||||
module->GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(
|
||||
Core::System::GetInstance(), user_context)};
|
||||
result != RESULT_SUCCESS) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_UNKNOWN); // TODO(ogniK): Find appropriate error code
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
Clock::SystemClockContext network_context{};
|
||||
if (const ResultCode result{
|
||||
module->GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext(
|
||||
Core::System::GetInstance(), network_context)};
|
||||
result != RESULT_SUCCESS) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& core_timing = system.CoreTiming();
|
||||
const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
|
||||
const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), {}};
|
||||
|
||||
CalendarTime calendar_time{};
|
||||
calendar_time.year = static_cast<u16_le>(tm->tm_year + 1900);
|
||||
calendar_time.month = static_cast<u8>(tm->tm_mon + 1);
|
||||
calendar_time.day = static_cast<u8>(tm->tm_mday);
|
||||
calendar_time.hour = static_cast<u8>(tm->tm_hour);
|
||||
calendar_time.minute = static_cast<u8>(tm->tm_min);
|
||||
calendar_time.second = static_cast<u8>(tm->tm_sec);
|
||||
|
||||
ClockSnapshot clock_snapshot{};
|
||||
clock_snapshot.system_posix_time = time_since_epoch;
|
||||
clock_snapshot.network_posix_time = time_since_epoch;
|
||||
clock_snapshot.system_calendar_time = calendar_time;
|
||||
clock_snapshot.network_calendar_time = calendar_time;
|
||||
|
||||
CalendarAdditionalInfo additional_info{};
|
||||
PosixToCalendar(time_since_epoch, calendar_time, additional_info, {});
|
||||
|
||||
clock_snapshot.system_calendar_info = additional_info;
|
||||
clock_snapshot.network_calendar_info = additional_info;
|
||||
|
||||
clock_snapshot.steady_clock_timepoint = steady_clock_time_point;
|
||||
clock_snapshot.location_name = LocationName{"UTC"};
|
||||
clock_snapshot.clock_auto_adjustment_enabled = 1;
|
||||
clock_snapshot.type = initial_type;
|
||||
Clock::ClockSnapshot clock_snapshot{};
|
||||
if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal(
|
||||
&ctx.GetThread(), user_context, network_context, type, clock_snapshot)};
|
||||
result != RESULT_SUCCESS) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot));
|
||||
ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot));
|
||||
}
|
||||
|
||||
void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
|
||||
Kernel::HLERequestContext& ctx) {
|
||||
void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto snapshot_a = rp.PopRaw<ClockSnapshot>();
|
||||
const auto snapshot_b = rp.PopRaw<ClockSnapshot>();
|
||||
const u64 difference =
|
||||
snapshot_b.user_clock_context.offset - snapshot_a.user_clock_context.offset;
|
||||
const auto type{rp.PopRaw<u8>()};
|
||||
rp.AlignWithPadding();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
const Clock::SystemClockContext user_context{rp.PopRaw<Clock::SystemClockContext>()};
|
||||
const Clock::SystemClockContext network_context{rp.PopRaw<Clock::SystemClockContext>()};
|
||||
|
||||
Clock::ClockSnapshot clock_snapshot{};
|
||||
if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal(
|
||||
&ctx.GetThread(), user_context, network_context, type, clock_snapshot)};
|
||||
result != RESULT_SUCCESS) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<u64>(difference);
|
||||
ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot));
|
||||
}
|
||||
|
||||
void Module::Interface::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(shared_memory->GetSharedMemoryHolder());
|
||||
rb.PushCopyObjects(module->GetTimeManager().GetSharedMemory().GetSharedMemoryHolder());
|
||||
}
|
||||
|
||||
void Module::Interface::IsStandardUserSystemClockAutomaticCorrectionEnabled(
|
||||
Kernel::HLERequestContext& ctx) {
|
||||
// ogniK(TODO): When clock contexts are implemented, the value should be read from the context
|
||||
// instead of our shared memory holder
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u8>(shared_memory->GetStandardUserSystemClockAutomaticCorrectionEnabled());
|
||||
}
|
||||
|
||||
void Module::Interface::SetStandardUserSystemClockAutomaticCorrectionEnabled(
|
||||
Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto enabled = rp.Pop<u8>();
|
||||
|
||||
LOG_WARNING(Service_Time, "(PARTIAL IMPLEMENTATION) called");
|
||||
|
||||
// TODO(ogniK): Update clock contexts and correct timespans
|
||||
|
||||
shared_memory->SetStandardUserSystemClockAutomaticCorrectionEnabled(enabled > 0);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> time,
|
||||
std::shared_ptr<SharedMemory> shared_memory, Core::System& system,
|
||||
const char* name)
|
||||
: ServiceFramework(name), time(std::move(time)), shared_memory(std::move(shared_memory)),
|
||||
system(system) {}
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
|
||||
: ServiceFramework(name), module{std::move(module)}, system{system} {}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
void InstallInterfaces(Core::System& system) {
|
||||
auto time = std::make_shared<Module>();
|
||||
auto shared_mem = std::make_shared<SharedMemory>(system);
|
||||
|
||||
std::make_shared<Time>(time, shared_mem, system, "time:a")
|
||||
->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<Time>(time, shared_mem, system, "time:s")
|
||||
->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<Time>(std::move(time), shared_mem, system, "time:u")
|
||||
->InstallAsService(system.ServiceManager());
|
||||
auto module{std::make_shared<Module>(system)};
|
||||
std::make_shared<Time>(module, system, "time:a")->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<Time>(module, system, "time:s")->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<Time>(module, system, "time:u")->InstallAsService(system.ServiceManager());
|
||||
}
|
||||
|
||||
} // namespace Service::Time
|
||||
|
||||
@@ -4,84 +4,23 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "common/common_funcs.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/time/clock_types.h"
|
||||
#include "core/hle/service/time/time_manager.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Time {
|
||||
|
||||
class SharedMemory;
|
||||
|
||||
struct LocationName {
|
||||
std::array<u8, 0x24> name;
|
||||
};
|
||||
static_assert(sizeof(LocationName) == 0x24, "LocationName is incorrect size");
|
||||
|
||||
struct CalendarTime {
|
||||
u16_le year;
|
||||
u8 month; // Starts at 1
|
||||
u8 day; // Starts at 1
|
||||
u8 hour;
|
||||
u8 minute;
|
||||
u8 second;
|
||||
};
|
||||
static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime structure has incorrect size");
|
||||
|
||||
struct CalendarAdditionalInfo {
|
||||
u32_le day_of_week;
|
||||
u32_le day_of_year;
|
||||
std::array<u8, 8> name;
|
||||
u8 is_dst;
|
||||
s32_le utc_offset;
|
||||
};
|
||||
static_assert(sizeof(CalendarAdditionalInfo) == 0x18,
|
||||
"CalendarAdditionalInfo structure has incorrect size");
|
||||
|
||||
// TODO(mailwl) RE this structure
|
||||
struct TimeZoneRule {
|
||||
INSERT_PADDING_BYTES(0x4000);
|
||||
};
|
||||
|
||||
struct SteadyClockTimePoint {
|
||||
using SourceID = std::array<u8, 16>;
|
||||
|
||||
u64_le value;
|
||||
SourceID source_id;
|
||||
};
|
||||
static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");
|
||||
|
||||
struct SystemClockContext {
|
||||
u64_le offset;
|
||||
SteadyClockTimePoint time_point;
|
||||
};
|
||||
static_assert(sizeof(SystemClockContext) == 0x20,
|
||||
"SystemClockContext structure has incorrect size");
|
||||
|
||||
struct ClockSnapshot {
|
||||
SystemClockContext user_clock_context;
|
||||
SystemClockContext network_clock_context;
|
||||
s64_le system_posix_time;
|
||||
s64_le network_posix_time;
|
||||
CalendarTime system_calendar_time;
|
||||
CalendarTime network_calendar_time;
|
||||
CalendarAdditionalInfo system_calendar_info;
|
||||
CalendarAdditionalInfo network_calendar_info;
|
||||
SteadyClockTimePoint steady_clock_timepoint;
|
||||
LocationName location_name;
|
||||
u8 clock_auto_adjustment_enabled;
|
||||
u8 type;
|
||||
u8 version;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
};
|
||||
static_assert(sizeof(ClockSnapshot) == 0xd0, "ClockSnapshot is an invalid size");
|
||||
|
||||
class Module final {
|
||||
public:
|
||||
Module(Core::System& system) : time_manager{system} {}
|
||||
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
explicit Interface(std::shared_ptr<Module> time,
|
||||
std::shared_ptr<SharedMemory> shared_memory, Core::System& system,
|
||||
const char* name);
|
||||
explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name);
|
||||
~Interface() override;
|
||||
|
||||
void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx);
|
||||
@@ -89,17 +28,29 @@ public:
|
||||
void GetStandardSteadyClock(Kernel::HLERequestContext& ctx);
|
||||
void GetTimeZoneService(Kernel::HLERequestContext& ctx);
|
||||
void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx);
|
||||
void IsStandardNetworkSystemClockAccuracySufficient(Kernel::HLERequestContext& ctx);
|
||||
void CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext& ctx);
|
||||
void GetClockSnapshot(Kernel::HLERequestContext& ctx);
|
||||
void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx);
|
||||
void GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx);
|
||||
void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx);
|
||||
void IsStandardUserSystemClockAutomaticCorrectionEnabled(Kernel::HLERequestContext& ctx);
|
||||
void SetStandardUserSystemClockAutomaticCorrectionEnabled(Kernel::HLERequestContext& ctx);
|
||||
|
||||
private:
|
||||
ResultCode GetClockSnapshotFromSystemClockContextInternal(
|
||||
Kernel::Thread* thread, Clock::SystemClockContext user_context,
|
||||
Clock::SystemClockContext network_context, u8 type,
|
||||
Clock::ClockSnapshot& cloc_snapshot);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> time;
|
||||
std::shared_ptr<SharedMemory> shared_memory;
|
||||
std::shared_ptr<Module> module;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
TimeManager& GetTimeManager() {
|
||||
return time_manager;
|
||||
}
|
||||
|
||||
private:
|
||||
TimeManager time_manager;
|
||||
};
|
||||
|
||||
/// Registers all Time services with the specified service manager.
|
||||
|
||||
137
src/core/hle/service/time/time_manager.cpp
Normal file
137
src/core/hle/service/time/time_manager.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
|
||||
#include "core/hle/service/time/ephemeral_network_system_clock_context_writer.h"
|
||||
#include "core/hle/service/time/local_system_clock_context_writer.h"
|
||||
#include "core/hle/service/time/network_system_clock_context_writer.h"
|
||||
#include "core/hle/service/time/time_manager.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::Time {
|
||||
|
||||
constexpr Clock::TimeSpanType standard_network_clock_accuracy{0x0009356907420000ULL};
|
||||
|
||||
static std::chrono::seconds GetSecondsSinceEpoch() {
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()) +
|
||||
Settings::values.custom_rtc_differential;
|
||||
}
|
||||
|
||||
static s64 GetExternalRtcValue() {
|
||||
return GetSecondsSinceEpoch().count();
|
||||
}
|
||||
|
||||
TimeManager::TimeManager(Core::System& system)
|
||||
: shared_memory{system}, standard_local_system_clock_core{standard_steady_clock_core},
|
||||
standard_network_system_clock_core{standard_steady_clock_core},
|
||||
standard_user_system_clock_core{standard_local_system_clock_core,
|
||||
standard_network_system_clock_core, system},
|
||||
ephemeral_network_system_clock_core{tick_based_steady_clock_core},
|
||||
local_system_clock_context_writer{
|
||||
std::make_shared<Clock::LocalSystemClockContextWriter>(shared_memory)},
|
||||
network_system_clock_context_writer{
|
||||
std::make_shared<Clock::NetworkSystemClockContextWriter>(shared_memory)},
|
||||
ephemeral_network_system_clock_context_writer{
|
||||
std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()},
|
||||
time_zone_content_manager{*this, system} {
|
||||
|
||||
const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())};
|
||||
SetupStandardSteadyClock(system, Common::UUID::Generate(), system_time, {}, {});
|
||||
SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds());
|
||||
SetupStandardNetworkSystemClock({}, standard_network_clock_accuracy);
|
||||
SetupStandardUserSystemClock(system, {}, Clock::SteadyClockTimePoint::GetRandom());
|
||||
SetupEphemeralNetworkSystemClock();
|
||||
}
|
||||
|
||||
TimeManager::~TimeManager() = default;
|
||||
|
||||
void TimeManager::SetupTimeZoneManager(std::string location_name,
|
||||
Clock::SteadyClockTimePoint time_zone_updated_time_point,
|
||||
std::size_t total_location_name_count,
|
||||
u128 time_zone_rule_version,
|
||||
FileSys::VirtualFile& vfs_file) {
|
||||
if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule(
|
||||
location_name, vfs_file) != RESULT_SUCCESS) {
|
||||
UNREACHABLE();
|
||||
return;
|
||||
}
|
||||
|
||||
time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point);
|
||||
time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount(
|
||||
total_location_name_count);
|
||||
time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion(time_zone_rule_version);
|
||||
time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized();
|
||||
}
|
||||
|
||||
void TimeManager::SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id,
|
||||
Clock::TimeSpanType setup_value,
|
||||
Clock::TimeSpanType internal_offset,
|
||||
bool is_rtc_reset_detected) {
|
||||
standard_steady_clock_core.SetClockSourceId(clock_source_id);
|
||||
standard_steady_clock_core.SetSetupValue(setup_value);
|
||||
standard_steady_clock_core.SetInternalOffset(internal_offset);
|
||||
standard_steady_clock_core.MarkAsInitialized();
|
||||
|
||||
const auto current_time_point{standard_steady_clock_core.GetCurrentRawTimePoint(system)};
|
||||
shared_memory.SetupStandardSteadyClock(system, clock_source_id, current_time_point);
|
||||
}
|
||||
|
||||
void TimeManager::SetupStandardLocalSystemClock(Core::System& system,
|
||||
Clock::SystemClockContext clock_context,
|
||||
s64 posix_time) {
|
||||
standard_local_system_clock_core.SetUpdateCallbackInstance(local_system_clock_context_writer);
|
||||
|
||||
const auto current_time_point{
|
||||
standard_local_system_clock_core.GetSteadyClockCore().GetCurrentTimePoint(system)};
|
||||
if (current_time_point.clock_source_id == clock_context.steady_time_point.clock_source_id) {
|
||||
standard_local_system_clock_core.SetSystemClockContext(clock_context);
|
||||
} else {
|
||||
if (standard_local_system_clock_core.SetCurrentTime(system, posix_time) != RESULT_SUCCESS) {
|
||||
UNREACHABLE();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
standard_local_system_clock_core.MarkAsInitialized();
|
||||
}
|
||||
|
||||
void TimeManager::SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
|
||||
Clock::TimeSpanType sufficient_accuracy) {
|
||||
standard_network_system_clock_core.SetUpdateCallbackInstance(
|
||||
network_system_clock_context_writer);
|
||||
|
||||
if (standard_network_system_clock_core.SetSystemClockContext(clock_context) != RESULT_SUCCESS) {
|
||||
UNREACHABLE();
|
||||
return;
|
||||
}
|
||||
|
||||
standard_network_system_clock_core.SetStandardNetworkClockSufficientAccuracy(
|
||||
sufficient_accuracy);
|
||||
standard_network_system_clock_core.MarkAsInitialized();
|
||||
}
|
||||
|
||||
void TimeManager::SetupStandardUserSystemClock(
|
||||
Core::System& system, bool is_automatic_correction_enabled,
|
||||
Clock::SteadyClockTimePoint steady_clock_time_point) {
|
||||
if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled(
|
||||
system, is_automatic_correction_enabled) != RESULT_SUCCESS) {
|
||||
UNREACHABLE();
|
||||
return;
|
||||
}
|
||||
|
||||
standard_user_system_clock_core.SetAutomaticCorrectionUpdatedTime(steady_clock_time_point);
|
||||
standard_user_system_clock_core.MarkAsInitialized();
|
||||
shared_memory.SetAutomaticCorrectionEnabled(is_automatic_correction_enabled);
|
||||
}
|
||||
|
||||
void TimeManager::SetupEphemeralNetworkSystemClock() {
|
||||
ephemeral_network_system_clock_core.SetUpdateCallbackInstance(
|
||||
ephemeral_network_system_clock_context_writer);
|
||||
ephemeral_network_system_clock_core.MarkAsInitialized();
|
||||
}
|
||||
|
||||
} // namespace Service::Time
|
||||
117
src/core/hle/service/time/time_manager.h
Normal file
117
src/core/hle/service/time/time_manager.h
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
#include "core/hle/service/time/clock_types.h"
|
||||
#include "core/hle/service/time/ephemeral_network_system_clock_core.h"
|
||||
#include "core/hle/service/time/standard_local_system_clock_core.h"
|
||||
#include "core/hle/service/time/standard_network_system_clock_core.h"
|
||||
#include "core/hle/service/time/standard_steady_clock_core.h"
|
||||
#include "core/hle/service/time/standard_user_system_clock_core.h"
|
||||
#include "core/hle/service/time/tick_based_steady_clock_core.h"
|
||||
#include "core/hle/service/time/time_sharedmemory.h"
|
||||
#include "core/hle/service/time/time_zone_content_manager.h"
|
||||
|
||||
namespace Service::Time {
|
||||
|
||||
namespace Clock {
|
||||
class EphemeralNetworkSystemClockContextWriter;
|
||||
class LocalSystemClockContextWriter;
|
||||
class NetworkSystemClockContextWriter;
|
||||
} // namespace Clock
|
||||
|
||||
// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783).
|
||||
// This code was released under public domain.
|
||||
|
||||
class TimeManager final {
|
||||
public:
|
||||
explicit TimeManager(Core::System& system);
|
||||
~TimeManager();
|
||||
|
||||
Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() {
|
||||
return standard_steady_clock_core;
|
||||
}
|
||||
|
||||
const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const {
|
||||
return standard_steady_clock_core;
|
||||
}
|
||||
|
||||
Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() {
|
||||
return standard_local_system_clock_core;
|
||||
}
|
||||
|
||||
const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const {
|
||||
return standard_local_system_clock_core;
|
||||
}
|
||||
|
||||
Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() {
|
||||
return standard_network_system_clock_core;
|
||||
}
|
||||
|
||||
const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const {
|
||||
return standard_network_system_clock_core;
|
||||
}
|
||||
|
||||
Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() {
|
||||
return standard_user_system_clock_core;
|
||||
}
|
||||
|
||||
const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const {
|
||||
return standard_user_system_clock_core;
|
||||
}
|
||||
|
||||
TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() {
|
||||
return time_zone_content_manager;
|
||||
}
|
||||
|
||||
const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const {
|
||||
return time_zone_content_manager;
|
||||
}
|
||||
|
||||
SharedMemory& GetSharedMemory() {
|
||||
return shared_memory;
|
||||
}
|
||||
|
||||
const SharedMemory& GetSharedMemory() const {
|
||||
return shared_memory;
|
||||
}
|
||||
|
||||
void SetupTimeZoneManager(std::string location_name,
|
||||
Clock::SteadyClockTimePoint time_zone_updated_time_point,
|
||||
std::size_t total_location_name_count, u128 time_zone_rule_version,
|
||||
FileSys::VirtualFile& vfs_file);
|
||||
|
||||
private:
|
||||
void SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id,
|
||||
Clock::TimeSpanType setup_value,
|
||||
Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected);
|
||||
void SetupStandardLocalSystemClock(Core::System& system,
|
||||
Clock::SystemClockContext clock_context, s64 posix_time);
|
||||
void SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
|
||||
Clock::TimeSpanType sufficient_accuracy);
|
||||
void SetupStandardUserSystemClock(Core::System& system, bool is_automatic_correction_enabled,
|
||||
Clock::SteadyClockTimePoint steady_clock_time_point);
|
||||
void SetupEphemeralNetworkSystemClock();
|
||||
|
||||
SharedMemory shared_memory;
|
||||
|
||||
Clock::StandardSteadyClockCore standard_steady_clock_core;
|
||||
Clock::TickBasedSteadyClockCore tick_based_steady_clock_core;
|
||||
Clock::StandardLocalSystemClockCore standard_local_system_clock_core;
|
||||
Clock::StandardNetworkSystemClockCore standard_network_system_clock_core;
|
||||
Clock::StandardUserSystemClockCore standard_user_system_clock_core;
|
||||
Clock::EphemeralNetworkSystemClockCore ephemeral_network_system_clock_core;
|
||||
|
||||
std::shared_ptr<Clock::LocalSystemClockContextWriter> local_system_clock_context_writer;
|
||||
std::shared_ptr<Clock::NetworkSystemClockContextWriter> network_system_clock_context_writer;
|
||||
std::shared_ptr<Clock::EphemeralNetworkSystemClockContextWriter>
|
||||
ephemeral_network_system_clock_context_writer;
|
||||
|
||||
TimeZone::TimeZoneContentManager time_zone_content_manager;
|
||||
};
|
||||
|
||||
} // namespace Service::Time
|
||||
@@ -3,20 +3,21 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/hle/service/time/clock_types.h"
|
||||
#include "core/hle/service/time/steady_clock_core.h"
|
||||
#include "core/hle/service/time/time_sharedmemory.h"
|
||||
|
||||
namespace Service::Time {
|
||||
const std::size_t SHARED_MEMORY_SIZE = 0x1000;
|
||||
|
||||
static constexpr std::size_t SHARED_MEMORY_SIZE{0x1000};
|
||||
|
||||
SharedMemory::SharedMemory(Core::System& system) : system(system) {
|
||||
shared_memory_holder = Kernel::SharedMemory::Create(
|
||||
system.Kernel(), nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite,
|
||||
Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "Time:SharedMemory");
|
||||
|
||||
// Seems static from 1.0.0 -> 8.1.0. Specific games seem to check this value and crash
|
||||
// if it's set to anything else
|
||||
shared_memory_format.format_version = 14;
|
||||
std::memcpy(shared_memory_holder->GetPointer(), &shared_memory_format, sizeof(Format));
|
||||
std::memset(shared_memory_holder->GetPointer(), 0, SHARED_MEMORY_SIZE);
|
||||
}
|
||||
|
||||
SharedMemory::~SharedMemory() = default;
|
||||
@@ -25,44 +26,32 @@ std::shared_ptr<Kernel::SharedMemory> SharedMemory::GetSharedMemoryHolder() cons
|
||||
return shared_memory_holder;
|
||||
}
|
||||
|
||||
void SharedMemory::SetStandardSteadyClockTimepoint(const SteadyClockTimePoint& timepoint) {
|
||||
void SharedMemory::SetupStandardSteadyClock(Core::System& system,
|
||||
const Common::UUID& clock_source_id,
|
||||
Clock::TimeSpanType current_time_point) {
|
||||
const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks(
|
||||
Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()),
|
||||
Core::Timing::CNTFREQ)};
|
||||
const Clock::SteadyClockContext context{
|
||||
static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds),
|
||||
clock_source_id};
|
||||
shared_memory_format.standard_steady_clock_timepoint.StoreData(
|
||||
shared_memory_holder->GetPointer(), timepoint);
|
||||
shared_memory_holder->GetPointer(), context);
|
||||
}
|
||||
|
||||
void SharedMemory::SetStandardLocalSystemClockContext(const SystemClockContext& context) {
|
||||
void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) {
|
||||
shared_memory_format.standard_local_system_clock_context.StoreData(
|
||||
shared_memory_holder->GetPointer(), context);
|
||||
}
|
||||
|
||||
void SharedMemory::SetStandardNetworkSystemClockContext(const SystemClockContext& context) {
|
||||
void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) {
|
||||
shared_memory_format.standard_network_system_clock_context.StoreData(
|
||||
shared_memory_holder->GetPointer(), context);
|
||||
}
|
||||
|
||||
void SharedMemory::SetStandardUserSystemClockAutomaticCorrectionEnabled(bool enabled) {
|
||||
void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) {
|
||||
shared_memory_format.standard_user_system_clock_automatic_correction.StoreData(
|
||||
shared_memory_holder->GetPointer(), enabled);
|
||||
}
|
||||
|
||||
SteadyClockTimePoint SharedMemory::GetStandardSteadyClockTimepoint() {
|
||||
return shared_memory_format.standard_steady_clock_timepoint.ReadData(
|
||||
shared_memory_holder->GetPointer());
|
||||
}
|
||||
|
||||
SystemClockContext SharedMemory::GetStandardLocalSystemClockContext() {
|
||||
return shared_memory_format.standard_local_system_clock_context.ReadData(
|
||||
shared_memory_holder->GetPointer());
|
||||
}
|
||||
|
||||
SystemClockContext SharedMemory::GetStandardNetworkSystemClockContext() {
|
||||
return shared_memory_format.standard_network_system_clock_context.ReadData(
|
||||
shared_memory_holder->GetPointer());
|
||||
}
|
||||
|
||||
bool SharedMemory::GetStandardUserSystemClockAutomaticCorrectionEnabled() {
|
||||
return shared_memory_format.standard_user_system_clock_automatic_correction.ReadData(
|
||||
shared_memory_holder->GetPointer());
|
||||
shared_memory_holder->GetPointer(), is_enabled);
|
||||
}
|
||||
|
||||
} // namespace Service::Time
|
||||
|
||||
@@ -5,11 +5,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/uuid.h"
|
||||
#include "core/hle/kernel/shared_memory.h"
|
||||
#include "core/hle/service/time/time.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/service/time/clock_types.h"
|
||||
|
||||
namespace Service::Time {
|
||||
class SharedMemory {
|
||||
|
||||
class SharedMemory final {
|
||||
public:
|
||||
explicit SharedMemory(Core::System& system);
|
||||
~SharedMemory();
|
||||
@@ -17,22 +20,10 @@ public:
|
||||
// Return the shared memory handle
|
||||
std::shared_ptr<Kernel::SharedMemory> GetSharedMemoryHolder() const;
|
||||
|
||||
// Set memory barriers in shared memory and update them
|
||||
void SetStandardSteadyClockTimepoint(const SteadyClockTimePoint& timepoint);
|
||||
void SetStandardLocalSystemClockContext(const SystemClockContext& context);
|
||||
void SetStandardNetworkSystemClockContext(const SystemClockContext& context);
|
||||
void SetStandardUserSystemClockAutomaticCorrectionEnabled(bool enabled);
|
||||
|
||||
// Pull from memory barriers in the shared memory
|
||||
SteadyClockTimePoint GetStandardSteadyClockTimepoint();
|
||||
SystemClockContext GetStandardLocalSystemClockContext();
|
||||
SystemClockContext GetStandardNetworkSystemClockContext();
|
||||
bool GetStandardUserSystemClockAutomaticCorrectionEnabled();
|
||||
|
||||
// TODO(ogniK): We have to properly simulate memory barriers, how are we going to do this?
|
||||
template <typename T, std::size_t Offset>
|
||||
struct MemoryBarrier {
|
||||
static_assert(std::is_trivially_constructible_v<T>, "T must be trivially constructable");
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
|
||||
u32_le read_attempt{};
|
||||
std::array<T, 2> data{};
|
||||
|
||||
@@ -57,16 +48,22 @@ public:
|
||||
|
||||
// Shared memory format
|
||||
struct Format {
|
||||
MemoryBarrier<SteadyClockTimePoint, 0x0> standard_steady_clock_timepoint;
|
||||
MemoryBarrier<SystemClockContext, 0x38> standard_local_system_clock_context;
|
||||
MemoryBarrier<SystemClockContext, 0x80> standard_network_system_clock_context;
|
||||
MemoryBarrier<Clock::SteadyClockContext, 0x0> standard_steady_clock_timepoint;
|
||||
MemoryBarrier<Clock::SystemClockContext, 0x38> standard_local_system_clock_context;
|
||||
MemoryBarrier<Clock::SystemClockContext, 0x80> standard_network_system_clock_context;
|
||||
MemoryBarrier<bool, 0xc8> standard_user_system_clock_automatic_correction;
|
||||
u32_le format_version;
|
||||
};
|
||||
static_assert(sizeof(Format) == 0xd8, "Format is an invalid size");
|
||||
|
||||
void SetupStandardSteadyClock(Core::System& system, const Common::UUID& clock_source_id,
|
||||
Clock::TimeSpanType currentTimePoint);
|
||||
void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context);
|
||||
void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context);
|
||||
void SetAutomaticCorrectionEnabled(bool is_enabled);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Kernel::SharedMemory> shared_memory_holder{};
|
||||
std::shared_ptr<Kernel::SharedMemory> shared_memory_holder;
|
||||
Core::System& system;
|
||||
Format shared_memory_format{};
|
||||
};
|
||||
|
||||
125
src/core/hle/service/time/time_zone_content_manager.cpp
Normal file
125
src/core/hle/service/time/time_zone_content_manager.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/romfs.h"
|
||||
#include "core/file_sys/system_archive/system_archive.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/time/time_manager.h"
|
||||
#include "core/hle/service/time/time_zone_content_manager.h"
|
||||
|
||||
namespace Service::Time::TimeZone {
|
||||
|
||||
constexpr u64 time_zone_binary_titleid{0x010000000000080E};
|
||||
|
||||
static FileSys::VirtualDir GetTimeZoneBinary(Core::System& system) {
|
||||
const auto* nand{system.GetFileSystemController().GetSystemNANDContents()};
|
||||
const auto nca{nand->GetEntry(time_zone_binary_titleid, FileSys::ContentRecordType::Data)};
|
||||
|
||||
FileSys::VirtualFile romfs;
|
||||
if (nca) {
|
||||
romfs = nca->GetRomFS();
|
||||
}
|
||||
|
||||
if (!romfs) {
|
||||
romfs = FileSys::SystemArchive::SynthesizeSystemArchive(time_zone_binary_titleid);
|
||||
}
|
||||
|
||||
if (!romfs) {
|
||||
LOG_ERROR(Service_Time, "Failed to find or synthesize {:016X!}", time_zone_binary_titleid);
|
||||
return {};
|
||||
}
|
||||
|
||||
return FileSys::ExtractRomFS(romfs);
|
||||
}
|
||||
|
||||
static std::vector<std::string> BuildLocationNameCache(Core::System& system) {
|
||||
const FileSys::VirtualDir extracted_romfs{GetTimeZoneBinary(system)};
|
||||
if (!extracted_romfs) {
|
||||
LOG_ERROR(Service_Time, "Failed to extract RomFS for {:016X}!", time_zone_binary_titleid);
|
||||
return {};
|
||||
}
|
||||
|
||||
const FileSys::VirtualFile binary_list{extracted_romfs->GetFile("binaryList.txt")};
|
||||
if (!binary_list) {
|
||||
LOG_ERROR(Service_Time, "{:016X} has no file binaryList.txt!", time_zone_binary_titleid);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<char> raw_data(binary_list->GetSize());
|
||||
binary_list->ReadBytes<char>(raw_data.data(), binary_list->GetSize());
|
||||
|
||||
std::stringstream data_stream{raw_data.data()};
|
||||
std::string name;
|
||||
std::vector<std::string> location_name_cache;
|
||||
while (std::getline(data_stream, name)) {
|
||||
name.pop_back(); // Remove carriage return
|
||||
location_name_cache.emplace_back(std::move(name));
|
||||
}
|
||||
return location_name_cache;
|
||||
}
|
||||
|
||||
TimeZoneContentManager::TimeZoneContentManager(TimeManager& time_manager, Core::System& system)
|
||||
: system{system}, location_name_cache{BuildLocationNameCache(system)} {
|
||||
if (FileSys::VirtualFile vfs_file; GetTimeZoneInfoFile("GMT", vfs_file) == RESULT_SUCCESS) {
|
||||
const auto time_point{
|
||||
time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)};
|
||||
time_manager.SetupTimeZoneManager("GMT", time_point, location_name_cache.size(), {},
|
||||
vfs_file);
|
||||
} else {
|
||||
time_zone_manager.MarkAsInitialized();
|
||||
}
|
||||
}
|
||||
|
||||
ResultCode TimeZoneContentManager::LoadTimeZoneRule(TimeZoneRule& rules,
|
||||
const std::string& location_name) const {
|
||||
FileSys::VirtualFile vfs_file;
|
||||
if (const ResultCode result{GetTimeZoneInfoFile(location_name, vfs_file)};
|
||||
result != RESULT_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return time_zone_manager.ParseTimeZoneRuleBinary(rules, vfs_file);
|
||||
}
|
||||
|
||||
bool TimeZoneContentManager::IsLocationNameValid(const std::string& location_name) const {
|
||||
return std::find(location_name_cache.begin(), location_name_cache.end(), location_name) !=
|
||||
location_name_cache.end();
|
||||
}
|
||||
|
||||
ResultCode TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& location_name,
|
||||
FileSys::VirtualFile& vfs_file) const {
|
||||
if (!IsLocationNameValid(location_name)) {
|
||||
return ERROR_TIME_NOT_FOUND;
|
||||
}
|
||||
|
||||
const FileSys::VirtualDir extracted_romfs{GetTimeZoneBinary(system)};
|
||||
if (!extracted_romfs) {
|
||||
LOG_ERROR(Service_Time, "Failed to extract RomFS for {:016X}!", time_zone_binary_titleid);
|
||||
return ERROR_TIME_NOT_FOUND;
|
||||
}
|
||||
|
||||
const FileSys::VirtualDir zoneinfo_dir{extracted_romfs->GetSubdirectory("zoneinfo")};
|
||||
if (!zoneinfo_dir) {
|
||||
LOG_ERROR(Service_Time, "{:016X} has no directory zoneinfo!", time_zone_binary_titleid);
|
||||
return ERROR_TIME_NOT_FOUND;
|
||||
}
|
||||
|
||||
vfs_file = zoneinfo_dir->GetFile(location_name);
|
||||
if (!vfs_file) {
|
||||
LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"!", time_zone_binary_titleid,
|
||||
location_name);
|
||||
return ERROR_TIME_NOT_FOUND;
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace Service::Time::TimeZone
|
||||
46
src/core/hle/service/time/time_zone_content_manager.h
Normal file
46
src/core/hle/service/time/time_zone_content_manager.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "core/hle/service/time/time_zone_manager.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Time {
|
||||
class TimeManager;
|
||||
}
|
||||
|
||||
namespace Service::Time::TimeZone {
|
||||
|
||||
class TimeZoneContentManager final {
|
||||
public:
|
||||
TimeZoneContentManager(TimeManager& time_manager, Core::System& system);
|
||||
|
||||
TimeZoneManager& GetTimeZoneManager() {
|
||||
return time_zone_manager;
|
||||
}
|
||||
|
||||
const TimeZoneManager& GetTimeZoneManager() const {
|
||||
return time_zone_manager;
|
||||
}
|
||||
|
||||
ResultCode LoadTimeZoneRule(TimeZoneRule& rules, const std::string& location_name) const;
|
||||
|
||||
private:
|
||||
bool IsLocationNameValid(const std::string& location_name) const;
|
||||
ResultCode GetTimeZoneInfoFile(const std::string& location_name,
|
||||
FileSys::VirtualFile& vfs_file) const;
|
||||
|
||||
Core::System& system;
|
||||
TimeZoneManager time_zone_manager;
|
||||
const std::vector<std::string> location_name_cache;
|
||||
};
|
||||
|
||||
} // namespace Service::Time::TimeZone
|
||||
1030
src/core/hle/service/time/time_zone_manager.cpp
Normal file
1030
src/core/hle/service/time/time_zone_manager.cpp
Normal file
File diff suppressed because it is too large
Load Diff
53
src/core/hle/service/time/time_zone_manager.h
Normal file
53
src/core/hle/service/time/time_zone_manager.h
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
#include "core/hle/service/time/clock_types.h"
|
||||
#include "core/hle/service/time/time_zone_types.h"
|
||||
|
||||
namespace Service::Time::TimeZone {
|
||||
|
||||
class TimeZoneManager final {
|
||||
public:
|
||||
TimeZoneManager();
|
||||
~TimeZoneManager();
|
||||
|
||||
void SetTotalLocationNameCount(std::size_t value) {
|
||||
total_location_name_count = value;
|
||||
}
|
||||
|
||||
void SetTimeZoneRuleVersion(const u128& value) {
|
||||
time_zone_rule_version = value;
|
||||
}
|
||||
|
||||
void MarkAsInitialized() {
|
||||
is_initialized = true;
|
||||
}
|
||||
|
||||
ResultCode SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name,
|
||||
FileSys::VirtualFile& vfs_file);
|
||||
ResultCode SetUpdatedTime(const Clock::SteadyClockTimePoint& value);
|
||||
ResultCode GetDeviceLocationName(TimeZone::LocationName& value) const;
|
||||
ResultCode ToCalendarTime(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) const;
|
||||
ResultCode ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const;
|
||||
ResultCode ParseTimeZoneRuleBinary(TimeZoneRule& rules, FileSys::VirtualFile& vfs_file) const;
|
||||
ResultCode ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time,
|
||||
s64& posix_time) const;
|
||||
|
||||
private:
|
||||
bool is_initialized{};
|
||||
TimeZoneRule time_zone_rule{};
|
||||
std::string device_location_name{"GMT"};
|
||||
u128 time_zone_rule_version{};
|
||||
std::size_t total_location_name_count{};
|
||||
Clock::SteadyClockTimePoint time_zone_update_time_point{
|
||||
Clock::SteadyClockTimePoint::GetRandom()};
|
||||
};
|
||||
|
||||
} // namespace Service::Time::TimeZone
|
||||
148
src/core/hle/service/time/time_zone_service.cpp
Normal file
148
src/core/hle/service/time/time_zone_service.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/time/time_zone_content_manager.h"
|
||||
#include "core/hle/service/time/time_zone_service.h"
|
||||
#include "core/hle/service/time/time_zone_types.h"
|
||||
|
||||
namespace Service::Time {
|
||||
|
||||
ITimeZoneService ::ITimeZoneService(TimeZone::TimeZoneContentManager& time_zone_content_manager)
|
||||
: ServiceFramework("ITimeZoneService"), time_zone_content_manager{time_zone_content_manager} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ITimeZoneService::GetDeviceLocationName, "GetDeviceLocationName"},
|
||||
{1, nullptr, "SetDeviceLocationName"},
|
||||
{2, nullptr, "GetTotalLocationNameCount"},
|
||||
{3, nullptr, "LoadLocationNameList"},
|
||||
{4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"},
|
||||
{5, nullptr, "GetTimeZoneRuleVersion"},
|
||||
{100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
|
||||
{101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
|
||||
{201, &ITimeZoneService::ToPosixTime, "ToPosixTime"},
|
||||
{202, nullptr, "ToPosixTimeWithMyRule"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
void ITimeZoneService::GetDeviceLocationName(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
TimeZone::LocationName location_name{};
|
||||
if (const ResultCode result{
|
||||
time_zone_content_manager.GetTimeZoneManager().GetDeviceLocationName(location_name)};
|
||||
result != RESULT_SUCCESS) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, (sizeof(location_name) / 4) + 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(location_name);
|
||||
}
|
||||
|
||||
void ITimeZoneService::LoadTimeZoneRule(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto raw_location_name{rp.PopRaw<std::array<u8, 0x24>>()};
|
||||
|
||||
std::string location_name;
|
||||
for (const auto& byte : raw_location_name) {
|
||||
// Strip extra bytes
|
||||
if (byte == '\0') {
|
||||
break;
|
||||
}
|
||||
location_name.push_back(byte);
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Time, "called, location_name={}", location_name);
|
||||
|
||||
TimeZone::TimeZoneRule time_zone_rule{};
|
||||
if (const ResultCode result{
|
||||
time_zone_content_manager.LoadTimeZoneRule(time_zone_rule, location_name)};
|
||||
result != RESULT_SUCCESS) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<u8> time_zone_rule_outbuffer(sizeof(TimeZone::TimeZoneRule));
|
||||
std::memcpy(time_zone_rule_outbuffer.data(), &time_zone_rule, sizeof(TimeZone::TimeZoneRule));
|
||||
ctx.WriteBuffer(time_zone_rule_outbuffer);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ITimeZoneService::ToCalendarTime(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto posix_time{rp.Pop<s64>()};
|
||||
|
||||
LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time);
|
||||
|
||||
TimeZone::TimeZoneRule time_zone_rule{};
|
||||
const auto buffer{ctx.ReadBuffer()};
|
||||
std::memcpy(&time_zone_rule, buffer.data(), buffer.size());
|
||||
|
||||
TimeZone::CalendarInfo calendar_info{};
|
||||
if (const ResultCode result{time_zone_content_manager.GetTimeZoneManager().ToCalendarTime(
|
||||
time_zone_rule, posix_time, calendar_info)};
|
||||
result != RESULT_SUCCESS) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + (sizeof(TimeZone::CalendarInfo) / 4)};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(calendar_info);
|
||||
}
|
||||
|
||||
void ITimeZoneService::ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto posix_time{rp.Pop<s64>()};
|
||||
|
||||
LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time);
|
||||
|
||||
TimeZone::CalendarInfo calendar_info{};
|
||||
if (const ResultCode result{
|
||||
time_zone_content_manager.GetTimeZoneManager().ToCalendarTimeWithMyRules(
|
||||
posix_time, calendar_info)};
|
||||
result != RESULT_SUCCESS) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + (sizeof(TimeZone::CalendarInfo) / 4)};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(calendar_info);
|
||||
}
|
||||
|
||||
void ITimeZoneService::ToPosixTime(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto calendar_time{rp.PopRaw<TimeZone::CalendarTime>()};
|
||||
TimeZone::TimeZoneRule time_zone_rule{};
|
||||
std::memcpy(&time_zone_rule, ctx.ReadBuffer().data(), sizeof(TimeZone::TimeZoneRule));
|
||||
|
||||
s64 posix_time{};
|
||||
if (const ResultCode result{time_zone_content_manager.GetTimeZoneManager().ToPosixTime(
|
||||
time_zone_rule, calendar_time, posix_time)};
|
||||
result != RESULT_SUCCESS) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(bunnei): Handle multiple times
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<u32>(1); // Number of times we're returning
|
||||
ctx.WriteBuffer(&posix_time, sizeof(s64));
|
||||
}
|
||||
|
||||
} // namespace Service::Time
|
||||
30
src/core/hle/service/time/time_zone_service.h
Normal file
30
src/core/hle/service/time/time_zone_service.h
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::Time {
|
||||
|
||||
namespace TimeZone {
|
||||
class TimeZoneContentManager;
|
||||
}
|
||||
|
||||
class ITimeZoneService final : public ServiceFramework<ITimeZoneService> {
|
||||
public:
|
||||
explicit ITimeZoneService(TimeZone::TimeZoneContentManager& time_zone_manager);
|
||||
|
||||
private:
|
||||
void GetDeviceLocationName(Kernel::HLERequestContext& ctx);
|
||||
void LoadTimeZoneRule(Kernel::HLERequestContext& ctx);
|
||||
void ToCalendarTime(Kernel::HLERequestContext& ctx);
|
||||
void ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx);
|
||||
void ToPosixTime(Kernel::HLERequestContext& ctx);
|
||||
|
||||
private:
|
||||
TimeZone::TimeZoneContentManager& time_zone_content_manager;
|
||||
};
|
||||
|
||||
} // namespace Service::Time
|
||||
87
src/core/hle/service/time/time_zone_types.h
Normal file
87
src/core/hle/service/time/time_zone_types.h
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
|
||||
namespace Service::Time::TimeZone {
|
||||
|
||||
using LocationName = std::array<char, 0x24>;
|
||||
|
||||
/// https://switchbrew.org/wiki/Glue_services#ttinfo
|
||||
struct TimeTypeInfo {
|
||||
s32 gmt_offset{};
|
||||
u8 is_dst{};
|
||||
INSERT_PADDING_BYTES(3);
|
||||
s32 abbreviation_list_index{};
|
||||
u8 is_standard_time_daylight{};
|
||||
u8 is_gmt{};
|
||||
INSERT_PADDING_BYTES(2);
|
||||
};
|
||||
static_assert(sizeof(TimeTypeInfo) == 0x10, "TimeTypeInfo is incorrect size");
|
||||
|
||||
/// https://switchbrew.org/wiki/Glue_services#TimeZoneRule
|
||||
struct TimeZoneRule {
|
||||
s32 time_count{};
|
||||
s32 type_count{};
|
||||
s32 char_count{};
|
||||
u8 go_back{};
|
||||
u8 go_ahead{};
|
||||
INSERT_PADDING_BYTES(2);
|
||||
std::array<s64, 1000> ats{};
|
||||
std::array<s8, 1000> types{};
|
||||
std::array<TimeTypeInfo, 128> ttis{};
|
||||
std::array<char, 512> chars{};
|
||||
s32 default_type{};
|
||||
INSERT_PADDING_BYTES(0x12C4);
|
||||
};
|
||||
static_assert(sizeof(TimeZoneRule) == 0x4000, "TimeZoneRule is incorrect size");
|
||||
|
||||
/// https://switchbrew.org/wiki/Glue_services#CalendarAdditionalInfo
|
||||
struct CalendarAdditionalInfo {
|
||||
u32 day_of_week{};
|
||||
u32 day_of_year{};
|
||||
std::array<char, 8> timezone_name;
|
||||
u32 is_dst{};
|
||||
s32 gmt_offset{};
|
||||
};
|
||||
static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo is incorrect size");
|
||||
|
||||
/// https://switchbrew.org/wiki/Glue_services#CalendarTime
|
||||
struct CalendarTime {
|
||||
s16 year{};
|
||||
s8 month{};
|
||||
s8 day{};
|
||||
s8 hour{};
|
||||
s8 minute{};
|
||||
s8 second{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
};
|
||||
static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime is incorrect size");
|
||||
|
||||
struct CalendarInfo {
|
||||
CalendarTime time{};
|
||||
CalendarAdditionalInfo additiona_info{};
|
||||
};
|
||||
static_assert(sizeof(CalendarInfo) == 0x20, "CalendarInfo is incorrect size");
|
||||
|
||||
struct TzifHeader {
|
||||
u32_be magic{};
|
||||
u8 version{};
|
||||
INSERT_PADDING_BYTES(15);
|
||||
s32_be ttis_gmt_count{};
|
||||
s32_be ttis_std_count{};
|
||||
s32_be leap_count{};
|
||||
s32_be time_count{};
|
||||
s32_be type_count{};
|
||||
s32_be char_count{};
|
||||
};
|
||||
static_assert(sizeof(TzifHeader) == 0x2C, "TzifHeader is incorrect size");
|
||||
|
||||
} // namespace Service::Time::TimeZone
|
||||
@@ -4,8 +4,6 @@ add_library(video_core STATIC
|
||||
buffer_cache/map_interval.h
|
||||
dma_pusher.cpp
|
||||
dma_pusher.h
|
||||
debug_utils/debug_utils.cpp
|
||||
debug_utils/debug_utils.h
|
||||
engines/const_buffer_engine_interface.h
|
||||
engines/const_buffer_info.h
|
||||
engines/engine_upload.cpp
|
||||
@@ -151,12 +149,16 @@ add_library(video_core STATIC
|
||||
if (ENABLE_VULKAN)
|
||||
target_sources(video_core PRIVATE
|
||||
renderer_vulkan/declarations.h
|
||||
renderer_vulkan/fixed_pipeline_state.cpp
|
||||
renderer_vulkan/fixed_pipeline_state.h
|
||||
renderer_vulkan/maxwell_to_vk.cpp
|
||||
renderer_vulkan/maxwell_to_vk.h
|
||||
renderer_vulkan/vk_buffer_cache.cpp
|
||||
renderer_vulkan/vk_buffer_cache.h
|
||||
renderer_vulkan/vk_device.cpp
|
||||
renderer_vulkan/vk_device.h
|
||||
renderer_vulkan/vk_image.cpp
|
||||
renderer_vulkan/vk_image.h
|
||||
renderer_vulkan/vk_memory_manager.cpp
|
||||
renderer_vulkan/vk_memory_manager.h
|
||||
renderer_vulkan/vk_resource_manager.cpp
|
||||
@@ -167,6 +169,8 @@ if (ENABLE_VULKAN)
|
||||
renderer_vulkan/vk_scheduler.h
|
||||
renderer_vulkan/vk_shader_decompiler.cpp
|
||||
renderer_vulkan/vk_shader_decompiler.h
|
||||
renderer_vulkan/vk_staging_buffer_pool.cpp
|
||||
renderer_vulkan/vk_staging_buffer_pool.h
|
||||
renderer_vulkan/vk_stream_buffer.cpp
|
||||
renderer_vulkan/vk_stream_buffer.h
|
||||
renderer_vulkan/vk_swapchain.cpp
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
void DebugContext::DoOnEvent(Event event, void* data) {
|
||||
{
|
||||
std::unique_lock lock{breakpoint_mutex};
|
||||
|
||||
// TODO(Subv): Commit the rasterizer's caches so framebuffers, render targets, etc. will
|
||||
// show on debug widgets
|
||||
|
||||
// TODO: Should stop the CPU thread here once we multithread emulation.
|
||||
|
||||
active_breakpoint = event;
|
||||
at_breakpoint = true;
|
||||
|
||||
// Tell all observers that we hit a breakpoint
|
||||
for (auto& breakpoint_observer : breakpoint_observers) {
|
||||
breakpoint_observer->OnMaxwellBreakPointHit(event, data);
|
||||
}
|
||||
|
||||
// Wait until another thread tells us to Resume()
|
||||
resume_from_breakpoint.wait(lock, [&] { return !at_breakpoint; });
|
||||
}
|
||||
}
|
||||
|
||||
void DebugContext::Resume() {
|
||||
{
|
||||
std::lock_guard lock{breakpoint_mutex};
|
||||
|
||||
// Tell all observers that we are about to resume
|
||||
for (auto& breakpoint_observer : breakpoint_observers) {
|
||||
breakpoint_observer->OnMaxwellResume();
|
||||
}
|
||||
|
||||
// Resume the waiting thread (i.e. OnEvent())
|
||||
at_breakpoint = false;
|
||||
}
|
||||
|
||||
resume_from_breakpoint.notify_one();
|
||||
}
|
||||
|
||||
} // namespace Tegra
|
||||
@@ -1,157 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <condition_variable>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
class DebugContext {
|
||||
public:
|
||||
enum class Event {
|
||||
FirstEvent = 0,
|
||||
|
||||
MaxwellCommandLoaded = FirstEvent,
|
||||
MaxwellCommandProcessed,
|
||||
IncomingPrimitiveBatch,
|
||||
FinishedPrimitiveBatch,
|
||||
|
||||
NumEvents
|
||||
};
|
||||
|
||||
/**
|
||||
* Inherit from this class to be notified of events registered to some debug context.
|
||||
* Most importantly this is used for our debugger GUI.
|
||||
*
|
||||
* To implement event handling, override the OnMaxwellBreakPointHit and OnMaxwellResume methods.
|
||||
* @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state
|
||||
* access
|
||||
* @todo Evaluate an alternative interface, in which there is only one managing observer and
|
||||
* multiple child observers running (by design) on the same thread.
|
||||
*/
|
||||
class BreakPointObserver {
|
||||
public:
|
||||
/// Constructs the object such that it observes events of the given DebugContext.
|
||||
explicit BreakPointObserver(std::shared_ptr<DebugContext> debug_context)
|
||||
: context_weak(debug_context) {
|
||||
std::unique_lock lock{debug_context->breakpoint_mutex};
|
||||
debug_context->breakpoint_observers.push_back(this);
|
||||
}
|
||||
|
||||
virtual ~BreakPointObserver() {
|
||||
auto context = context_weak.lock();
|
||||
if (context) {
|
||||
{
|
||||
std::unique_lock lock{context->breakpoint_mutex};
|
||||
context->breakpoint_observers.remove(this);
|
||||
}
|
||||
|
||||
// If we are the last observer to be destroyed, tell the debugger context that
|
||||
// it is free to continue. In particular, this is required for a proper yuzu
|
||||
// shutdown, when the emulation thread is waiting at a breakpoint.
|
||||
if (context->breakpoint_observers.empty())
|
||||
context->Resume();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to perform when a breakpoint was reached.
|
||||
* @param event Type of event which triggered the breakpoint
|
||||
* @param data Optional data pointer (if unused, this is a nullptr)
|
||||
* @note This function will perform nothing unless it is overridden in the child class.
|
||||
*/
|
||||
virtual void OnMaxwellBreakPointHit(Event event, void* data) {}
|
||||
|
||||
/**
|
||||
* Action to perform when emulation is resumed from a breakpoint.
|
||||
* @note This function will perform nothing unless it is overridden in the child class.
|
||||
*/
|
||||
virtual void OnMaxwellResume() {}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Weak context pointer. This need not be valid, so when requesting a shared_ptr via
|
||||
* context_weak.lock(), always compare the result against nullptr.
|
||||
*/
|
||||
std::weak_ptr<DebugContext> context_weak;
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple structure defining a breakpoint state
|
||||
*/
|
||||
struct BreakPoint {
|
||||
bool enabled = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Static constructor used to create a shared_ptr of a DebugContext.
|
||||
*/
|
||||
static std::shared_ptr<DebugContext> Construct() {
|
||||
return std::shared_ptr<DebugContext>(new DebugContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the emulation core when a given event has happened. If a breakpoint has been set
|
||||
* for this event, OnEvent calls the event handlers of the registered breakpoint observers.
|
||||
* The current thread then is halted until Resume() is called from another thread (or until
|
||||
* emulation is stopped).
|
||||
* @param event Event which has happened
|
||||
* @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until
|
||||
* Resume() is called.
|
||||
*/
|
||||
void OnEvent(Event event, void* data) {
|
||||
// This check is left in the header to allow the compiler to inline it.
|
||||
if (!breakpoints[(int)event].enabled)
|
||||
return;
|
||||
// For the rest of event handling, call a separate function.
|
||||
DoOnEvent(event, data);
|
||||
}
|
||||
|
||||
void DoOnEvent(Event event, void* data);
|
||||
|
||||
/**
|
||||
* Resume from the current breakpoint.
|
||||
* @warning Calling this from the same thread that OnEvent was called in will cause a deadlock.
|
||||
* Calling from any other thread is safe.
|
||||
*/
|
||||
void Resume();
|
||||
|
||||
/**
|
||||
* Delete all set breakpoints and resume emulation.
|
||||
*/
|
||||
void ClearBreakpoints() {
|
||||
for (auto& bp : breakpoints) {
|
||||
bp.enabled = false;
|
||||
}
|
||||
Resume();
|
||||
}
|
||||
|
||||
// TODO: Evaluate if access to these members should be hidden behind a public interface.
|
||||
std::array<BreakPoint, static_cast<int>(Event::NumEvents)> breakpoints;
|
||||
Event active_breakpoint{};
|
||||
bool at_breakpoint = false;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Private default constructor to make sure people always construct this through Construct()
|
||||
* instead.
|
||||
*/
|
||||
DebugContext() = default;
|
||||
|
||||
/// Mutex protecting current breakpoint state and the observer list.
|
||||
std::mutex breakpoint_mutex;
|
||||
|
||||
/// Used by OnEvent to wait for resumption.
|
||||
std::condition_variable resume_from_breakpoint;
|
||||
|
||||
/// List of registered observers
|
||||
std::list<BreakPointObserver*> breakpoint_observers;
|
||||
};
|
||||
|
||||
} // namespace Tegra
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/shader_type.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
@@ -88,11 +87,11 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
color_mask.A.Assign(1);
|
||||
}
|
||||
|
||||
// Commercial games seem to assume this value is enabled and nouveau sets this value manually.
|
||||
// NVN games expect these values to be enabled at boot
|
||||
regs.rasterize_enable = 1;
|
||||
regs.rt_separate_frag_data = 1;
|
||||
|
||||
// Some games (like Super Mario Odyssey) assume that SRGB is enabled.
|
||||
regs.framebuffer_srgb = 1;
|
||||
|
||||
mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_end_gl)] = true;
|
||||
mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)] = true;
|
||||
mme_inline[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true;
|
||||
@@ -273,8 +272,6 @@ void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u3
|
||||
}
|
||||
|
||||
void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
|
||||
auto debug_context = system.GetGPUDebugContext();
|
||||
|
||||
const u32 method = method_call.method;
|
||||
|
||||
if (method == cb_data_state.current) {
|
||||
@@ -315,10 +312,6 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
"Invalid Maxwell3D register, increase the size of the Regs structure");
|
||||
|
||||
if (debug_context) {
|
||||
debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr);
|
||||
}
|
||||
|
||||
if (regs.reg_array[method] != method_call.argument) {
|
||||
regs.reg_array[method] = method_call.argument;
|
||||
const std::size_t dirty_reg = dirty_pointers[method];
|
||||
@@ -424,10 +417,6 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (debug_context) {
|
||||
debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) {
|
||||
@@ -485,12 +474,6 @@ void Maxwell3D::FlushMMEInlineDraw() {
|
||||
ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?");
|
||||
ASSERT(mme_draw.instance_count == mme_draw.gl_end_count);
|
||||
|
||||
auto debug_context = system.GetGPUDebugContext();
|
||||
|
||||
if (debug_context) {
|
||||
debug_context->OnEvent(Tegra::DebugContext::Event::IncomingPrimitiveBatch, nullptr);
|
||||
}
|
||||
|
||||
// Both instance configuration registers can not be set at the same time.
|
||||
ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont,
|
||||
"Illegal combination of instancing parameters");
|
||||
@@ -500,10 +483,6 @@ void Maxwell3D::FlushMMEInlineDraw() {
|
||||
rasterizer.DrawMultiBatch(is_indexed);
|
||||
}
|
||||
|
||||
if (debug_context) {
|
||||
debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr);
|
||||
}
|
||||
|
||||
// TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
|
||||
// the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
|
||||
// it's possible that it is incorrect and that there is some other register used to specify the
|
||||
@@ -650,12 +629,6 @@ void Maxwell3D::DrawArrays() {
|
||||
regs.vertex_buffer.count);
|
||||
ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?");
|
||||
|
||||
auto debug_context = system.GetGPUDebugContext();
|
||||
|
||||
if (debug_context) {
|
||||
debug_context->OnEvent(Tegra::DebugContext::Event::IncomingPrimitiveBatch, nullptr);
|
||||
}
|
||||
|
||||
// Both instance configuration registers can not be set at the same time.
|
||||
ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont,
|
||||
"Illegal combination of instancing parameters");
|
||||
@@ -673,10 +646,6 @@ void Maxwell3D::DrawArrays() {
|
||||
rasterizer.DrawBatch(is_indexed);
|
||||
}
|
||||
|
||||
if (debug_context) {
|
||||
debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr);
|
||||
}
|
||||
|
||||
// TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
|
||||
// the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
|
||||
// it's possible that it is incorrect and that there is some other register used to specify the
|
||||
|
||||
@@ -310,6 +310,11 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
enum class DepthMode : u32 {
|
||||
MinusOneToOne = 0,
|
||||
ZeroToOne = 1,
|
||||
};
|
||||
|
||||
enum class PrimitiveTopology : u32 {
|
||||
Points = 0x0,
|
||||
Lines = 0x1,
|
||||
@@ -491,11 +496,6 @@ public:
|
||||
INSERT_UNION_PADDING_WORDS(1);
|
||||
};
|
||||
|
||||
enum class DepthMode : u32 {
|
||||
MinusOneToOne = 0,
|
||||
ZeroToOne = 1,
|
||||
};
|
||||
|
||||
enum class TessellationPrimitive : u32 {
|
||||
Isolines = 0,
|
||||
Triangles = 1,
|
||||
@@ -657,7 +657,11 @@ public:
|
||||
std::array<f32, 4> tess_level_outer;
|
||||
std::array<f32, 2> tess_level_inner;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x102);
|
||||
INSERT_UNION_PADDING_WORDS(0x10);
|
||||
|
||||
u32 rasterize_enable;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0xF1);
|
||||
|
||||
u32 tfb_enabled;
|
||||
|
||||
@@ -676,7 +680,7 @@ public:
|
||||
u32 count;
|
||||
} vertex_buffer;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(1);
|
||||
DepthMode depth_mode;
|
||||
|
||||
float clear_color[4];
|
||||
float clear_depth;
|
||||
@@ -707,13 +711,15 @@ public:
|
||||
|
||||
u32 color_mask_common;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x6);
|
||||
|
||||
u32 rt_separate_frag_data;
|
||||
INSERT_UNION_PADDING_WORDS(0x2);
|
||||
|
||||
f32 depth_bounds[2];
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0xA);
|
||||
INSERT_UNION_PADDING_WORDS(0x2);
|
||||
|
||||
u32 rt_separate_frag_data;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0xC);
|
||||
|
||||
struct {
|
||||
u32 address_high;
|
||||
@@ -1030,7 +1036,12 @@ public:
|
||||
BitField<4, 1, u32> depth_clamp_far;
|
||||
} view_volume_clip_control;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x21);
|
||||
INSERT_UNION_PADDING_WORDS(0x1F);
|
||||
|
||||
u32 depth_bounds_enable;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(1);
|
||||
|
||||
struct {
|
||||
u32 enable;
|
||||
LogicOperation operation;
|
||||
@@ -1420,11 +1431,13 @@ ASSERT_REG_POSITION(sync_info, 0xB2);
|
||||
ASSERT_REG_POSITION(tess_mode, 0xC8);
|
||||
ASSERT_REG_POSITION(tess_level_outer, 0xC9);
|
||||
ASSERT_REG_POSITION(tess_level_inner, 0xCD);
|
||||
ASSERT_REG_POSITION(rasterize_enable, 0xDF);
|
||||
ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
|
||||
ASSERT_REG_POSITION(rt, 0x200);
|
||||
ASSERT_REG_POSITION(viewport_transform, 0x280);
|
||||
ASSERT_REG_POSITION(viewports, 0x300);
|
||||
ASSERT_REG_POSITION(vertex_buffer, 0x35D);
|
||||
ASSERT_REG_POSITION(depth_mode, 0x35F);
|
||||
ASSERT_REG_POSITION(clear_color[0], 0x360);
|
||||
ASSERT_REG_POSITION(clear_depth, 0x364);
|
||||
ASSERT_REG_POSITION(clear_stencil, 0x368);
|
||||
@@ -1438,7 +1451,7 @@ ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D6);
|
||||
ASSERT_REG_POSITION(stencil_back_mask, 0x3D7);
|
||||
ASSERT_REG_POSITION(color_mask_common, 0x3E4);
|
||||
ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB);
|
||||
ASSERT_REG_POSITION(depth_bounds, 0x3EC);
|
||||
ASSERT_REG_POSITION(depth_bounds, 0x3E7);
|
||||
ASSERT_REG_POSITION(zeta, 0x3F8);
|
||||
ASSERT_REG_POSITION(clear_flags, 0x43E);
|
||||
ASSERT_REG_POSITION(vertex_attrib_format, 0x458);
|
||||
@@ -1494,6 +1507,7 @@ ASSERT_REG_POSITION(cull, 0x646);
|
||||
ASSERT_REG_POSITION(pixel_center_integer, 0x649);
|
||||
ASSERT_REG_POSITION(viewport_transform_enabled, 0x64B);
|
||||
ASSERT_REG_POSITION(view_volume_clip_control, 0x64F);
|
||||
ASSERT_REG_POSITION(depth_bounds_enable, 0x66F);
|
||||
ASSERT_REG_POSITION(logic_op, 0x671);
|
||||
ASSERT_REG_POSITION(clear_buffers, 0x674);
|
||||
ASSERT_REG_POSITION(color_mask, 0x680);
|
||||
|
||||
@@ -98,10 +98,11 @@ union Attribute {
|
||||
BitField<20, 10, u64> immediate;
|
||||
BitField<22, 2, u64> element;
|
||||
BitField<24, 6, Index> index;
|
||||
BitField<31, 1, u64> patch;
|
||||
BitField<47, 3, AttributeSize> size;
|
||||
|
||||
bool IsPhysical() const {
|
||||
return element == 0 && static_cast<u64>(index.Value()) == 0;
|
||||
return patch == 0 && element == 0 && static_cast<u64>(index.Value()) == 0;
|
||||
}
|
||||
} fmt20;
|
||||
|
||||
@@ -383,6 +384,15 @@ enum class IsberdMode : u64 {
|
||||
|
||||
enum class IsberdShift : u64 { None = 0, U16 = 1, B32 = 2 };
|
||||
|
||||
enum class MembarType : u64 {
|
||||
CTA = 0,
|
||||
GL = 1,
|
||||
SYS = 2,
|
||||
VC = 3,
|
||||
};
|
||||
|
||||
enum class MembarUnknown : u64 { Default = 0, IVALLD = 1, IVALLT = 2, IVALLTD = 3 };
|
||||
|
||||
enum class HalfType : u64 {
|
||||
H0_H1 = 0,
|
||||
F32 = 1,
|
||||
@@ -1041,7 +1051,7 @@ union Instruction {
|
||||
BitField<40, 1, R2pMode> mode;
|
||||
BitField<41, 2, u64> byte;
|
||||
BitField<20, 7, u64> immediate_mask;
|
||||
} r2p;
|
||||
} p2r_r2p;
|
||||
|
||||
union {
|
||||
BitField<39, 3, u64> pred39;
|
||||
@@ -1229,7 +1239,7 @@ union Instruction {
|
||||
BitField<35, 1, u64> ndv_flag;
|
||||
BitField<49, 1, u64> nodep_flag;
|
||||
BitField<50, 1, u64> dc_flag;
|
||||
BitField<54, 2, u64> info;
|
||||
BitField<54, 2, u64> offset_mode;
|
||||
BitField<56, 2, u64> component;
|
||||
|
||||
bool UsesMiscMode(TextureMiscMode mode) const {
|
||||
@@ -1241,9 +1251,9 @@ union Instruction {
|
||||
case TextureMiscMode::DC:
|
||||
return dc_flag != 0;
|
||||
case TextureMiscMode::AOFFI:
|
||||
return info == 1;
|
||||
return offset_mode == 1;
|
||||
case TextureMiscMode::PTP:
|
||||
return info == 2;
|
||||
return offset_mode == 2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1255,7 +1265,7 @@ union Instruction {
|
||||
BitField<35, 1, u64> ndv_flag;
|
||||
BitField<49, 1, u64> nodep_flag;
|
||||
BitField<50, 1, u64> dc_flag;
|
||||
BitField<33, 2, u64> info;
|
||||
BitField<33, 2, u64> offset_mode;
|
||||
BitField<37, 2, u64> component;
|
||||
|
||||
bool UsesMiscMode(TextureMiscMode mode) const {
|
||||
@@ -1267,9 +1277,9 @@ union Instruction {
|
||||
case TextureMiscMode::DC:
|
||||
return dc_flag != 0;
|
||||
case TextureMiscMode::AOFFI:
|
||||
return info == 1;
|
||||
return offset_mode == 1;
|
||||
case TextureMiscMode::PTP:
|
||||
return info == 2;
|
||||
return offset_mode == 2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1282,6 +1292,7 @@ union Instruction {
|
||||
BitField<50, 1, u64> dc_flag;
|
||||
BitField<51, 1, u64> aoffi_flag;
|
||||
BitField<52, 2, u64> component;
|
||||
BitField<55, 1, u64> fp16_flag;
|
||||
|
||||
bool UsesMiscMode(TextureMiscMode mode) const {
|
||||
switch (mode) {
|
||||
@@ -1544,6 +1555,11 @@ union Instruction {
|
||||
BitField<47, 2, IsberdShift> shift;
|
||||
} isberd;
|
||||
|
||||
union {
|
||||
BitField<8, 2, MembarType> type;
|
||||
BitField<0, 2, MembarUnknown> unknown;
|
||||
} membar;
|
||||
|
||||
union {
|
||||
BitField<48, 1, u64> signed_a;
|
||||
BitField<38, 1, u64> is_byte_chunk_a;
|
||||
@@ -1668,6 +1684,7 @@ public:
|
||||
IPA,
|
||||
OUT_R, // Emit vertex/primitive
|
||||
ISBERD,
|
||||
MEMBAR,
|
||||
VMAD,
|
||||
VSETP,
|
||||
FFMA_IMM, // Fused Multiply and Add
|
||||
@@ -1784,6 +1801,7 @@ public:
|
||||
PSET,
|
||||
CSETP,
|
||||
R2P_IMM,
|
||||
P2R_IMM,
|
||||
XMAD_IMM,
|
||||
XMAD_CR,
|
||||
XMAD_RC,
|
||||
@@ -1929,7 +1947,7 @@ private:
|
||||
INST("111000100100----", Id::BRA, Type::Flow, "BRA"),
|
||||
INST("111000100101----", Id::BRX, Type::Flow, "BRX"),
|
||||
INST("1111000011111---", Id::SYNC, Type::Flow, "SYNC"),
|
||||
INST("111000110100---", Id::BRK, Type::Flow, "BRK"),
|
||||
INST("111000110100----", Id::BRK, Type::Flow, "BRK"),
|
||||
INST("111000110000----", Id::EXIT, Type::Flow, "EXIT"),
|
||||
INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"),
|
||||
INST("0101000011011---", Id::VOTE, Type::Warp, "VOTE"),
|
||||
@@ -1956,7 +1974,7 @@ private:
|
||||
INST("1101-01---------", Id::TLDS, Type::Texture, "TLDS"),
|
||||
INST("110010----111---", Id::TLD4, Type::Texture, "TLD4"),
|
||||
INST("1101111011111---", Id::TLD4_B, Type::Texture, "TLD4_B"),
|
||||
INST("1101111100------", Id::TLD4S, Type::Texture, "TLD4S"),
|
||||
INST("11011111-0------", Id::TLD4S, Type::Texture, "TLD4S"),
|
||||
INST("110111110110----", Id::TMML_B, Type::Texture, "TMML_B"),
|
||||
INST("1101111101011---", Id::TMML, Type::Texture, "TMML"),
|
||||
INST("11011110011110--", Id::TXD_B, Type::Texture, "TXD_B"),
|
||||
@@ -1968,6 +1986,7 @@ private:
|
||||
INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
|
||||
INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"),
|
||||
INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"),
|
||||
INST("1110111110011---", Id::MEMBAR, Type::Trivial, "MEMBAR"),
|
||||
INST("01011111--------", Id::VMAD, Type::Video, "VMAD"),
|
||||
INST("0101000011110---", Id::VSETP, Type::Video, "VSETP"),
|
||||
INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
|
||||
@@ -2088,6 +2107,7 @@ private:
|
||||
INST("0101000010010---", Id::PSETP, Type::PredicateSetPredicate, "PSETP"),
|
||||
INST("010100001010----", Id::CSETP, Type::PredicateSetPredicate, "CSETP"),
|
||||
INST("0011100-11110---", Id::R2P_IMM, Type::RegisterSetPredicate, "R2P_IMM"),
|
||||
INST("0011100-11101---", Id::P2R_IMM, Type::RegisterSetPredicate, "P2R_IMM"),
|
||||
INST("0011011-00------", Id::XMAD_IMM, Type::Xmad, "XMAD_IMM"),
|
||||
INST("0100111---------", Id::XMAD_CR, Type::Xmad, "XMAD_CR"),
|
||||
INST("010100010-------", Id::XMAD_RC, Type::Xmad, "XMAD_RC"),
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <mutex>
|
||||
|
||||
#include <boost/icl/interval_map.hpp>
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
@@ -134,11 +135,13 @@ std::array<Device::BaseBindings, Tegra::Engines::MaxShaderTypes> BuildBaseBindin
|
||||
|
||||
Device::Device() : base_bindings{BuildBaseBindings()} {
|
||||
const std::string_view vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
|
||||
const auto renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
|
||||
const std::vector extensions = GetExtensions();
|
||||
|
||||
const bool is_nvidia = vendor == "NVIDIA Corporation";
|
||||
const bool is_amd = vendor == "ATI Technologies Inc.";
|
||||
const bool is_intel = vendor == "Intel";
|
||||
const bool is_intel_proprietary = is_intel && std::strstr(renderer, "Mesa") == nullptr;
|
||||
|
||||
uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
|
||||
shader_storage_alignment = GetInteger<std::size_t>(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT);
|
||||
@@ -152,7 +155,7 @@ Device::Device() : base_bindings{BuildBaseBindings()} {
|
||||
has_variable_aoffi = TestVariableAoffi();
|
||||
has_component_indexing_bug = is_amd;
|
||||
has_precise_bug = TestPreciseBug();
|
||||
has_broken_compute = is_intel;
|
||||
has_broken_compute = is_intel_proprietary;
|
||||
has_fast_buffer_sub_data = is_nvidia;
|
||||
|
||||
LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi);
|
||||
|
||||
@@ -271,12 +271,23 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
case Maxwell::ShaderProgram::Geometry:
|
||||
shader_program_manager->UseTrivialGeometryShader();
|
||||
break;
|
||||
case Maxwell::ShaderProgram::Fragment:
|
||||
shader_program_manager->UseTrivialFragmentShader();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Currently this stages are not supported in the OpenGL backend.
|
||||
// Todo(Blinkhawk): Port tesselation shaders from Vulkan to OpenGL
|
||||
if (program == Maxwell::ShaderProgram::TesselationControl) {
|
||||
continue;
|
||||
} else if (program == Maxwell::ShaderProgram::TesselationEval) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Shader shader{shader_cache.GetStageProgram(program)};
|
||||
|
||||
// Stage indices are 0 - 5
|
||||
@@ -506,6 +517,7 @@ void RasterizerOpenGL::Clear() {
|
||||
ConfigureClearFramebuffer(clear_state, use_color, use_depth, use_stencil);
|
||||
|
||||
SyncViewport(clear_state);
|
||||
SyncRasterizeEnable(clear_state);
|
||||
if (regs.clear_flags.scissor) {
|
||||
SyncScissorTest(clear_state);
|
||||
}
|
||||
@@ -533,6 +545,7 @@ void RasterizerOpenGL::Clear() {
|
||||
void RasterizerOpenGL::DrawPrelude() {
|
||||
auto& gpu = system.GPU().Maxwell3D();
|
||||
|
||||
SyncRasterizeEnable(state);
|
||||
SyncColorMask();
|
||||
SyncFragmentColorClampState();
|
||||
SyncMultiSampleState();
|
||||
@@ -1028,6 +1041,10 @@ void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
|
||||
flip_y = !flip_y;
|
||||
}
|
||||
state.clip_control.origin = flip_y ? GL_UPPER_LEFT : GL_LOWER_LEFT;
|
||||
state.clip_control.depth_mode =
|
||||
regs.depth_mode == Tegra::Engines::Maxwell3D::Regs::DepthMode::ZeroToOne
|
||||
? GL_ZERO_TO_ONE
|
||||
: GL_NEGATIVE_ONE_TO_ONE;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncClipEnabled(
|
||||
@@ -1121,6 +1138,11 @@ void RasterizerOpenGL::SyncStencilTestState() {
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncRasterizeEnable(OpenGLState& current_state) {
|
||||
const auto& regs = system.GPU().Maxwell3D().regs;
|
||||
current_state.rasterizer_discard = regs.rasterize_enable == 0;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncColorMask() {
|
||||
auto& maxwell3d = system.GPU().Maxwell3D();
|
||||
if (!maxwell3d.dirty.color_mask) {
|
||||
|
||||
@@ -168,6 +168,9 @@ private:
|
||||
/// Syncs the point state to match the guest state
|
||||
void SyncPointState();
|
||||
|
||||
/// Syncs the rasterizer enable state to match the guest state
|
||||
void SyncRasterizeEnable(OpenGLState& current_state);
|
||||
|
||||
/// Syncs Color Mask
|
||||
void SyncColorMask();
|
||||
|
||||
|
||||
@@ -112,25 +112,25 @@ constexpr GLenum GetGLShaderType(ShaderType shader_type) {
|
||||
}
|
||||
|
||||
/// Describes primitive behavior on geometry shaders
|
||||
constexpr std::tuple<const char*, const char*, u32> GetPrimitiveDescription(GLenum primitive_mode) {
|
||||
constexpr std::pair<const char*, u32> GetPrimitiveDescription(GLenum primitive_mode) {
|
||||
switch (primitive_mode) {
|
||||
case GL_POINTS:
|
||||
return {"points", "Points", 1};
|
||||
return {"points", 1};
|
||||
case GL_LINES:
|
||||
case GL_LINE_STRIP:
|
||||
return {"lines", "Lines", 2};
|
||||
return {"lines", 2};
|
||||
case GL_LINES_ADJACENCY:
|
||||
case GL_LINE_STRIP_ADJACENCY:
|
||||
return {"lines_adjacency", "LinesAdj", 4};
|
||||
return {"lines_adjacency", 4};
|
||||
case GL_TRIANGLES:
|
||||
case GL_TRIANGLE_STRIP:
|
||||
case GL_TRIANGLE_FAN:
|
||||
return {"triangles", "Triangles", 3};
|
||||
return {"triangles", 3};
|
||||
case GL_TRIANGLES_ADJACENCY:
|
||||
case GL_TRIANGLE_STRIP_ADJACENCY:
|
||||
return {"triangles_adjacency", "TrianglesAdj", 6};
|
||||
return {"triangles_adjacency", 6};
|
||||
default:
|
||||
return {"points", "Invalid", 1};
|
||||
return {"points", 1};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,29 +264,24 @@ CachedProgram BuildShader(const Device& device, u64 unique_identifier, ShaderTyp
|
||||
"#extension GL_NV_shader_thread_group : require\n"
|
||||
"#extension GL_NV_shader_thread_shuffle : require\n";
|
||||
}
|
||||
source += '\n';
|
||||
|
||||
if (shader_type == ShaderType::Geometry) {
|
||||
const auto [glsl_topology, debug_name, max_vertices] =
|
||||
GetPrimitiveDescription(variant.primitive_mode);
|
||||
|
||||
source += fmt::format("layout ({}) in;\n\n", glsl_topology);
|
||||
const auto [glsl_topology, max_vertices] = GetPrimitiveDescription(variant.primitive_mode);
|
||||
source += fmt::format("#define MAX_VERTEX_INPUT {}\n", max_vertices);
|
||||
source += fmt::format("layout ({}) in;\n", glsl_topology);
|
||||
}
|
||||
if (shader_type == ShaderType::Compute) {
|
||||
if (variant.local_memory_size > 0) {
|
||||
source += fmt::format("#define LOCAL_MEMORY_SIZE {}\n",
|
||||
Common::AlignUp(variant.local_memory_size, 4) / 4);
|
||||
}
|
||||
source +=
|
||||
fmt::format("layout (local_size_x = {}, local_size_y = {}, local_size_z = {}) in;\n",
|
||||
variant.block_x, variant.block_y, variant.block_z);
|
||||
|
||||
if (variant.shared_memory_size > 0) {
|
||||
// TODO(Rodrigo): We should divide by four here, but having a larger shared memory pool
|
||||
// avoids out of bound stores. Find out why shared memory size is being invalid.
|
||||
source += fmt::format("shared uint smem[{}];", variant.shared_memory_size);
|
||||
}
|
||||
|
||||
if (variant.local_memory_size > 0) {
|
||||
source += fmt::format("#define LOCAL_MEMORY_SIZE {}",
|
||||
Common::AlignUp(variant.local_memory_size, 4) / 4);
|
||||
// shared_memory_size is described in number of words
|
||||
source += fmt::format("shared uint smem[{}];\n", variant.shared_memory_size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,10 +48,10 @@ class ExprDecompiler;
|
||||
|
||||
enum class Type { Void, Bool, Bool2, Float, Int, Uint, HalfFloat };
|
||||
|
||||
struct TextureAoffi {};
|
||||
struct TextureOffset {};
|
||||
struct TextureDerivates {};
|
||||
using TextureArgument = std::pair<Type, Node>;
|
||||
using TextureIR = std::variant<TextureAoffi, TextureDerivates, TextureArgument>;
|
||||
using TextureIR = std::variant<TextureOffset, TextureDerivates, TextureArgument>;
|
||||
|
||||
constexpr u32 MAX_CONSTBUFFER_ELEMENTS =
|
||||
static_cast<u32>(Maxwell::MaxConstBufferSize) / (4 * sizeof(float));
|
||||
@@ -399,6 +399,7 @@ public:
|
||||
DeclareConstantBuffers();
|
||||
DeclareGlobalMemory();
|
||||
DeclareSamplers();
|
||||
DeclareImages();
|
||||
DeclarePhysicalAttributeReader();
|
||||
|
||||
code.AddLine("void execute_{}() {{", suffix);
|
||||
@@ -1076,7 +1077,7 @@ private:
|
||||
}
|
||||
|
||||
std::string GenerateTexture(Operation operation, const std::string& function_suffix,
|
||||
const std::vector<TextureIR>& extras) {
|
||||
const std::vector<TextureIR>& extras, bool separate_dc = false) {
|
||||
constexpr std::array coord_constructors = {"float", "vec2", "vec3", "vec4"};
|
||||
|
||||
const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
|
||||
@@ -1089,9 +1090,12 @@ private:
|
||||
std::string expr = "texture" + function_suffix;
|
||||
if (!meta->aoffi.empty()) {
|
||||
expr += "Offset";
|
||||
} else if (!meta->ptp.empty()) {
|
||||
expr += "Offsets";
|
||||
}
|
||||
expr += '(' + GetSampler(meta->sampler) + ", ";
|
||||
expr += coord_constructors.at(count + (has_array ? 1 : 0) + (has_shadow ? 1 : 0) - 1);
|
||||
expr += coord_constructors.at(count + (has_array ? 1 : 0) +
|
||||
(has_shadow && !separate_dc ? 1 : 0) - 1);
|
||||
expr += '(';
|
||||
for (std::size_t i = 0; i < count; ++i) {
|
||||
expr += Visit(operation[i]).AsFloat();
|
||||
@@ -1104,15 +1108,24 @@ private:
|
||||
expr += ", float(" + Visit(meta->array).AsInt() + ')';
|
||||
}
|
||||
if (has_shadow) {
|
||||
expr += ", " + Visit(meta->depth_compare).AsFloat();
|
||||
if (separate_dc) {
|
||||
expr += "), " + Visit(meta->depth_compare).AsFloat();
|
||||
} else {
|
||||
expr += ", " + Visit(meta->depth_compare).AsFloat() + ')';
|
||||
}
|
||||
} else {
|
||||
expr += ')';
|
||||
}
|
||||
expr += ')';
|
||||
|
||||
for (const auto& variant : extras) {
|
||||
if (const auto argument = std::get_if<TextureArgument>(&variant)) {
|
||||
expr += GenerateTextureArgument(*argument);
|
||||
} else if (std::holds_alternative<TextureAoffi>(variant)) {
|
||||
expr += GenerateTextureAoffi(meta->aoffi);
|
||||
} else if (std::holds_alternative<TextureOffset>(variant)) {
|
||||
if (!meta->aoffi.empty()) {
|
||||
expr += GenerateTextureAoffi(meta->aoffi);
|
||||
} else if (!meta->ptp.empty()) {
|
||||
expr += GenerateTexturePtp(meta->ptp);
|
||||
}
|
||||
} else if (std::holds_alternative<TextureDerivates>(variant)) {
|
||||
expr += GenerateTextureDerivates(meta->derivates);
|
||||
} else {
|
||||
@@ -1153,6 +1166,20 @@ private:
|
||||
return expr;
|
||||
}
|
||||
|
||||
std::string ReadTextureOffset(const Node& value) {
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(&*value)) {
|
||||
// Inline the string as an immediate integer in GLSL (AOFFI arguments are required
|
||||
// to be constant by the standard).
|
||||
return std::to_string(static_cast<s32>(immediate->GetValue()));
|
||||
} else if (device.HasVariableAoffi()) {
|
||||
// Avoid using variable AOFFI on unsupported devices.
|
||||
return Visit(value).AsInt();
|
||||
} else {
|
||||
// Insert 0 on devices not supporting variable AOFFI.
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
|
||||
std::string GenerateTextureAoffi(const std::vector<Node>& aoffi) {
|
||||
if (aoffi.empty()) {
|
||||
return {};
|
||||
@@ -1163,18 +1190,7 @@ private:
|
||||
expr += '(';
|
||||
|
||||
for (std::size_t index = 0; index < aoffi.size(); ++index) {
|
||||
const auto operand{aoffi.at(index)};
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(&*operand)) {
|
||||
// Inline the string as an immediate integer in GLSL (AOFFI arguments are required
|
||||
// to be constant by the standard).
|
||||
expr += std::to_string(static_cast<s32>(immediate->GetValue()));
|
||||
} else if (device.HasVariableAoffi()) {
|
||||
// Avoid using variable AOFFI on unsupported devices.
|
||||
expr += Visit(operand).AsInt();
|
||||
} else {
|
||||
// Insert 0 on devices not supporting variable AOFFI.
|
||||
expr += '0';
|
||||
}
|
||||
expr += ReadTextureOffset(aoffi.at(index));
|
||||
if (index + 1 < aoffi.size()) {
|
||||
expr += ", ";
|
||||
}
|
||||
@@ -1184,6 +1200,20 @@ private:
|
||||
return expr;
|
||||
}
|
||||
|
||||
std::string GenerateTexturePtp(const std::vector<Node>& ptp) {
|
||||
static constexpr std::size_t num_vectors = 4;
|
||||
ASSERT(ptp.size() == num_vectors * 2);
|
||||
|
||||
std::string expr = ", ivec2[](";
|
||||
for (std::size_t vector = 0; vector < num_vectors; ++vector) {
|
||||
const bool has_next = vector + 1 < num_vectors;
|
||||
expr += fmt::format("ivec2({}, {}){}", ReadTextureOffset(ptp.at(vector * 2)),
|
||||
ReadTextureOffset(ptp.at(vector * 2 + 1)), has_next ? ", " : "");
|
||||
}
|
||||
expr += ')';
|
||||
return expr;
|
||||
}
|
||||
|
||||
std::string GenerateTextureDerivates(const std::vector<Node>& derivates) {
|
||||
if (derivates.empty()) {
|
||||
return {};
|
||||
@@ -1682,7 +1712,7 @@ private:
|
||||
ASSERT(meta);
|
||||
|
||||
std::string expr = GenerateTexture(
|
||||
operation, "", {TextureAoffi{}, TextureArgument{Type::Float, meta->bias}});
|
||||
operation, "", {TextureOffset{}, TextureArgument{Type::Float, meta->bias}});
|
||||
if (meta->sampler.IsShadow()) {
|
||||
expr = "vec4(" + expr + ')';
|
||||
}
|
||||
@@ -1694,7 +1724,7 @@ private:
|
||||
ASSERT(meta);
|
||||
|
||||
std::string expr = GenerateTexture(
|
||||
operation, "Lod", {TextureArgument{Type::Float, meta->lod}, TextureAoffi{}});
|
||||
operation, "Lod", {TextureArgument{Type::Float, meta->lod}, TextureOffset{}});
|
||||
if (meta->sampler.IsShadow()) {
|
||||
expr = "vec4(" + expr + ')';
|
||||
}
|
||||
@@ -1702,13 +1732,18 @@ private:
|
||||
}
|
||||
|
||||
Expression TextureGather(Operation operation) {
|
||||
const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
|
||||
ASSERT(meta);
|
||||
const auto& meta = std::get<MetaTexture>(operation.GetMeta());
|
||||
|
||||
const auto type = meta->sampler.IsShadow() ? Type::Float : Type::Int;
|
||||
return {GenerateTexture(operation, "Gather",
|
||||
{TextureAoffi{}, TextureArgument{type, meta->component}}) +
|
||||
GetSwizzle(meta->element),
|
||||
const auto type = meta.sampler.IsShadow() ? Type::Float : Type::Int;
|
||||
const bool separate_dc = meta.sampler.IsShadow();
|
||||
|
||||
std::vector<TextureIR> ir;
|
||||
if (meta.sampler.IsShadow()) {
|
||||
ir = {TextureOffset{}};
|
||||
} else {
|
||||
ir = {TextureOffset{}, TextureArgument{type, meta.component}};
|
||||
}
|
||||
return {GenerateTexture(operation, "Gather", ir, separate_dc) + GetSwizzle(meta.element),
|
||||
Type::Float};
|
||||
}
|
||||
|
||||
@@ -1780,7 +1815,8 @@ private:
|
||||
const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
|
||||
ASSERT(meta);
|
||||
|
||||
std::string expr = GenerateTexture(operation, "Grad", {TextureDerivates{}, TextureAoffi{}});
|
||||
std::string expr =
|
||||
GenerateTexture(operation, "Grad", {TextureDerivates{}, TextureOffset{}});
|
||||
return {std::move(expr) + GetSwizzle(meta->element), Type::Float};
|
||||
}
|
||||
|
||||
@@ -1915,6 +1951,10 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
Expression InvocationId(Operation operation) {
|
||||
return {"gl_InvocationID", Type::Int};
|
||||
}
|
||||
|
||||
Expression YNegate(Operation operation) {
|
||||
return {"y_direction", Type::Float};
|
||||
}
|
||||
@@ -1988,6 +2028,11 @@ private:
|
||||
return {fmt::format("readInvocationARB({}, {})", value, index), Type::Float};
|
||||
}
|
||||
|
||||
Expression MemoryBarrierGL(Operation) {
|
||||
code.AddLine("memoryBarrier();");
|
||||
return {};
|
||||
}
|
||||
|
||||
struct Func final {
|
||||
Func() = delete;
|
||||
~Func() = delete;
|
||||
@@ -2153,6 +2198,7 @@ private:
|
||||
&GLSLDecompiler::EmitVertex,
|
||||
&GLSLDecompiler::EndPrimitive,
|
||||
|
||||
&GLSLDecompiler::InvocationId,
|
||||
&GLSLDecompiler::YNegate,
|
||||
&GLSLDecompiler::LocalInvocationId<0>,
|
||||
&GLSLDecompiler::LocalInvocationId<1>,
|
||||
@@ -2168,6 +2214,8 @@ private:
|
||||
|
||||
&GLSLDecompiler::ThreadId,
|
||||
&GLSLDecompiler::ShuffleIndexed,
|
||||
|
||||
&GLSLDecompiler::MemoryBarrierGL,
|
||||
};
|
||||
static_assert(operation_decompilers.size() == static_cast<std::size_t>(OperationCode::Amount));
|
||||
|
||||
|
||||
@@ -50,6 +50,10 @@ public:
|
||||
current_state.geometry_shader = 0;
|
||||
}
|
||||
|
||||
void UseTrivialFragmentShader() {
|
||||
current_state.fragment_shader = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
struct PipelineState {
|
||||
bool operator==(const PipelineState& rhs) const {
|
||||
|
||||
@@ -182,6 +182,10 @@ void OpenGLState::ApplyCulling() {
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyRasterizerDiscard() {
|
||||
Enable(GL_RASTERIZER_DISCARD, cur_state.rasterizer_discard, rasterizer_discard);
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyColorMask() {
|
||||
if (!dirty.color_mask) {
|
||||
return;
|
||||
@@ -411,8 +415,9 @@ void OpenGLState::ApplyAlphaTest() {
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyClipControl() {
|
||||
if (UpdateValue(cur_state.clip_control.origin, clip_control.origin)) {
|
||||
glClipControl(clip_control.origin, GL_NEGATIVE_ONE_TO_ONE);
|
||||
if (UpdateTie(std::tie(cur_state.clip_control.origin, cur_state.clip_control.depth_mode),
|
||||
std::tie(clip_control.origin, clip_control.depth_mode))) {
|
||||
glClipControl(clip_control.origin, clip_control.depth_mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,6 +459,7 @@ void OpenGLState::Apply() {
|
||||
ApplyPointSize();
|
||||
ApplyFragmentColorClamp();
|
||||
ApplyMultisample();
|
||||
ApplyRasterizerDiscard();
|
||||
ApplyColorMask();
|
||||
ApplyDepthClamp();
|
||||
ApplyViewport();
|
||||
|
||||
@@ -48,6 +48,8 @@ public:
|
||||
GLuint index = 0;
|
||||
} primitive_restart; // GL_PRIMITIVE_RESTART
|
||||
|
||||
bool rasterizer_discard = false; // GL_RASTERIZER_DISCARD
|
||||
|
||||
struct ColorMask {
|
||||
GLboolean red_enabled = GL_TRUE;
|
||||
GLboolean green_enabled = GL_TRUE;
|
||||
@@ -56,6 +58,7 @@ public:
|
||||
};
|
||||
std::array<ColorMask, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets>
|
||||
color_mask; // GL_COLOR_WRITEMASK
|
||||
|
||||
struct {
|
||||
bool test_enabled = false; // GL_STENCIL_TEST
|
||||
struct {
|
||||
@@ -150,6 +153,7 @@ public:
|
||||
|
||||
struct {
|
||||
GLenum origin = GL_LOWER_LEFT;
|
||||
GLenum depth_mode = GL_NEGATIVE_ONE_TO_ONE;
|
||||
} clip_control;
|
||||
|
||||
OpenGLState();
|
||||
@@ -173,6 +177,7 @@ public:
|
||||
void ApplyMultisample();
|
||||
void ApplySRgb();
|
||||
void ApplyCulling();
|
||||
void ApplyRasterizerDiscard();
|
||||
void ApplyColorMask();
|
||||
void ApplyDepth();
|
||||
void ApplyPrimitiveRestart();
|
||||
|
||||
@@ -120,6 +120,8 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
|
||||
return GL_POINTS;
|
||||
case Maxwell::PrimitiveTopology::Lines:
|
||||
return GL_LINES;
|
||||
case Maxwell::PrimitiveTopology::LineLoop:
|
||||
return GL_LINE_LOOP;
|
||||
case Maxwell::PrimitiveTopology::LineStrip:
|
||||
return GL_LINE_STRIP;
|
||||
case Maxwell::PrimitiveTopology::Triangles:
|
||||
@@ -130,11 +132,23 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
|
||||
return GL_TRIANGLE_FAN;
|
||||
case Maxwell::PrimitiveTopology::Quads:
|
||||
return GL_QUADS;
|
||||
default:
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented topology={}", static_cast<u32>(topology));
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
case Maxwell::PrimitiveTopology::QuadStrip:
|
||||
return GL_QUAD_STRIP;
|
||||
case Maxwell::PrimitiveTopology::Polygon:
|
||||
return GL_POLYGON;
|
||||
case Maxwell::PrimitiveTopology::LinesAdjacency:
|
||||
return GL_LINES_ADJACENCY;
|
||||
case Maxwell::PrimitiveTopology::LineStripAdjacency:
|
||||
return GL_LINE_STRIP_ADJACENCY;
|
||||
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
|
||||
return GL_TRIANGLES_ADJACENCY;
|
||||
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
|
||||
return GL_TRIANGLE_STRIP_ADJACENCY;
|
||||
case Maxwell::PrimitiveTopology::Patches:
|
||||
return GL_PATCHES;
|
||||
}
|
||||
UNREACHABLE_MSG("Invalid topology={}", static_cast<int>(topology));
|
||||
return GL_POINTS;
|
||||
}
|
||||
|
||||
inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
|
||||
|
||||
@@ -24,19 +24,21 @@
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
static const char vertex_shader[] = R"(
|
||||
#version 150 core
|
||||
namespace {
|
||||
|
||||
in vec2 vert_position;
|
||||
in vec2 vert_tex_coord;
|
||||
out vec2 frag_tex_coord;
|
||||
constexpr char vertex_shader[] = R"(
|
||||
#version 430 core
|
||||
|
||||
layout (location = 0) in vec2 vert_position;
|
||||
layout (location = 1) in vec2 vert_tex_coord;
|
||||
layout (location = 0) out vec2 frag_tex_coord;
|
||||
|
||||
// This is a truncated 3x3 matrix for 2D transformations:
|
||||
// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
|
||||
// The third column performs translation.
|
||||
// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
|
||||
// implicitly be [0, 0, 1]
|
||||
uniform mat3x2 modelview_matrix;
|
||||
layout (location = 0) uniform mat3x2 modelview_matrix;
|
||||
|
||||
void main() {
|
||||
// Multiply input position by the rotscale part of the matrix and then manually translate by
|
||||
@@ -47,34 +49,29 @@ void main() {
|
||||
}
|
||||
)";
|
||||
|
||||
static const char fragment_shader[] = R"(
|
||||
#version 150 core
|
||||
constexpr char fragment_shader[] = R"(
|
||||
#version 430 core
|
||||
|
||||
in vec2 frag_tex_coord;
|
||||
out vec4 color;
|
||||
layout (location = 0) in vec2 frag_tex_coord;
|
||||
layout (location = 0) out vec4 color;
|
||||
|
||||
uniform sampler2D color_texture;
|
||||
layout (binding = 0) uniform sampler2D color_texture;
|
||||
|
||||
void main() {
|
||||
// Swap RGBA -> ABGR so we don't have to do this on the CPU. This needs to change if we have to
|
||||
// support more framebuffer pixel formats.
|
||||
color = texture(color_texture, frag_tex_coord);
|
||||
}
|
||||
)";
|
||||
|
||||
/**
|
||||
* Vertex structure that the drawn screen rectangles are composed of.
|
||||
*/
|
||||
struct ScreenRectVertex {
|
||||
ScreenRectVertex(GLfloat x, GLfloat y, GLfloat u, GLfloat v) {
|
||||
position[0] = x;
|
||||
position[1] = y;
|
||||
tex_coord[0] = u;
|
||||
tex_coord[1] = v;
|
||||
}
|
||||
constexpr GLint PositionLocation = 0;
|
||||
constexpr GLint TexCoordLocation = 1;
|
||||
constexpr GLint ModelViewMatrixLocation = 0;
|
||||
|
||||
GLfloat position[2];
|
||||
GLfloat tex_coord[2];
|
||||
struct ScreenRectVertex {
|
||||
constexpr ScreenRectVertex(GLfloat x, GLfloat y, GLfloat u, GLfloat v)
|
||||
: position{{x, y}}, tex_coord{{u, v}} {}
|
||||
|
||||
std::array<GLfloat, 2> position;
|
||||
std::array<GLfloat, 2> tex_coord;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -84,18 +81,82 @@ struct ScreenRectVertex {
|
||||
* The projection part of the matrix is trivial, hence these operations are represented
|
||||
* by a 3x2 matrix.
|
||||
*/
|
||||
static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, const float height) {
|
||||
std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(float width, float height) {
|
||||
std::array<GLfloat, 3 * 2> matrix; // Laid out in column-major order
|
||||
|
||||
// clang-format off
|
||||
matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f;
|
||||
matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f;
|
||||
matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f;
|
||||
matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f;
|
||||
// Last matrix row is implicitly assumed to be [0, 0, 1].
|
||||
// clang-format on
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
const char* GetSource(GLenum source) {
|
||||
switch (source) {
|
||||
case GL_DEBUG_SOURCE_API:
|
||||
return "API";
|
||||
case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
|
||||
return "WINDOW_SYSTEM";
|
||||
case GL_DEBUG_SOURCE_SHADER_COMPILER:
|
||||
return "SHADER_COMPILER";
|
||||
case GL_DEBUG_SOURCE_THIRD_PARTY:
|
||||
return "THIRD_PARTY";
|
||||
case GL_DEBUG_SOURCE_APPLICATION:
|
||||
return "APPLICATION";
|
||||
case GL_DEBUG_SOURCE_OTHER:
|
||||
return "OTHER";
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return "Unknown source";
|
||||
}
|
||||
}
|
||||
|
||||
const char* GetType(GLenum type) {
|
||||
switch (type) {
|
||||
case GL_DEBUG_TYPE_ERROR:
|
||||
return "ERROR";
|
||||
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
|
||||
return "DEPRECATED_BEHAVIOR";
|
||||
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
|
||||
return "UNDEFINED_BEHAVIOR";
|
||||
case GL_DEBUG_TYPE_PORTABILITY:
|
||||
return "PORTABILITY";
|
||||
case GL_DEBUG_TYPE_PERFORMANCE:
|
||||
return "PERFORMANCE";
|
||||
case GL_DEBUG_TYPE_OTHER:
|
||||
return "OTHER";
|
||||
case GL_DEBUG_TYPE_MARKER:
|
||||
return "MARKER";
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return "Unknown type";
|
||||
}
|
||||
}
|
||||
|
||||
void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
|
||||
const GLchar* message, const void* user_param) {
|
||||
const char format[] = "{} {} {}: {}";
|
||||
const char* const str_source = GetSource(source);
|
||||
const char* const str_type = GetType(type);
|
||||
|
||||
switch (severity) {
|
||||
case GL_DEBUG_SEVERITY_HIGH:
|
||||
LOG_CRITICAL(Render_OpenGL, format, str_source, str_type, id, message);
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_MEDIUM:
|
||||
LOG_WARNING(Render_OpenGL, format, str_source, str_type, id, message);
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_NOTIFICATION:
|
||||
case GL_DEBUG_SEVERITY_LOW:
|
||||
LOG_DEBUG(Render_OpenGL, format, str_source, str_type, id, message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system)
|
||||
: VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system} {}
|
||||
|
||||
@@ -138,9 +199,6 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
prev_state.Apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads framebuffer from emulated memory into the active OpenGL texture.
|
||||
*/
|
||||
void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) {
|
||||
// Framebuffer orientation handling
|
||||
framebuffer_transform_flags = framebuffer.transform_flags;
|
||||
@@ -181,19 +239,12 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills active OpenGL texture with the given RGB color. Since the color is solid, the texture can
|
||||
* be 1x1 but will stretch across whatever it's rendered on.
|
||||
*/
|
||||
void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a,
|
||||
const TextureInfo& texture) {
|
||||
const u8 framebuffer_data[4] = {color_a, color_b, color_g, color_r};
|
||||
glClearTexImage(texture.resource.handle, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the OpenGL state and creates persistent objects.
|
||||
*/
|
||||
void RendererOpenGL::InitOpenGLObjects() {
|
||||
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||
0.0f);
|
||||
@@ -203,10 +254,6 @@ void RendererOpenGL::InitOpenGLObjects() {
|
||||
state.draw.shader_program = shader.handle;
|
||||
state.AllDirty();
|
||||
state.Apply();
|
||||
uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix");
|
||||
uniform_color_texture = glGetUniformLocation(shader.handle, "color_texture");
|
||||
attrib_position = glGetAttribLocation(shader.handle, "vert_position");
|
||||
attrib_tex_coord = glGetAttribLocation(shader.handle, "vert_tex_coord");
|
||||
|
||||
// Generate VBO handle for drawing
|
||||
vertex_buffer.Create();
|
||||
@@ -217,14 +264,14 @@ void RendererOpenGL::InitOpenGLObjects() {
|
||||
|
||||
// Attach vertex data to VAO
|
||||
glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW);
|
||||
glVertexArrayAttribFormat(vertex_array.handle, attrib_position, 2, GL_FLOAT, GL_FALSE,
|
||||
glVertexArrayAttribFormat(vertex_array.handle, PositionLocation, 2, GL_FLOAT, GL_FALSE,
|
||||
offsetof(ScreenRectVertex, position));
|
||||
glVertexArrayAttribFormat(vertex_array.handle, attrib_tex_coord, 2, GL_FLOAT, GL_FALSE,
|
||||
glVertexArrayAttribFormat(vertex_array.handle, TexCoordLocation, 2, GL_FLOAT, GL_FALSE,
|
||||
offsetof(ScreenRectVertex, tex_coord));
|
||||
glVertexArrayAttribBinding(vertex_array.handle, attrib_position, 0);
|
||||
glVertexArrayAttribBinding(vertex_array.handle, attrib_tex_coord, 0);
|
||||
glEnableVertexArrayAttrib(vertex_array.handle, attrib_position);
|
||||
glEnableVertexArrayAttrib(vertex_array.handle, attrib_tex_coord);
|
||||
glVertexArrayAttribBinding(vertex_array.handle, PositionLocation, 0);
|
||||
glVertexArrayAttribBinding(vertex_array.handle, TexCoordLocation, 0);
|
||||
glEnableVertexArrayAttrib(vertex_array.handle, PositionLocation);
|
||||
glEnableVertexArrayAttrib(vertex_array.handle, TexCoordLocation);
|
||||
glVertexArrayVertexBuffer(vertex_array.handle, 0, vertex_buffer.handle, 0,
|
||||
sizeof(ScreenRectVertex));
|
||||
|
||||
@@ -331,18 +378,18 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
|
||||
static_cast<f32>(screen_info.texture.height);
|
||||
}
|
||||
|
||||
std::array<ScreenRectVertex, 4> vertices = {{
|
||||
const std::array vertices = {
|
||||
ScreenRectVertex(x, y, texcoords.top * scale_u, left * scale_v),
|
||||
ScreenRectVertex(x + w, y, texcoords.bottom * scale_u, left * scale_v),
|
||||
ScreenRectVertex(x, y + h, texcoords.top * scale_u, right * scale_v),
|
||||
ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, right * scale_v),
|
||||
}};
|
||||
};
|
||||
|
||||
state.textures[0] = screen_info.display_texture;
|
||||
state.framebuffer_srgb.enabled = screen_info.display_srgb;
|
||||
state.AllDirty();
|
||||
state.Apply();
|
||||
glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), vertices.data());
|
||||
glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
// Restore default state
|
||||
state.framebuffer_srgb.enabled = false;
|
||||
@@ -351,9 +398,6 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
|
||||
state.Apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the emulated screens to the emulator window.
|
||||
*/
|
||||
void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
|
||||
if (renderer_settings.set_background_color) {
|
||||
// Update background color before drawing
|
||||
@@ -367,21 +411,17 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// Set projection matrix
|
||||
std::array<GLfloat, 3 * 2> ortho_matrix =
|
||||
MakeOrthographicMatrix((float)layout.width, (float)layout.height);
|
||||
glUniformMatrix3x2fv(uniform_modelview_matrix, 1, GL_FALSE, ortho_matrix.data());
|
||||
const std::array ortho_matrix =
|
||||
MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
|
||||
glUniformMatrix3x2fv(ModelViewMatrixLocation, 1, GL_FALSE, ortho_matrix.data());
|
||||
|
||||
// Bind texture in Texture Unit 0
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glUniform1i(uniform_color_texture, 0);
|
||||
|
||||
DrawScreenTriangles(screen_info, (float)screen.left, (float)screen.top,
|
||||
(float)screen.GetWidth(), (float)screen.GetHeight());
|
||||
DrawScreenTriangles(screen_info, static_cast<float>(screen.left),
|
||||
static_cast<float>(screen.top), static_cast<float>(screen.GetWidth()),
|
||||
static_cast<float>(screen.GetHeight()));
|
||||
|
||||
m_current_frame++;
|
||||
}
|
||||
|
||||
/// Updates the framerate
|
||||
void RendererOpenGL::UpdateFramerate() {}
|
||||
|
||||
void RendererOpenGL::CaptureScreenshot() {
|
||||
@@ -418,63 +458,6 @@ void RendererOpenGL::CaptureScreenshot() {
|
||||
renderer_settings.screenshot_requested = false;
|
||||
}
|
||||
|
||||
static const char* GetSource(GLenum source) {
|
||||
#define RET(s) \
|
||||
case GL_DEBUG_SOURCE_##s: \
|
||||
return #s
|
||||
switch (source) {
|
||||
RET(API);
|
||||
RET(WINDOW_SYSTEM);
|
||||
RET(SHADER_COMPILER);
|
||||
RET(THIRD_PARTY);
|
||||
RET(APPLICATION);
|
||||
RET(OTHER);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return "Unknown source";
|
||||
}
|
||||
#undef RET
|
||||
}
|
||||
|
||||
static const char* GetType(GLenum type) {
|
||||
#define RET(t) \
|
||||
case GL_DEBUG_TYPE_##t: \
|
||||
return #t
|
||||
switch (type) {
|
||||
RET(ERROR);
|
||||
RET(DEPRECATED_BEHAVIOR);
|
||||
RET(UNDEFINED_BEHAVIOR);
|
||||
RET(PORTABILITY);
|
||||
RET(PERFORMANCE);
|
||||
RET(OTHER);
|
||||
RET(MARKER);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return "Unknown type";
|
||||
}
|
||||
#undef RET
|
||||
}
|
||||
|
||||
static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity,
|
||||
GLsizei length, const GLchar* message, const void* user_param) {
|
||||
const char format[] = "{} {} {}: {}";
|
||||
const char* const str_source = GetSource(source);
|
||||
const char* const str_type = GetType(type);
|
||||
|
||||
switch (severity) {
|
||||
case GL_DEBUG_SEVERITY_HIGH:
|
||||
LOG_CRITICAL(Render_OpenGL, format, str_source, str_type, id, message);
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_MEDIUM:
|
||||
LOG_WARNING(Render_OpenGL, format, str_source, str_type, id, message);
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_NOTIFICATION:
|
||||
case GL_DEBUG_SEVERITY_LOW:
|
||||
LOG_DEBUG(Render_OpenGL, format, str_source, str_type, id, message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool RendererOpenGL::Init() {
|
||||
Core::Frontend::ScopeAcquireWindowContext acquire_context{render_window};
|
||||
|
||||
@@ -495,7 +478,6 @@ bool RendererOpenGL::Init() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Shutdown the renderer
|
||||
void RendererOpenGL::ShutDown() {}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -59,21 +59,31 @@ public:
|
||||
void ShutDown() override;
|
||||
|
||||
private:
|
||||
/// Initializes the OpenGL state and creates persistent objects.
|
||||
void InitOpenGLObjects();
|
||||
|
||||
void AddTelemetryFields();
|
||||
|
||||
void CreateRasterizer();
|
||||
|
||||
void ConfigureFramebufferTexture(TextureInfo& texture,
|
||||
const Tegra::FramebufferConfig& framebuffer);
|
||||
|
||||
/// Draws the emulated screens to the emulator window.
|
||||
void DrawScreen(const Layout::FramebufferLayout& layout);
|
||||
|
||||
void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h);
|
||||
|
||||
/// Updates the framerate.
|
||||
void UpdateFramerate();
|
||||
|
||||
void CaptureScreenshot();
|
||||
|
||||
// Loads framebuffer from emulated memory into the display information structure
|
||||
/// Loads framebuffer from emulated memory into the active OpenGL texture.
|
||||
void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer);
|
||||
// Fills active OpenGL texture with the given RGBA color.
|
||||
|
||||
/// Fills active OpenGL texture with the given RGB color.Since the color is solid, the texture
|
||||
/// can be 1x1 but will stretch across whatever it's rendered on.
|
||||
void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a,
|
||||
const TextureInfo& texture);
|
||||
|
||||
@@ -94,14 +104,6 @@ private:
|
||||
/// OpenGL framebuffer data
|
||||
std::vector<u8> gl_framebuffer_data;
|
||||
|
||||
// Shader uniform location indices
|
||||
GLuint uniform_modelview_matrix;
|
||||
GLuint uniform_color_texture;
|
||||
|
||||
// Shader attribute input indices
|
||||
GLuint attrib_position;
|
||||
GLuint attrib_tex_coord;
|
||||
|
||||
/// Used for transforming the framebuffer orientation
|
||||
Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags;
|
||||
Common::Rectangle<int> framebuffer_crop_rect;
|
||||
|
||||
296
src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
Normal file
296
src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
Normal file
@@ -0,0 +1,296 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr FixedPipelineState::DepthStencil GetDepthStencilState(const Maxwell& regs) {
|
||||
const FixedPipelineState::StencilFace front_stencil(
|
||||
regs.stencil_front_op_fail, regs.stencil_front_op_zfail, regs.stencil_front_op_zpass,
|
||||
regs.stencil_front_func_func);
|
||||
const FixedPipelineState::StencilFace back_stencil =
|
||||
regs.stencil_two_side_enable
|
||||
? FixedPipelineState::StencilFace(regs.stencil_back_op_fail, regs.stencil_back_op_zfail,
|
||||
regs.stencil_back_op_zpass,
|
||||
regs.stencil_back_func_func)
|
||||
: front_stencil;
|
||||
return FixedPipelineState::DepthStencil(
|
||||
regs.depth_test_enable == 1, regs.depth_write_enabled == 1, regs.depth_bounds_enable == 1,
|
||||
regs.stencil_enable == 1, regs.depth_test_func, front_stencil, back_stencil);
|
||||
}
|
||||
|
||||
constexpr FixedPipelineState::InputAssembly GetInputAssemblyState(const Maxwell& regs) {
|
||||
return FixedPipelineState::InputAssembly(
|
||||
regs.draw.topology, regs.primitive_restart.enabled,
|
||||
regs.draw.topology == Maxwell::PrimitiveTopology::Points ? regs.point_size : 0.0f);
|
||||
}
|
||||
|
||||
constexpr FixedPipelineState::BlendingAttachment GetBlendingAttachmentState(
|
||||
const Maxwell& regs, std::size_t render_target) {
|
||||
const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : render_target];
|
||||
const std::array components = {mask.R != 0, mask.G != 0, mask.B != 0, mask.A != 0};
|
||||
|
||||
const FixedPipelineState::BlendingAttachment default_blending(
|
||||
false, Maxwell::Blend::Equation::Add, Maxwell::Blend::Factor::One,
|
||||
Maxwell::Blend::Factor::Zero, Maxwell::Blend::Equation::Add, Maxwell::Blend::Factor::One,
|
||||
Maxwell::Blend::Factor::Zero, components);
|
||||
if (render_target >= regs.rt_control.count) {
|
||||
return default_blending;
|
||||
}
|
||||
|
||||
if (!regs.independent_blend_enable) {
|
||||
const auto& src = regs.blend;
|
||||
if (!src.enable[render_target]) {
|
||||
return default_blending;
|
||||
}
|
||||
return FixedPipelineState::BlendingAttachment(
|
||||
true, src.equation_rgb, src.factor_source_rgb, src.factor_dest_rgb, src.equation_a,
|
||||
src.factor_source_a, src.factor_dest_a, components);
|
||||
}
|
||||
|
||||
if (!regs.blend.enable[render_target]) {
|
||||
return default_blending;
|
||||
}
|
||||
const auto& src = regs.independent_blend[render_target];
|
||||
return FixedPipelineState::BlendingAttachment(
|
||||
true, src.equation_rgb, src.factor_source_rgb, src.factor_dest_rgb, src.equation_a,
|
||||
src.factor_source_a, src.factor_dest_a, components);
|
||||
}
|
||||
|
||||
constexpr FixedPipelineState::ColorBlending GetColorBlendingState(const Maxwell& regs) {
|
||||
return FixedPipelineState::ColorBlending(
|
||||
{regs.blend_color.r, regs.blend_color.g, regs.blend_color.b, regs.blend_color.a},
|
||||
regs.rt_control.count,
|
||||
{GetBlendingAttachmentState(regs, 0), GetBlendingAttachmentState(regs, 1),
|
||||
GetBlendingAttachmentState(regs, 2), GetBlendingAttachmentState(regs, 3),
|
||||
GetBlendingAttachmentState(regs, 4), GetBlendingAttachmentState(regs, 5),
|
||||
GetBlendingAttachmentState(regs, 6), GetBlendingAttachmentState(regs, 7)});
|
||||
}
|
||||
|
||||
constexpr FixedPipelineState::Tessellation GetTessellationState(const Maxwell& regs) {
|
||||
return FixedPipelineState::Tessellation(regs.patch_vertices, regs.tess_mode.prim,
|
||||
regs.tess_mode.spacing, regs.tess_mode.cw != 0);
|
||||
}
|
||||
|
||||
constexpr std::size_t Point = 0;
|
||||
constexpr std::size_t Line = 1;
|
||||
constexpr std::size_t Polygon = 2;
|
||||
constexpr std::array PolygonOffsetEnableLUT = {
|
||||
Point, // Points
|
||||
Line, // Lines
|
||||
Line, // LineLoop
|
||||
Line, // LineStrip
|
||||
Polygon, // Triangles
|
||||
Polygon, // TriangleStrip
|
||||
Polygon, // TriangleFan
|
||||
Polygon, // Quads
|
||||
Polygon, // QuadStrip
|
||||
Polygon, // Polygon
|
||||
Line, // LinesAdjacency
|
||||
Line, // LineStripAdjacency
|
||||
Polygon, // TrianglesAdjacency
|
||||
Polygon, // TriangleStripAdjacency
|
||||
Polygon, // Patches
|
||||
};
|
||||
|
||||
constexpr FixedPipelineState::Rasterizer GetRasterizerState(const Maxwell& regs) {
|
||||
const std::array enabled_lut = {regs.polygon_offset_point_enable,
|
||||
regs.polygon_offset_line_enable,
|
||||
regs.polygon_offset_fill_enable};
|
||||
const auto topology = static_cast<std::size_t>(regs.draw.topology.Value());
|
||||
const bool depth_bias_enabled = enabled_lut[PolygonOffsetEnableLUT[topology]];
|
||||
|
||||
Maxwell::Cull::FrontFace front_face = regs.cull.front_face;
|
||||
if (regs.screen_y_control.triangle_rast_flip != 0 &&
|
||||
regs.viewport_transform[0].scale_y > 0.0f) {
|
||||
if (front_face == Maxwell::Cull::FrontFace::CounterClockWise)
|
||||
front_face = Maxwell::Cull::FrontFace::ClockWise;
|
||||
else if (front_face == Maxwell::Cull::FrontFace::ClockWise)
|
||||
front_face = Maxwell::Cull::FrontFace::CounterClockWise;
|
||||
}
|
||||
|
||||
const bool gl_ndc = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne;
|
||||
return FixedPipelineState::Rasterizer(regs.cull.enabled, depth_bias_enabled, gl_ndc,
|
||||
regs.cull.cull_face, front_face);
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::size_t FixedPipelineState::VertexBinding::Hash() const noexcept {
|
||||
return (index << stride) ^ divisor;
|
||||
}
|
||||
|
||||
bool FixedPipelineState::VertexBinding::operator==(const VertexBinding& rhs) const noexcept {
|
||||
return std::tie(index, stride, divisor) == std::tie(rhs.index, rhs.stride, rhs.divisor);
|
||||
}
|
||||
|
||||
std::size_t FixedPipelineState::VertexAttribute::Hash() const noexcept {
|
||||
return static_cast<std::size_t>(index) ^ (static_cast<std::size_t>(buffer) << 13) ^
|
||||
(static_cast<std::size_t>(type) << 22) ^ (static_cast<std::size_t>(size) << 31) ^
|
||||
(static_cast<std::size_t>(offset) << 36);
|
||||
}
|
||||
|
||||
bool FixedPipelineState::VertexAttribute::operator==(const VertexAttribute& rhs) const noexcept {
|
||||
return std::tie(index, buffer, type, size, offset) ==
|
||||
std::tie(rhs.index, rhs.buffer, rhs.type, rhs.size, rhs.offset);
|
||||
}
|
||||
|
||||
std::size_t FixedPipelineState::StencilFace::Hash() const noexcept {
|
||||
return static_cast<std::size_t>(action_stencil_fail) ^
|
||||
(static_cast<std::size_t>(action_depth_fail) << 4) ^
|
||||
(static_cast<std::size_t>(action_depth_fail) << 20) ^
|
||||
(static_cast<std::size_t>(action_depth_pass) << 36);
|
||||
}
|
||||
|
||||
bool FixedPipelineState::StencilFace::operator==(const StencilFace& rhs) const noexcept {
|
||||
return std::tie(action_stencil_fail, action_depth_fail, action_depth_pass, test_func) ==
|
||||
std::tie(rhs.action_stencil_fail, rhs.action_depth_fail, rhs.action_depth_pass,
|
||||
rhs.test_func);
|
||||
}
|
||||
|
||||
std::size_t FixedPipelineState::BlendingAttachment::Hash() const noexcept {
|
||||
return static_cast<std::size_t>(enable) ^ (static_cast<std::size_t>(rgb_equation) << 5) ^
|
||||
(static_cast<std::size_t>(src_rgb_func) << 10) ^
|
||||
(static_cast<std::size_t>(dst_rgb_func) << 15) ^
|
||||
(static_cast<std::size_t>(a_equation) << 20) ^
|
||||
(static_cast<std::size_t>(src_a_func) << 25) ^
|
||||
(static_cast<std::size_t>(dst_a_func) << 30) ^
|
||||
(static_cast<std::size_t>(components[0]) << 35) ^
|
||||
(static_cast<std::size_t>(components[1]) << 36) ^
|
||||
(static_cast<std::size_t>(components[2]) << 37) ^
|
||||
(static_cast<std::size_t>(components[3]) << 38);
|
||||
}
|
||||
|
||||
bool FixedPipelineState::BlendingAttachment::operator==(const BlendingAttachment& rhs) const
|
||||
noexcept {
|
||||
return std::tie(enable, rgb_equation, src_rgb_func, dst_rgb_func, a_equation, src_a_func,
|
||||
dst_a_func, components) ==
|
||||
std::tie(rhs.enable, rhs.rgb_equation, rhs.src_rgb_func, rhs.dst_rgb_func,
|
||||
rhs.a_equation, rhs.src_a_func, rhs.dst_a_func, rhs.components);
|
||||
}
|
||||
|
||||
std::size_t FixedPipelineState::VertexInput::Hash() const noexcept {
|
||||
std::size_t hash = num_bindings ^ (num_attributes << 32);
|
||||
for (std::size_t i = 0; i < num_bindings; ++i) {
|
||||
boost::hash_combine(hash, bindings[i].Hash());
|
||||
}
|
||||
for (std::size_t i = 0; i < num_attributes; ++i) {
|
||||
boost::hash_combine(hash, attributes[i].Hash());
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
bool FixedPipelineState::VertexInput::operator==(const VertexInput& rhs) const noexcept {
|
||||
return std::equal(bindings.begin(), bindings.begin() + num_bindings, rhs.bindings.begin(),
|
||||
rhs.bindings.begin() + rhs.num_bindings) &&
|
||||
std::equal(attributes.begin(), attributes.begin() + num_attributes,
|
||||
rhs.attributes.begin(), rhs.attributes.begin() + rhs.num_attributes);
|
||||
}
|
||||
|
||||
std::size_t FixedPipelineState::InputAssembly::Hash() const noexcept {
|
||||
std::size_t point_size_int = 0;
|
||||
std::memcpy(&point_size_int, &point_size, sizeof(point_size));
|
||||
return (static_cast<std::size_t>(topology) << 24) ^ (point_size_int << 32) ^
|
||||
static_cast<std::size_t>(primitive_restart_enable);
|
||||
}
|
||||
|
||||
bool FixedPipelineState::InputAssembly::operator==(const InputAssembly& rhs) const noexcept {
|
||||
return std::tie(topology, primitive_restart_enable, point_size) ==
|
||||
std::tie(rhs.topology, rhs.primitive_restart_enable, rhs.point_size);
|
||||
}
|
||||
|
||||
std::size_t FixedPipelineState::Tessellation::Hash() const noexcept {
|
||||
return static_cast<std::size_t>(patch_control_points) ^
|
||||
(static_cast<std::size_t>(primitive) << 6) ^ (static_cast<std::size_t>(spacing) << 8) ^
|
||||
(static_cast<std::size_t>(clockwise) << 10);
|
||||
}
|
||||
|
||||
bool FixedPipelineState::Tessellation::operator==(const Tessellation& rhs) const noexcept {
|
||||
return std::tie(patch_control_points, primitive, spacing, clockwise) ==
|
||||
std::tie(rhs.patch_control_points, rhs.primitive, rhs.spacing, rhs.clockwise);
|
||||
}
|
||||
|
||||
std::size_t FixedPipelineState::Rasterizer::Hash() const noexcept {
|
||||
return static_cast<std::size_t>(cull_enable) ^
|
||||
(static_cast<std::size_t>(depth_bias_enable) << 1) ^
|
||||
(static_cast<std::size_t>(ndc_minus_one_to_one) << 2) ^
|
||||
(static_cast<std::size_t>(cull_face) << 24) ^
|
||||
(static_cast<std::size_t>(front_face) << 48);
|
||||
}
|
||||
|
||||
bool FixedPipelineState::Rasterizer::operator==(const Rasterizer& rhs) const noexcept {
|
||||
return std::tie(cull_enable, depth_bias_enable, ndc_minus_one_to_one, cull_face, front_face) ==
|
||||
std::tie(rhs.cull_enable, rhs.depth_bias_enable, rhs.ndc_minus_one_to_one, rhs.cull_face,
|
||||
rhs.front_face);
|
||||
}
|
||||
|
||||
std::size_t FixedPipelineState::DepthStencil::Hash() const noexcept {
|
||||
std::size_t hash = static_cast<std::size_t>(depth_test_enable) ^
|
||||
(static_cast<std::size_t>(depth_write_enable) << 1) ^
|
||||
(static_cast<std::size_t>(depth_bounds_enable) << 2) ^
|
||||
(static_cast<std::size_t>(stencil_enable) << 3) ^
|
||||
(static_cast<std::size_t>(depth_test_function) << 4);
|
||||
boost::hash_combine(hash, front_stencil.Hash());
|
||||
boost::hash_combine(hash, back_stencil.Hash());
|
||||
return hash;
|
||||
}
|
||||
|
||||
bool FixedPipelineState::DepthStencil::operator==(const DepthStencil& rhs) const noexcept {
|
||||
return std::tie(depth_test_enable, depth_write_enable, depth_bounds_enable, depth_test_function,
|
||||
stencil_enable, front_stencil, back_stencil) ==
|
||||
std::tie(rhs.depth_test_enable, rhs.depth_write_enable, rhs.depth_bounds_enable,
|
||||
rhs.depth_test_function, rhs.stencil_enable, rhs.front_stencil,
|
||||
rhs.back_stencil);
|
||||
}
|
||||
|
||||
std::size_t FixedPipelineState::ColorBlending::Hash() const noexcept {
|
||||
std::size_t hash = attachments_count << 13;
|
||||
for (std::size_t rt = 0; rt < static_cast<std::size_t>(attachments_count); ++rt) {
|
||||
boost::hash_combine(hash, attachments[rt].Hash());
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
bool FixedPipelineState::ColorBlending::operator==(const ColorBlending& rhs) const noexcept {
|
||||
return std::equal(attachments.begin(), attachments.begin() + attachments_count,
|
||||
rhs.attachments.begin(), rhs.attachments.begin() + rhs.attachments_count);
|
||||
}
|
||||
|
||||
std::size_t FixedPipelineState::Hash() const noexcept {
|
||||
std::size_t hash = 0;
|
||||
boost::hash_combine(hash, vertex_input.Hash());
|
||||
boost::hash_combine(hash, input_assembly.Hash());
|
||||
boost::hash_combine(hash, tessellation.Hash());
|
||||
boost::hash_combine(hash, rasterizer.Hash());
|
||||
boost::hash_combine(hash, depth_stencil.Hash());
|
||||
boost::hash_combine(hash, color_blending.Hash());
|
||||
return hash;
|
||||
}
|
||||
|
||||
bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept {
|
||||
return std::tie(vertex_input, input_assembly, tessellation, rasterizer, depth_stencil,
|
||||
color_blending) == std::tie(rhs.vertex_input, rhs.input_assembly,
|
||||
rhs.tessellation, rhs.rasterizer, rhs.depth_stencil,
|
||||
rhs.color_blending);
|
||||
}
|
||||
|
||||
FixedPipelineState GetFixedPipelineState(const Maxwell& regs) {
|
||||
FixedPipelineState fixed_state;
|
||||
fixed_state.input_assembly = GetInputAssemblyState(regs);
|
||||
fixed_state.tessellation = GetTessellationState(regs);
|
||||
fixed_state.rasterizer = GetRasterizerState(regs);
|
||||
fixed_state.depth_stencil = GetDepthStencilState(regs);
|
||||
fixed_state.color_blending = GetColorBlendingState(regs);
|
||||
return fixed_state;
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
282
src/video_core/renderer_vulkan/fixed_pipeline_state.h
Normal file
282
src/video_core/renderer_vulkan/fixed_pipeline_state.h
Normal file
@@ -0,0 +1,282 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/surface.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
// TODO(Rodrigo): Optimize this structure.
|
||||
|
||||
struct FixedPipelineState {
|
||||
using PixelFormat = VideoCore::Surface::PixelFormat;
|
||||
|
||||
struct VertexBinding {
|
||||
constexpr VertexBinding(u32 index, u32 stride, u32 divisor)
|
||||
: index{index}, stride{stride}, divisor{divisor} {}
|
||||
VertexBinding() = default;
|
||||
|
||||
u32 index;
|
||||
u32 stride;
|
||||
u32 divisor;
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
bool operator==(const VertexBinding& rhs) const noexcept;
|
||||
|
||||
bool operator!=(const VertexBinding& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct VertexAttribute {
|
||||
constexpr VertexAttribute(u32 index, u32 buffer, Maxwell::VertexAttribute::Type type,
|
||||
Maxwell::VertexAttribute::Size size, u32 offset)
|
||||
: index{index}, buffer{buffer}, type{type}, size{size}, offset{offset} {}
|
||||
VertexAttribute() = default;
|
||||
|
||||
u32 index;
|
||||
u32 buffer;
|
||||
Maxwell::VertexAttribute::Type type;
|
||||
Maxwell::VertexAttribute::Size size;
|
||||
u32 offset;
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
bool operator==(const VertexAttribute& rhs) const noexcept;
|
||||
|
||||
bool operator!=(const VertexAttribute& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct StencilFace {
|
||||
constexpr StencilFace(Maxwell::StencilOp action_stencil_fail,
|
||||
Maxwell::StencilOp action_depth_fail,
|
||||
Maxwell::StencilOp action_depth_pass, Maxwell::ComparisonOp test_func)
|
||||
: action_stencil_fail{action_stencil_fail}, action_depth_fail{action_depth_fail},
|
||||
action_depth_pass{action_depth_pass}, test_func{test_func} {}
|
||||
StencilFace() = default;
|
||||
|
||||
Maxwell::StencilOp action_stencil_fail;
|
||||
Maxwell::StencilOp action_depth_fail;
|
||||
Maxwell::StencilOp action_depth_pass;
|
||||
Maxwell::ComparisonOp test_func;
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
bool operator==(const StencilFace& rhs) const noexcept;
|
||||
|
||||
bool operator!=(const StencilFace& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct BlendingAttachment {
|
||||
constexpr BlendingAttachment(bool enable, Maxwell::Blend::Equation rgb_equation,
|
||||
Maxwell::Blend::Factor src_rgb_func,
|
||||
Maxwell::Blend::Factor dst_rgb_func,
|
||||
Maxwell::Blend::Equation a_equation,
|
||||
Maxwell::Blend::Factor src_a_func,
|
||||
Maxwell::Blend::Factor dst_a_func,
|
||||
std::array<bool, 4> components)
|
||||
: enable{enable}, rgb_equation{rgb_equation}, src_rgb_func{src_rgb_func},
|
||||
dst_rgb_func{dst_rgb_func}, a_equation{a_equation}, src_a_func{src_a_func},
|
||||
dst_a_func{dst_a_func}, components{components} {}
|
||||
BlendingAttachment() = default;
|
||||
|
||||
bool enable;
|
||||
Maxwell::Blend::Equation rgb_equation;
|
||||
Maxwell::Blend::Factor src_rgb_func;
|
||||
Maxwell::Blend::Factor dst_rgb_func;
|
||||
Maxwell::Blend::Equation a_equation;
|
||||
Maxwell::Blend::Factor src_a_func;
|
||||
Maxwell::Blend::Factor dst_a_func;
|
||||
std::array<bool, 4> components;
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
bool operator==(const BlendingAttachment& rhs) const noexcept;
|
||||
|
||||
bool operator!=(const BlendingAttachment& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct VertexInput {
|
||||
std::size_t num_bindings = 0;
|
||||
std::size_t num_attributes = 0;
|
||||
std::array<VertexBinding, Maxwell::NumVertexArrays> bindings;
|
||||
std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes;
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
bool operator==(const VertexInput& rhs) const noexcept;
|
||||
|
||||
bool operator!=(const VertexInput& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct InputAssembly {
|
||||
constexpr InputAssembly(Maxwell::PrimitiveTopology topology, bool primitive_restart_enable,
|
||||
float point_size)
|
||||
: topology{topology}, primitive_restart_enable{primitive_restart_enable},
|
||||
point_size{point_size} {}
|
||||
InputAssembly() = default;
|
||||
|
||||
Maxwell::PrimitiveTopology topology;
|
||||
bool primitive_restart_enable;
|
||||
float point_size;
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
bool operator==(const InputAssembly& rhs) const noexcept;
|
||||
|
||||
bool operator!=(const InputAssembly& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct Tessellation {
|
||||
constexpr Tessellation(u32 patch_control_points, Maxwell::TessellationPrimitive primitive,
|
||||
Maxwell::TessellationSpacing spacing, bool clockwise)
|
||||
: patch_control_points{patch_control_points}, primitive{primitive}, spacing{spacing},
|
||||
clockwise{clockwise} {}
|
||||
Tessellation() = default;
|
||||
|
||||
u32 patch_control_points;
|
||||
Maxwell::TessellationPrimitive primitive;
|
||||
Maxwell::TessellationSpacing spacing;
|
||||
bool clockwise;
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
bool operator==(const Tessellation& rhs) const noexcept;
|
||||
|
||||
bool operator!=(const Tessellation& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct Rasterizer {
|
||||
constexpr Rasterizer(bool cull_enable, bool depth_bias_enable, bool ndc_minus_one_to_one,
|
||||
Maxwell::Cull::CullFace cull_face, Maxwell::Cull::FrontFace front_face)
|
||||
: cull_enable{cull_enable}, depth_bias_enable{depth_bias_enable},
|
||||
ndc_minus_one_to_one{ndc_minus_one_to_one}, cull_face{cull_face}, front_face{
|
||||
front_face} {}
|
||||
Rasterizer() = default;
|
||||
|
||||
bool cull_enable;
|
||||
bool depth_bias_enable;
|
||||
bool ndc_minus_one_to_one;
|
||||
Maxwell::Cull::CullFace cull_face;
|
||||
Maxwell::Cull::FrontFace front_face;
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
bool operator==(const Rasterizer& rhs) const noexcept;
|
||||
|
||||
bool operator!=(const Rasterizer& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct DepthStencil {
|
||||
constexpr DepthStencil(bool depth_test_enable, bool depth_write_enable,
|
||||
bool depth_bounds_enable, bool stencil_enable,
|
||||
Maxwell::ComparisonOp depth_test_function, StencilFace front_stencil,
|
||||
StencilFace back_stencil)
|
||||
: depth_test_enable{depth_test_enable}, depth_write_enable{depth_write_enable},
|
||||
depth_bounds_enable{depth_bounds_enable}, stencil_enable{stencil_enable},
|
||||
depth_test_function{depth_test_function}, front_stencil{front_stencil},
|
||||
back_stencil{back_stencil} {}
|
||||
DepthStencil() = default;
|
||||
|
||||
bool depth_test_enable;
|
||||
bool depth_write_enable;
|
||||
bool depth_bounds_enable;
|
||||
bool stencil_enable;
|
||||
Maxwell::ComparisonOp depth_test_function;
|
||||
StencilFace front_stencil;
|
||||
StencilFace back_stencil;
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
bool operator==(const DepthStencil& rhs) const noexcept;
|
||||
|
||||
bool operator!=(const DepthStencil& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct ColorBlending {
|
||||
constexpr ColorBlending(
|
||||
std::array<float, 4> blend_constants, std::size_t attachments_count,
|
||||
std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments)
|
||||
: attachments_count{attachments_count}, attachments{attachments} {}
|
||||
ColorBlending() = default;
|
||||
|
||||
std::size_t attachments_count;
|
||||
std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
bool operator==(const ColorBlending& rhs) const noexcept;
|
||||
|
||||
bool operator!=(const ColorBlending& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
bool operator==(const FixedPipelineState& rhs) const noexcept;
|
||||
|
||||
bool operator!=(const FixedPipelineState& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
VertexInput vertex_input;
|
||||
InputAssembly input_assembly;
|
||||
Tessellation tessellation;
|
||||
Rasterizer rasterizer;
|
||||
DepthStencil depth_stencil;
|
||||
ColorBlending color_blending;
|
||||
};
|
||||
static_assert(std::is_trivially_copyable_v<FixedPipelineState::VertexBinding>);
|
||||
static_assert(std::is_trivially_copyable_v<FixedPipelineState::VertexAttribute>);
|
||||
static_assert(std::is_trivially_copyable_v<FixedPipelineState::StencilFace>);
|
||||
static_assert(std::is_trivially_copyable_v<FixedPipelineState::BlendingAttachment>);
|
||||
static_assert(std::is_trivially_copyable_v<FixedPipelineState::VertexInput>);
|
||||
static_assert(std::is_trivially_copyable_v<FixedPipelineState::InputAssembly>);
|
||||
static_assert(std::is_trivially_copyable_v<FixedPipelineState::Tessellation>);
|
||||
static_assert(std::is_trivially_copyable_v<FixedPipelineState::Rasterizer>);
|
||||
static_assert(std::is_trivially_copyable_v<FixedPipelineState::DepthStencil>);
|
||||
static_assert(std::is_trivially_copyable_v<FixedPipelineState::ColorBlending>);
|
||||
static_assert(std::is_trivially_copyable_v<FixedPipelineState>);
|
||||
|
||||
FixedPipelineState GetFixedPipelineState(const Maxwell& regs);
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct hash<Vulkan::FixedPipelineState> {
|
||||
std::size_t operator()(const Vulkan::FixedPipelineState& k) const noexcept {
|
||||
return k.Hash();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
@@ -44,7 +44,8 @@ vk::SamplerMipmapMode MipmapMode(Tegra::Texture::TextureMipmapFilter mipmap_filt
|
||||
return {};
|
||||
}
|
||||
|
||||
vk::SamplerAddressMode WrapMode(Tegra::Texture::WrapMode wrap_mode) {
|
||||
vk::SamplerAddressMode WrapMode(Tegra::Texture::WrapMode wrap_mode,
|
||||
Tegra::Texture::TextureFilter filter) {
|
||||
switch (wrap_mode) {
|
||||
case Tegra::Texture::WrapMode::Wrap:
|
||||
return vk::SamplerAddressMode::eRepeat;
|
||||
@@ -55,10 +56,15 @@ vk::SamplerAddressMode WrapMode(Tegra::Texture::WrapMode wrap_mode) {
|
||||
case Tegra::Texture::WrapMode::Border:
|
||||
return vk::SamplerAddressMode::eClampToBorder;
|
||||
case Tegra::Texture::WrapMode::Clamp:
|
||||
// TODO(Rodrigo): GL_CLAMP was removed as of OpenGL 3.1, to implement GL_CLAMP, we can use
|
||||
// eClampToBorder to get the border color of the texture, and then sample the edge to
|
||||
// manually mix them. However the shader part of this is not yet implemented.
|
||||
return vk::SamplerAddressMode::eClampToBorder;
|
||||
// TODO(Rodrigo): Emulate GL_CLAMP properly
|
||||
switch (filter) {
|
||||
case Tegra::Texture::TextureFilter::Nearest:
|
||||
return vk::SamplerAddressMode::eClampToEdge;
|
||||
case Tegra::Texture::TextureFilter::Linear:
|
||||
return vk::SamplerAddressMode::eClampToBorder;
|
||||
}
|
||||
UNREACHABLE();
|
||||
return vk::SamplerAddressMode::eClampToEdge;
|
||||
case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
|
||||
return vk::SamplerAddressMode::eMirrorClampToEdge;
|
||||
case Tegra::Texture::WrapMode::MirrorOnceBorder:
|
||||
@@ -96,106 +102,140 @@ vk::CompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compar
|
||||
|
||||
} // namespace Sampler
|
||||
|
||||
namespace {
|
||||
|
||||
enum : u32 { Attachable = 1, Storage = 2 };
|
||||
|
||||
struct FormatTuple {
|
||||
vk::Format format; ///< Vulkan format
|
||||
bool attachable; ///< True when this format can be used as an attachment
|
||||
};
|
||||
|
||||
static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex_format_tuples = {{
|
||||
{vk::Format::eA8B8G8R8UnormPack32, true}, // ABGR8U
|
||||
{vk::Format::eUndefined, false}, // ABGR8S
|
||||
{vk::Format::eUndefined, false}, // ABGR8UI
|
||||
{vk::Format::eB5G6R5UnormPack16, false}, // B5G6R5U
|
||||
{vk::Format::eA2B10G10R10UnormPack32, true}, // A2B10G10R10U
|
||||
{vk::Format::eUndefined, false}, // A1B5G5R5U
|
||||
{vk::Format::eR8Unorm, true}, // R8U
|
||||
{vk::Format::eUndefined, false}, // R8UI
|
||||
{vk::Format::eUndefined, false}, // RGBA16F
|
||||
{vk::Format::eUndefined, false}, // RGBA16U
|
||||
{vk::Format::eUndefined, false}, // RGBA16UI
|
||||
{vk::Format::eUndefined, false}, // R11FG11FB10F
|
||||
{vk::Format::eUndefined, false}, // RGBA32UI
|
||||
{vk::Format::eBc1RgbaUnormBlock, false}, // DXT1
|
||||
{vk::Format::eBc2UnormBlock, false}, // DXT23
|
||||
{vk::Format::eBc3UnormBlock, false}, // DXT45
|
||||
{vk::Format::eBc4UnormBlock, false}, // DXN1
|
||||
{vk::Format::eUndefined, false}, // DXN2UNORM
|
||||
{vk::Format::eUndefined, false}, // DXN2SNORM
|
||||
{vk::Format::eUndefined, false}, // BC7U
|
||||
{vk::Format::eUndefined, false}, // BC6H_UF16
|
||||
{vk::Format::eUndefined, false}, // BC6H_SF16
|
||||
{vk::Format::eUndefined, false}, // ASTC_2D_4X4
|
||||
{vk::Format::eUndefined, false}, // BGRA8
|
||||
{vk::Format::eUndefined, false}, // RGBA32F
|
||||
{vk::Format::eUndefined, false}, // RG32F
|
||||
{vk::Format::eUndefined, false}, // R32F
|
||||
{vk::Format::eUndefined, false}, // R16F
|
||||
{vk::Format::eUndefined, false}, // R16U
|
||||
{vk::Format::eUndefined, false}, // R16S
|
||||
{vk::Format::eUndefined, false}, // R16UI
|
||||
{vk::Format::eUndefined, false}, // R16I
|
||||
{vk::Format::eUndefined, false}, // RG16
|
||||
{vk::Format::eUndefined, false}, // RG16F
|
||||
{vk::Format::eUndefined, false}, // RG16UI
|
||||
{vk::Format::eUndefined, false}, // RG16I
|
||||
{vk::Format::eUndefined, false}, // RG16S
|
||||
{vk::Format::eUndefined, false}, // RGB32F
|
||||
{vk::Format::eA8B8G8R8SrgbPack32, true}, // RGBA8_SRGB
|
||||
{vk::Format::eUndefined, false}, // RG8U
|
||||
{vk::Format::eUndefined, false}, // RG8S
|
||||
{vk::Format::eUndefined, false}, // RG32UI
|
||||
{vk::Format::eUndefined, false}, // RGBX16F
|
||||
{vk::Format::eUndefined, false}, // R32UI
|
||||
{vk::Format::eUndefined, false}, // ASTC_2D_8X8
|
||||
{vk::Format::eUndefined, false}, // ASTC_2D_8X5
|
||||
{vk::Format::eUndefined, false}, // ASTC_2D_5X4
|
||||
|
||||
// Compressed sRGB formats
|
||||
{vk::Format::eUndefined, false}, // BGRA8_SRGB
|
||||
{vk::Format::eUndefined, false}, // DXT1_SRGB
|
||||
{vk::Format::eUndefined, false}, // DXT23_SRGB
|
||||
{vk::Format::eUndefined, false}, // DXT45_SRGB
|
||||
{vk::Format::eUndefined, false}, // BC7U_SRGB
|
||||
{vk::Format::eUndefined, false}, // ASTC_2D_4X4_SRGB
|
||||
{vk::Format::eUndefined, false}, // ASTC_2D_8X8_SRGB
|
||||
{vk::Format::eUndefined, false}, // ASTC_2D_8X5_SRGB
|
||||
{vk::Format::eUndefined, false}, // ASTC_2D_5X4_SRGB
|
||||
{vk::Format::eUndefined, false}, // ASTC_2D_5X5
|
||||
{vk::Format::eUndefined, false}, // ASTC_2D_5X5_SRGB
|
||||
{vk::Format::eUndefined, false}, // ASTC_2D_10X8
|
||||
{vk::Format::eUndefined, false}, // ASTC_2D_10X8_SRGB
|
||||
int usage; ///< Describes image format usage
|
||||
} constexpr tex_format_tuples[] = {
|
||||
{vk::Format::eA8B8G8R8UnormPack32, Attachable | Storage}, // ABGR8U
|
||||
{vk::Format::eA8B8G8R8SnormPack32, Attachable | Storage}, // ABGR8S
|
||||
{vk::Format::eA8B8G8R8UintPack32, Attachable | Storage}, // ABGR8UI
|
||||
{vk::Format::eB5G6R5UnormPack16, {}}, // B5G6R5U
|
||||
{vk::Format::eA2B10G10R10UnormPack32, Attachable | Storage}, // A2B10G10R10U
|
||||
{vk::Format::eA1R5G5B5UnormPack16, Attachable | Storage}, // A1B5G5R5U (flipped with swizzle)
|
||||
{vk::Format::eR8Unorm, Attachable | Storage}, // R8U
|
||||
{vk::Format::eR8Uint, Attachable | Storage}, // R8UI
|
||||
{vk::Format::eR16G16B16A16Sfloat, Attachable | Storage}, // RGBA16F
|
||||
{vk::Format::eR16G16B16A16Unorm, Attachable | Storage}, // RGBA16U
|
||||
{vk::Format::eR16G16B16A16Uint, Attachable | Storage}, // RGBA16UI
|
||||
{vk::Format::eB10G11R11UfloatPack32, Attachable | Storage}, // R11FG11FB10F
|
||||
{vk::Format::eR32G32B32A32Uint, Attachable | Storage}, // RGBA32UI
|
||||
{vk::Format::eBc1RgbaUnormBlock, {}}, // DXT1
|
||||
{vk::Format::eBc2UnormBlock, {}}, // DXT23
|
||||
{vk::Format::eBc3UnormBlock, {}}, // DXT45
|
||||
{vk::Format::eBc4UnormBlock, {}}, // DXN1
|
||||
{vk::Format::eBc5UnormBlock, {}}, // DXN2UNORM
|
||||
{vk::Format::eBc5SnormBlock, {}}, // DXN2SNORM
|
||||
{vk::Format::eBc7UnormBlock, {}}, // BC7U
|
||||
{vk::Format::eBc6HUfloatBlock, {}}, // BC6H_UF16
|
||||
{vk::Format::eBc6HSfloatBlock, {}}, // BC6H_SF16
|
||||
{vk::Format::eAstc4x4UnormBlock, {}}, // ASTC_2D_4X4
|
||||
{vk::Format::eB8G8R8A8Unorm, {}}, // BGRA8
|
||||
{vk::Format::eR32G32B32A32Sfloat, Attachable | Storage}, // RGBA32F
|
||||
{vk::Format::eR32G32Sfloat, Attachable | Storage}, // RG32F
|
||||
{vk::Format::eR32Sfloat, Attachable | Storage}, // R32F
|
||||
{vk::Format::eR16Sfloat, Attachable | Storage}, // R16F
|
||||
{vk::Format::eR16Unorm, Attachable | Storage}, // R16U
|
||||
{vk::Format::eUndefined, {}}, // R16S
|
||||
{vk::Format::eUndefined, {}}, // R16UI
|
||||
{vk::Format::eUndefined, {}}, // R16I
|
||||
{vk::Format::eR16G16Unorm, Attachable | Storage}, // RG16
|
||||
{vk::Format::eR16G16Sfloat, Attachable | Storage}, // RG16F
|
||||
{vk::Format::eUndefined, {}}, // RG16UI
|
||||
{vk::Format::eUndefined, {}}, // RG16I
|
||||
{vk::Format::eR16G16Snorm, Attachable | Storage}, // RG16S
|
||||
{vk::Format::eUndefined, {}}, // RGB32F
|
||||
{vk::Format::eR8G8B8A8Srgb, Attachable}, // RGBA8_SRGB
|
||||
{vk::Format::eR8G8Unorm, Attachable | Storage}, // RG8U
|
||||
{vk::Format::eR8G8Snorm, Attachable | Storage}, // RG8S
|
||||
{vk::Format::eR32G32Uint, Attachable | Storage}, // RG32UI
|
||||
{vk::Format::eUndefined, {}}, // RGBX16F
|
||||
{vk::Format::eR32Uint, Attachable | Storage}, // R32UI
|
||||
{vk::Format::eAstc8x8UnormBlock, {}}, // ASTC_2D_8X8
|
||||
{vk::Format::eUndefined, {}}, // ASTC_2D_8X5
|
||||
{vk::Format::eUndefined, {}}, // ASTC_2D_5X4
|
||||
{vk::Format::eUndefined, {}}, // BGRA8_SRGB
|
||||
{vk::Format::eBc1RgbaSrgbBlock, {}}, // DXT1_SRGB
|
||||
{vk::Format::eUndefined, {}}, // DXT23_SRGB
|
||||
{vk::Format::eBc3SrgbBlock, {}}, // DXT45_SRGB
|
||||
{vk::Format::eBc7SrgbBlock, {}}, // BC7U_SRGB
|
||||
{vk::Format::eR4G4B4A4UnormPack16, Attachable}, // R4G4B4A4U
|
||||
{vk::Format::eAstc4x4SrgbBlock, {}}, // ASTC_2D_4X4_SRGB
|
||||
{vk::Format::eAstc8x8SrgbBlock, {}}, // ASTC_2D_8X8_SRGB
|
||||
{vk::Format::eAstc8x5SrgbBlock, {}}, // ASTC_2D_8X5_SRGB
|
||||
{vk::Format::eAstc5x4SrgbBlock, {}}, // ASTC_2D_5X4_SRGB
|
||||
{vk::Format::eAstc5x5UnormBlock, {}}, // ASTC_2D_5X5
|
||||
{vk::Format::eAstc5x5SrgbBlock, {}}, // ASTC_2D_5X5_SRGB
|
||||
{vk::Format::eAstc10x8UnormBlock, {}}, // ASTC_2D_10X8
|
||||
{vk::Format::eAstc10x8SrgbBlock, {}}, // ASTC_2D_10X8_SRGB
|
||||
{vk::Format::eAstc6x6UnormBlock, {}}, // ASTC_2D_6X6
|
||||
{vk::Format::eAstc6x6SrgbBlock, {}}, // ASTC_2D_6X6_SRGB
|
||||
{vk::Format::eAstc10x10UnormBlock, {}}, // ASTC_2D_10X10
|
||||
{vk::Format::eAstc10x10SrgbBlock, {}}, // ASTC_2D_10X10_SRGB
|
||||
{vk::Format::eAstc12x12UnormBlock, {}}, // ASTC_2D_12X12
|
||||
{vk::Format::eAstc12x12SrgbBlock, {}}, // ASTC_2D_12X12_SRGB
|
||||
{vk::Format::eAstc8x6UnormBlock, {}}, // ASTC_2D_8X6
|
||||
{vk::Format::eAstc8x6SrgbBlock, {}}, // ASTC_2D_8X6_SRGB
|
||||
{vk::Format::eAstc6x5UnormBlock, {}}, // ASTC_2D_6X5
|
||||
{vk::Format::eAstc6x5SrgbBlock, {}}, // ASTC_2D_6X5_SRGB
|
||||
{vk::Format::eE5B9G9R9UfloatPack32, {}}, // E5B9G9R9F
|
||||
|
||||
// Depth formats
|
||||
{vk::Format::eD32Sfloat, true}, // Z32F
|
||||
{vk::Format::eD16Unorm, true}, // Z16
|
||||
{vk::Format::eD32Sfloat, Attachable}, // Z32F
|
||||
{vk::Format::eD16Unorm, Attachable}, // Z16
|
||||
|
||||
// DepthStencil formats
|
||||
{vk::Format::eD24UnormS8Uint, true}, // Z24S8
|
||||
{vk::Format::eD24UnormS8Uint, true}, // S8Z24 (emulated)
|
||||
{vk::Format::eUndefined, false}, // Z32FS8
|
||||
}};
|
||||
{vk::Format::eD24UnormS8Uint, Attachable}, // Z24S8
|
||||
{vk::Format::eD24UnormS8Uint, Attachable}, // S8Z24 (emulated)
|
||||
{vk::Format::eD32SfloatS8Uint, Attachable}, // Z32FS8
|
||||
};
|
||||
static_assert(std::size(tex_format_tuples) == VideoCore::Surface::MaxPixelFormat);
|
||||
|
||||
static constexpr bool IsZetaFormat(PixelFormat pixel_format) {
|
||||
constexpr bool IsZetaFormat(PixelFormat pixel_format) {
|
||||
return pixel_format >= PixelFormat::MaxColorFormat &&
|
||||
pixel_format < PixelFormat::MaxDepthStencilFormat;
|
||||
}
|
||||
|
||||
std::pair<vk::Format, bool> SurfaceFormat(const VKDevice& device, FormatType format_type,
|
||||
PixelFormat pixel_format) {
|
||||
ASSERT(static_cast<std::size_t>(pixel_format) < tex_format_tuples.size());
|
||||
} // Anonymous namespace
|
||||
|
||||
const auto tuple = tex_format_tuples[static_cast<u32>(pixel_format)];
|
||||
UNIMPLEMENTED_IF_MSG(tuple.format == vk::Format::eUndefined,
|
||||
"Unimplemented texture format with pixel format={}",
|
||||
static_cast<u32>(pixel_format));
|
||||
FormatInfo SurfaceFormat(const VKDevice& device, FormatType format_type, PixelFormat pixel_format) {
|
||||
ASSERT(static_cast<std::size_t>(pixel_format) < std::size(tex_format_tuples));
|
||||
|
||||
auto usage = vk::FormatFeatureFlagBits::eSampledImage |
|
||||
vk::FormatFeatureFlagBits::eTransferDst | vk::FormatFeatureFlagBits::eTransferSrc;
|
||||
if (tuple.attachable) {
|
||||
usage |= IsZetaFormat(pixel_format) ? vk::FormatFeatureFlagBits::eDepthStencilAttachment
|
||||
: vk::FormatFeatureFlagBits::eColorAttachment;
|
||||
auto tuple = tex_format_tuples[static_cast<std::size_t>(pixel_format)];
|
||||
if (tuple.format == vk::Format::eUndefined) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented texture format with pixel format={}",
|
||||
static_cast<u32>(pixel_format));
|
||||
return {vk::Format::eA8B8G8R8UnormPack32, true, true};
|
||||
}
|
||||
return {device.GetSupportedFormat(tuple.format, usage, format_type), tuple.attachable};
|
||||
|
||||
// Use ABGR8 on hardware that doesn't support ASTC natively
|
||||
if (!device.IsOptimalAstcSupported() && VideoCore::Surface::IsPixelFormatASTC(pixel_format)) {
|
||||
tuple.format = VideoCore::Surface::IsPixelFormatSRGB(pixel_format)
|
||||
? vk::Format::eA8B8G8R8SrgbPack32
|
||||
: vk::Format::eA8B8G8R8UnormPack32;
|
||||
}
|
||||
const bool attachable = tuple.usage & Attachable;
|
||||
const bool storage = tuple.usage & Storage;
|
||||
|
||||
vk::FormatFeatureFlags usage;
|
||||
if (format_type == FormatType::Buffer) {
|
||||
usage = vk::FormatFeatureFlagBits::eStorageTexelBuffer |
|
||||
vk::FormatFeatureFlagBits::eUniformTexelBuffer;
|
||||
} else {
|
||||
usage = vk::FormatFeatureFlagBits::eSampledImage | vk::FormatFeatureFlagBits::eTransferDst |
|
||||
vk::FormatFeatureFlagBits::eTransferSrc;
|
||||
if (attachable) {
|
||||
usage |= IsZetaFormat(pixel_format) ? vk::FormatFeatureFlagBits::eDepthStencilAttachment
|
||||
: vk::FormatFeatureFlagBits::eColorAttachment;
|
||||
}
|
||||
if (storage) {
|
||||
usage |= vk::FormatFeatureFlagBits::eStorageImage;
|
||||
}
|
||||
}
|
||||
return {device.GetSupportedFormat(tuple.format, usage, format_type), attachable, storage};
|
||||
}
|
||||
|
||||
vk::ShaderStageFlagBits ShaderStage(Tegra::Engines::ShaderType stage) {
|
||||
@@ -215,7 +255,8 @@ vk::ShaderStageFlagBits ShaderStage(Tegra::Engines::ShaderType stage) {
|
||||
return {};
|
||||
}
|
||||
|
||||
vk::PrimitiveTopology PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
|
||||
vk::PrimitiveTopology PrimitiveTopology([[maybe_unused]] const VKDevice& device,
|
||||
Maxwell::PrimitiveTopology topology) {
|
||||
switch (topology) {
|
||||
case Maxwell::PrimitiveTopology::Points:
|
||||
return vk::PrimitiveTopology::ePointList;
|
||||
@@ -227,6 +268,13 @@ vk::PrimitiveTopology PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
|
||||
return vk::PrimitiveTopology::eTriangleList;
|
||||
case Maxwell::PrimitiveTopology::TriangleStrip:
|
||||
return vk::PrimitiveTopology::eTriangleStrip;
|
||||
case Maxwell::PrimitiveTopology::TriangleFan:
|
||||
return vk::PrimitiveTopology::eTriangleFan;
|
||||
case Maxwell::PrimitiveTopology::Quads:
|
||||
// TODO(Rodrigo): Use VK_PRIMITIVE_TOPOLOGY_QUAD_LIST_EXT whenever it releases
|
||||
return vk::PrimitiveTopology::eTriangleList;
|
||||
case Maxwell::PrimitiveTopology::Patches:
|
||||
return vk::PrimitiveTopology::ePatchList;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented topology={}", static_cast<u32>(topology));
|
||||
return {};
|
||||
@@ -236,37 +284,111 @@ vk::PrimitiveTopology PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
|
||||
vk::Format VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) {
|
||||
switch (type) {
|
||||
case Maxwell::VertexAttribute::Type::SignedNorm:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
return vk::Format::eR8Snorm;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
return vk::Format::eR8G8Snorm;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
return vk::Format::eR8G8B8Snorm;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
return vk::Format::eR8G8B8A8Snorm;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
return vk::Format::eR16Snorm;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
return vk::Format::eR16G16Snorm;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
return vk::Format::eR16G16B16Snorm;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
return vk::Format::eR16G16B16A16Snorm;
|
||||
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
|
||||
return vk::Format::eA2B10G10R10SnormPack32;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::UnsignedNorm:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
return vk::Format::eR8Unorm;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
return vk::Format::eR8G8Unorm;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
return vk::Format::eR8G8B8Unorm;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
return vk::Format::eR8G8B8A8Unorm;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
return vk::Format::eR16Unorm;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
return vk::Format::eR16G16Unorm;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
return vk::Format::eR16G16B16Unorm;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
return vk::Format::eR16G16B16A16Unorm;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::SignedInt:
|
||||
break;
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
return vk::Format::eR16G16B16A16Sint;
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
return vk::Format::eR8Sint;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
return vk::Format::eR8G8Sint;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
return vk::Format::eR8G8B8Sint;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
return vk::Format::eR8G8B8A8Sint;
|
||||
case Maxwell::VertexAttribute::Size::Size_32:
|
||||
return vk::Format::eR32Sint;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
case Maxwell::VertexAttribute::Type::UnsignedInt:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
return vk::Format::eR8Uint;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
return vk::Format::eR8G8Uint;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
return vk::Format::eR8G8B8Uint;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
return vk::Format::eR8G8B8A8Uint;
|
||||
case Maxwell::VertexAttribute::Size::Size_32:
|
||||
return vk::Format::eR32Uint;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
case Maxwell::VertexAttribute::Type::UnsignedScaled:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
return vk::Format::eR8G8Uscaled;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::SignedScaled:
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::Float:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
|
||||
return vk::Format::eR32G32B32A32Sfloat;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32:
|
||||
return vk::Format::eR32G32B32Sfloat;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32:
|
||||
return vk::Format::eR32G32Sfloat;
|
||||
case Maxwell::VertexAttribute::Size::Size_32:
|
||||
return vk::Format::eR32Sfloat;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32:
|
||||
return vk::Format::eR32G32Sfloat;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32:
|
||||
return vk::Format::eR32G32B32Sfloat;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
|
||||
return vk::Format::eR32G32B32A32Sfloat;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
return vk::Format::eR16Sfloat;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
return vk::Format::eR16G16Sfloat;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
return vk::Format::eR16G16B16Sfloat;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
return vk::Format::eR16G16B16A16Sfloat;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -308,11 +430,14 @@ vk::CompareOp ComparisonOp(Maxwell::ComparisonOp comparison) {
|
||||
return {};
|
||||
}
|
||||
|
||||
vk::IndexType IndexFormat(Maxwell::IndexFormat index_format) {
|
||||
vk::IndexType IndexFormat(const VKDevice& device, Maxwell::IndexFormat index_format) {
|
||||
switch (index_format) {
|
||||
case Maxwell::IndexFormat::UnsignedByte:
|
||||
UNIMPLEMENTED_MSG("Vulkan does not support native u8 index format");
|
||||
return vk::IndexType::eUint16;
|
||||
if (!device.IsExtIndexTypeUint8Supported()) {
|
||||
UNIMPLEMENTED_MSG("Native uint8 indices are not supported on this device");
|
||||
return vk::IndexType::eUint16;
|
||||
}
|
||||
return vk::IndexType::eUint8EXT;
|
||||
case Maxwell::IndexFormat::UnsignedShort:
|
||||
return vk::IndexType::eUint16;
|
||||
case Maxwell::IndexFormat::UnsignedInt:
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/renderer_vulkan/declarations.h"
|
||||
@@ -23,24 +22,31 @@ vk::Filter Filter(Tegra::Texture::TextureFilter filter);
|
||||
|
||||
vk::SamplerMipmapMode MipmapMode(Tegra::Texture::TextureMipmapFilter mipmap_filter);
|
||||
|
||||
vk::SamplerAddressMode WrapMode(Tegra::Texture::WrapMode wrap_mode);
|
||||
vk::SamplerAddressMode WrapMode(Tegra::Texture::WrapMode wrap_mode,
|
||||
Tegra::Texture::TextureFilter filter);
|
||||
|
||||
vk::CompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_func);
|
||||
|
||||
} // namespace Sampler
|
||||
|
||||
std::pair<vk::Format, bool> SurfaceFormat(const VKDevice& device, FormatType format_type,
|
||||
PixelFormat pixel_format);
|
||||
struct FormatInfo {
|
||||
vk::Format format;
|
||||
bool attachable;
|
||||
bool storage;
|
||||
};
|
||||
|
||||
FormatInfo SurfaceFormat(const VKDevice& device, FormatType format_type, PixelFormat pixel_format);
|
||||
|
||||
vk::ShaderStageFlagBits ShaderStage(Tegra::Engines::ShaderType stage);
|
||||
|
||||
vk::PrimitiveTopology PrimitiveTopology(Maxwell::PrimitiveTopology topology);
|
||||
vk::PrimitiveTopology PrimitiveTopology(const VKDevice& device,
|
||||
Maxwell::PrimitiveTopology topology);
|
||||
|
||||
vk::Format VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size);
|
||||
|
||||
vk::CompareOp ComparisonOp(Maxwell::ComparisonOp comparison);
|
||||
|
||||
vk::IndexType IndexFormat(Maxwell::IndexFormat index_format);
|
||||
vk::IndexType IndexFormat(const VKDevice& device, Maxwell::IndexFormat index_format);
|
||||
|
||||
vk::StencilOp StencilOp(Maxwell::StencilOp stencil_op);
|
||||
|
||||
|
||||
24
src/video_core/renderer_vulkan/shaders/blit.frag
Normal file
24
src/video_core/renderer_vulkan/shaders/blit.frag
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
/*
|
||||
* Build instructions:
|
||||
* $ glslangValidator -V $THIS_FILE -o output.spv
|
||||
* $ spirv-opt -O --strip-debug output.spv -o optimized.spv
|
||||
* $ xxd -i optimized.spv
|
||||
*
|
||||
* Then copy that bytecode to the C++ file
|
||||
*/
|
||||
|
||||
#version 460 core
|
||||
|
||||
layout (location = 0) in vec2 frag_tex_coord;
|
||||
|
||||
layout (location = 0) out vec4 color;
|
||||
|
||||
layout (binding = 1) uniform sampler2D color_texture;
|
||||
|
||||
void main() {
|
||||
color = texture(color_texture, frag_tex_coord);
|
||||
}
|
||||
28
src/video_core/renderer_vulkan/shaders/blit.vert
Normal file
28
src/video_core/renderer_vulkan/shaders/blit.vert
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
/*
|
||||
* Build instructions:
|
||||
* $ glslangValidator -V $THIS_FILE -o output.spv
|
||||
* $ spirv-opt -O --strip-debug output.spv -o optimized.spv
|
||||
* $ xxd -i optimized.spv
|
||||
*
|
||||
* Then copy that bytecode to the C++ file
|
||||
*/
|
||||
|
||||
#version 460 core
|
||||
|
||||
layout (location = 0) in vec2 vert_position;
|
||||
layout (location = 1) in vec2 vert_tex_coord;
|
||||
|
||||
layout (location = 0) out vec2 frag_tex_coord;
|
||||
|
||||
layout (set = 0, binding = 0) uniform MatrixBlock {
|
||||
mat4 modelview_matrix;
|
||||
};
|
||||
|
||||
void main() {
|
||||
gl_Position = modelview_matrix * vec4(vert_position, 0.0, 1.0);
|
||||
frag_tex_coord = vert_tex_coord;
|
||||
}
|
||||
37
src/video_core/renderer_vulkan/shaders/quad_array.comp
Normal file
37
src/video_core/renderer_vulkan/shaders/quad_array.comp
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
/*
|
||||
* Build instructions:
|
||||
* $ glslangValidator -V $THIS_FILE -o output.spv
|
||||
* $ spirv-opt -O --strip-debug output.spv -o optimized.spv
|
||||
* $ xxd -i optimized.spv
|
||||
*
|
||||
* Then copy that bytecode to the C++ file
|
||||
*/
|
||||
|
||||
#version 460 core
|
||||
|
||||
layout (local_size_x = 1024) in;
|
||||
|
||||
layout (std430, set = 0, binding = 0) buffer OutputBuffer {
|
||||
uint output_indexes[];
|
||||
};
|
||||
|
||||
layout (push_constant) uniform PushConstants {
|
||||
uint first;
|
||||
};
|
||||
|
||||
void main() {
|
||||
uint primitive = gl_GlobalInvocationID.x;
|
||||
if (primitive * 6 >= output_indexes.length()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint quad_map[6] = uint[](0, 1, 2, 0, 2, 3);
|
||||
for (uint vertex = 0; vertex < 6; ++vertex) {
|
||||
uint index = first + primitive * 4 + quad_map[vertex];
|
||||
output_indexes[primitive * 6 + vertex] = index;
|
||||
}
|
||||
}
|
||||
33
src/video_core/renderer_vulkan/shaders/uint8.comp
Normal file
33
src/video_core/renderer_vulkan/shaders/uint8.comp
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
/*
|
||||
* Build instructions:
|
||||
* $ glslangValidator -V $THIS_FILE -o output.spv
|
||||
* $ spirv-opt -O --strip-debug output.spv -o optimized.spv
|
||||
* $ xxd -i optimized.spv
|
||||
*
|
||||
* Then copy that bytecode to the C++ file
|
||||
*/
|
||||
|
||||
#version 460 core
|
||||
#extension GL_EXT_shader_16bit_storage : require
|
||||
#extension GL_EXT_shader_8bit_storage : require
|
||||
|
||||
layout (local_size_x = 1024) in;
|
||||
|
||||
layout (std430, set = 0, binding = 0) readonly buffer InputBuffer {
|
||||
uint8_t input_indexes[];
|
||||
};
|
||||
|
||||
layout (std430, set = 0, binding = 1) writeonly buffer OutputBuffer {
|
||||
uint16_t output_indexes[];
|
||||
};
|
||||
|
||||
void main() {
|
||||
uint id = gl_GlobalInvocationID.x;
|
||||
if (id < input_indexes.length()) {
|
||||
output_indexes[id] = uint16_t(input_indexes[id]);
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,15 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <bitset>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include "common/assert.h"
|
||||
#include "core/settings.h"
|
||||
#include "video_core/renderer_vulkan/declarations.h"
|
||||
#include "video_core/renderer_vulkan/vk_device.h"
|
||||
|
||||
@@ -201,6 +204,22 @@ vk::Format VKDevice::GetSupportedFormat(vk::Format wanted_format,
|
||||
return wanted_format;
|
||||
}
|
||||
|
||||
void VKDevice::ReportLoss() const {
|
||||
LOG_CRITICAL(Render_Vulkan, "Device loss occured!");
|
||||
|
||||
// Wait some time to let the log flush
|
||||
std::this_thread::sleep_for(std::chrono::seconds{1});
|
||||
|
||||
if (!nv_device_diagnostic_checkpoints) {
|
||||
return;
|
||||
}
|
||||
|
||||
[[maybe_unused]] const std::vector data = graphics_queue.getCheckpointDataNV(dld);
|
||||
// Catch here in debug builds (or with optimizations disabled) the last graphics pipeline to be
|
||||
// executed. It can be done on a debugger by evaluating the expression:
|
||||
// *(VKGraphicsPipeline*)data[0]
|
||||
}
|
||||
|
||||
bool VKDevice::IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features,
|
||||
const vk::DispatchLoaderDynamic& dldi) const {
|
||||
// Disable for now to avoid converting ASTC twice.
|
||||
@@ -381,6 +400,8 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami
|
||||
VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, true);
|
||||
Test(extension, ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME,
|
||||
false);
|
||||
Test(extension, nv_device_diagnostic_checkpoints,
|
||||
VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true);
|
||||
}
|
||||
|
||||
if (khr_shader_float16_int8) {
|
||||
@@ -464,6 +485,7 @@ std::vector<vk::DeviceQueueCreateInfo> VKDevice::GetDeviceQueueCreateInfos() con
|
||||
std::unordered_map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperties(
|
||||
const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical) {
|
||||
static constexpr std::array formats{vk::Format::eA8B8G8R8UnormPack32,
|
||||
vk::Format::eA8B8G8R8UintPack32,
|
||||
vk::Format::eA8B8G8R8SnormPack32,
|
||||
vk::Format::eA8B8G8R8SrgbPack32,
|
||||
vk::Format::eB5G6R5UnormPack16,
|
||||
|
||||
@@ -39,6 +39,9 @@ public:
|
||||
vk::Format GetSupportedFormat(vk::Format wanted_format, vk::FormatFeatureFlags wanted_usage,
|
||||
FormatType format_type) const;
|
||||
|
||||
/// Reports a device loss.
|
||||
void ReportLoss() const;
|
||||
|
||||
/// Returns the dispatch loader with direct function pointers of the device.
|
||||
const vk::DispatchLoaderDynamic& GetDispatchLoader() const {
|
||||
return dld;
|
||||
@@ -159,6 +162,11 @@ public:
|
||||
return ext_shader_viewport_index_layer;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_NV_device_diagnostic_checkpoints.
|
||||
bool IsNvDeviceDiagnosticCheckpoints() const {
|
||||
return nv_device_diagnostic_checkpoints;
|
||||
}
|
||||
|
||||
/// Returns the vendor name reported from Vulkan.
|
||||
std::string_view GetVendorName() const {
|
||||
return vendor_name;
|
||||
@@ -218,6 +226,7 @@ private:
|
||||
bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
|
||||
bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
|
||||
bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
|
||||
bool nv_device_diagnostic_checkpoints{}; ///< Support for VK_NV_device_diagnostic_checkpoints.
|
||||
|
||||
// Telemetry parameters
|
||||
std::string vendor_name; ///< Device's driver name.
|
||||
|
||||
106
src/video_core/renderer_vulkan/vk_image.cpp
Normal file
106
src/video_core/renderer_vulkan/vk_image.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "video_core/renderer_vulkan/declarations.h"
|
||||
#include "video_core/renderer_vulkan/vk_device.h"
|
||||
#include "video_core/renderer_vulkan/vk_image.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
VKImage::VKImage(const VKDevice& device, VKScheduler& scheduler,
|
||||
const vk::ImageCreateInfo& image_ci, vk::ImageAspectFlags aspect_mask)
|
||||
: device{device}, scheduler{scheduler}, format{image_ci.format}, aspect_mask{aspect_mask},
|
||||
image_num_layers{image_ci.arrayLayers}, image_num_levels{image_ci.mipLevels} {
|
||||
UNIMPLEMENTED_IF_MSG(image_ci.queueFamilyIndexCount != 0,
|
||||
"Queue family tracking is not implemented");
|
||||
|
||||
const auto dev = device.GetLogical();
|
||||
image = dev.createImageUnique(image_ci, nullptr, device.GetDispatchLoader());
|
||||
|
||||
const u32 num_ranges = image_num_layers * image_num_levels;
|
||||
barriers.resize(num_ranges);
|
||||
subrange_states.resize(num_ranges, {{}, image_ci.initialLayout});
|
||||
}
|
||||
|
||||
VKImage::~VKImage() = default;
|
||||
|
||||
void VKImage::Transition(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels,
|
||||
vk::PipelineStageFlags new_stage_mask, vk::AccessFlags new_access,
|
||||
vk::ImageLayout new_layout) {
|
||||
if (!HasChanged(base_layer, num_layers, base_level, num_levels, new_access, new_layout)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::size_t cursor = 0;
|
||||
for (u32 layer_it = 0; layer_it < num_layers; ++layer_it) {
|
||||
for (u32 level_it = 0; level_it < num_levels; ++level_it, ++cursor) {
|
||||
const u32 layer = base_layer + layer_it;
|
||||
const u32 level = base_level + level_it;
|
||||
auto& state = GetSubrangeState(layer, level);
|
||||
barriers[cursor] = vk::ImageMemoryBarrier(
|
||||
state.access, new_access, state.layout, new_layout, VK_QUEUE_FAMILY_IGNORED,
|
||||
VK_QUEUE_FAMILY_IGNORED, *image, {aspect_mask, level, 1, layer, 1});
|
||||
state.access = new_access;
|
||||
state.layout = new_layout;
|
||||
}
|
||||
}
|
||||
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
|
||||
scheduler.Record([barriers = barriers, cursor](auto cmdbuf, auto& dld) {
|
||||
// TODO(Rodrigo): Implement a way to use the latest stage across subresources.
|
||||
constexpr auto stage_stub = vk::PipelineStageFlagBits::eAllCommands;
|
||||
cmdbuf.pipelineBarrier(stage_stub, stage_stub, {}, 0, nullptr, 0, nullptr,
|
||||
static_cast<u32>(cursor), barriers.data(), dld);
|
||||
});
|
||||
}
|
||||
|
||||
bool VKImage::HasChanged(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels,
|
||||
vk::AccessFlags new_access, vk::ImageLayout new_layout) noexcept {
|
||||
const bool is_full_range = base_layer == 0 && num_layers == image_num_layers &&
|
||||
base_level == 0 && num_levels == image_num_levels;
|
||||
if (!is_full_range) {
|
||||
state_diverged = true;
|
||||
}
|
||||
|
||||
if (!state_diverged) {
|
||||
auto& state = GetSubrangeState(0, 0);
|
||||
if (state.access != new_access || state.layout != new_layout) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 layer_it = 0; layer_it < num_layers; ++layer_it) {
|
||||
for (u32 level_it = 0; level_it < num_levels; ++level_it) {
|
||||
const u32 layer = base_layer + layer_it;
|
||||
const u32 level = base_level + level_it;
|
||||
auto& state = GetSubrangeState(layer, level);
|
||||
if (state.access != new_access || state.layout != new_layout) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void VKImage::CreatePresentView() {
|
||||
// Image type has to be 2D to be presented.
|
||||
const vk::ImageViewCreateInfo image_view_ci({}, *image, vk::ImageViewType::e2D, format, {},
|
||||
{aspect_mask, 0, 1, 0, 1});
|
||||
const auto dev = device.GetLogical();
|
||||
const auto& dld = device.GetDispatchLoader();
|
||||
present_view = dev.createImageViewUnique(image_view_ci, nullptr, dld);
|
||||
}
|
||||
|
||||
VKImage::SubrangeState& VKImage::GetSubrangeState(u32 layer, u32 level) noexcept {
|
||||
return subrange_states[static_cast<std::size_t>(layer * image_num_levels) +
|
||||
static_cast<std::size_t>(level)];
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
84
src/video_core/renderer_vulkan/vk_image.h
Normal file
84
src/video_core/renderer_vulkan/vk_image.h
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/renderer_vulkan/declarations.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
class VKDevice;
|
||||
class VKScheduler;
|
||||
|
||||
class VKImage {
|
||||
public:
|
||||
explicit VKImage(const VKDevice& device, VKScheduler& scheduler,
|
||||
const vk::ImageCreateInfo& image_ci, vk::ImageAspectFlags aspect_mask);
|
||||
~VKImage();
|
||||
|
||||
/// Records in the passed command buffer an image transition and updates the state of the image.
|
||||
void Transition(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels,
|
||||
vk::PipelineStageFlags new_stage_mask, vk::AccessFlags new_access,
|
||||
vk::ImageLayout new_layout);
|
||||
|
||||
/// Returns a view compatible with presentation, the image has to be 2D.
|
||||
vk::ImageView GetPresentView() {
|
||||
if (!present_view) {
|
||||
CreatePresentView();
|
||||
}
|
||||
return *present_view;
|
||||
}
|
||||
|
||||
/// Returns the Vulkan image handler.
|
||||
vk::Image GetHandle() const {
|
||||
return *image;
|
||||
}
|
||||
|
||||
/// Returns the Vulkan format for this image.
|
||||
vk::Format GetFormat() const {
|
||||
return format;
|
||||
}
|
||||
|
||||
/// Returns the Vulkan aspect mask.
|
||||
vk::ImageAspectFlags GetAspectMask() const {
|
||||
return aspect_mask;
|
||||
}
|
||||
|
||||
private:
|
||||
struct SubrangeState final {
|
||||
vk::AccessFlags access{}; ///< Current access bits.
|
||||
vk::ImageLayout layout = vk::ImageLayout::eUndefined; ///< Current image layout.
|
||||
};
|
||||
|
||||
bool HasChanged(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels,
|
||||
vk::AccessFlags new_access, vk::ImageLayout new_layout) noexcept;
|
||||
|
||||
/// Creates a presentation view.
|
||||
void CreatePresentView();
|
||||
|
||||
/// Returns the subrange state for a layer and layer.
|
||||
SubrangeState& GetSubrangeState(u32 layer, u32 level) noexcept;
|
||||
|
||||
const VKDevice& device; ///< Device handler.
|
||||
VKScheduler& scheduler; ///< Device scheduler.
|
||||
|
||||
const vk::Format format; ///< Vulkan format.
|
||||
const vk::ImageAspectFlags aspect_mask; ///< Vulkan aspect mask.
|
||||
const u32 image_num_layers; ///< Number of layers.
|
||||
const u32 image_num_levels; ///< Number of mipmap levels.
|
||||
|
||||
UniqueImage image; ///< Image handle.
|
||||
UniqueImageView present_view; ///< Image view compatible with presentation.
|
||||
|
||||
std::vector<vk::ImageMemoryBarrier> barriers; ///< Pool of barriers.
|
||||
std::vector<SubrangeState> subrange_states; ///< Current subrange state.
|
||||
|
||||
bool state_diverged = false; ///< True when subresources mismatch in layout.
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
@@ -72,12 +72,22 @@ VKFence::VKFence(const VKDevice& device, UniqueFence handle)
|
||||
VKFence::~VKFence() = default;
|
||||
|
||||
void VKFence::Wait() {
|
||||
static constexpr u64 timeout = std::numeric_limits<u64>::max();
|
||||
const auto dev = device.GetLogical();
|
||||
const auto& dld = device.GetDispatchLoader();
|
||||
dev.waitForFences({*handle}, true, std::numeric_limits<u64>::max(), dld);
|
||||
switch (const auto result = dev.waitForFences(1, &*handle, true, timeout, dld)) {
|
||||
case vk::Result::eSuccess:
|
||||
return;
|
||||
case vk::Result::eErrorDeviceLost:
|
||||
device.ReportLoss();
|
||||
[[fallthrough]];
|
||||
default:
|
||||
vk::throwResultException(result, "vk::waitForFences");
|
||||
}
|
||||
}
|
||||
|
||||
void VKFence::Release() {
|
||||
ASSERT(is_owned);
|
||||
is_owned = false;
|
||||
}
|
||||
|
||||
@@ -133,8 +143,32 @@ void VKFence::Unprotect(VKResource* resource) {
|
||||
protected_resources.erase(it);
|
||||
}
|
||||
|
||||
void VKFence::RedirectProtection(VKResource* old_resource, VKResource* new_resource) noexcept {
|
||||
std::replace(std::begin(protected_resources), std::end(protected_resources), old_resource,
|
||||
new_resource);
|
||||
}
|
||||
|
||||
VKFenceWatch::VKFenceWatch() = default;
|
||||
|
||||
VKFenceWatch::VKFenceWatch(VKFence& initial_fence) {
|
||||
Watch(initial_fence);
|
||||
}
|
||||
|
||||
VKFenceWatch::VKFenceWatch(VKFenceWatch&& rhs) noexcept {
|
||||
fence = std::exchange(rhs.fence, nullptr);
|
||||
if (fence) {
|
||||
fence->RedirectProtection(&rhs, this);
|
||||
}
|
||||
}
|
||||
|
||||
VKFenceWatch& VKFenceWatch::operator=(VKFenceWatch&& rhs) noexcept {
|
||||
fence = std::exchange(rhs.fence, nullptr);
|
||||
if (fence) {
|
||||
fence->RedirectProtection(&rhs, this);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
VKFenceWatch::~VKFenceWatch() {
|
||||
if (fence) {
|
||||
fence->Unprotect(this);
|
||||
|
||||
@@ -65,6 +65,9 @@ public:
|
||||
/// Removes protection for a resource.
|
||||
void Unprotect(VKResource* resource);
|
||||
|
||||
/// Redirects one protected resource to a new address.
|
||||
void RedirectProtection(VKResource* old_resource, VKResource* new_resource) noexcept;
|
||||
|
||||
/// Retreives the fence.
|
||||
operator vk::Fence() const {
|
||||
return *handle;
|
||||
@@ -97,8 +100,13 @@ private:
|
||||
class VKFenceWatch final : public VKResource {
|
||||
public:
|
||||
explicit VKFenceWatch();
|
||||
VKFenceWatch(VKFence& initial_fence);
|
||||
VKFenceWatch(VKFenceWatch&&) noexcept;
|
||||
VKFenceWatch(const VKFenceWatch&) = delete;
|
||||
~VKFenceWatch() override;
|
||||
|
||||
VKFenceWatch& operator=(VKFenceWatch&&) noexcept;
|
||||
|
||||
/// Waits for the fence to be released.
|
||||
void Wait();
|
||||
|
||||
@@ -116,6 +124,14 @@ public:
|
||||
|
||||
void OnFenceRemoval(VKFence* signaling_fence) override;
|
||||
|
||||
/**
|
||||
* Do not use it paired with Watch. Use TryWatch instead.
|
||||
* Returns true when the watch is free.
|
||||
*/
|
||||
bool IsUsed() const {
|
||||
return fence != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
VKFence* fence{}; ///< Fence watching this resource. nullptr when the watch is free.
|
||||
};
|
||||
|
||||
@@ -46,9 +46,10 @@ UniqueSampler VKSamplerCache::CreateSampler(const Tegra::Texture::TSCEntry& tsc)
|
||||
{}, MaxwellToVK::Sampler::Filter(tsc.mag_filter),
|
||||
MaxwellToVK::Sampler::Filter(tsc.min_filter),
|
||||
MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter),
|
||||
MaxwellToVK::Sampler::WrapMode(tsc.wrap_u), MaxwellToVK::Sampler::WrapMode(tsc.wrap_v),
|
||||
MaxwellToVK::Sampler::WrapMode(tsc.wrap_p), tsc.GetLodBias(), has_anisotropy,
|
||||
max_anisotropy, tsc.depth_compare_enabled,
|
||||
MaxwellToVK::Sampler::WrapMode(tsc.wrap_u, tsc.mag_filter),
|
||||
MaxwellToVK::Sampler::WrapMode(tsc.wrap_v, tsc.mag_filter),
|
||||
MaxwellToVK::Sampler::WrapMode(tsc.wrap_p, tsc.mag_filter), tsc.GetLodBias(),
|
||||
has_anisotropy, max_anisotropy, tsc.depth_compare_enabled,
|
||||
MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func), tsc.GetMinLod(),
|
||||
tsc.GetMaxLod(), vk_border_color.value_or(vk::BorderColor::eFloatTransparentBlack),
|
||||
unnormalized_coords);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "video_core/renderer_vulkan/declarations.h"
|
||||
#include "video_core/renderer_vulkan/vk_device.h"
|
||||
#include "video_core/renderer_vulkan/vk_resource_manager.h"
|
||||
@@ -11,46 +11,172 @@
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
VKScheduler::VKScheduler(const VKDevice& device, VKResourceManager& resource_manager)
|
||||
: device{device}, resource_manager{resource_manager} {
|
||||
next_fence = &resource_manager.CommitFence();
|
||||
AllocateNewContext();
|
||||
MICROPROFILE_DECLARE(Vulkan_WaitForWorker);
|
||||
|
||||
void VKScheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf,
|
||||
const vk::DispatchLoaderDynamic& dld) {
|
||||
auto command = first;
|
||||
while (command != nullptr) {
|
||||
auto next = command->GetNext();
|
||||
command->Execute(cmdbuf, dld);
|
||||
command->~Command();
|
||||
command = next;
|
||||
}
|
||||
|
||||
command_offset = 0;
|
||||
first = nullptr;
|
||||
last = nullptr;
|
||||
}
|
||||
|
||||
VKScheduler::~VKScheduler() = default;
|
||||
VKScheduler::VKScheduler(const VKDevice& device, VKResourceManager& resource_manager)
|
||||
: device{device}, resource_manager{resource_manager}, next_fence{
|
||||
&resource_manager.CommitFence()} {
|
||||
AcquireNewChunk();
|
||||
AllocateNewContext();
|
||||
worker_thread = std::thread(&VKScheduler::WorkerThread, this);
|
||||
}
|
||||
|
||||
VKScheduler::~VKScheduler() {
|
||||
quit = true;
|
||||
cv.notify_all();
|
||||
worker_thread.join();
|
||||
}
|
||||
|
||||
void VKScheduler::Flush(bool release_fence, vk::Semaphore semaphore) {
|
||||
SubmitExecution(semaphore);
|
||||
if (release_fence)
|
||||
if (release_fence) {
|
||||
current_fence->Release();
|
||||
}
|
||||
AllocateNewContext();
|
||||
}
|
||||
|
||||
void VKScheduler::Finish(bool release_fence, vk::Semaphore semaphore) {
|
||||
SubmitExecution(semaphore);
|
||||
current_fence->Wait();
|
||||
if (release_fence)
|
||||
if (release_fence) {
|
||||
current_fence->Release();
|
||||
}
|
||||
AllocateNewContext();
|
||||
}
|
||||
|
||||
void VKScheduler::WaitWorker() {
|
||||
MICROPROFILE_SCOPE(Vulkan_WaitForWorker);
|
||||
DispatchWork();
|
||||
|
||||
bool finished = false;
|
||||
do {
|
||||
cv.notify_all();
|
||||
std::unique_lock lock{mutex};
|
||||
finished = chunk_queue.Empty();
|
||||
} while (!finished);
|
||||
}
|
||||
|
||||
void VKScheduler::DispatchWork() {
|
||||
if (chunk->Empty()) {
|
||||
return;
|
||||
}
|
||||
chunk_queue.Push(std::move(chunk));
|
||||
cv.notify_all();
|
||||
AcquireNewChunk();
|
||||
}
|
||||
|
||||
void VKScheduler::RequestRenderpass(const vk::RenderPassBeginInfo& renderpass_bi) {
|
||||
if (state.renderpass && renderpass_bi == *state.renderpass) {
|
||||
return;
|
||||
}
|
||||
const bool end_renderpass = state.renderpass.has_value();
|
||||
state.renderpass = renderpass_bi;
|
||||
Record([renderpass_bi, end_renderpass](auto cmdbuf, auto& dld) {
|
||||
if (end_renderpass) {
|
||||
cmdbuf.endRenderPass(dld);
|
||||
}
|
||||
cmdbuf.beginRenderPass(renderpass_bi, vk::SubpassContents::eInline, dld);
|
||||
});
|
||||
}
|
||||
|
||||
void VKScheduler::RequestOutsideRenderPassOperationContext() {
|
||||
EndRenderPass();
|
||||
}
|
||||
|
||||
void VKScheduler::BindGraphicsPipeline(vk::Pipeline pipeline) {
|
||||
if (state.graphics_pipeline == pipeline) {
|
||||
return;
|
||||
}
|
||||
state.graphics_pipeline = pipeline;
|
||||
Record([pipeline](auto cmdbuf, auto& dld) {
|
||||
cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, dld);
|
||||
});
|
||||
}
|
||||
|
||||
void VKScheduler::WorkerThread() {
|
||||
std::unique_lock lock{mutex};
|
||||
do {
|
||||
cv.wait(lock, [this] { return !chunk_queue.Empty() || quit; });
|
||||
if (quit) {
|
||||
continue;
|
||||
}
|
||||
auto extracted_chunk = std::move(chunk_queue.Front());
|
||||
chunk_queue.Pop();
|
||||
extracted_chunk->ExecuteAll(current_cmdbuf, device.GetDispatchLoader());
|
||||
chunk_reserve.Push(std::move(extracted_chunk));
|
||||
} while (!quit);
|
||||
}
|
||||
|
||||
void VKScheduler::SubmitExecution(vk::Semaphore semaphore) {
|
||||
EndPendingOperations();
|
||||
InvalidateState();
|
||||
WaitWorker();
|
||||
|
||||
std::unique_lock lock{mutex};
|
||||
|
||||
const auto queue = device.GetGraphicsQueue();
|
||||
const auto& dld = device.GetDispatchLoader();
|
||||
current_cmdbuf.end(dld);
|
||||
|
||||
const auto queue = device.GetGraphicsQueue();
|
||||
const vk::SubmitInfo submit_info(0, nullptr, nullptr, 1, ¤t_cmdbuf, semaphore ? 1u : 0u,
|
||||
const vk::SubmitInfo submit_info(0, nullptr, nullptr, 1, ¤t_cmdbuf, semaphore ? 1U : 0U,
|
||||
&semaphore);
|
||||
queue.submit({submit_info}, *current_fence, dld);
|
||||
queue.submit({submit_info}, static_cast<vk::Fence>(*current_fence), dld);
|
||||
}
|
||||
|
||||
void VKScheduler::AllocateNewContext() {
|
||||
std::unique_lock lock{mutex};
|
||||
current_fence = next_fence;
|
||||
current_cmdbuf = resource_manager.CommitCommandBuffer(*current_fence);
|
||||
next_fence = &resource_manager.CommitFence();
|
||||
|
||||
const auto& dld = device.GetDispatchLoader();
|
||||
current_cmdbuf.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit}, dld);
|
||||
current_cmdbuf = resource_manager.CommitCommandBuffer(*current_fence);
|
||||
current_cmdbuf.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit},
|
||||
device.GetDispatchLoader());
|
||||
}
|
||||
|
||||
void VKScheduler::InvalidateState() {
|
||||
state.graphics_pipeline = nullptr;
|
||||
state.viewports = false;
|
||||
state.scissors = false;
|
||||
state.depth_bias = false;
|
||||
state.blend_constants = false;
|
||||
state.depth_bounds = false;
|
||||
state.stencil_values = false;
|
||||
}
|
||||
|
||||
void VKScheduler::EndPendingOperations() {
|
||||
EndRenderPass();
|
||||
}
|
||||
|
||||
void VKScheduler::EndRenderPass() {
|
||||
if (!state.renderpass) {
|
||||
return;
|
||||
}
|
||||
state.renderpass = std::nullopt;
|
||||
Record([](auto cmdbuf, auto& dld) { cmdbuf.endRenderPass(dld); });
|
||||
}
|
||||
|
||||
void VKScheduler::AcquireNewChunk() {
|
||||
if (chunk_reserve.Empty()) {
|
||||
chunk = std::make_unique<CommandChunk>();
|
||||
return;
|
||||
}
|
||||
chunk = std::move(chunk_reserve.Front());
|
||||
chunk_reserve.Pop();
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -4,7 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stack>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include "common/common_types.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "video_core/renderer_vulkan/declarations.h"
|
||||
|
||||
namespace Vulkan {
|
||||
@@ -30,23 +37,6 @@ private:
|
||||
VKFence* const& fence;
|
||||
};
|
||||
|
||||
class VKCommandBufferView {
|
||||
public:
|
||||
VKCommandBufferView() = default;
|
||||
VKCommandBufferView(const vk::CommandBuffer& cmdbuf) : cmdbuf{cmdbuf} {}
|
||||
|
||||
const vk::CommandBuffer* operator->() const noexcept {
|
||||
return &cmdbuf;
|
||||
}
|
||||
|
||||
operator vk::CommandBuffer() const noexcept {
|
||||
return cmdbuf;
|
||||
}
|
||||
|
||||
private:
|
||||
const vk::CommandBuffer& cmdbuf;
|
||||
};
|
||||
|
||||
/// The scheduler abstracts command buffer and fence management with an interface that's able to do
|
||||
/// OpenGL-like operations on Vulkan command buffers.
|
||||
class VKScheduler {
|
||||
@@ -54,32 +44,190 @@ public:
|
||||
explicit VKScheduler(const VKDevice& device, VKResourceManager& resource_manager);
|
||||
~VKScheduler();
|
||||
|
||||
/// Gets a reference to the current fence.
|
||||
VKFenceView GetFence() const {
|
||||
return current_fence;
|
||||
}
|
||||
|
||||
/// Gets a reference to the current command buffer.
|
||||
VKCommandBufferView GetCommandBuffer() const {
|
||||
return current_cmdbuf;
|
||||
}
|
||||
|
||||
/// Sends the current execution context to the GPU.
|
||||
void Flush(bool release_fence = true, vk::Semaphore semaphore = nullptr);
|
||||
|
||||
/// Sends the current execution context to the GPU and waits for it to complete.
|
||||
void Finish(bool release_fence = true, vk::Semaphore semaphore = nullptr);
|
||||
|
||||
/// Waits for the worker thread to finish executing everything. After this function returns it's
|
||||
/// safe to touch worker resources.
|
||||
void WaitWorker();
|
||||
|
||||
/// Sends currently recorded work to the worker thread.
|
||||
void DispatchWork();
|
||||
|
||||
/// Requests to begin a renderpass.
|
||||
void RequestRenderpass(const vk::RenderPassBeginInfo& renderpass_bi);
|
||||
|
||||
/// Requests the current executino context to be able to execute operations only allowed outside
|
||||
/// of a renderpass.
|
||||
void RequestOutsideRenderPassOperationContext();
|
||||
|
||||
/// Binds a pipeline to the current execution context.
|
||||
void BindGraphicsPipeline(vk::Pipeline pipeline);
|
||||
|
||||
/// Returns true when viewports have been set in the current command buffer.
|
||||
bool TouchViewports() {
|
||||
return std::exchange(state.viewports, true);
|
||||
}
|
||||
|
||||
/// Returns true when scissors have been set in the current command buffer.
|
||||
bool TouchScissors() {
|
||||
return std::exchange(state.scissors, true);
|
||||
}
|
||||
|
||||
/// Returns true when depth bias have been set in the current command buffer.
|
||||
bool TouchDepthBias() {
|
||||
return std::exchange(state.depth_bias, true);
|
||||
}
|
||||
|
||||
/// Returns true when blend constants have been set in the current command buffer.
|
||||
bool TouchBlendConstants() {
|
||||
return std::exchange(state.blend_constants, true);
|
||||
}
|
||||
|
||||
/// Returns true when depth bounds have been set in the current command buffer.
|
||||
bool TouchDepthBounds() {
|
||||
return std::exchange(state.depth_bounds, true);
|
||||
}
|
||||
|
||||
/// Returns true when stencil values have been set in the current command buffer.
|
||||
bool TouchStencilValues() {
|
||||
return std::exchange(state.stencil_values, true);
|
||||
}
|
||||
|
||||
/// Send work to a separate thread.
|
||||
template <typename T>
|
||||
void Record(T&& command) {
|
||||
if (chunk->Record(command)) {
|
||||
return;
|
||||
}
|
||||
DispatchWork();
|
||||
(void)chunk->Record(command);
|
||||
}
|
||||
|
||||
/// Gets a reference to the current fence.
|
||||
VKFenceView GetFence() const {
|
||||
return current_fence;
|
||||
}
|
||||
|
||||
private:
|
||||
class Command {
|
||||
public:
|
||||
virtual ~Command() = default;
|
||||
|
||||
virtual void Execute(vk::CommandBuffer cmdbuf,
|
||||
const vk::DispatchLoaderDynamic& dld) const = 0;
|
||||
|
||||
Command* GetNext() const {
|
||||
return next;
|
||||
}
|
||||
|
||||
void SetNext(Command* next_) {
|
||||
next = next_;
|
||||
}
|
||||
|
||||
private:
|
||||
Command* next = nullptr;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class TypedCommand final : public Command {
|
||||
public:
|
||||
explicit TypedCommand(T&& command) : command{std::move(command)} {}
|
||||
~TypedCommand() override = default;
|
||||
|
||||
TypedCommand(TypedCommand&&) = delete;
|
||||
TypedCommand& operator=(TypedCommand&&) = delete;
|
||||
|
||||
void Execute(vk::CommandBuffer cmdbuf,
|
||||
const vk::DispatchLoaderDynamic& dld) const override {
|
||||
command(cmdbuf, dld);
|
||||
}
|
||||
|
||||
private:
|
||||
T command;
|
||||
};
|
||||
|
||||
class CommandChunk final {
|
||||
public:
|
||||
void ExecuteAll(vk::CommandBuffer cmdbuf, const vk::DispatchLoaderDynamic& dld);
|
||||
|
||||
template <typename T>
|
||||
bool Record(T& command) {
|
||||
using FuncType = TypedCommand<T>;
|
||||
static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large");
|
||||
|
||||
if (command_offset > sizeof(data) - sizeof(FuncType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Command* current_last = last;
|
||||
|
||||
last = new (data.data() + command_offset) FuncType(std::move(command));
|
||||
|
||||
if (current_last) {
|
||||
current_last->SetNext(last);
|
||||
} else {
|
||||
first = last;
|
||||
}
|
||||
|
||||
command_offset += sizeof(FuncType);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Empty() const {
|
||||
return command_offset == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
Command* first = nullptr;
|
||||
Command* last = nullptr;
|
||||
|
||||
std::size_t command_offset = 0;
|
||||
std::array<u8, 0x8000> data{};
|
||||
};
|
||||
|
||||
void WorkerThread();
|
||||
|
||||
void SubmitExecution(vk::Semaphore semaphore);
|
||||
|
||||
void AllocateNewContext();
|
||||
|
||||
void InvalidateState();
|
||||
|
||||
void EndPendingOperations();
|
||||
|
||||
void EndRenderPass();
|
||||
|
||||
void AcquireNewChunk();
|
||||
|
||||
const VKDevice& device;
|
||||
VKResourceManager& resource_manager;
|
||||
vk::CommandBuffer current_cmdbuf;
|
||||
VKFence* current_fence = nullptr;
|
||||
VKFence* next_fence = nullptr;
|
||||
|
||||
struct State {
|
||||
std::optional<vk::RenderPassBeginInfo> renderpass;
|
||||
vk::Pipeline graphics_pipeline;
|
||||
bool viewports = false;
|
||||
bool scissors = false;
|
||||
bool depth_bias = false;
|
||||
bool blend_constants = false;
|
||||
bool depth_bounds = false;
|
||||
bool stencil_values = false;
|
||||
} state;
|
||||
|
||||
std::unique_ptr<CommandChunk> chunk;
|
||||
std::thread worker_thread;
|
||||
|
||||
Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_queue;
|
||||
Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_reserve;
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
bool quit = false;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,29 +5,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <sirit/sirit.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/shader_type.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
class ShaderIR;
|
||||
}
|
||||
|
||||
namespace Vulkan {
|
||||
class VKDevice;
|
||||
}
|
||||
|
||||
namespace Vulkan::VKShader {
|
||||
namespace Vulkan {
|
||||
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
using TexelBufferEntry = VideoCommon::Shader::Sampler;
|
||||
using SamplerEntry = VideoCommon::Shader::Sampler;
|
||||
using ImageEntry = VideoCommon::Shader::Image;
|
||||
|
||||
constexpr u32 DESCRIPTOR_SET = 0;
|
||||
|
||||
@@ -46,39 +45,78 @@ private:
|
||||
|
||||
class GlobalBufferEntry {
|
||||
public:
|
||||
explicit GlobalBufferEntry(u32 cbuf_index, u32 cbuf_offset)
|
||||
: cbuf_index{cbuf_index}, cbuf_offset{cbuf_offset} {}
|
||||
constexpr explicit GlobalBufferEntry(u32 cbuf_index, u32 cbuf_offset, bool is_written)
|
||||
: cbuf_index{cbuf_index}, cbuf_offset{cbuf_offset}, is_written{is_written} {}
|
||||
|
||||
u32 GetCbufIndex() const {
|
||||
constexpr u32 GetCbufIndex() const {
|
||||
return cbuf_index;
|
||||
}
|
||||
|
||||
u32 GetCbufOffset() const {
|
||||
constexpr u32 GetCbufOffset() const {
|
||||
return cbuf_offset;
|
||||
}
|
||||
|
||||
constexpr bool IsWritten() const {
|
||||
return is_written;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 cbuf_index{};
|
||||
u32 cbuf_offset{};
|
||||
bool is_written{};
|
||||
};
|
||||
|
||||
struct ShaderEntries {
|
||||
u32 const_buffers_base_binding{};
|
||||
u32 global_buffers_base_binding{};
|
||||
u32 samplers_base_binding{};
|
||||
u32 NumBindings() const {
|
||||
return static_cast<u32>(const_buffers.size() + global_buffers.size() +
|
||||
texel_buffers.size() + samplers.size() + images.size());
|
||||
}
|
||||
|
||||
std::vector<ConstBufferEntry> const_buffers;
|
||||
std::vector<GlobalBufferEntry> global_buffers;
|
||||
std::vector<TexelBufferEntry> texel_buffers;
|
||||
std::vector<SamplerEntry> samplers;
|
||||
std::vector<ImageEntry> images;
|
||||
std::set<u32> attributes;
|
||||
std::array<bool, Maxwell::NumClipDistances> clip_distances{};
|
||||
std::size_t shader_length{};
|
||||
Sirit::Id entry_function{};
|
||||
std::vector<Sirit::Id> interfaces;
|
||||
bool uses_warps{};
|
||||
};
|
||||
|
||||
using DecompilerResult = std::pair<std::unique_ptr<Sirit::Module>, ShaderEntries>;
|
||||
struct Specialization final {
|
||||
u32 base_binding{};
|
||||
|
||||
DecompilerResult Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir,
|
||||
Tegra::Engines::ShaderType stage);
|
||||
// Compute specific
|
||||
std::array<u32, 3> workgroup_size{};
|
||||
u32 shared_memory_size{};
|
||||
|
||||
} // namespace Vulkan::VKShader
|
||||
// Graphics specific
|
||||
Maxwell::PrimitiveTopology primitive_topology{};
|
||||
std::optional<float> point_size{};
|
||||
std::array<Maxwell::VertexAttribute::Type, Maxwell::NumVertexAttributes> attribute_types{};
|
||||
bool ndc_minus_one_to_one{};
|
||||
|
||||
// Tessellation specific
|
||||
struct {
|
||||
Maxwell::TessellationPrimitive primitive{};
|
||||
Maxwell::TessellationSpacing spacing{};
|
||||
bool clockwise{};
|
||||
} tessellation;
|
||||
|
||||
// Fragment specific
|
||||
std::bitset<8> enabled_rendertargets;
|
||||
};
|
||||
// Old gcc versions don't consider this trivially copyable.
|
||||
// static_assert(std::is_trivially_copyable_v<Specialization>);
|
||||
|
||||
struct SPIRVShader {
|
||||
std::vector<u32> code;
|
||||
ShaderEntries entries;
|
||||
};
|
||||
|
||||
ShaderEntries GenerateShaderEntries(const VideoCommon::Shader::ShaderIR& ir);
|
||||
|
||||
std::vector<u32> Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir,
|
||||
Tegra::Engines::ShaderType stage, const Specialization& specialization);
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
127
src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
Normal file
127
src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/bit_util.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/renderer_vulkan/vk_device.h"
|
||||
#include "video_core/renderer_vulkan/vk_resource_manager.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
VKStagingBufferPool::StagingBuffer::StagingBuffer(std::unique_ptr<VKBuffer> buffer, VKFence& fence,
|
||||
u64 last_epoch)
|
||||
: buffer{std::move(buffer)}, watch{fence}, last_epoch{last_epoch} {}
|
||||
|
||||
VKStagingBufferPool::StagingBuffer::StagingBuffer(StagingBuffer&& rhs) noexcept {
|
||||
buffer = std::move(rhs.buffer);
|
||||
watch = std::move(rhs.watch);
|
||||
last_epoch = rhs.last_epoch;
|
||||
}
|
||||
|
||||
VKStagingBufferPool::StagingBuffer::~StagingBuffer() = default;
|
||||
|
||||
VKStagingBufferPool::StagingBuffer& VKStagingBufferPool::StagingBuffer::operator=(
|
||||
StagingBuffer&& rhs) noexcept {
|
||||
buffer = std::move(rhs.buffer);
|
||||
watch = std::move(rhs.watch);
|
||||
last_epoch = rhs.last_epoch;
|
||||
return *this;
|
||||
}
|
||||
|
||||
VKStagingBufferPool::VKStagingBufferPool(const VKDevice& device, VKMemoryManager& memory_manager,
|
||||
VKScheduler& scheduler)
|
||||
: device{device}, memory_manager{memory_manager}, scheduler{scheduler},
|
||||
is_device_integrated{device.IsIntegrated()} {}
|
||||
|
||||
VKStagingBufferPool::~VKStagingBufferPool() = default;
|
||||
|
||||
VKBuffer& VKStagingBufferPool::GetUnusedBuffer(std::size_t size, bool host_visible) {
|
||||
if (const auto buffer = TryGetReservedBuffer(size, host_visible)) {
|
||||
return *buffer;
|
||||
}
|
||||
return CreateStagingBuffer(size, host_visible);
|
||||
}
|
||||
|
||||
void VKStagingBufferPool::TickFrame() {
|
||||
++epoch;
|
||||
current_delete_level = (current_delete_level + 1) % NumLevels;
|
||||
|
||||
ReleaseCache(true);
|
||||
if (!is_device_integrated) {
|
||||
ReleaseCache(false);
|
||||
}
|
||||
}
|
||||
|
||||
VKBuffer* VKStagingBufferPool::TryGetReservedBuffer(std::size_t size, bool host_visible) {
|
||||
for (auto& entry : GetCache(host_visible)[Common::Log2Ceil64(size)].entries) {
|
||||
if (entry.watch.TryWatch(scheduler.GetFence())) {
|
||||
entry.last_epoch = epoch;
|
||||
return &*entry.buffer;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_visible) {
|
||||
const auto usage =
|
||||
vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst |
|
||||
vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndexBuffer;
|
||||
const u32 log2 = Common::Log2Ceil64(size);
|
||||
const vk::BufferCreateInfo buffer_ci({}, 1ULL << log2, usage, vk::SharingMode::eExclusive, 0,
|
||||
nullptr);
|
||||
const auto dev = device.GetLogical();
|
||||
auto buffer = std::make_unique<VKBuffer>();
|
||||
buffer->handle = dev.createBufferUnique(buffer_ci, nullptr, device.GetDispatchLoader());
|
||||
buffer->commit = memory_manager.Commit(*buffer->handle, host_visible);
|
||||
|
||||
auto& entries = GetCache(host_visible)[log2].entries;
|
||||
return *entries.emplace_back(std::move(buffer), scheduler.GetFence(), epoch).buffer;
|
||||
}
|
||||
|
||||
VKStagingBufferPool::StagingBuffersCache& VKStagingBufferPool::GetCache(bool host_visible) {
|
||||
return is_device_integrated || host_visible ? host_staging_buffers : device_staging_buffers;
|
||||
}
|
||||
|
||||
void VKStagingBufferPool::ReleaseCache(bool host_visible) {
|
||||
auto& cache = GetCache(host_visible);
|
||||
const u64 size = ReleaseLevel(cache, current_delete_level);
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
u64 VKStagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, std::size_t log2) {
|
||||
static constexpr u64 epochs_to_destroy = 180;
|
||||
static constexpr std::size_t deletions_per_tick = 16;
|
||||
|
||||
auto& staging = cache[log2];
|
||||
auto& entries = staging.entries;
|
||||
const std::size_t old_size = entries.size();
|
||||
|
||||
const auto is_deleteable = [this](const auto& entry) {
|
||||
return entry.last_epoch + epochs_to_destroy < epoch && !entry.watch.IsUsed();
|
||||
};
|
||||
const std::size_t begin_offset = staging.delete_index;
|
||||
const std::size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size);
|
||||
const auto begin = std::begin(entries) + begin_offset;
|
||||
const auto end = std::begin(entries) + end_offset;
|
||||
entries.erase(std::remove_if(begin, end, is_deleteable), end);
|
||||
|
||||
const std::size_t new_size = entries.size();
|
||||
staging.delete_index += deletions_per_tick;
|
||||
if (staging.delete_index >= new_size) {
|
||||
staging.delete_index = 0;
|
||||
}
|
||||
|
||||
return (1ULL << log2) * (old_size - new_size);
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
83
src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
Normal file
83
src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <climits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "video_core/renderer_vulkan/declarations.h"
|
||||
#include "video_core/renderer_vulkan/vk_memory_manager.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
class VKDevice;
|
||||
class VKFenceWatch;
|
||||
class VKScheduler;
|
||||
|
||||
struct VKBuffer final {
|
||||
UniqueBuffer handle;
|
||||
VKMemoryCommit commit;
|
||||
};
|
||||
|
||||
class VKStagingBufferPool final {
|
||||
public:
|
||||
explicit VKStagingBufferPool(const VKDevice& device, VKMemoryManager& memory_manager,
|
||||
VKScheduler& scheduler);
|
||||
~VKStagingBufferPool();
|
||||
|
||||
VKBuffer& GetUnusedBuffer(std::size_t size, bool host_visible);
|
||||
|
||||
void TickFrame();
|
||||
|
||||
private:
|
||||
struct StagingBuffer final {
|
||||
explicit StagingBuffer(std::unique_ptr<VKBuffer> buffer, VKFence& fence, u64 last_epoch);
|
||||
StagingBuffer(StagingBuffer&& rhs) noexcept;
|
||||
StagingBuffer(const StagingBuffer&) = delete;
|
||||
~StagingBuffer();
|
||||
|
||||
StagingBuffer& operator=(StagingBuffer&& rhs) noexcept;
|
||||
|
||||
std::unique_ptr<VKBuffer> buffer;
|
||||
VKFenceWatch watch;
|
||||
u64 last_epoch = 0;
|
||||
};
|
||||
|
||||
struct StagingBuffers final {
|
||||
std::vector<StagingBuffer> entries;
|
||||
std::size_t delete_index = 0;
|
||||
};
|
||||
|
||||
static constexpr std::size_t NumLevels = sizeof(std::size_t) * CHAR_BIT;
|
||||
using StagingBuffersCache = std::array<StagingBuffers, NumLevels>;
|
||||
|
||||
VKBuffer* TryGetReservedBuffer(std::size_t size, bool host_visible);
|
||||
|
||||
VKBuffer& CreateStagingBuffer(std::size_t size, bool host_visible);
|
||||
|
||||
StagingBuffersCache& GetCache(bool host_visible);
|
||||
|
||||
void ReleaseCache(bool host_visible);
|
||||
|
||||
u64 ReleaseLevel(StagingBuffersCache& cache, std::size_t log2);
|
||||
|
||||
const VKDevice& device;
|
||||
VKMemoryManager& memory_manager;
|
||||
VKScheduler& scheduler;
|
||||
const bool is_device_integrated;
|
||||
|
||||
StagingBuffersCache host_staging_buffers;
|
||||
StagingBuffersCache device_staging_buffers;
|
||||
|
||||
u64 epoch = 0;
|
||||
|
||||
std::size_t current_delete_level = 0;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
@@ -63,12 +63,11 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
|
||||
case OpCode::Id::I2F_R:
|
||||
case OpCode::Id::I2F_C:
|
||||
case OpCode::Id::I2F_IMM: {
|
||||
UNIMPLEMENTED_IF(instr.conversion.int_src.selector != 0);
|
||||
UNIMPLEMENTED_IF(instr.conversion.dst_size == Register::Size::Long);
|
||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
||||
"Condition codes generation in I2F is not implemented");
|
||||
|
||||
Node value = [&]() {
|
||||
Node value = [&] {
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::I2F_R:
|
||||
return GetRegister(instr.gpr20);
|
||||
@@ -81,7 +80,19 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
|
||||
return Immediate(0);
|
||||
}
|
||||
}();
|
||||
|
||||
const bool input_signed = instr.conversion.is_input_signed;
|
||||
|
||||
if (instr.conversion.src_size == Register::Size::Byte) {
|
||||
const u32 offset = static_cast<u32>(instr.conversion.int_src.selector) * 8;
|
||||
if (offset > 0) {
|
||||
value = SignedOperation(OperationCode::ILogicalShiftRight, input_signed,
|
||||
std::move(value), Immediate(offset));
|
||||
}
|
||||
} else {
|
||||
UNIMPLEMENTED_IF(instr.conversion.int_src.selector != 0);
|
||||
}
|
||||
|
||||
value = ConvertIntegerSize(value, instr.conversion.src_size, input_signed);
|
||||
value = GetOperandAbsNegInteger(value, instr.conversion.abs_a, false, input_signed);
|
||||
value = SignedOperation(OperationCode::FCastInteger, input_signed, PRECISE, value);
|
||||
|
||||
@@ -21,7 +21,24 @@ using Tegra::Shader::OpCode;
|
||||
using Tegra::Shader::Register;
|
||||
|
||||
namespace {
|
||||
u32 GetUniformTypeElementsCount(Tegra::Shader::UniformType uniform_type) {
|
||||
|
||||
u32 GetLdgMemorySize(Tegra::Shader::UniformType uniform_type) {
|
||||
switch (uniform_type) {
|
||||
case Tegra::Shader::UniformType::UnsignedByte:
|
||||
case Tegra::Shader::UniformType::Single:
|
||||
return 1;
|
||||
case Tegra::Shader::UniformType::Double:
|
||||
return 2;
|
||||
case Tegra::Shader::UniformType::Quad:
|
||||
case Tegra::Shader::UniformType::UnsignedQuad:
|
||||
return 4;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented size={}!", static_cast<u32>(uniform_type));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
u32 GetStgMemorySize(Tegra::Shader::UniformType uniform_type) {
|
||||
switch (uniform_type) {
|
||||
case Tegra::Shader::UniformType::Single:
|
||||
return 1;
|
||||
@@ -35,6 +52,7 @@ u32 GetUniformTypeElementsCount(Tegra::Shader::UniformType uniform_type) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
@@ -168,7 +186,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
const auto [real_address_base, base_address, descriptor] =
|
||||
TrackGlobalMemory(bb, instr, false);
|
||||
|
||||
const u32 count = GetUniformTypeElementsCount(type);
|
||||
const u32 count = GetLdgMemorySize(type);
|
||||
if (!real_address_base || !base_address) {
|
||||
// Tracking failed, load zeroes.
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
@@ -179,12 +197,22 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
const Node it_offset = Immediate(i * 4);
|
||||
const Node real_address =
|
||||
Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset);
|
||||
const Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor);
|
||||
const Node real_address = Operation(OperationCode::UAdd, real_address_base, it_offset);
|
||||
Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor);
|
||||
|
||||
if (type == Tegra::Shader::UniformType::UnsignedByte) {
|
||||
// To handle unaligned loads get the byte used to dereferenced global memory
|
||||
// and extract that byte from the loaded uint32.
|
||||
Node byte = Operation(OperationCode::UBitwiseAnd, real_address, Immediate(3));
|
||||
byte = Operation(OperationCode::ULogicalShiftLeft, std::move(byte), Immediate(3));
|
||||
|
||||
gmem = Operation(OperationCode::UBitfieldExtract, std::move(gmem), std::move(byte),
|
||||
Immediate(8));
|
||||
}
|
||||
|
||||
SetTemporary(bb, i, gmem);
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i));
|
||||
}
|
||||
@@ -196,28 +224,28 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0,
|
||||
"Unaligned attribute loads are not supported");
|
||||
|
||||
u64 next_element = instr.attribute.fmt20.element;
|
||||
auto next_index = static_cast<u64>(instr.attribute.fmt20.index.Value());
|
||||
u64 element = instr.attribute.fmt20.element;
|
||||
auto index = static_cast<u64>(instr.attribute.fmt20.index.Value());
|
||||
|
||||
const auto StoreNextElement = [&](u32 reg_offset) {
|
||||
const auto dest = GetOutputAttribute(static_cast<Attribute::Index>(next_index),
|
||||
next_element, GetRegister(instr.gpr39));
|
||||
const u32 num_words = static_cast<u32>(instr.attribute.fmt20.size.Value()) + 1;
|
||||
for (u32 reg_offset = 0; reg_offset < num_words; ++reg_offset) {
|
||||
Node dest;
|
||||
if (instr.attribute.fmt20.patch) {
|
||||
const u32 offset = static_cast<u32>(index) * 4 + static_cast<u32>(element);
|
||||
dest = MakeNode<PatchNode>(offset);
|
||||
} else {
|
||||
dest = GetOutputAttribute(static_cast<Attribute::Index>(index), element,
|
||||
GetRegister(instr.gpr39));
|
||||
}
|
||||
const auto src = GetRegister(instr.gpr0.Value() + reg_offset);
|
||||
|
||||
bb.push_back(Operation(OperationCode::Assign, dest, src));
|
||||
|
||||
// Load the next attribute element into the following register. If the element
|
||||
// to load goes beyond the vec4 size, load the first element of the next
|
||||
// attribute.
|
||||
next_element = (next_element + 1) % 4;
|
||||
next_index = next_index + (next_element == 0 ? 1 : 0);
|
||||
};
|
||||
|
||||
const u32 num_words = static_cast<u32>(instr.attribute.fmt20.size.Value()) + 1;
|
||||
for (u32 reg_offset = 0; reg_offset < num_words; ++reg_offset) {
|
||||
StoreNextElement(reg_offset);
|
||||
// Load the next attribute element into the following register. If the element to load
|
||||
// goes beyond the vec4 size, load the first element of the next attribute.
|
||||
element = (element + 1) % 4;
|
||||
index = index + (element == 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::ST_L:
|
||||
@@ -274,7 +302,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
break;
|
||||
}
|
||||
|
||||
const u32 count = GetUniformTypeElementsCount(type);
|
||||
const u32 count = GetStgMemorySize(type);
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
const Node it_offset = Immediate(i * 4);
|
||||
const Node real_address = Operation(OperationCode::UAdd, real_address_base, it_offset);
|
||||
|
||||
@@ -69,6 +69,8 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||
case OpCode::Id::MOV_SYS: {
|
||||
const Node value = [this, instr] {
|
||||
switch (instr.sys20) {
|
||||
case SystemVariable::InvocationId:
|
||||
return Operation(OperationCode::InvocationId);
|
||||
case SystemVariable::Ydirection:
|
||||
return Operation(OperationCode::YNegate);
|
||||
case SystemVariable::InvocationInfo:
|
||||
@@ -255,6 +257,12 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||
SetRegister(bb, instr.gpr0, GetRegister(instr.gpr8));
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::MEMBAR: {
|
||||
UNIMPLEMENTED_IF(instr.membar.type != Tegra::Shader::MembarType::GL);
|
||||
UNIMPLEMENTED_IF(instr.membar.unknown != Tegra::Shader::MembarUnknown::Default);
|
||||
bb.push_back(Operation(OperationCode::MemoryBarrierGL));
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::DEPBAR: {
|
||||
LOG_DEBUG(HW_GPU, "DEPBAR instruction is stubbed");
|
||||
break;
|
||||
|
||||
@@ -13,37 +13,65 @@ namespace VideoCommon::Shader {
|
||||
using Tegra::Shader::Instruction;
|
||||
using Tegra::Shader::OpCode;
|
||||
|
||||
namespace {
|
||||
constexpr u64 NUM_PROGRAMMABLE_PREDICATES = 7;
|
||||
}
|
||||
|
||||
u32 ShaderIR::DecodeRegisterSetPredicate(NodeBlock& bb, u32 pc) {
|
||||
const Instruction instr = {program_code[pc]};
|
||||
const auto opcode = OpCode::Decode(instr);
|
||||
|
||||
UNIMPLEMENTED_IF(instr.r2p.mode != Tegra::Shader::R2pMode::Pr);
|
||||
UNIMPLEMENTED_IF(instr.p2r_r2p.mode != Tegra::Shader::R2pMode::Pr);
|
||||
|
||||
const Node apply_mask = [&]() {
|
||||
const Node apply_mask = [&] {
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::R2P_IMM:
|
||||
return Immediate(static_cast<u32>(instr.r2p.immediate_mask));
|
||||
case OpCode::Id::P2R_IMM:
|
||||
return Immediate(static_cast<u32>(instr.p2r_r2p.immediate_mask));
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return Immediate(static_cast<u32>(instr.r2p.immediate_mask));
|
||||
return Immediate(0);
|
||||
}
|
||||
}();
|
||||
const Node mask = GetRegister(instr.gpr8);
|
||||
const auto offset = static_cast<u32>(instr.r2p.byte) * 8;
|
||||
|
||||
constexpr u32 programmable_preds = 7;
|
||||
for (u64 pred = 0; pred < programmable_preds; ++pred) {
|
||||
const auto shift = static_cast<u32>(pred);
|
||||
const auto offset = static_cast<u32>(instr.p2r_r2p.byte) * 8;
|
||||
|
||||
const Node apply_compare = BitfieldExtract(apply_mask, shift, 1);
|
||||
const Node condition =
|
||||
Operation(OperationCode::LogicalUNotEqual, apply_compare, Immediate(0));
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::R2P_IMM: {
|
||||
const Node mask = GetRegister(instr.gpr8);
|
||||
|
||||
const Node value_compare = BitfieldExtract(mask, offset + shift, 1);
|
||||
const Node value = Operation(OperationCode::LogicalUNotEqual, value_compare, Immediate(0));
|
||||
for (u64 pred = 0; pred < NUM_PROGRAMMABLE_PREDICATES; ++pred) {
|
||||
const auto shift = static_cast<u32>(pred);
|
||||
|
||||
const Node code = Operation(OperationCode::LogicalAssign, GetPredicate(pred), value);
|
||||
bb.push_back(Conditional(condition, {code}));
|
||||
const Node apply_compare = BitfieldExtract(apply_mask, shift, 1);
|
||||
const Node condition =
|
||||
Operation(OperationCode::LogicalUNotEqual, apply_compare, Immediate(0));
|
||||
|
||||
const Node value_compare = BitfieldExtract(mask, offset + shift, 1);
|
||||
const Node value =
|
||||
Operation(OperationCode::LogicalUNotEqual, value_compare, Immediate(0));
|
||||
|
||||
const Node code = Operation(OperationCode::LogicalAssign, GetPredicate(pred), value);
|
||||
bb.push_back(Conditional(condition, {code}));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::P2R_IMM: {
|
||||
Node value = Immediate(0);
|
||||
for (u64 pred = 0; pred < NUM_PROGRAMMABLE_PREDICATES; ++pred) {
|
||||
Node bit = Operation(OperationCode::Select, GetPredicate(pred), Immediate(1U << pred),
|
||||
Immediate(0));
|
||||
value = Operation(OperationCode::UBitwiseOr, std::move(value), std::move(bit));
|
||||
}
|
||||
value = Operation(OperationCode::UBitwiseAnd, std::move(value), apply_mask);
|
||||
value = BitfieldInsert(GetRegister(instr.gpr8), std::move(value), offset, 8);
|
||||
|
||||
SetRegister(bb, instr.gpr0, std::move(value));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unhandled P2R/R2R instruction: {}", opcode->get().GetName());
|
||||
break;
|
||||
}
|
||||
|
||||
return pc;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user