Compare commits
125 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
218ee18417 | ||
|
|
a3916588b6 | ||
|
|
51c9e98677 | ||
|
|
aea978e037 | ||
|
|
27efcc15e9 | ||
|
|
16dcfacbfc | ||
|
|
af93909c9c | ||
|
|
7be65c6a68 | ||
|
|
6d55b14cc0 | ||
|
|
1eb4a95d2b | ||
|
|
253aa52351 | ||
|
|
d53cf05513 | ||
|
|
ae8d4b6c0c | ||
|
|
a7d6bd1ef1 | ||
|
|
c053269017 | ||
|
|
15a753b9a5 | ||
|
|
e438079b50 | ||
|
|
8b26b4228b | ||
|
|
8825b88a45 | ||
|
|
8a23c32cf0 | ||
|
|
67b8ecc73e | ||
|
|
b52297767e | ||
|
|
65b1b05e05 | ||
|
|
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 | ||
|
|
19ce0d4f1a | ||
|
|
faf5ae6a50 | ||
|
|
116a940dbb | ||
|
|
7ea362e134 | ||
|
|
e54699565a | ||
|
|
f73e569ba8 | ||
|
|
c3e43c7e81 | ||
|
|
67b8265bd6 | ||
|
|
f632d00eb1 | ||
|
|
36651f215a | ||
|
|
707bf41c6f | ||
|
|
d2b50c5ebd | ||
|
|
4bbb22a477 | ||
|
|
d49ed4a421 | ||
|
|
74f515e8b6 | ||
|
|
e36814d6d5 | ||
|
|
ef2b6733d0 | ||
|
|
dc70a36b44 | ||
|
|
40cd4df584 | ||
|
|
2f79cc3ef5 | ||
|
|
2883cc1658 | ||
|
|
560cfbc21a | ||
|
|
be9f80ef56 | ||
|
|
526e533e90 | ||
|
|
3c1b6b5723 | ||
|
|
5c7253f8d3 | ||
|
|
930b7c18a6 | ||
|
|
b2c7636710 | ||
|
|
ff64c3951a | ||
|
|
fb6cf12a17 | ||
|
|
c34da106ed | ||
|
|
e6a0a30334 | ||
|
|
dee7844443 | ||
|
|
3a44faff11 | ||
|
|
75cc501d52 | ||
|
|
056f049b26 | ||
|
|
4589582eaf | ||
|
|
c3d3b173d3 | ||
|
|
e3ee017e91 | ||
|
|
fe65045dcb | ||
|
|
e0242a4654 | ||
|
|
c47fc3301d | ||
|
|
d66ab2b8aa | ||
|
|
ba3c55ab7b | ||
|
|
a3149536e2 | ||
|
|
e7e939104b | ||
|
|
50a518be69 | ||
|
|
e4c381b885 | ||
|
|
b05bfc6036 | ||
|
|
89ef3ef575 | ||
|
|
849581075a | ||
|
|
b2165c6b35 | ||
|
|
3f08e8d8d4 | ||
|
|
536fc7f0ea | ||
|
|
fc7d0a17b6 | ||
|
|
e58748fd80 | ||
|
|
323680e5ad | ||
|
|
4c2ed2706e | ||
|
|
cc81c0ce64 | ||
|
|
c8473f399e | ||
|
|
cd0f5dfc17 | ||
|
|
f3d1b370aa | ||
|
|
95137a04e1 |
@@ -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
|
||||
}
|
||||
@@ -39,6 +39,7 @@ mkdir "artifacts"
|
||||
# Build a tar.xz for the source of the release
|
||||
Copy-Item .\license.txt -Destination $MSVC_SOURCE
|
||||
Copy-Item .\README.md -Destination $MSVC_SOURCE
|
||||
Copy-Item .\CMakeLists.txt -Destination $MSVC_SOURCE
|
||||
Copy-Item .\src -Recurse -Destination $MSVC_SOURCE
|
||||
Copy-Item .\externals -Recurse -Destination $MSVC_SOURCE
|
||||
Copy-Item .\dist -Recurse -Destination $MSVC_SOURCE
|
||||
@@ -60,4 +61,4 @@ Get-ChildItem "$BUILD_DIR" -Recurse -Filter "QtWebEngineProcess*.exe" | Copy-Ite
|
||||
|
||||
Get-ChildItem . -Filter "*.zip" | Copy-Item -destination "artifacts"
|
||||
Get-ChildItem . -Filter "*.7z" | Copy-Item -destination "artifacts"
|
||||
Get-ChildItem . -Filter "*.tar.xz" | Copy-Item -destination "artifacts"
|
||||
Get-ChildItem . -Filter "*.tar.xz" | Copy-Item -destination "artifacts"
|
||||
|
||||
@@ -29,7 +29,7 @@ option(ENABLE_VULKAN "Enables Vulkan backend" ON)
|
||||
|
||||
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
|
||||
|
||||
if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit)
|
||||
if(EXISTS ${PROJECT_SOURCE_DIR}/hooks/pre-commit AND NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit)
|
||||
message(STATUS "Copying pre-commit hook")
|
||||
file(COPY hooks/pre-commit
|
||||
DESTINATION ${PROJECT_SOURCE_DIR}/.git/hooks)
|
||||
@@ -49,7 +49,10 @@ function(check_submodules_present)
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
check_submodules_present()
|
||||
|
||||
if(EXISTS ${PROJECT_SOURCE_DIR}/.gitmodules)
|
||||
check_submodules_present()
|
||||
endif()
|
||||
|
||||
configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
|
||||
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/Vulkan-Headers
vendored
2
externals/Vulkan-Headers
vendored
Submodule externals/Vulkan-Headers updated: fd568d51ed...d42d0747ee
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...12f40a8032
@@ -36,9 +36,9 @@ public:
|
||||
}
|
||||
|
||||
void SetWaveIndex(std::size_t index);
|
||||
std::vector<s16> DequeueSamples(std::size_t sample_count);
|
||||
std::vector<s16> DequeueSamples(std::size_t sample_count, Memory::Memory& memory);
|
||||
void UpdateState();
|
||||
void RefreshBuffer();
|
||||
void RefreshBuffer(Memory::Memory& memory);
|
||||
|
||||
private:
|
||||
bool is_in_use{};
|
||||
@@ -66,17 +66,18 @@ public:
|
||||
return info;
|
||||
}
|
||||
|
||||
void UpdateState();
|
||||
void UpdateState(Memory::Memory& memory);
|
||||
|
||||
private:
|
||||
EffectOutStatus out_status{};
|
||||
EffectInStatus info{};
|
||||
};
|
||||
AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, AudioRendererParameter params,
|
||||
AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Memory::Memory& memory_,
|
||||
AudioRendererParameter params,
|
||||
std::shared_ptr<Kernel::WritableEvent> buffer_event,
|
||||
std::size_t instance_number)
|
||||
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),
|
||||
effects(params.effect_count) {
|
||||
effects(params.effect_count), memory{memory_} {
|
||||
|
||||
audio_out = std::make_unique<AudioCore::AudioOut>();
|
||||
stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS,
|
||||
@@ -162,7 +163,7 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
|
||||
}
|
||||
|
||||
for (auto& effect : effects) {
|
||||
effect.UpdateState();
|
||||
effect.UpdateState(memory);
|
||||
}
|
||||
|
||||
// Release previous buffers and queue next ones for playback
|
||||
@@ -206,13 +207,14 @@ void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) {
|
||||
is_refresh_pending = true;
|
||||
}
|
||||
|
||||
std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count) {
|
||||
std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count,
|
||||
Memory::Memory& memory) {
|
||||
if (!IsPlaying()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (is_refresh_pending) {
|
||||
RefreshBuffer();
|
||||
RefreshBuffer(memory);
|
||||
}
|
||||
|
||||
const std::size_t max_size{samples.size() - offset};
|
||||
@@ -256,10 +258,11 @@ void AudioRenderer::VoiceState::UpdateState() {
|
||||
is_in_use = info.is_in_use;
|
||||
}
|
||||
|
||||
void AudioRenderer::VoiceState::RefreshBuffer() {
|
||||
std::vector<s16> new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16));
|
||||
Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(),
|
||||
info.wave_buffer[wave_index].buffer_sz);
|
||||
void AudioRenderer::VoiceState::RefreshBuffer(Memory::Memory& memory) {
|
||||
const auto wave_buffer_address = info.wave_buffer[wave_index].buffer_addr;
|
||||
const auto wave_buffer_size = info.wave_buffer[wave_index].buffer_sz;
|
||||
std::vector<s16> new_samples(wave_buffer_size / sizeof(s16));
|
||||
memory.ReadBlock(wave_buffer_address, new_samples.data(), wave_buffer_size);
|
||||
|
||||
switch (static_cast<Codec::PcmFormat>(info.sample_format)) {
|
||||
case Codec::PcmFormat::Int16: {
|
||||
@@ -269,7 +272,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
|
||||
case Codec::PcmFormat::Adpcm: {
|
||||
// Decode ADPCM to PCM16
|
||||
Codec::ADPCM_Coeff coeffs;
|
||||
Memory::ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
|
||||
memory.ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
|
||||
new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()),
|
||||
new_samples.size() * sizeof(s16), coeffs, adpcm_state);
|
||||
break;
|
||||
@@ -307,18 +310,18 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
|
||||
is_refresh_pending = false;
|
||||
}
|
||||
|
||||
void AudioRenderer::EffectState::UpdateState() {
|
||||
void AudioRenderer::EffectState::UpdateState(Memory::Memory& memory) {
|
||||
if (info.is_new) {
|
||||
out_status.state = EffectStatus::New;
|
||||
} else {
|
||||
if (info.type == Effect::Aux) {
|
||||
ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_info) == 0,
|
||||
ASSERT_MSG(memory.Read32(info.aux_info.return_buffer_info) == 0,
|
||||
"Aux buffers tried to update");
|
||||
ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_info) == 0,
|
||||
ASSERT_MSG(memory.Read32(info.aux_info.send_buffer_info) == 0,
|
||||
"Aux buffers tried to update");
|
||||
ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_base) == 0,
|
||||
ASSERT_MSG(memory.Read32(info.aux_info.return_buffer_base) == 0,
|
||||
"Aux buffers tried to update");
|
||||
ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_base) == 0,
|
||||
ASSERT_MSG(memory.Read32(info.aux_info.send_buffer_base) == 0,
|
||||
"Aux buffers tried to update");
|
||||
}
|
||||
}
|
||||
@@ -340,7 +343,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||
std::size_t offset{};
|
||||
s64 samples_remaining{BUFFER_SIZE};
|
||||
while (samples_remaining > 0) {
|
||||
const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)};
|
||||
const std::vector<s16> samples{voice.DequeueSamples(samples_remaining, memory)};
|
||||
|
||||
if (samples.empty()) {
|
||||
break;
|
||||
|
||||
@@ -22,6 +22,10 @@ namespace Kernel {
|
||||
class WritableEvent;
|
||||
}
|
||||
|
||||
namespace Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class AudioOut;
|
||||
@@ -217,7 +221,8 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size
|
||||
|
||||
class AudioRenderer {
|
||||
public:
|
||||
AudioRenderer(Core::Timing::CoreTiming& core_timing, AudioRendererParameter params,
|
||||
AudioRenderer(Core::Timing::CoreTiming& core_timing, Memory::Memory& memory_,
|
||||
AudioRendererParameter params,
|
||||
std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number);
|
||||
~AudioRenderer();
|
||||
|
||||
@@ -238,7 +243,8 @@ private:
|
||||
std::vector<VoiceState> voices;
|
||||
std::vector<EffectState> effects;
|
||||
std::unique_ptr<AudioOut> audio_out;
|
||||
AudioCore::StreamPtr stream;
|
||||
StreamPtr stream;
|
||||
Memory::Memory& memory;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -170,6 +170,7 @@ add_library(core STATIC
|
||||
hle/kernel/server_port.h
|
||||
hle/kernel/server_session.cpp
|
||||
hle/kernel/server_session.h
|
||||
hle/kernel/session.cpp
|
||||
hle/kernel/session.h
|
||||
hle/kernel/shared_memory.cpp
|
||||
hle/kernel/shared_memory.h
|
||||
@@ -509,7 +510,6 @@ add_library(core STATIC
|
||||
memory/dmnt_cheat_vm.h
|
||||
memory.cpp
|
||||
memory.h
|
||||
memory_setup.h
|
||||
perf_stats.cpp
|
||||
perf_stats.h
|
||||
reporter.cpp
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
|
||||
@@ -61,15 +60,15 @@ static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
|
||||
|
||||
using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
|
||||
|
||||
Symbols GetSymbols(VAddr text_offset) {
|
||||
const auto mod_offset = text_offset + Memory::Read32(text_offset + 4);
|
||||
Symbols GetSymbols(VAddr text_offset, Memory::Memory& memory) {
|
||||
const auto mod_offset = text_offset + memory.Read32(text_offset + 4);
|
||||
|
||||
if (mod_offset < text_offset || (mod_offset & 0b11) != 0 ||
|
||||
Memory::Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
|
||||
memory.Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto dynamic_offset = Memory::Read32(mod_offset + 0x4) + mod_offset;
|
||||
const auto dynamic_offset = memory.Read32(mod_offset + 0x4) + mod_offset;
|
||||
|
||||
VAddr string_table_offset{};
|
||||
VAddr symbol_table_offset{};
|
||||
@@ -77,8 +76,8 @@ Symbols GetSymbols(VAddr text_offset) {
|
||||
|
||||
VAddr dynamic_index = dynamic_offset;
|
||||
while (true) {
|
||||
const auto tag = Memory::Read64(dynamic_index);
|
||||
const auto value = Memory::Read64(dynamic_index + 0x8);
|
||||
const u64 tag = memory.Read64(dynamic_index);
|
||||
const u64 value = memory.Read64(dynamic_index + 0x8);
|
||||
dynamic_index += 0x10;
|
||||
|
||||
if (tag == ELF_DYNAMIC_TAG_NULL) {
|
||||
@@ -106,11 +105,11 @@ Symbols GetSymbols(VAddr text_offset) {
|
||||
VAddr symbol_index = symbol_table_address;
|
||||
while (symbol_index < string_table_address) {
|
||||
ELFSymbol symbol{};
|
||||
Memory::ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
|
||||
memory.ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
|
||||
|
||||
VAddr string_offset = string_table_address + symbol.name_index;
|
||||
std::string name;
|
||||
for (u8 c = Memory::Read8(string_offset); c != 0; c = Memory::Read8(++string_offset)) {
|
||||
for (u8 c = memory.Read8(string_offset); c != 0; c = memory.Read8(++string_offset)) {
|
||||
name += static_cast<char>(c);
|
||||
}
|
||||
|
||||
@@ -142,28 +141,28 @@ constexpr u64 SEGMENT_BASE = 0x7100000000ull;
|
||||
|
||||
std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
|
||||
std::vector<BacktraceEntry> out;
|
||||
auto& memory = system.Memory();
|
||||
|
||||
auto fp = GetReg(29);
|
||||
auto lr = GetReg(30);
|
||||
|
||||
while (true) {
|
||||
out.push_back({"", 0, lr, 0});
|
||||
if (!fp) {
|
||||
break;
|
||||
}
|
||||
lr = Memory::Read64(fp + 8) - 4;
|
||||
fp = Memory::Read64(fp);
|
||||
lr = memory.Read64(fp + 8) - 4;
|
||||
fp = memory.Read64(fp);
|
||||
}
|
||||
|
||||
std::map<VAddr, std::string> modules;
|
||||
auto& loader{System::GetInstance().GetAppLoader()};
|
||||
auto& loader{system.GetAppLoader()};
|
||||
if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::map<std::string, Symbols> symbols;
|
||||
for (const auto& module : modules) {
|
||||
symbols.insert_or_assign(module.second, GetSymbols(module.first));
|
||||
symbols.insert_or_assign(module.second, GetSymbols(module.first, memory));
|
||||
}
|
||||
|
||||
for (auto& entry : out) {
|
||||
|
||||
@@ -17,11 +17,13 @@ enum class VMAPermission : u8;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
|
||||
/// Generic ARMv8 CPU interface
|
||||
class ARM_Interface : NonCopyable {
|
||||
public:
|
||||
virtual ~ARM_Interface() {}
|
||||
explicit ARM_Interface(System& system_) : system{system_} {}
|
||||
virtual ~ARM_Interface() = default;
|
||||
|
||||
struct ThreadContext {
|
||||
std::array<u64, 31> cpu_registers;
|
||||
@@ -163,6 +165,10 @@ public:
|
||||
/// fp+0 : pointer to previous frame record
|
||||
/// fp+8 : value of lr for frame
|
||||
void LogBacktrace() const;
|
||||
|
||||
protected:
|
||||
/// System context that this ARM interface is running under.
|
||||
System& system;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -28,36 +28,38 @@ public:
|
||||
explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {}
|
||||
|
||||
u8 MemoryRead8(u64 vaddr) override {
|
||||
return Memory::Read8(vaddr);
|
||||
return parent.system.Memory().Read8(vaddr);
|
||||
}
|
||||
u16 MemoryRead16(u64 vaddr) override {
|
||||
return Memory::Read16(vaddr);
|
||||
return parent.system.Memory().Read16(vaddr);
|
||||
}
|
||||
u32 MemoryRead32(u64 vaddr) override {
|
||||
return Memory::Read32(vaddr);
|
||||
return parent.system.Memory().Read32(vaddr);
|
||||
}
|
||||
u64 MemoryRead64(u64 vaddr) override {
|
||||
return Memory::Read64(vaddr);
|
||||
return parent.system.Memory().Read64(vaddr);
|
||||
}
|
||||
Vector MemoryRead128(u64 vaddr) override {
|
||||
return {Memory::Read64(vaddr), Memory::Read64(vaddr + 8)};
|
||||
auto& memory = parent.system.Memory();
|
||||
return {memory.Read64(vaddr), memory.Read64(vaddr + 8)};
|
||||
}
|
||||
|
||||
void MemoryWrite8(u64 vaddr, u8 value) override {
|
||||
Memory::Write8(vaddr, value);
|
||||
parent.system.Memory().Write8(vaddr, value);
|
||||
}
|
||||
void MemoryWrite16(u64 vaddr, u16 value) override {
|
||||
Memory::Write16(vaddr, value);
|
||||
parent.system.Memory().Write16(vaddr, value);
|
||||
}
|
||||
void MemoryWrite32(u64 vaddr, u32 value) override {
|
||||
Memory::Write32(vaddr, value);
|
||||
parent.system.Memory().Write32(vaddr, value);
|
||||
}
|
||||
void MemoryWrite64(u64 vaddr, u64 value) override {
|
||||
Memory::Write64(vaddr, value);
|
||||
parent.system.Memory().Write64(vaddr, value);
|
||||
}
|
||||
void MemoryWrite128(u64 vaddr, Vector value) override {
|
||||
Memory::Write64(vaddr, value[0]);
|
||||
Memory::Write64(vaddr + 8, value[1]);
|
||||
auto& memory = parent.system.Memory();
|
||||
memory.Write64(vaddr, value[0]);
|
||||
memory.Write64(vaddr + 8, value[1]);
|
||||
}
|
||||
|
||||
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
|
||||
@@ -171,9 +173,10 @@ void ARM_Dynarmic::Step() {
|
||||
|
||||
ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor,
|
||||
std::size_t core_index)
|
||||
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system},
|
||||
core_index{core_index}, system{system},
|
||||
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
|
||||
: ARM_Interface{system},
|
||||
cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system},
|
||||
core_index{core_index}, exclusive_monitor{
|
||||
dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
|
||||
|
||||
ARM_Dynarmic::~ARM_Dynarmic() = default;
|
||||
|
||||
@@ -264,7 +267,9 @@ void ARM_Dynarmic::PageTableChanged(Common::PageTable& page_table,
|
||||
jit = MakeJit(page_table, new_address_space_size_in_bits);
|
||||
}
|
||||
|
||||
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {}
|
||||
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count)
|
||||
: monitor(core_count), memory{memory_} {}
|
||||
|
||||
DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
|
||||
|
||||
void DynarmicExclusiveMonitor::SetExclusive(std::size_t core_index, VAddr addr) {
|
||||
@@ -277,29 +282,28 @@ void DynarmicExclusiveMonitor::ClearExclusive() {
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 1,
|
||||
[&] { Memory::Write8(vaddr, value); });
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 1, [&] { memory.Write8(vaddr, value); });
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 2,
|
||||
[&] { Memory::Write16(vaddr, value); });
|
||||
[&] { memory.Write16(vaddr, value); });
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 4,
|
||||
[&] { Memory::Write32(vaddr, value); });
|
||||
[&] { memory.Write32(vaddr, value); });
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 8,
|
||||
[&] { Memory::Write64(vaddr, value); });
|
||||
[&] { memory.Write64(vaddr, value); });
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] {
|
||||
Memory::Write64(vaddr + 0, value[0]);
|
||||
Memory::Write64(vaddr + 8, value[1]);
|
||||
memory.Write64(vaddr + 0, value[0]);
|
||||
memory.Write64(vaddr + 8, value[1]);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
|
||||
namespace Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
class ARM_Dynarmic_Callbacks;
|
||||
@@ -58,13 +62,12 @@ private:
|
||||
ARM_Unicorn inner_unicorn;
|
||||
|
||||
std::size_t core_index;
|
||||
System& system;
|
||||
DynarmicExclusiveMonitor& exclusive_monitor;
|
||||
};
|
||||
|
||||
class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
|
||||
public:
|
||||
explicit DynarmicExclusiveMonitor(std::size_t core_count);
|
||||
explicit DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count);
|
||||
~DynarmicExclusiveMonitor() override;
|
||||
|
||||
void SetExclusive(std::size_t core_index, VAddr addr) override;
|
||||
@@ -79,6 +82,7 @@ public:
|
||||
private:
|
||||
friend class ARM_Dynarmic;
|
||||
Dynarmic::A64::ExclusiveMonitor monitor;
|
||||
Memory::Memory& memory;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -60,7 +60,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
|
||||
return false;
|
||||
}
|
||||
|
||||
ARM_Unicorn::ARM_Unicorn(System& system) : system{system} {
|
||||
ARM_Unicorn::ARM_Unicorn(System& system) : ARM_Interface{system} {
|
||||
CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc));
|
||||
|
||||
auto fpv = 3 << 20;
|
||||
|
||||
@@ -45,7 +45,6 @@ private:
|
||||
static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data);
|
||||
|
||||
uc_engine* uc{};
|
||||
System& system;
|
||||
GDBStub::BreakpointAddress last_bkpt{};
|
||||
bool last_bkpt_hit = false;
|
||||
};
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/memory/cheat_engine.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "core/reporter.h"
|
||||
@@ -112,8 +113,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
}
|
||||
struct System::Impl {
|
||||
explicit Impl(System& system)
|
||||
: kernel{system}, fs_controller{system}, cpu_core_manager{system}, reporter{system},
|
||||
applet_manager{system} {}
|
||||
: kernel{system}, fs_controller{system}, memory{system},
|
||||
cpu_core_manager{system}, reporter{system}, applet_manager{system} {}
|
||||
|
||||
Cpu& CurrentCpuCore() {
|
||||
return cpu_core_manager.GetCurrentCore();
|
||||
@@ -341,7 +342,8 @@ struct System::Impl {
|
||||
std::unique_ptr<VideoCore::RendererBase> renderer;
|
||||
std::unique_ptr<Tegra::GPU> gpu_core;
|
||||
std::shared_ptr<Tegra::DebugContext> debug_context;
|
||||
std::unique_ptr<Core::Hardware::InterruptManager> interrupt_manager;
|
||||
std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
|
||||
Memory::Memory memory;
|
||||
CpuCoreManager cpu_core_manager;
|
||||
bool is_powered_on = false;
|
||||
bool exit_lock = false;
|
||||
@@ -498,6 +500,14 @@ const ExclusiveMonitor& System::Monitor() const {
|
||||
return impl->cpu_core_manager.GetExclusiveMonitor();
|
||||
}
|
||||
|
||||
Memory::Memory& System::Memory() {
|
||||
return impl->memory;
|
||||
}
|
||||
|
||||
const Memory::Memory& System::Memory() const {
|
||||
return impl->memory;
|
||||
}
|
||||
|
||||
Tegra::GPU& System::GPU() {
|
||||
return *impl->gpu_core;
|
||||
}
|
||||
|
||||
@@ -86,6 +86,10 @@ namespace Core::Hardware {
|
||||
class InterruptManager;
|
||||
}
|
||||
|
||||
namespace Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
class ARM_Interface;
|
||||
@@ -225,6 +229,12 @@ public:
|
||||
/// Gets a constant reference to the exclusive monitor
|
||||
const ExclusiveMonitor& Monitor() const;
|
||||
|
||||
/// Gets a mutable reference to the system memory instance.
|
||||
Memory::Memory& Memory();
|
||||
|
||||
/// Gets a constant reference to the system memory instance.
|
||||
const Memory::Memory& Memory() const;
|
||||
|
||||
/// Gets a mutable reference to the GPU interface
|
||||
Tegra::GPU& GPU();
|
||||
|
||||
|
||||
@@ -66,9 +66,10 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba
|
||||
|
||||
Cpu::~Cpu() = default;
|
||||
|
||||
std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
|
||||
std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(
|
||||
[[maybe_unused]] Memory::Memory& memory, [[maybe_unused]] std::size_t num_cores) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
return std::make_unique<DynarmicExclusiveMonitor>(num_cores);
|
||||
return std::make_unique<DynarmicExclusiveMonitor>(memory, num_cores);
|
||||
#else
|
||||
// TODO(merry): Passthrough exclusive monitor
|
||||
return nullptr;
|
||||
@@ -95,6 +96,8 @@ void Cpu::RunLoop(bool tight_loop) {
|
||||
} else {
|
||||
arm_interface->Step();
|
||||
}
|
||||
// We are stopping a run, exclusive state must be cleared
|
||||
arm_interface->ClearExclusiveState();
|
||||
}
|
||||
core_timing.Advance();
|
||||
|
||||
|
||||
@@ -24,6 +24,10 @@ namespace Core::Timing {
|
||||
class CoreTiming;
|
||||
}
|
||||
|
||||
namespace Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
class ARM_Interface;
|
||||
@@ -86,7 +90,19 @@ public:
|
||||
|
||||
void Shutdown();
|
||||
|
||||
static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores);
|
||||
/**
|
||||
* Creates an exclusive monitor to handle exclusive reads/writes.
|
||||
*
|
||||
* @param memory The current memory subsystem that the monitor may wish
|
||||
* to keep track of.
|
||||
*
|
||||
* @param num_cores The number of cores to assume about the CPU.
|
||||
*
|
||||
* @returns The constructed exclusive monitor instance, or nullptr if the current
|
||||
* CPU backend is unable to use an exclusive monitor.
|
||||
*/
|
||||
static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory,
|
||||
std::size_t num_cores);
|
||||
|
||||
private:
|
||||
void Reschedule();
|
||||
|
||||
@@ -25,7 +25,7 @@ CpuCoreManager::~CpuCoreManager() = default;
|
||||
|
||||
void CpuCoreManager::Initialize() {
|
||||
barrier = std::make_unique<CpuBarrier>();
|
||||
exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
|
||||
exclusive_monitor = Cpu::MakeExclusiveMonitor(system.Memory(), cores.size());
|
||||
|
||||
for (std::size_t index = 0; index < cores.size(); ++index) {
|
||||
cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
enum EntryType : u8 {
|
||||
enum class EntryType : u8 {
|
||||
Directory = 0,
|
||||
File = 1,
|
||||
};
|
||||
|
||||
@@ -76,7 +76,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
const auto update_disabled =
|
||||
std::find(disabled.begin(), disabled.end(), "Update") != disabled.end();
|
||||
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
|
||||
|
||||
// Game Updates
|
||||
const auto update_tid = GetUpdateTitleID(title_id);
|
||||
@@ -127,7 +127,7 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
|
||||
std::vector<VirtualFile> out;
|
||||
out.reserve(patch_dirs.size());
|
||||
for (const auto& subdir : patch_dirs) {
|
||||
if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
|
||||
if (std::find(disabled.cbegin(), disabled.cend(), subdir->GetName()) != disabled.cend())
|
||||
continue;
|
||||
|
||||
auto exefs_dir = subdir->GetSubdirectory("exefs");
|
||||
@@ -284,12 +284,17 @@ std::vector<Memory::CheatEntry> PatchManager::CreateCheatList(
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
auto patch_dirs = load_dir->GetSubdirectories();
|
||||
std::sort(patch_dirs.begin(), patch_dirs.end(),
|
||||
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
|
||||
|
||||
std::vector<Memory::CheatEntry> out;
|
||||
for (const auto& subdir : patch_dirs) {
|
||||
if (std::find(disabled.cbegin(), disabled.cend(), subdir->GetName()) != disabled.cend()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto cheats_dir = subdir->GetSubdirectory("cheats");
|
||||
if (cheats_dir != nullptr) {
|
||||
auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true);
|
||||
@@ -331,8 +336,9 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
|
||||
layers.reserve(patch_dirs.size() + 1);
|
||||
layers_ext.reserve(patch_dirs.size() + 1);
|
||||
for (const auto& subdir : patch_dirs) {
|
||||
if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
|
||||
if (std::find(disabled.cbegin(), disabled.cend(), subdir->GetName()) != disabled.cend()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto romfs_dir = subdir->GetSubdirectory("romfs");
|
||||
if (romfs_dir != nullptr)
|
||||
@@ -381,7 +387,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
const auto update_disabled =
|
||||
std::find(disabled.begin(), disabled.end(), "Update") != disabled.end();
|
||||
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
|
||||
|
||||
if (!update_disabled && update != nullptr) {
|
||||
const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset);
|
||||
@@ -431,7 +437,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
|
||||
auto [nacp, discard_icon_file] = update.GetControlMetadata();
|
||||
|
||||
const auto update_disabled =
|
||||
std::find(disabled.begin(), disabled.end(), "Update") != disabled.end();
|
||||
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
|
||||
const auto update_label = update_disabled ? "[D] Update" : "Update";
|
||||
|
||||
if (nacp != nullptr) {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/fsmitm_romfsbuild.h"
|
||||
@@ -12,7 +14,7 @@
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
namespace {
|
||||
constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF;
|
||||
|
||||
struct TableLocation {
|
||||
@@ -51,7 +53,7 @@ struct FileEntry {
|
||||
static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size.");
|
||||
|
||||
template <typename Entry>
|
||||
static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, std::size_t offset) {
|
||||
std::pair<Entry, std::string> GetEntry(const VirtualFile& file, std::size_t offset) {
|
||||
Entry entry{};
|
||||
if (file->ReadObject(&entry, offset) != sizeof(Entry))
|
||||
return {};
|
||||
@@ -99,6 +101,7 @@ void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file
|
||||
this_dir_offset = entry.first.sibling;
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
|
||||
RomFSHeader header{};
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
@@ -508,8 +508,9 @@ static void RemoveBreakpoint(BreakpointType type, VAddr addr) {
|
||||
bp->second.len, bp->second.addr, static_cast<int>(type));
|
||||
|
||||
if (type == BreakpointType::Execute) {
|
||||
Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size());
|
||||
Core::System::GetInstance().InvalidateCpuInstructionCaches();
|
||||
auto& system = Core::System::GetInstance();
|
||||
system.Memory().WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size());
|
||||
system.InvalidateCpuInstructionCaches();
|
||||
}
|
||||
p.erase(addr);
|
||||
}
|
||||
@@ -969,12 +970,13 @@ static void ReadMemory() {
|
||||
SendReply("E01");
|
||||
}
|
||||
|
||||
if (!Memory::IsValidVirtualAddress(addr)) {
|
||||
auto& memory = Core::System::GetInstance().Memory();
|
||||
if (!memory.IsValidVirtualAddress(addr)) {
|
||||
return SendReply("E00");
|
||||
}
|
||||
|
||||
std::vector<u8> data(len);
|
||||
Memory::ReadBlock(addr, data.data(), len);
|
||||
memory.ReadBlock(addr, data.data(), len);
|
||||
|
||||
MemToGdbHex(reply, data.data(), len);
|
||||
reply[len * 2] = '\0';
|
||||
@@ -984,22 +986,23 @@ static void ReadMemory() {
|
||||
/// Modify location in memory with data received from the gdb client.
|
||||
static void WriteMemory() {
|
||||
auto start_offset = command_buffer + 1;
|
||||
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
|
||||
VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
|
||||
const auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
|
||||
const VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
|
||||
|
||||
start_offset = addr_pos + 1;
|
||||
auto len_pos = std::find(start_offset, command_buffer + command_length, ':');
|
||||
u64 len = HexToLong(start_offset, static_cast<u64>(len_pos - start_offset));
|
||||
const auto len_pos = std::find(start_offset, command_buffer + command_length, ':');
|
||||
const u64 len = HexToLong(start_offset, static_cast<u64>(len_pos - start_offset));
|
||||
|
||||
if (!Memory::IsValidVirtualAddress(addr)) {
|
||||
auto& system = Core::System::GetInstance();
|
||||
auto& memory = system.Memory();
|
||||
if (!memory.IsValidVirtualAddress(addr)) {
|
||||
return SendReply("E00");
|
||||
}
|
||||
|
||||
std::vector<u8> data(len);
|
||||
|
||||
GdbHexToMem(data.data(), len_pos + 1, len);
|
||||
Memory::WriteBlock(addr, data.data(), len);
|
||||
Core::System::GetInstance().InvalidateCpuInstructionCaches();
|
||||
memory.WriteBlock(addr, data.data(), len);
|
||||
system.InvalidateCpuInstructionCaches();
|
||||
SendReply("OK");
|
||||
}
|
||||
|
||||
@@ -1055,12 +1058,15 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
|
||||
breakpoint.active = true;
|
||||
breakpoint.addr = addr;
|
||||
breakpoint.len = len;
|
||||
Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
auto& memory = system.Memory();
|
||||
memory.ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
|
||||
|
||||
static constexpr std::array<u8, 4> btrap{0x00, 0x7d, 0x20, 0xd4};
|
||||
if (type == BreakpointType::Execute) {
|
||||
Memory::WriteBlock(addr, btrap.data(), btrap.size());
|
||||
Core::System::GetInstance().InvalidateCpuInstructionCaches();
|
||||
memory.WriteBlock(addr, btrap.data(), btrap.size());
|
||||
system.InvalidateCpuInstructionCaches();
|
||||
}
|
||||
p.insert({addr, breakpoint});
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/session.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace IPC {
|
||||
@@ -139,10 +140,9 @@ public:
|
||||
context->AddDomainObject(std::move(iface));
|
||||
} else {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto [server, client] =
|
||||
Kernel::ServerSession::CreateSessionPair(kernel, iface->GetServiceName());
|
||||
iface->ClientConnected(server);
|
||||
auto [client, server] = Kernel::Session::Create(kernel, iface->GetServiceName());
|
||||
context->AddMoveObject(std::move(client));
|
||||
iface->ClientConnected(std::move(server));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,18 +11,16 @@
|
||||
#include "core/core_cpu.h"
|
||||
#include "core/hle/kernel/address_arbiter.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/result.h"
|
||||
#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();
|
||||
@@ -34,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;
|
||||
@@ -67,23 +65,27 @@ ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) {
|
||||
|
||||
ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value,
|
||||
s32 num_to_wake) {
|
||||
auto& memory = system.Memory();
|
||||
|
||||
// Ensure that we can write to the address.
|
||||
if (!Memory::IsValidVirtualAddress(address)) {
|
||||
if (!memory.IsValidVirtualAddress(address)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
if (static_cast<s32>(Memory::Read32(address)) != value) {
|
||||
if (static_cast<s32>(memory.Read32(address)) != value) {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
Memory::Write32(address, static_cast<u32>(value + 1));
|
||||
memory.Write32(address, static_cast<u32>(value + 1));
|
||||
return SignalToAddressOnly(address, num_to_wake);
|
||||
}
|
||||
|
||||
ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
|
||||
s32 num_to_wake) {
|
||||
auto& memory = system.Memory();
|
||||
|
||||
// Ensure that we can write to the address.
|
||||
if (!Memory::IsValidVirtualAddress(address)) {
|
||||
if (!memory.IsValidVirtualAddress(address)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
@@ -109,11 +111,11 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a
|
||||
}
|
||||
}
|
||||
|
||||
if (static_cast<s32>(Memory::Read32(address)) != value) {
|
||||
if (static_cast<s32>(memory.Read32(address)) != value) {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
Memory::Write32(address, static_cast<u32>(updated_value));
|
||||
memory.Write32(address, static_cast<u32>(updated_value));
|
||||
WakeThreads(waiting_threads, num_to_wake);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@@ -134,18 +136,20 @@ ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s
|
||||
|
||||
ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
|
||||
bool should_decrement) {
|
||||
auto& memory = system.Memory();
|
||||
|
||||
// Ensure that we can read the address.
|
||||
if (!Memory::IsValidVirtualAddress(address)) {
|
||||
if (!memory.IsValidVirtualAddress(address)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
const s32 cur_value = static_cast<s32>(Memory::Read32(address));
|
||||
const s32 cur_value = static_cast<s32>(memory.Read32(address));
|
||||
if (cur_value >= value) {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (should_decrement) {
|
||||
Memory::Write32(address, static_cast<u32>(cur_value - 1));
|
||||
memory.Write32(address, static_cast<u32>(cur_value - 1));
|
||||
}
|
||||
|
||||
// Short-circuit without rescheduling, if timeout is zero.
|
||||
@@ -157,15 +161,19 @@ ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s6
|
||||
}
|
||||
|
||||
ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
|
||||
auto& memory = system.Memory();
|
||||
|
||||
// Ensure that we can read the address.
|
||||
if (!Memory::IsValidVirtualAddress(address)) {
|
||||
if (!memory.IsValidVirtualAddress(address)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
// Only wait for the address if equal.
|
||||
if (static_cast<s32>(Memory::Read32(address)) != value) {
|
||||
if (static_cast<s32>(memory.Read32(address)) != value) {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
// Short-circuit without rescheduling, if timeout is zero.
|
||||
|
||||
// Short-circuit without rescheduling if timeout is zero.
|
||||
if (timeout == 0) {
|
||||
return RESULT_TIMEOUT;
|
||||
}
|
||||
@@ -176,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);
|
||||
@@ -184,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,10 +4,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/server_port.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/session.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -20,28 +20,23 @@ std::shared_ptr<ServerPort> ClientPort::GetServerPort() const {
|
||||
}
|
||||
|
||||
ResultVal<std::shared_ptr<ClientSession>> ClientPort::Connect() {
|
||||
// Note: Threads do not wait for the server endpoint to call
|
||||
// AcceptSession before returning from this call.
|
||||
|
||||
if (active_sessions >= max_sessions) {
|
||||
return ERR_MAX_CONNECTIONS_REACHED;
|
||||
}
|
||||
active_sessions++;
|
||||
|
||||
// Create a new session pair, let the created sessions inherit the parent port's HLE handler.
|
||||
auto [server, client] =
|
||||
ServerSession::CreateSessionPair(kernel, server_port->GetName(), SharedFrom(this));
|
||||
auto [client, server] = Kernel::Session::Create(kernel, name);
|
||||
|
||||
if (server_port->HasHLEHandler()) {
|
||||
server_port->GetHLEHandler()->ClientConnected(server);
|
||||
server_port->GetHLEHandler()->ClientConnected(std::move(server));
|
||||
} else {
|
||||
server_port->AppendPendingSession(server);
|
||||
server_port->AppendPendingSession(std::move(server));
|
||||
}
|
||||
|
||||
// Wake the threads waiting on the ServerPort
|
||||
server_port->WakeupAllWaitingThreads();
|
||||
|
||||
return MakeResult(client);
|
||||
return MakeResult(std::move(client));
|
||||
}
|
||||
|
||||
void ClientPort::ConnectionClosed() {
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -12,22 +12,44 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ClientSession::ClientSession(KernelCore& kernel) : Object{kernel} {}
|
||||
ClientSession::ClientSession(KernelCore& kernel) : WaitObject{kernel} {}
|
||||
|
||||
ClientSession::~ClientSession() {
|
||||
// This destructor will be called automatically when the last ClientSession handle is closed by
|
||||
// the emulated application.
|
||||
if (auto server = parent->server.lock()) {
|
||||
server->ClientDisconnected();
|
||||
if (parent->Server()) {
|
||||
parent->Server()->ClientDisconnected();
|
||||
}
|
||||
}
|
||||
|
||||
ResultCode ClientSession::SendSyncRequest(Thread* thread) {
|
||||
// Signal the server session that new data is available
|
||||
if (auto server = parent->server.lock()) {
|
||||
return server->HandleSyncRequest(SharedFrom(thread));
|
||||
bool ClientSession::ShouldWait(const Thread* thread) const {
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
|
||||
void ClientSession::Acquire(Thread* thread) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kernel,
|
||||
std::shared_ptr<Session> parent,
|
||||
std::string name) {
|
||||
std::shared_ptr<ClientSession> client_session{std::make_shared<ClientSession>(kernel)};
|
||||
|
||||
client_session->name = std::move(name);
|
||||
client_session->parent = std::move(parent);
|
||||
|
||||
return MakeResult(std::move(client_session));
|
||||
}
|
||||
|
||||
ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory) {
|
||||
// Keep ServerSession alive until we're done working with it.
|
||||
if (!parent->Server()) {
|
||||
return ERR_SESSION_CLOSED_BY_REMOTE;
|
||||
}
|
||||
|
||||
return ERR_SESSION_CLOSED_BY_REMOTE;
|
||||
// Signal the server session that new data is available
|
||||
return parent->Server()->HandleSyncRequest(std::move(thread), memory);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -6,23 +6,28 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
namespace Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
class Session;
|
||||
class ServerSession;
|
||||
class Thread;
|
||||
|
||||
class ClientSession final : public Object {
|
||||
class ClientSession final : public WaitObject {
|
||||
public:
|
||||
explicit ClientSession(KernelCore& kernel);
|
||||
~ClientSession() override;
|
||||
|
||||
friend class ServerSession;
|
||||
friend class Session;
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "ClientSession";
|
||||
@@ -37,9 +42,17 @@ public:
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
ResultCode SendSyncRequest(Thread* thread);
|
||||
ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory);
|
||||
|
||||
bool ShouldWait(const Thread* thread) const override;
|
||||
|
||||
void Acquire(Thread* thread) override;
|
||||
|
||||
private:
|
||||
static ResultVal<std::shared_ptr<ClientSession>> Create(KernelCore& kernel,
|
||||
std::shared_ptr<Session> parent,
|
||||
std::string name = "Unknown");
|
||||
|
||||
/// The parent session, which links to the server endpoint.
|
||||
std::shared_ptr<Session> parent;
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
@@ -74,6 +74,8 @@ std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread(
|
||||
thread->WakeAfterDelay(timeout);
|
||||
}
|
||||
|
||||
is_thread_waiting = true;
|
||||
|
||||
return writable_event;
|
||||
}
|
||||
|
||||
@@ -214,10 +216,11 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTabl
|
||||
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
auto& owner_process = *thread.GetOwnerProcess();
|
||||
auto& handle_table = owner_process.GetHandleTable();
|
||||
auto& memory = Core::System::GetInstance().Memory();
|
||||
|
||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
|
||||
Memory::ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
dst_cmdbuf.size() * sizeof(u32));
|
||||
memory.ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
dst_cmdbuf.size() * sizeof(u32));
|
||||
|
||||
// The header was already built in the internal command buffer. Attempt to parse it to verify
|
||||
// the integrity and then copy it over to the target command buffer.
|
||||
@@ -273,8 +276,8 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
}
|
||||
|
||||
// Copy the translated command buffer back into the thread's command buffer area.
|
||||
Memory::WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
dst_cmdbuf.size() * sizeof(u32));
|
||||
memory.WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
dst_cmdbuf.size() * sizeof(u32));
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@@ -282,15 +285,14 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const {
|
||||
std::vector<u8> buffer;
|
||||
const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()};
|
||||
auto& memory = Core::System::GetInstance().Memory();
|
||||
|
||||
if (is_buffer_a) {
|
||||
buffer.resize(BufferDescriptorA()[buffer_index].Size());
|
||||
Memory::ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(),
|
||||
buffer.size());
|
||||
memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size());
|
||||
} else {
|
||||
buffer.resize(BufferDescriptorX()[buffer_index].Size());
|
||||
Memory::ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(),
|
||||
buffer.size());
|
||||
memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size());
|
||||
}
|
||||
|
||||
return buffer;
|
||||
@@ -311,10 +313,11 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
|
||||
size = buffer_size; // TODO(bunnei): This needs to be HW tested
|
||||
}
|
||||
|
||||
auto& memory = Core::System::GetInstance().Memory();
|
||||
if (is_buffer_b) {
|
||||
Memory::WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
|
||||
memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
|
||||
} else {
|
||||
Memory::WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
|
||||
memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
|
||||
}
|
||||
|
||||
return size;
|
||||
|
||||
@@ -264,6 +264,18 @@ public:
|
||||
|
||||
std::string Description() const;
|
||||
|
||||
Thread& GetThread() {
|
||||
return *thread;
|
||||
}
|
||||
|
||||
const Thread& GetThread() const {
|
||||
return *thread;
|
||||
}
|
||||
|
||||
bool IsThreadWaiting() const {
|
||||
return is_thread_waiting;
|
||||
}
|
||||
|
||||
private:
|
||||
void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming);
|
||||
|
||||
@@ -290,6 +302,7 @@ private:
|
||||
u32_le command{};
|
||||
|
||||
std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers;
|
||||
bool is_thread_waiting{};
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/hle/kernel/address_arbiter.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
@@ -79,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) {
|
||||
@@ -154,6 +153,16 @@ struct KernelCore::Impl {
|
||||
system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
|
||||
}
|
||||
|
||||
void MakeCurrentProcess(Process* process) {
|
||||
current_process = process;
|
||||
|
||||
if (process == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
system.Memory().SetCurrentPageTable(*process);
|
||||
}
|
||||
|
||||
std::atomic<u32> next_object_id{0};
|
||||
std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin};
|
||||
std::atomic<u64> next_user_process_id{Process::ProcessIDMin};
|
||||
@@ -208,13 +217,7 @@ void KernelCore::AppendNewProcess(std::shared_ptr<Process> process) {
|
||||
}
|
||||
|
||||
void KernelCore::MakeCurrentProcess(Process* process) {
|
||||
impl->current_process = process;
|
||||
|
||||
if (process == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Memory::SetCurrentPageTable(*process);
|
||||
impl->MakeCurrentProcess(process);
|
||||
}
|
||||
|
||||
Process* KernelCore::CurrentProcess() {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -79,7 +80,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
|
||||
// thread.
|
||||
ASSERT(requesting_thread == current_thread);
|
||||
|
||||
const u32 addr_value = Memory::Read32(address);
|
||||
const u32 addr_value = system.Memory().Read32(address);
|
||||
|
||||
// If the mutex isn't being held, just return success.
|
||||
if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
|
||||
@@ -117,7 +118,7 @@ ResultCode Mutex::Release(VAddr address) {
|
||||
|
||||
// There are no more threads waiting for the mutex, release it completely.
|
||||
if (thread == nullptr) {
|
||||
Memory::Write32(address, 0);
|
||||
system.Memory().Write32(address, 0);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -132,7 +133,7 @@ ResultCode Mutex::Release(VAddr address) {
|
||||
}
|
||||
|
||||
// Grant the mutex to the next waiting thread and resume it.
|
||||
Memory::Write32(address, mutex_value);
|
||||
system.Memory().Write32(address, mutex_value);
|
||||
|
||||
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
|
||||
thread->ResumeFromWait();
|
||||
|
||||
@@ -27,6 +27,7 @@ bool Object::IsWaitable() const {
|
||||
case HandleType::ResourceLimit:
|
||||
case HandleType::ClientPort:
|
||||
case HandleType::ClientSession:
|
||||
case HandleType::Session:
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ enum class HandleType : u32 {
|
||||
ServerPort,
|
||||
ClientSession,
|
||||
ServerSession,
|
||||
Session,
|
||||
};
|
||||
|
||||
class Object : NonCopyable, public std::enable_shared_from_this<Object> {
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
|
||||
@@ -458,7 +458,6 @@ void Scheduler::SwitchContext() {
|
||||
cpu_core.LoadContext(new_thread->GetContext());
|
||||
cpu_core.SetTlsAddress(new_thread->GetTLSAddress());
|
||||
cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
|
||||
cpu_core.ClearExclusiveState();
|
||||
} else {
|
||||
current_thread = nullptr;
|
||||
// Note: We do not reset the current process and current page table when idling because
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/multi_level_queue.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
@@ -19,38 +20,34 @@
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/session.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ServerSession::ServerSession(KernelCore& kernel) : WaitObject{kernel} {}
|
||||
ServerSession::~ServerSession() {
|
||||
// This destructor will be called automatically when the last ServerSession handle is closed by
|
||||
// the emulated application.
|
||||
|
||||
// Decrease the port's connection count.
|
||||
if (parent->port) {
|
||||
parent->port->ConnectionClosed();
|
||||
}
|
||||
}
|
||||
ServerSession::~ServerSession() = default;
|
||||
|
||||
ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kernel,
|
||||
std::shared_ptr<Session> parent,
|
||||
std::string name) {
|
||||
std::shared_ptr<ServerSession> server_session = std::make_shared<ServerSession>(kernel);
|
||||
std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)};
|
||||
|
||||
server_session->name = std::move(name);
|
||||
server_session->parent = nullptr;
|
||||
session->request_event = Core::Timing::CreateEvent(
|
||||
name, [session](u64 userdata, s64 cycles_late) { session->CompleteSyncRequest(); });
|
||||
session->name = std::move(name);
|
||||
session->parent = std::move(parent);
|
||||
|
||||
return MakeResult(std::move(server_session));
|
||||
return MakeResult(std::move(session));
|
||||
}
|
||||
|
||||
bool ServerSession::ShouldWait(const Thread* thread) const {
|
||||
// Wait if we have no pending requests, or if we're currently handling a request.
|
||||
if (auto client = parent->client.lock()) {
|
||||
return pending_requesting_threads.empty() || currently_handling != nullptr;
|
||||
// Closed sessions should never wait, an error will be returned from svcReplyAndReceive.
|
||||
if (!parent->Client()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Closed sessions should never wait, an error will be returned from svcReplyAndReceive.
|
||||
return {};
|
||||
// Wait if we have no pending requests, or if we're currently handling a request.
|
||||
return pending_requesting_threads.empty() || currently_handling != nullptr;
|
||||
}
|
||||
|
||||
void ServerSession::Acquire(Thread* thread) {
|
||||
@@ -127,13 +124,21 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread) {
|
||||
// The ServerSession received a sync request, this means that there's new data available
|
||||
// from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
|
||||
// similar.
|
||||
Kernel::HLERequestContext context(SharedFrom(this), thread);
|
||||
u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
|
||||
context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
|
||||
ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory) {
|
||||
u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))};
|
||||
std::shared_ptr<Kernel::HLERequestContext> context{
|
||||
std::make_shared<Kernel::HLERequestContext>(SharedFrom(this), std::move(thread))};
|
||||
|
||||
context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
|
||||
request_queue.Push(std::move(context));
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ServerSession::CompleteSyncRequest() {
|
||||
ASSERT(!request_queue.Empty());
|
||||
|
||||
auto& context = *request_queue.Front();
|
||||
|
||||
ResultCode result = RESULT_SUCCESS;
|
||||
// If the session has been converted to a domain, handle the domain request
|
||||
@@ -145,61 +150,27 @@ ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread) {
|
||||
result = hle_handler->HandleSyncRequest(context);
|
||||
}
|
||||
|
||||
if (thread->GetStatus() == ThreadStatus::Running) {
|
||||
// Put the thread to sleep until the server replies, it will be awoken in
|
||||
// svcReplyAndReceive for LLE servers.
|
||||
thread->SetStatus(ThreadStatus::WaitIPC);
|
||||
|
||||
if (hle_handler != nullptr) {
|
||||
// For HLE services, we put the request threads to sleep for a short duration to
|
||||
// simulate IPC overhead, but only if the HLE handler didn't put the thread to sleep for
|
||||
// other reasons like an async callback. The IPC overhead is needed to prevent
|
||||
// starvation when a thread only does sync requests to HLE services while a
|
||||
// lower-priority thread is waiting to run.
|
||||
|
||||
// This delay was approximated in a homebrew application by measuring the average time
|
||||
// it takes for svcSendSyncRequest to return when performing the SetLcdForceBlack IPC
|
||||
// request to the GSP:GPU service in a n3DS with firmware 11.6. The measured values have
|
||||
// a high variance and vary between models.
|
||||
static constexpr u64 IPCDelayNanoseconds = 39000;
|
||||
thread->WakeAfterDelay(IPCDelayNanoseconds);
|
||||
} else {
|
||||
// Add the thread to the list of threads that have issued a sync request with this
|
||||
// server.
|
||||
pending_requesting_threads.push_back(std::move(thread));
|
||||
}
|
||||
}
|
||||
|
||||
// If this ServerSession does not have an HLE implementation, just wake up the threads waiting
|
||||
// on it.
|
||||
WakeupAllWaitingThreads();
|
||||
|
||||
// Handle scenario when ConvertToDomain command was issued, as we must do the conversion at the
|
||||
// end of the command such that only commands following this one are handled as domains
|
||||
if (convert_to_domain) {
|
||||
ASSERT_MSG(IsSession(), "ServerSession is already a domain instance.");
|
||||
domain_request_handlers = {hle_handler};
|
||||
convert_to_domain = false;
|
||||
}
|
||||
|
||||
// Some service requests require the thread to block
|
||||
if (!context.IsThreadWaiting()) {
|
||||
context.GetThread().ResumeFromWait();
|
||||
context.GetThread().SetWaitSynchronizationResult(result);
|
||||
}
|
||||
|
||||
request_queue.Pop();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ServerSession::SessionPair ServerSession::CreateSessionPair(KernelCore& kernel,
|
||||
const std::string& name,
|
||||
std::shared_ptr<ClientPort> port) {
|
||||
auto server_session = ServerSession::Create(kernel, name + "_Server").Unwrap();
|
||||
std::shared_ptr<ClientSession> client_session = std::make_shared<ClientSession>(kernel);
|
||||
client_session->name = name + "_Client";
|
||||
|
||||
std::shared_ptr<Session> parent = std::make_shared<Session>();
|
||||
parent->client = client_session;
|
||||
parent->server = server_session;
|
||||
parent->port = std::move(port);
|
||||
|
||||
client_session->parent = parent;
|
||||
server_session->parent = parent;
|
||||
|
||||
return std::make_pair(std::move(server_session), std::move(client_session));
|
||||
ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
|
||||
Memory::Memory& memory) {
|
||||
Core::System::GetInstance().CoreTiming().ScheduleEvent(20000, request_event, {});
|
||||
return QueueSyncRequest(std::move(thread), memory);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -9,17 +9,22 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace Core::Timing {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class ClientPort;
|
||||
class ClientSession;
|
||||
class HLERequestContext;
|
||||
class KernelCore;
|
||||
class ServerSession;
|
||||
class Session;
|
||||
class SessionRequestHandler;
|
||||
class Thread;
|
||||
@@ -41,6 +46,12 @@ public:
|
||||
explicit ServerSession(KernelCore& kernel);
|
||||
~ServerSession() override;
|
||||
|
||||
friend class Session;
|
||||
|
||||
static ResultVal<std::shared_ptr<ServerSession>> Create(KernelCore& kernel,
|
||||
std::shared_ptr<Session> parent,
|
||||
std::string name = "Unknown");
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "ServerSession";
|
||||
}
|
||||
@@ -62,18 +73,6 @@ public:
|
||||
return parent.get();
|
||||
}
|
||||
|
||||
using SessionPair = std::pair<std::shared_ptr<ServerSession>, std::shared_ptr<ClientSession>>;
|
||||
|
||||
/**
|
||||
* Creates a pair of ServerSession and an associated ClientSession.
|
||||
* @param kernel The kernal instance to create the session pair under.
|
||||
* @param name Optional name of the ports.
|
||||
* @param client_port Optional The ClientPort that spawned this session.
|
||||
* @return The created session tuple
|
||||
*/
|
||||
static SessionPair CreateSessionPair(KernelCore& kernel, const std::string& name = "Unknown",
|
||||
std::shared_ptr<ClientPort> client_port = nullptr);
|
||||
|
||||
/**
|
||||
* Sets the HLE handler for the session. This handler will be called to service IPC requests
|
||||
* instead of the regular IPC machinery. (The regular IPC machinery is currently not
|
||||
@@ -85,10 +84,13 @@ public:
|
||||
|
||||
/**
|
||||
* Handle a sync request from the emulated application.
|
||||
*
|
||||
* @param thread Thread that initiated the request.
|
||||
* @param memory Memory context to handle the sync request under.
|
||||
*
|
||||
* @returns ResultCode from the operation.
|
||||
*/
|
||||
ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread);
|
||||
ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory);
|
||||
|
||||
bool ShouldWait(const Thread* thread) const override;
|
||||
|
||||
@@ -121,15 +123,11 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Creates a server session. The server session can have an optional HLE handler,
|
||||
* which will be invoked to handle the IPC requests that this session receives.
|
||||
* @param kernel The kernel instance to create this server session under.
|
||||
* @param name Optional name of the server session.
|
||||
* @return The created server session
|
||||
*/
|
||||
static ResultVal<std::shared_ptr<ServerSession>> Create(KernelCore& kernel,
|
||||
std::string name = "Unknown");
|
||||
/// Queues a sync request from the emulated application.
|
||||
ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory);
|
||||
|
||||
/// Completes a sync request from the emulated application.
|
||||
ResultCode CompleteSyncRequest();
|
||||
|
||||
/// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an
|
||||
/// object handle.
|
||||
@@ -159,6 +157,12 @@ private:
|
||||
|
||||
/// The name of this session (optional)
|
||||
std::string name;
|
||||
|
||||
/// Core timing event used to schedule the service request at some point in the future
|
||||
std::shared_ptr<Core::Timing::EventType> request_event;
|
||||
|
||||
/// Queue of scheduled service requests
|
||||
Common::MPSCQueue<std::shared_ptr<Kernel::HLERequestContext>> request_queue;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -1,12 +1,36 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// 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/hle/kernel/client_session.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/session.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
Session::Session() {}
|
||||
Session::~Session() {}
|
||||
Session::Session(KernelCore& kernel) : WaitObject{kernel} {}
|
||||
Session::~Session() = default;
|
||||
|
||||
Session::SessionPair Session::Create(KernelCore& kernel, std::string name) {
|
||||
auto session{std::make_shared<Session>(kernel)};
|
||||
auto client_session{Kernel::ClientSession::Create(kernel, session, name + "_Client").Unwrap()};
|
||||
auto server_session{Kernel::ServerSession::Create(kernel, session, name + "_Server").Unwrap()};
|
||||
|
||||
session->name = std::move(name);
|
||||
session->client = client_session;
|
||||
session->server = server_session;
|
||||
|
||||
return std::make_pair(std::move(client_session), std::move(server_session));
|
||||
}
|
||||
|
||||
bool Session::ShouldWait(const Thread* thread) const {
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
|
||||
void Session::Acquire(Thread* thread) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -1,27 +1,64 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// 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/object.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class ClientSession;
|
||||
class ClientPort;
|
||||
class ServerSession;
|
||||
|
||||
/**
|
||||
* Parent structure to link the client and server endpoints of a session with their associated
|
||||
* client port. The client port need not exist, as is the case for portless sessions like the
|
||||
* FS File and Directory sessions. When one of the endpoints of a session is destroyed, its
|
||||
* corresponding field in this structure will be set to nullptr.
|
||||
* client port.
|
||||
*/
|
||||
class Session final {
|
||||
class Session final : public WaitObject {
|
||||
public:
|
||||
std::weak_ptr<ClientSession> client; ///< The client endpoint of the session.
|
||||
std::weak_ptr<ServerSession> server; ///< The server endpoint of the session.
|
||||
std::shared_ptr<ClientPort> port; ///< The port that this session is associated with (optional).
|
||||
explicit Session(KernelCore& kernel);
|
||||
~Session() override;
|
||||
|
||||
using SessionPair = std::pair<std::shared_ptr<ClientSession>, std::shared_ptr<ServerSession>>;
|
||||
|
||||
static SessionPair Create(KernelCore& kernel, std::string name = "Unknown");
|
||||
|
||||
std::string GetName() const override {
|
||||
return name;
|
||||
}
|
||||
|
||||
static constexpr HandleType HANDLE_TYPE = HandleType::Session;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
bool ShouldWait(const Thread* thread) const override;
|
||||
|
||||
void Acquire(Thread* thread) override;
|
||||
|
||||
std::shared_ptr<ClientSession> Client() {
|
||||
if (auto result{client.lock()}) {
|
||||
return result;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::shared_ptr<ServerSession> Server() {
|
||||
if (auto result{server.lock()}) {
|
||||
return result;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
std::weak_ptr<ClientSession> client;
|
||||
std::weak_ptr<ServerSession> server;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
@@ -332,7 +332,9 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad
|
||||
/// Connect to an OS service given the port name, returns the handle to the port to out
|
||||
static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
|
||||
VAddr port_name_address) {
|
||||
if (!Memory::IsValidVirtualAddress(port_name_address)) {
|
||||
auto& memory = system.Memory();
|
||||
|
||||
if (!memory.IsValidVirtualAddress(port_name_address)) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
|
||||
port_name_address);
|
||||
@@ -341,7 +343,7 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
|
||||
|
||||
static constexpr std::size_t PortNameMaxLength = 11;
|
||||
// Read 1 char beyond the max allowed port name to detect names that are too long.
|
||||
std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1);
|
||||
const std::string port_name = memory.ReadCString(port_name_address, PortNameMaxLength + 1);
|
||||
if (port_name.size() > PortNameMaxLength) {
|
||||
LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength,
|
||||
port_name.size());
|
||||
@@ -379,11 +381,12 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
|
||||
|
||||
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
|
||||
|
||||
system.PrepareReschedule();
|
||||
auto thread = system.CurrentScheduler().GetCurrentThread();
|
||||
thread->InvalidateWakeupCallback();
|
||||
thread->SetStatus(ThreadStatus::WaitIPC);
|
||||
system.PrepareReschedule(thread->GetProcessorID());
|
||||
|
||||
// TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server
|
||||
// responds and cause a reschedule.
|
||||
return session->SendSyncRequest(system.CurrentScheduler().GetCurrentThread());
|
||||
return session->SendSyncRequest(SharedFrom(thread), system.Memory());
|
||||
}
|
||||
|
||||
/// Get the ID for the specified thread.
|
||||
@@ -452,7 +455,8 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
|
||||
LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}",
|
||||
handles_address, handle_count, nano_seconds);
|
||||
|
||||
if (!Memory::IsValidVirtualAddress(handles_address)) {
|
||||
auto& memory = system.Memory();
|
||||
if (!memory.IsValidVirtualAddress(handles_address)) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Handle address is not a valid virtual address, handle_address=0x{:016X}",
|
||||
handles_address);
|
||||
@@ -474,7 +478,7 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
|
||||
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
||||
|
||||
for (u64 i = 0; i < handle_count; ++i) {
|
||||
const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle));
|
||||
const Handle handle = memory.Read32(handles_address + i * sizeof(Handle));
|
||||
const auto object = handle_table.Get<WaitObject>(handle);
|
||||
|
||||
if (object == nullptr) {
|
||||
@@ -616,13 +620,15 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& memory = system.Memory();
|
||||
|
||||
// This typically is an error code so we're going to assume this is the case
|
||||
if (sz == sizeof(u32)) {
|
||||
LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", Memory::Read32(addr));
|
||||
LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", memory.Read32(addr));
|
||||
} else {
|
||||
// We don't know what's in here so we'll hexdump it
|
||||
debug_buffer.resize(sz);
|
||||
Memory::ReadBlock(addr, debug_buffer.data(), sz);
|
||||
memory.ReadBlock(addr, debug_buffer.data(), sz);
|
||||
std::string hexdump;
|
||||
for (std::size_t i = 0; i < debug_buffer.size(); i++) {
|
||||
hexdump += fmt::format("{:02X} ", debug_buffer[i]);
|
||||
@@ -712,7 +718,7 @@ static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr addre
|
||||
}
|
||||
|
||||
std::string str(len, '\0');
|
||||
Memory::ReadBlock(address, str.data(), str.size());
|
||||
system.Memory().ReadBlock(address, str.data(), str.size());
|
||||
LOG_DEBUG(Debug_Emulated, "{}", str);
|
||||
}
|
||||
|
||||
@@ -1115,7 +1121,7 @@ static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, H
|
||||
std::fill(ctx.vector_registers.begin() + 16, ctx.vector_registers.end(), u128{});
|
||||
}
|
||||
|
||||
Memory::WriteBlock(thread_context, &ctx, sizeof(ctx));
|
||||
system.Memory().WriteBlock(thread_context, &ctx, sizeof(ctx));
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1275,20 +1281,21 @@ static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_add
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
auto& memory = system.Memory();
|
||||
const auto& vm_manager = process->VMManager();
|
||||
const MemoryInfo memory_info = vm_manager.QueryMemory(address);
|
||||
|
||||
Memory::Write64(memory_info_address, memory_info.base_address);
|
||||
Memory::Write64(memory_info_address + 8, memory_info.size);
|
||||
Memory::Write32(memory_info_address + 16, memory_info.state);
|
||||
Memory::Write32(memory_info_address + 20, memory_info.attributes);
|
||||
Memory::Write32(memory_info_address + 24, memory_info.permission);
|
||||
Memory::Write32(memory_info_address + 32, memory_info.ipc_ref_count);
|
||||
Memory::Write32(memory_info_address + 28, memory_info.device_ref_count);
|
||||
Memory::Write32(memory_info_address + 36, 0);
|
||||
memory.Write64(memory_info_address, memory_info.base_address);
|
||||
memory.Write64(memory_info_address + 8, memory_info.size);
|
||||
memory.Write32(memory_info_address + 16, memory_info.state);
|
||||
memory.Write32(memory_info_address + 20, memory_info.attributes);
|
||||
memory.Write32(memory_info_address + 24, memory_info.permission);
|
||||
memory.Write32(memory_info_address + 32, memory_info.ipc_ref_count);
|
||||
memory.Write32(memory_info_address + 28, memory_info.device_ref_count);
|
||||
memory.Write32(memory_info_address + 36, 0);
|
||||
|
||||
// Page info appears to be currently unused by the kernel and is always set to zero.
|
||||
Memory::Write32(page_info_address, 0);
|
||||
memory.Write32(page_info_address, 0);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@@ -1643,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);
|
||||
|
||||
@@ -1672,6 +1678,7 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var
|
||||
|
||||
const std::size_t current_core = system.CurrentCoreIndex();
|
||||
auto& monitor = system.Monitor();
|
||||
auto& memory = system.Memory();
|
||||
|
||||
// Atomically read the value of the mutex.
|
||||
u32 mutex_val = 0;
|
||||
@@ -1681,7 +1688,7 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var
|
||||
monitor.SetExclusive(current_core, mutex_address);
|
||||
|
||||
// If the mutex is not yet acquired, acquire it.
|
||||
mutex_val = Memory::Read32(mutex_address);
|
||||
mutex_val = memory.Read32(mutex_address);
|
||||
|
||||
if (mutex_val != 0) {
|
||||
update_val = mutex_val | Mutex::MutexHasWaitersFlag;
|
||||
@@ -1718,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)
|
||||
@@ -1773,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");
|
||||
@@ -2284,12 +2300,13 @@ static ResultCode GetProcessList(Core::System& system, u32* out_num_processes,
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
auto& memory = system.Memory();
|
||||
const auto& process_list = kernel.GetProcessList();
|
||||
const auto num_processes = process_list.size();
|
||||
const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes);
|
||||
|
||||
for (std::size_t i = 0; i < copy_amount; ++i) {
|
||||
Memory::Write64(out_process_ids, process_list[i]->GetProcessID());
|
||||
memory.Write64(out_process_ids, process_list[i]->GetProcessID());
|
||||
out_process_ids += sizeof(u64);
|
||||
}
|
||||
|
||||
@@ -2323,13 +2340,14 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
auto& memory = system.Memory();
|
||||
const auto& thread_list = current_process->GetThreadList();
|
||||
const auto num_threads = thread_list.size();
|
||||
const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads);
|
||||
|
||||
auto list_iter = thread_list.cbegin();
|
||||
for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) {
|
||||
Memory::Write64(out_thread_ids, (*list_iter)->GetThreadID());
|
||||
memory.Write64(out_thread_ids, (*list_iter)->GetThreadID());
|
||||
out_thread_ids += sizeof(u64);
|
||||
}
|
||||
|
||||
@@ -2408,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));
|
||||
|
||||
@@ -162,13 +162,13 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
if (!Memory::IsValidVirtualAddress(owner_process, entry_point)) {
|
||||
auto& system = Core::System::GetInstance();
|
||||
if (!system.Memory().IsValidVirtualAddress(owner_process, entry_point)) {
|
||||
LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point);
|
||||
// TODO (bunnei): Find the correct error code to use here
|
||||
return RESULT_UNKNOWN;
|
||||
}
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel);
|
||||
|
||||
thread->thread_id = kernel.CreateNewThreadID();
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/physical_memory.h"
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/memory_setup.h"
|
||||
|
||||
namespace Kernel {
|
||||
namespace {
|
||||
@@ -786,19 +785,21 @@ void VMManager::MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryAre
|
||||
}
|
||||
|
||||
void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
|
||||
auto& memory = system.Memory();
|
||||
|
||||
switch (vma.type) {
|
||||
case VMAType::Free:
|
||||
Memory::UnmapRegion(page_table, vma.base, vma.size);
|
||||
memory.UnmapRegion(page_table, vma.base, vma.size);
|
||||
break;
|
||||
case VMAType::AllocatedMemoryBlock:
|
||||
Memory::MapMemoryRegion(page_table, vma.base, vma.size,
|
||||
vma.backing_block->data() + vma.offset);
|
||||
memory.MapMemoryRegion(page_table, vma.base, vma.size,
|
||||
vma.backing_block->data() + vma.offset);
|
||||
break;
|
||||
case VMAType::BackingMemory:
|
||||
Memory::MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory);
|
||||
memory.MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory);
|
||||
break;
|
||||
case VMAType::MMIO:
|
||||
Memory::MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler);
|
||||
memory.MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <boost/smart_ptr/intrusive_ptr.hpp>
|
||||
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -43,7 +43,8 @@ public:
|
||||
IAudioOut(Core::System& system, AudoutParams audio_params, AudioCore::AudioOut& audio_core,
|
||||
std::string&& device_name, std::string&& unique_name)
|
||||
: ServiceFramework("IAudioOut"), audio_core(audio_core),
|
||||
device_name(std::move(device_name)), audio_params(audio_params) {
|
||||
device_name(std::move(device_name)),
|
||||
audio_params(audio_params), main_memory{system.Memory()} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
|
||||
@@ -137,7 +138,7 @@ private:
|
||||
const u64 tag{rp.Pop<u64>()};
|
||||
|
||||
std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16));
|
||||
Memory::ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size);
|
||||
main_memory.ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size);
|
||||
|
||||
if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -209,6 +210,7 @@ private:
|
||||
|
||||
/// This is the event handle used to check if the audio buffer was released
|
||||
Kernel::EventPair buffer_event;
|
||||
Memory::Memory& main_memory;
|
||||
};
|
||||
|
||||
AudOutU::AudOutU(Core::System& system_) : ServiceFramework("audout:u"), system{system_} {
|
||||
|
||||
@@ -49,8 +49,9 @@ public:
|
||||
|
||||
system_event =
|
||||
Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioRenderer:SystemEvent");
|
||||
renderer = std::make_unique<AudioCore::AudioRenderer>(
|
||||
system.CoreTiming(), audren_params, system_event.writable, instance_number);
|
||||
renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), system.Memory(),
|
||||
audren_params, system_event.writable,
|
||||
instance_number);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -256,8 +256,8 @@ public:
|
||||
|
||||
// TODO(DarkLordZach): Verify that this is the correct behavior.
|
||||
// Build entry index now to save time later.
|
||||
BuildEntryIndex(entries, backend->GetFiles(), FileSys::File);
|
||||
BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::Directory);
|
||||
BuildEntryIndex(entries, backend->GetFiles(), FileSys::EntryType::File);
|
||||
BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::EntryType::Directory);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -391,13 +391,10 @@ public:
|
||||
}
|
||||
|
||||
void RenameFile(Kernel::HLERequestContext& ctx) {
|
||||
std::vector<u8> buffer;
|
||||
buffer.resize(ctx.BufferDescriptorX()[0].Size());
|
||||
Memory::ReadBlock(ctx.BufferDescriptorX()[0].Address(), buffer.data(), buffer.size());
|
||||
std::vector<u8> buffer = ctx.ReadBuffer(0);
|
||||
const std::string src_name = Common::StringFromBuffer(buffer);
|
||||
|
||||
buffer.resize(ctx.BufferDescriptorX()[1].Size());
|
||||
Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size());
|
||||
buffer = ctx.ReadBuffer(1);
|
||||
const std::string dst_name = Common::StringFromBuffer(buffer);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name);
|
||||
|
||||
@@ -140,9 +140,10 @@ public:
|
||||
rb.Push(ERROR_INVALID_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Read NRR data from memory
|
||||
std::vector<u8> nrr_data(nrr_size);
|
||||
Memory::ReadBlock(nrr_address, nrr_data.data(), nrr_size);
|
||||
system.Memory().ReadBlock(nrr_address, nrr_data.data(), nrr_size);
|
||||
NRRHeader header;
|
||||
std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader));
|
||||
|
||||
@@ -291,7 +292,7 @@ public:
|
||||
|
||||
// Read NRO data from memory
|
||||
std::vector<u8> nro_data(nro_size);
|
||||
Memory::ReadBlock(nro_address, nro_data.data(), nro_size);
|
||||
system.Memory().ReadBlock(nro_address, nro_data.data(), nro_size);
|
||||
|
||||
SHA256Hash hash{};
|
||||
mbedtls_sha256_ret(nro_data.data(), nro_data.size(), hash.data(), 0);
|
||||
|
||||
@@ -17,7 +17,8 @@ namespace Service::LM {
|
||||
|
||||
class ILogger final : public ServiceFramework<ILogger> {
|
||||
public:
|
||||
ILogger(Manager& manager) : ServiceFramework("ILogger"), manager(manager) {
|
||||
explicit ILogger(Manager& manager_, Memory::Memory& memory_)
|
||||
: ServiceFramework("ILogger"), manager{manager_}, memory{memory_} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILogger::Log, "Log"},
|
||||
{1, &ILogger::SetDestination, "SetDestination"},
|
||||
@@ -35,15 +36,15 @@ private:
|
||||
MessageHeader header{};
|
||||
VAddr addr{ctx.BufferDescriptorX()[0].Address()};
|
||||
const VAddr end_addr{addr + ctx.BufferDescriptorX()[0].size};
|
||||
Memory::ReadBlock(addr, &header, sizeof(MessageHeader));
|
||||
memory.ReadBlock(addr, &header, sizeof(MessageHeader));
|
||||
addr += sizeof(MessageHeader);
|
||||
|
||||
FieldMap fields;
|
||||
while (addr < end_addr) {
|
||||
const auto field = static_cast<Field>(Memory::Read8(addr++));
|
||||
const auto length = Memory::Read8(addr++);
|
||||
const auto field = static_cast<Field>(memory.Read8(addr++));
|
||||
const auto length = memory.Read8(addr++);
|
||||
|
||||
if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) {
|
||||
if (static_cast<Field>(memory.Read8(addr)) == Field::Skip) {
|
||||
++addr;
|
||||
}
|
||||
|
||||
@@ -54,7 +55,7 @@ private:
|
||||
}
|
||||
|
||||
std::vector<u8> data(length);
|
||||
Memory::ReadBlock(addr, data.data(), length);
|
||||
memory.ReadBlock(addr, data.data(), length);
|
||||
fields.emplace(field, std::move(data));
|
||||
}
|
||||
|
||||
@@ -74,11 +75,13 @@ private:
|
||||
}
|
||||
|
||||
Manager& manager;
|
||||
Memory::Memory& memory;
|
||||
};
|
||||
|
||||
class LM final : public ServiceFramework<LM> {
|
||||
public:
|
||||
explicit LM(Manager& manager) : ServiceFramework{"lm"}, manager(manager) {
|
||||
explicit LM(Manager& manager_, Memory::Memory& memory_)
|
||||
: ServiceFramework{"lm"}, manager{manager_}, memory{memory_} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &LM::OpenLogger, "OpenLogger"},
|
||||
@@ -94,14 +97,16 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ILogger>(manager);
|
||||
rb.PushIpcInterface<ILogger>(manager, memory);
|
||||
}
|
||||
|
||||
Manager& manager;
|
||||
Memory::Memory& memory;
|
||||
};
|
||||
|
||||
void InstallInterfaces(Core::System& system) {
|
||||
std::make_shared<LM>(system.GetLogManager())->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<LM>(system.GetLogManager(), system.Memory())
|
||||
->InstallAsService(system.ServiceManager());
|
||||
}
|
||||
|
||||
} // namespace Service::LM
|
||||
|
||||
@@ -189,7 +189,7 @@ private:
|
||||
LOG_DEBUG(Service_NFP, "called");
|
||||
|
||||
auto nfc_event = nfp_interface.GetNFCEvent();
|
||||
if (!nfc_event->ShouldWait(Kernel::GetCurrentThread()) && !has_attached_handle) {
|
||||
if (!nfc_event->ShouldWait(&ctx.GetThread()) && !has_attached_handle) {
|
||||
device_state = DeviceState::TagFound;
|
||||
nfc_event->Clear();
|
||||
}
|
||||
|
||||
@@ -191,8 +191,8 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
|
||||
std::memcpy(entries.data(), input2.data(),
|
||||
params.num_entries * sizeof(Tegra::CommandListHeader));
|
||||
} else {
|
||||
Memory::ReadBlock(params.address, entries.data(),
|
||||
params.num_entries * sizeof(Tegra::CommandListHeader));
|
||||
system.Memory().ReadBlock(params.address, entries.data(),
|
||||
params.num_entries * sizeof(Tegra::CommandListHeader));
|
||||
}
|
||||
UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
|
||||
UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
|
||||
|
||||
@@ -186,7 +186,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
|
||||
UNIMPLEMENTED_MSG("command_type={}", static_cast<int>(context.GetCommandType()));
|
||||
}
|
||||
|
||||
context.WriteToOutgoingCommandBuffer(*Kernel::GetCurrentThread());
|
||||
context.WriteToOutgoingCommandBuffer(context.GetThread());
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@@ -201,7 +201,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
|
||||
auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system);
|
||||
system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
|
||||
|
||||
SM::ServiceManager::InstallInterfaces(sm);
|
||||
SM::ServiceManager::InstallInterfaces(sm, system.Kernel());
|
||||
|
||||
Account::InstallInterfaces(system);
|
||||
AM::InstallInterfaces(*sm, nv_flinger, system);
|
||||
|
||||
@@ -30,10 +30,7 @@ void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
std::shared_ptr<Kernel::ClientSession> session{ctx.Session()->GetParent()->client};
|
||||
rb.PushMoveObjects(session);
|
||||
|
||||
LOG_DEBUG(Service, "session={}", session->GetObjectId());
|
||||
rb.PushMoveObjects(ctx.Session()->GetParent()->Client());
|
||||
}
|
||||
|
||||
void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -36,10 +36,11 @@ static ResultCode ValidateServiceName(const std::string& name) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void ServiceManager::InstallInterfaces(std::shared_ptr<ServiceManager> self) {
|
||||
void ServiceManager::InstallInterfaces(std::shared_ptr<ServiceManager> self,
|
||||
Kernel::KernelCore& kernel) {
|
||||
ASSERT(self->sm_interface.expired());
|
||||
|
||||
auto sm = std::make_shared<SM>(self);
|
||||
auto sm = std::make_shared<SM>(self, kernel);
|
||||
sm->InstallAsNamedPort();
|
||||
self->sm_interface = sm;
|
||||
self->controller_interface = std::make_unique<Controller>();
|
||||
@@ -114,8 +115,6 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
std::string name(name_buf.begin(), end);
|
||||
|
||||
// TODO(yuriks): Permission checks go here
|
||||
|
||||
auto client_port = service_manager->GetServicePort(name);
|
||||
if (client_port.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -127,14 +126,22 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto session = client_port.Unwrap()->Connect();
|
||||
ASSERT(session.Succeeded());
|
||||
if (session.Succeeded()) {
|
||||
LOG_DEBUG(Service_SM, "called service={} -> session={}", name, (*session)->GetObjectId());
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(session.Code());
|
||||
rb.PushMoveObjects(std::move(session).Unwrap());
|
||||
auto [client, server] = Kernel::Session::Create(kernel, name);
|
||||
|
||||
const auto& server_port = client_port.Unwrap()->GetServerPort();
|
||||
if (server_port->GetHLEHandler()) {
|
||||
server_port->GetHLEHandler()->ClientConnected(server);
|
||||
} else {
|
||||
server_port->AppendPendingSession(server);
|
||||
}
|
||||
|
||||
// Wake the threads waiting on the ServerPort
|
||||
server_port->WakeupAllWaitingThreads();
|
||||
|
||||
LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId());
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushMoveObjects(std::move(client));
|
||||
}
|
||||
|
||||
void SM::RegisterService(Kernel::HLERequestContext& ctx) {
|
||||
@@ -178,8 +185,8 @@ void SM::UnregisterService(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(service_manager->UnregisterService(name));
|
||||
}
|
||||
|
||||
SM::SM(std::shared_ptr<ServiceManager> service_manager)
|
||||
: ServiceFramework("sm:", 4), service_manager(std::move(service_manager)) {
|
||||
SM::SM(std::shared_ptr<ServiceManager> service_manager, Kernel::KernelCore& kernel)
|
||||
: ServiceFramework{"sm:", 4}, service_manager{std::move(service_manager)}, kernel{kernel} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x00000000, &SM::Initialize, "Initialize"},
|
||||
{0x00000001, &SM::GetService, "GetService"},
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
namespace Kernel {
|
||||
class ClientPort;
|
||||
class ClientSession;
|
||||
class KernelCore;
|
||||
class ServerPort;
|
||||
class SessionRequestHandler;
|
||||
} // namespace Kernel
|
||||
@@ -29,7 +30,7 @@ class Controller;
|
||||
/// Interface to "sm:" service
|
||||
class SM final : public ServiceFramework<SM> {
|
||||
public:
|
||||
explicit SM(std::shared_ptr<ServiceManager> service_manager);
|
||||
explicit SM(std::shared_ptr<ServiceManager> service_manager, Kernel::KernelCore& kernel);
|
||||
~SM() override;
|
||||
|
||||
private:
|
||||
@@ -39,11 +40,12 @@ private:
|
||||
void UnregisterService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<ServiceManager> service_manager;
|
||||
Kernel::KernelCore& kernel;
|
||||
};
|
||||
|
||||
class ServiceManager {
|
||||
public:
|
||||
static void InstallInterfaces(std::shared_ptr<ServiceManager> self);
|
||||
static void InstallInterfaces(std::shared_ptr<ServiceManager> self, Kernel::KernelCore& kernel);
|
||||
|
||||
ServiceManager();
|
||||
~ServiceManager();
|
||||
|
||||
1144
src/core/memory.cpp
1144
src/core/memory.cpp
File diff suppressed because it is too large
Load Diff
@@ -5,8 +5,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "common/memory_hook.h"
|
||||
|
||||
namespace Common {
|
||||
struct PageTable;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class Process;
|
||||
@@ -36,41 +46,369 @@ enum : VAddr {
|
||||
KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE,
|
||||
};
|
||||
|
||||
/// Changes the currently active page table to that of
|
||||
/// the given process instance.
|
||||
void SetCurrentPageTable(Kernel::Process& process);
|
||||
/// Central class that handles all memory operations and state.
|
||||
class Memory {
|
||||
public:
|
||||
explicit Memory(Core::System& system);
|
||||
~Memory();
|
||||
|
||||
Memory(const Memory&) = delete;
|
||||
Memory& operator=(const Memory&) = delete;
|
||||
|
||||
Memory(Memory&&) = default;
|
||||
Memory& operator=(Memory&&) = default;
|
||||
|
||||
/**
|
||||
* Changes the currently active page table to that of the given process instance.
|
||||
*
|
||||
* @param process The process to use the page table of.
|
||||
*/
|
||||
void SetCurrentPageTable(Kernel::Process& process);
|
||||
|
||||
/**
|
||||
* Maps an allocated buffer onto a region of the emulated process address space.
|
||||
*
|
||||
* @param page_table The page table of the emulated process.
|
||||
* @param base The address to start mapping at. Must be page-aligned.
|
||||
* @param size The amount of bytes to map. Must be page-aligned.
|
||||
* @param target Buffer with the memory backing the mapping. Must be of length at least
|
||||
* `size`.
|
||||
*/
|
||||
void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target);
|
||||
|
||||
/**
|
||||
* Maps a region of the emulated process address space as a IO region.
|
||||
*
|
||||
* @param page_table The page table of the emulated process.
|
||||
* @param base The address to start mapping at. Must be page-aligned.
|
||||
* @param size The amount of bytes to map. Must be page-aligned.
|
||||
* @param mmio_handler The handler that backs the mapping.
|
||||
*/
|
||||
void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
|
||||
Common::MemoryHookPointer mmio_handler);
|
||||
|
||||
/**
|
||||
* Unmaps a region of the emulated process address space.
|
||||
*
|
||||
* @param page_table The page table of the emulated process.
|
||||
* @param base The address to begin unmapping at.
|
||||
* @param size The amount of bytes to unmap.
|
||||
*/
|
||||
void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size);
|
||||
|
||||
/**
|
||||
* Adds a memory hook to intercept reads and writes to given region of memory.
|
||||
*
|
||||
* @param page_table The page table of the emulated process
|
||||
* @param base The starting address to apply the hook to.
|
||||
* @param size The size of the memory region to apply the hook to, in bytes.
|
||||
* @param hook The hook to apply to the region of memory.
|
||||
*/
|
||||
void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
|
||||
Common::MemoryHookPointer hook);
|
||||
|
||||
/**
|
||||
* Removes a memory hook from a given range of memory.
|
||||
*
|
||||
* @param page_table The page table of the emulated process.
|
||||
* @param base The starting address to remove the hook from.
|
||||
* @param size The size of the memory region to remove the hook from, in bytes.
|
||||
* @param hook The hook to remove from the specified region of memory.
|
||||
*/
|
||||
void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
|
||||
Common::MemoryHookPointer hook);
|
||||
|
||||
/**
|
||||
* Checks whether or not the supplied address is a valid virtual
|
||||
* address for the given process.
|
||||
*
|
||||
* @param process The emulated process to check the address against.
|
||||
* @param vaddr The virtual address to check the validity of.
|
||||
*
|
||||
* @returns True if the given virtual address is valid, false otherwise.
|
||||
*/
|
||||
bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr) const;
|
||||
|
||||
/**
|
||||
* Checks whether or not the supplied address is a valid virtual
|
||||
* address for the current process.
|
||||
*
|
||||
* @param vaddr The virtual address to check the validity of.
|
||||
*
|
||||
* @returns True if the given virtual address is valid, false otherwise.
|
||||
*/
|
||||
bool IsValidVirtualAddress(VAddr vaddr) const;
|
||||
|
||||
/**
|
||||
* Gets a pointer to the given address.
|
||||
*
|
||||
* @param vaddr Virtual address to retrieve a pointer to.
|
||||
*
|
||||
* @returns The pointer to the given address, if the address is valid.
|
||||
* If the address is not valid, nullptr will be returned.
|
||||
*/
|
||||
u8* GetPointer(VAddr vaddr);
|
||||
|
||||
/**
|
||||
* Gets a pointer to the given address.
|
||||
*
|
||||
* @param vaddr Virtual address to retrieve a pointer to.
|
||||
*
|
||||
* @returns The pointer to the given address, if the address is valid.
|
||||
* If the address is not valid, nullptr will be returned.
|
||||
*/
|
||||
const u8* GetPointer(VAddr vaddr) const;
|
||||
|
||||
/**
|
||||
* Reads an 8-bit unsigned value from the current process' address space
|
||||
* at the given virtual address.
|
||||
*
|
||||
* @param addr The virtual address to read the 8-bit value from.
|
||||
*
|
||||
* @returns the read 8-bit unsigned value.
|
||||
*/
|
||||
u8 Read8(VAddr addr);
|
||||
|
||||
/**
|
||||
* Reads a 16-bit unsigned value from the current process' address space
|
||||
* at the given virtual address.
|
||||
*
|
||||
* @param addr The virtual address to read the 16-bit value from.
|
||||
*
|
||||
* @returns the read 16-bit unsigned value.
|
||||
*/
|
||||
u16 Read16(VAddr addr);
|
||||
|
||||
/**
|
||||
* Reads a 32-bit unsigned value from the current process' address space
|
||||
* at the given virtual address.
|
||||
*
|
||||
* @param addr The virtual address to read the 32-bit value from.
|
||||
*
|
||||
* @returns the read 32-bit unsigned value.
|
||||
*/
|
||||
u32 Read32(VAddr addr);
|
||||
|
||||
/**
|
||||
* Reads a 64-bit unsigned value from the current process' address space
|
||||
* at the given virtual address.
|
||||
*
|
||||
* @param addr The virtual address to read the 64-bit value from.
|
||||
*
|
||||
* @returns the read 64-bit value.
|
||||
*/
|
||||
u64 Read64(VAddr addr);
|
||||
|
||||
/**
|
||||
* Writes an 8-bit unsigned integer to the given virtual address in
|
||||
* the current process' address space.
|
||||
*
|
||||
* @param addr The virtual address to write the 8-bit unsigned integer to.
|
||||
* @param data The 8-bit unsigned integer to write to the given virtual address.
|
||||
*
|
||||
* @post The memory at the given virtual address contains the specified data value.
|
||||
*/
|
||||
void Write8(VAddr addr, u8 data);
|
||||
|
||||
/**
|
||||
* Writes a 16-bit unsigned integer to the given virtual address in
|
||||
* the current process' address space.
|
||||
*
|
||||
* @param addr The virtual address to write the 16-bit unsigned integer to.
|
||||
* @param data The 16-bit unsigned integer to write to the given virtual address.
|
||||
*
|
||||
* @post The memory range [addr, sizeof(data)) contains the given data value.
|
||||
*/
|
||||
void Write16(VAddr addr, u16 data);
|
||||
|
||||
/**
|
||||
* Writes a 32-bit unsigned integer to the given virtual address in
|
||||
* the current process' address space.
|
||||
*
|
||||
* @param addr The virtual address to write the 32-bit unsigned integer to.
|
||||
* @param data The 32-bit unsigned integer to write to the given virtual address.
|
||||
*
|
||||
* @post The memory range [addr, sizeof(data)) contains the given data value.
|
||||
*/
|
||||
void Write32(VAddr addr, u32 data);
|
||||
|
||||
/**
|
||||
* Writes a 64-bit unsigned integer to the given virtual address in
|
||||
* the current process' address space.
|
||||
*
|
||||
* @param addr The virtual address to write the 64-bit unsigned integer to.
|
||||
* @param data The 64-bit unsigned integer to write to the given virtual address.
|
||||
*
|
||||
* @post The memory range [addr, sizeof(data)) contains the given data value.
|
||||
*/
|
||||
void Write64(VAddr addr, u64 data);
|
||||
|
||||
/**
|
||||
* Reads a null-terminated string from the given virtual address.
|
||||
* This function will continually read characters until either:
|
||||
*
|
||||
* - A null character ('\0') is reached.
|
||||
* - max_length characters have been read.
|
||||
*
|
||||
* @note The final null-terminating character (if found) is not included
|
||||
* in the returned string.
|
||||
*
|
||||
* @param vaddr The address to begin reading the string from.
|
||||
* @param max_length The maximum length of the string to read in characters.
|
||||
*
|
||||
* @returns The read string.
|
||||
*/
|
||||
std::string ReadCString(VAddr vaddr, std::size_t max_length);
|
||||
|
||||
/**
|
||||
* Reads a contiguous block of bytes from a specified process' address space.
|
||||
*
|
||||
* @param process The process to read the data from.
|
||||
* @param src_addr The virtual address to begin reading from.
|
||||
* @param dest_buffer The buffer to place the read bytes into.
|
||||
* @param size The amount of data to read, in bytes.
|
||||
*
|
||||
* @note If a size of 0 is specified, then this function reads nothing and
|
||||
* no attempts to access memory are made at all.
|
||||
*
|
||||
* @pre dest_buffer must be at least size bytes in length, otherwise a
|
||||
* buffer overrun will occur.
|
||||
*
|
||||
* @post The range [dest_buffer, size) contains the read bytes from the
|
||||
* process' address space.
|
||||
*/
|
||||
void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer,
|
||||
std::size_t size);
|
||||
|
||||
/**
|
||||
* Reads a contiguous block of bytes from the current process' address space.
|
||||
*
|
||||
* @param src_addr The virtual address to begin reading from.
|
||||
* @param dest_buffer The buffer to place the read bytes into.
|
||||
* @param size The amount of data to read, in bytes.
|
||||
*
|
||||
* @note If a size of 0 is specified, then this function reads nothing and
|
||||
* no attempts to access memory are made at all.
|
||||
*
|
||||
* @pre dest_buffer must be at least size bytes in length, otherwise a
|
||||
* buffer overrun will occur.
|
||||
*
|
||||
* @post The range [dest_buffer, size) contains the read bytes from the
|
||||
* current process' address space.
|
||||
*/
|
||||
void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size);
|
||||
|
||||
/**
|
||||
* Writes a range of bytes into a given process' address space at the specified
|
||||
* virtual address.
|
||||
*
|
||||
* @param process The process to write data into the address space of.
|
||||
* @param dest_addr The destination virtual address to begin writing the data at.
|
||||
* @param src_buffer The data to write into the process' address space.
|
||||
* @param size The size of the data to write, in bytes.
|
||||
*
|
||||
* @post The address range [dest_addr, size) in the process' address space
|
||||
* contains the data that was within src_buffer.
|
||||
*
|
||||
* @post If an attempt is made to write into an unmapped region of memory, the writes
|
||||
* will be ignored and an error will be logged.
|
||||
*
|
||||
* @post If a write is performed into a region of memory that is considered cached
|
||||
* rasterizer memory, will cause the currently active rasterizer to be notified
|
||||
* and will mark that region as invalidated to caches that the active
|
||||
* graphics backend may be maintaining over the course of execution.
|
||||
*/
|
||||
void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer,
|
||||
std::size_t size);
|
||||
|
||||
/**
|
||||
* Writes a range of bytes into the current process' address space at the specified
|
||||
* virtual address.
|
||||
*
|
||||
* @param dest_addr The destination virtual address to begin writing the data at.
|
||||
* @param src_buffer The data to write into the current process' address space.
|
||||
* @param size The size of the data to write, in bytes.
|
||||
*
|
||||
* @post The address range [dest_addr, size) in the current process' address space
|
||||
* contains the data that was within src_buffer.
|
||||
*
|
||||
* @post If an attempt is made to write into an unmapped region of memory, the writes
|
||||
* will be ignored and an error will be logged.
|
||||
*
|
||||
* @post If a write is performed into a region of memory that is considered cached
|
||||
* rasterizer memory, will cause the currently active rasterizer to be notified
|
||||
* and will mark that region as invalidated to caches that the active
|
||||
* graphics backend may be maintaining over the course of execution.
|
||||
*/
|
||||
void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size);
|
||||
|
||||
/**
|
||||
* Fills the specified address range within a process' address space with zeroes.
|
||||
*
|
||||
* @param process The process that will have a portion of its memory zeroed out.
|
||||
* @param dest_addr The starting virtual address of the range to zero out.
|
||||
* @param size The size of the address range to zero out, in bytes.
|
||||
*
|
||||
* @post The range [dest_addr, size) within the process' address space is
|
||||
* filled with zeroes.
|
||||
*/
|
||||
void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size);
|
||||
|
||||
/**
|
||||
* Fills the specified address range within the current process' address space with zeroes.
|
||||
*
|
||||
* @param dest_addr The starting virtual address of the range to zero out.
|
||||
* @param size The size of the address range to zero out, in bytes.
|
||||
*
|
||||
* @post The range [dest_addr, size) within the current process' address space is
|
||||
* filled with zeroes.
|
||||
*/
|
||||
void ZeroBlock(VAddr dest_addr, std::size_t size);
|
||||
|
||||
/**
|
||||
* Copies data within a process' address space to another location within the
|
||||
* same address space.
|
||||
*
|
||||
* @param process The process that will have data copied within its address space.
|
||||
* @param dest_addr The destination virtual address to begin copying the data into.
|
||||
* @param src_addr The source virtual address to begin copying the data from.
|
||||
* @param size The size of the data to copy, in bytes.
|
||||
*
|
||||
* @post The range [dest_addr, size) within the process' address space contains the
|
||||
* same data within the range [src_addr, size).
|
||||
*/
|
||||
void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
|
||||
std::size_t size);
|
||||
|
||||
/**
|
||||
* Copies data within the current process' address space to another location within the
|
||||
* same address space.
|
||||
*
|
||||
* @param dest_addr The destination virtual address to begin copying the data into.
|
||||
* @param src_addr The source virtual address to begin copying the data from.
|
||||
* @param size The size of the data to copy, in bytes.
|
||||
*
|
||||
* @post The range [dest_addr, size) within the current process' address space
|
||||
* contains the same data within the range [src_addr, size).
|
||||
*/
|
||||
void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size);
|
||||
|
||||
/**
|
||||
* Marks each page within the specified address range as cached or uncached.
|
||||
*
|
||||
* @param vaddr The virtual address indicating the start of the address range.
|
||||
* @param size The size of the address range in bytes.
|
||||
* @param cached Whether or not any pages within the address range should be
|
||||
* marked as cached or uncached.
|
||||
*/
|
||||
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
/// Determines if the given VAddr is valid for the specified process.
|
||||
bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
|
||||
bool IsValidVirtualAddress(VAddr vaddr);
|
||||
/// Determines if the given VAddr is a kernel address
|
||||
bool IsKernelVirtualAddress(VAddr vaddr);
|
||||
|
||||
u8 Read8(VAddr addr);
|
||||
u16 Read16(VAddr addr);
|
||||
u32 Read32(VAddr addr);
|
||||
u64 Read64(VAddr addr);
|
||||
|
||||
void Write8(VAddr addr, u8 data);
|
||||
void Write16(VAddr addr, u16 data);
|
||||
void Write32(VAddr addr, u32 data);
|
||||
void Write64(VAddr addr, u64 data);
|
||||
|
||||
void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, std::size_t size);
|
||||
void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size);
|
||||
void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer,
|
||||
std::size_t size);
|
||||
void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size);
|
||||
void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size);
|
||||
void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size);
|
||||
|
||||
u8* GetPointer(VAddr vaddr);
|
||||
|
||||
std::string ReadCString(VAddr vaddr, std::size_t max_length);
|
||||
|
||||
/**
|
||||
* Mark each page touching the region as cached.
|
||||
*/
|
||||
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached);
|
||||
|
||||
} // namespace Memory
|
||||
|
||||
@@ -20,18 +20,17 @@ namespace Memory {
|
||||
constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 12);
|
||||
constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
|
||||
|
||||
StandardVmCallbacks::StandardVmCallbacks(const Core::System& system,
|
||||
const CheatProcessMetadata& metadata)
|
||||
StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata)
|
||||
: metadata(metadata), system(system) {}
|
||||
|
||||
StandardVmCallbacks::~StandardVmCallbacks() = default;
|
||||
|
||||
void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) {
|
||||
ReadBlock(SanitizeAddress(address), data, size);
|
||||
system.Memory().ReadBlock(SanitizeAddress(address), data, size);
|
||||
}
|
||||
|
||||
void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) {
|
||||
WriteBlock(SanitizeAddress(address), data, size);
|
||||
system.Memory().WriteBlock(SanitizeAddress(address), data, size);
|
||||
}
|
||||
|
||||
u64 StandardVmCallbacks::HidKeysDown() {
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Memory {
|
||||
|
||||
class StandardVmCallbacks : public DmntCheatVm::Callbacks {
|
||||
public:
|
||||
StandardVmCallbacks(const Core::System& system, const CheatProcessMetadata& metadata);
|
||||
StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata);
|
||||
~StandardVmCallbacks() override;
|
||||
|
||||
void MemoryRead(VAddr address, void* data, u64 size) override;
|
||||
@@ -37,7 +37,7 @@ private:
|
||||
VAddr SanitizeAddress(VAddr address) const;
|
||||
|
||||
const CheatProcessMetadata& metadata;
|
||||
const Core::System& system;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
// Intermediary class that parses a text file or other disk format for storing cheats into a
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/memory_hook.h"
|
||||
|
||||
namespace Common {
|
||||
struct PageTable;
|
||||
}
|
||||
|
||||
namespace Memory {
|
||||
|
||||
/**
|
||||
* Maps an allocated buffer onto a region of the emulated process address space.
|
||||
*
|
||||
* @param page_table The page table of the emulated process.
|
||||
* @param base The address to start mapping at. Must be page-aligned.
|
||||
* @param size The amount of bytes to map. Must be page-aligned.
|
||||
* @param target Buffer with the memory backing the mapping. Must be of length at least `size`.
|
||||
*/
|
||||
void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target);
|
||||
|
||||
/**
|
||||
* Maps a region of the emulated process address space as a IO region.
|
||||
* @param page_table The page table of the emulated process.
|
||||
* @param base The address to start mapping at. Must be page-aligned.
|
||||
* @param size The amount of bytes to map. Must be page-aligned.
|
||||
* @param mmio_handler The handler that backs the mapping.
|
||||
*/
|
||||
void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
|
||||
Common::MemoryHookPointer mmio_handler);
|
||||
|
||||
void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size);
|
||||
|
||||
void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
|
||||
Common::MemoryHookPointer hook);
|
||||
void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
|
||||
Common::MemoryHookPointer hook);
|
||||
|
||||
} // namespace Memory
|
||||
@@ -147,7 +147,7 @@ json GetFullDataAuto(const std::string& timestamp, u64 title_id, Core::System& s
|
||||
}
|
||||
|
||||
template <bool read_value, typename DescriptorType>
|
||||
json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer) {
|
||||
json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer, Memory::Memory& memory) {
|
||||
auto buffer_out = json::array();
|
||||
for (const auto& desc : buffer) {
|
||||
auto entry = json{
|
||||
@@ -157,7 +157,7 @@ json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer) {
|
||||
|
||||
if constexpr (read_value) {
|
||||
std::vector<u8> data(desc.Size());
|
||||
Memory::ReadBlock(desc.Address(), data.data(), desc.Size());
|
||||
memory.ReadBlock(desc.Address(), data.data(), desc.Size());
|
||||
entry["data"] = Common::HexToString(data);
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer) {
|
||||
return buffer_out;
|
||||
}
|
||||
|
||||
json GetHLERequestContextData(Kernel::HLERequestContext& ctx) {
|
||||
json GetHLERequestContextData(Kernel::HLERequestContext& ctx, Memory::Memory& memory) {
|
||||
json out;
|
||||
|
||||
auto cmd_buf = json::array();
|
||||
@@ -177,10 +177,10 @@ json GetHLERequestContextData(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
out["command_buffer"] = std::move(cmd_buf);
|
||||
|
||||
out["buffer_descriptor_a"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorA());
|
||||
out["buffer_descriptor_b"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorB());
|
||||
out["buffer_descriptor_c"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorC());
|
||||
out["buffer_descriptor_x"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorX());
|
||||
out["buffer_descriptor_a"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorA(), memory);
|
||||
out["buffer_descriptor_b"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorB(), memory);
|
||||
out["buffer_descriptor_c"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorC(), memory);
|
||||
out["buffer_descriptor_x"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorX(), memory);
|
||||
|
||||
return out;
|
||||
}
|
||||
@@ -259,7 +259,7 @@ void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u
|
||||
const auto title_id = system.CurrentProcess()->GetTitleID();
|
||||
auto out = GetFullDataAuto(timestamp, title_id, system);
|
||||
|
||||
auto function_out = GetHLERequestContextData(ctx);
|
||||
auto function_out = GetHLERequestContextData(ctx, system.Memory());
|
||||
function_out["command_id"] = command_id;
|
||||
function_out["function_name"] = name;
|
||||
function_out["service_name"] = service_name;
|
||||
|
||||
@@ -165,24 +165,20 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
|
||||
Telemetry::AppendOSInfo(field_collection);
|
||||
|
||||
// Log user configuration information
|
||||
AddField(Telemetry::FieldType::UserConfig, "Audio_SinkId", Settings::values.sink_id);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Audio_EnableAudioStretching",
|
||||
Settings::values.enable_audio_stretching);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Core_UseMultiCore",
|
||||
Settings::values.use_multi_core);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Renderer_ResolutionFactor",
|
||||
Settings::values.resolution_factor);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimit",
|
||||
Settings::values.use_frame_limit);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Renderer_UseDiskShaderCache",
|
||||
Settings::values.use_disk_shader_cache);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation",
|
||||
constexpr auto field_type = Telemetry::FieldType::UserConfig;
|
||||
AddField(field_type, "Audio_SinkId", Settings::values.sink_id);
|
||||
AddField(field_type, "Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
|
||||
AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core);
|
||||
AddField(field_type, "Renderer_Backend", "OpenGL");
|
||||
AddField(field_type, "Renderer_ResolutionFactor", Settings::values.resolution_factor);
|
||||
AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit);
|
||||
AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit);
|
||||
AddField(field_type, "Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
|
||||
AddField(field_type, "Renderer_UseAccurateGpuEmulation",
|
||||
Settings::values.use_accurate_gpu_emulation);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAsynchronousGpuEmulation",
|
||||
AddField(field_type, "Renderer_UseAsynchronousGpuEmulation",
|
||||
Settings::values.use_asynchronous_gpu_emulation);
|
||||
AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode",
|
||||
Settings::values.use_docked_mode);
|
||||
AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode);
|
||||
}
|
||||
|
||||
bool TelemetrySession::SubmitTestcase() {
|
||||
|
||||
@@ -11,40 +11,39 @@
|
||||
#include "core/tools/freezer.h"
|
||||
|
||||
namespace Tools {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
|
||||
|
||||
u64 MemoryReadWidth(u32 width, VAddr addr) {
|
||||
u64 MemoryReadWidth(Memory::Memory& memory, u32 width, VAddr addr) {
|
||||
switch (width) {
|
||||
case 1:
|
||||
return Memory::Read8(addr);
|
||||
return memory.Read8(addr);
|
||||
case 2:
|
||||
return Memory::Read16(addr);
|
||||
return memory.Read16(addr);
|
||||
case 4:
|
||||
return Memory::Read32(addr);
|
||||
return memory.Read32(addr);
|
||||
case 8:
|
||||
return Memory::Read64(addr);
|
||||
return memory.Read64(addr);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryWriteWidth(u32 width, VAddr addr, u64 value) {
|
||||
void MemoryWriteWidth(Memory::Memory& memory, u32 width, VAddr addr, u64 value) {
|
||||
switch (width) {
|
||||
case 1:
|
||||
Memory::Write8(addr, static_cast<u8>(value));
|
||||
memory.Write8(addr, static_cast<u8>(value));
|
||||
break;
|
||||
case 2:
|
||||
Memory::Write16(addr, static_cast<u16>(value));
|
||||
memory.Write16(addr, static_cast<u16>(value));
|
||||
break;
|
||||
case 4:
|
||||
Memory::Write32(addr, static_cast<u32>(value));
|
||||
memory.Write32(addr, static_cast<u32>(value));
|
||||
break;
|
||||
case 8:
|
||||
Memory::Write64(addr, value);
|
||||
memory.Write64(addr, value);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
@@ -53,7 +52,8 @@ void MemoryWriteWidth(u32 width, VAddr addr, u64 value) {
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
Freezer::Freezer(Core::Timing::CoreTiming& core_timing) : core_timing(core_timing) {
|
||||
Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Memory::Memory& memory_)
|
||||
: core_timing{core_timing_}, memory{memory_} {
|
||||
event = Core::Timing::CreateEvent(
|
||||
"MemoryFreezer::FrameCallback",
|
||||
[this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
|
||||
@@ -89,7 +89,7 @@ void Freezer::Clear() {
|
||||
u64 Freezer::Freeze(VAddr address, u32 width) {
|
||||
std::lock_guard lock{entries_mutex};
|
||||
|
||||
const auto current_value = MemoryReadWidth(width, address);
|
||||
const auto current_value = MemoryReadWidth(memory, width, address);
|
||||
entries.push_back({address, width, current_value});
|
||||
|
||||
LOG_DEBUG(Common_Memory,
|
||||
@@ -169,7 +169,7 @@ void Freezer::FrameCallback(u64 userdata, s64 cycles_late) {
|
||||
LOG_DEBUG(Common_Memory,
|
||||
"Enforcing memory freeze at address={:016X}, value={:016X}, width={:02X}",
|
||||
entry.address, entry.value, entry.width);
|
||||
MemoryWriteWidth(entry.width, entry.address, entry.value);
|
||||
MemoryWriteWidth(memory, entry.width, entry.address, entry.value);
|
||||
}
|
||||
|
||||
core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - cycles_late, event);
|
||||
@@ -181,7 +181,7 @@ void Freezer::FillEntryReads() {
|
||||
LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values.");
|
||||
|
||||
for (auto& entry : entries) {
|
||||
entry.value = MemoryReadWidth(entry.width, entry.address);
|
||||
entry.value = MemoryReadWidth(memory, entry.width, entry.address);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,10 @@ class CoreTiming;
|
||||
struct EventType;
|
||||
} // namespace Core::Timing
|
||||
|
||||
namespace Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace Tools {
|
||||
|
||||
/**
|
||||
@@ -34,7 +38,7 @@ public:
|
||||
u64 value;
|
||||
};
|
||||
|
||||
explicit Freezer(Core::Timing::CoreTiming& core_timing);
|
||||
explicit Freezer(Core::Timing::CoreTiming& core_timing_, Memory::Memory& memory_);
|
||||
~Freezer();
|
||||
|
||||
// Enables or disables the entire memory freezer.
|
||||
@@ -78,6 +82,7 @@ private:
|
||||
|
||||
std::shared_ptr<Core::Timing::EventType> event;
|
||||
Core::Timing::CoreTiming& core_timing;
|
||||
Memory::Memory& memory;
|
||||
};
|
||||
|
||||
} // namespace Tools
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/memory_setup.h"
|
||||
#include "tests/core/arm/arm_test_common.h"
|
||||
|
||||
namespace ArmTests {
|
||||
@@ -16,8 +15,9 @@ namespace ArmTests {
|
||||
TestEnvironment::TestEnvironment(bool mutable_memory_)
|
||||
: mutable_memory(mutable_memory_),
|
||||
test_memory(std::make_shared<TestMemory>(this)), kernel{Core::System::GetInstance()} {
|
||||
auto process = Kernel::Process::Create(Core::System::GetInstance(), "",
|
||||
Kernel::Process::ProcessType::Userland);
|
||||
auto& system = Core::System::GetInstance();
|
||||
|
||||
auto process = Kernel::Process::Create(system, "", Kernel::Process::ProcessType::Userland);
|
||||
page_table = &process->VMManager().page_table;
|
||||
|
||||
std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr);
|
||||
@@ -25,15 +25,16 @@ TestEnvironment::TestEnvironment(bool mutable_memory_)
|
||||
std::fill(page_table->attributes.begin(), page_table->attributes.end(),
|
||||
Common::PageType::Unmapped);
|
||||
|
||||
Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory);
|
||||
Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory);
|
||||
system.Memory().MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory);
|
||||
system.Memory().MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory);
|
||||
|
||||
kernel.MakeCurrentProcess(process.get());
|
||||
}
|
||||
|
||||
TestEnvironment::~TestEnvironment() {
|
||||
Memory::UnmapRegion(*page_table, 0x80000000, 0x80000000);
|
||||
Memory::UnmapRegion(*page_table, 0x00000000, 0x80000000);
|
||||
auto& system = Core::System::GetInstance();
|
||||
system.Memory().UnmapRegion(*page_table, 0x80000000, 0x80000000);
|
||||
system.Memory().UnmapRegion(*page_table, 0x00000000, 0x80000000);
|
||||
}
|
||||
|
||||
void TestEnvironment::SetMemory64(VAddr vaddr, u64 value) {
|
||||
|
||||
@@ -310,6 +310,11 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
enum class DepthMode : u32 {
|
||||
MinusOneToOne = 0,
|
||||
ZeroToOne = 1,
|
||||
};
|
||||
|
||||
enum class PrimitiveTopology : u32 {
|
||||
Points = 0x0,
|
||||
Lines = 0x1,
|
||||
@@ -491,6 +496,18 @@ public:
|
||||
INSERT_UNION_PADDING_WORDS(1);
|
||||
};
|
||||
|
||||
enum class TessellationPrimitive : u32 {
|
||||
Isolines = 0,
|
||||
Triangles = 1,
|
||||
Quads = 2,
|
||||
};
|
||||
|
||||
enum class TessellationSpacing : u32 {
|
||||
Equal = 0,
|
||||
FractionalOdd = 1,
|
||||
FractionalEven = 2,
|
||||
};
|
||||
|
||||
struct RenderTargetConfig {
|
||||
u32 address_high;
|
||||
u32 address_low;
|
||||
@@ -628,7 +645,19 @@ public:
|
||||
};
|
||||
} sync_info;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x11E);
|
||||
INSERT_UNION_PADDING_WORDS(0x15);
|
||||
|
||||
union {
|
||||
BitField<0, 2, TessellationPrimitive> prim;
|
||||
BitField<4, 2, TessellationSpacing> spacing;
|
||||
BitField<8, 1, u32> cw;
|
||||
BitField<9, 1, u32> connected;
|
||||
} tess_mode;
|
||||
|
||||
std::array<f32, 4> tess_level_outer;
|
||||
std::array<f32, 2> tess_level_inner;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x102);
|
||||
|
||||
u32 tfb_enabled;
|
||||
|
||||
@@ -647,7 +676,7 @@ public:
|
||||
u32 count;
|
||||
} vertex_buffer;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(1);
|
||||
DepthMode depth_mode;
|
||||
|
||||
float clear_color[4];
|
||||
float clear_depth;
|
||||
@@ -662,7 +691,9 @@ public:
|
||||
u32 polygon_offset_line_enable;
|
||||
u32 polygon_offset_fill_enable;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0xD);
|
||||
u32 patch_vertices;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0xC);
|
||||
|
||||
std::array<ScissorTest, NumViewports> scissor_test;
|
||||
|
||||
@@ -1386,17 +1417,22 @@ ASSERT_REG_POSITION(upload, 0x60);
|
||||
ASSERT_REG_POSITION(exec_upload, 0x6C);
|
||||
ASSERT_REG_POSITION(data_upload, 0x6D);
|
||||
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(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);
|
||||
ASSERT_REG_POSITION(polygon_offset_point_enable, 0x370);
|
||||
ASSERT_REG_POSITION(polygon_offset_line_enable, 0x371);
|
||||
ASSERT_REG_POSITION(polygon_offset_fill_enable, 0x372);
|
||||
ASSERT_REG_POSITION(patch_vertices, 0x373);
|
||||
ASSERT_REG_POSITION(scissor_test, 0x380);
|
||||
ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5);
|
||||
ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D6);
|
||||
|
||||
@@ -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,
|
||||
@@ -799,6 +809,12 @@ union Instruction {
|
||||
BitField<40, 1, u64> invert;
|
||||
} popc;
|
||||
|
||||
union {
|
||||
BitField<41, 1, u64> sh;
|
||||
BitField<40, 1, u64> invert;
|
||||
BitField<48, 1, u64> is_signed;
|
||||
} flo;
|
||||
|
||||
union {
|
||||
BitField<39, 3, u64> pred;
|
||||
BitField<42, 1, u64> neg_pred;
|
||||
@@ -1276,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) {
|
||||
@@ -1439,6 +1456,26 @@ union Instruction {
|
||||
}
|
||||
} tlds;
|
||||
|
||||
union {
|
||||
BitField<28, 1, u64> is_array;
|
||||
BitField<29, 2, TextureType> texture_type;
|
||||
BitField<35, 1, u64> aoffi_flag;
|
||||
BitField<49, 1, u64> nodep_flag;
|
||||
|
||||
bool UsesMiscMode(TextureMiscMode mode) const {
|
||||
switch (mode) {
|
||||
case TextureMiscMode::AOFFI:
|
||||
return aoffi_flag != 0;
|
||||
case TextureMiscMode::NODEP:
|
||||
return nodep_flag != 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} txd;
|
||||
|
||||
union {
|
||||
BitField<24, 2, StoreCacheManagement> cache_management;
|
||||
BitField<33, 3, ImageType> image_type;
|
||||
@@ -1518,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;
|
||||
@@ -1632,6 +1674,8 @@ public:
|
||||
TLD4S, // Texture Load 4 with scalar / non - vec4 source / destinations
|
||||
TMML_B, // Texture Mip Map Level
|
||||
TMML, // Texture Mip Map Level
|
||||
TXD, // Texture Gradient/Load with Derivates
|
||||
TXD_B, // Texture Gradient/Load with Derivates Bindless
|
||||
SUST, // Surface Store
|
||||
SULD, // Surface Load
|
||||
SUATOM, // Surface Atomic Operation
|
||||
@@ -1640,6 +1684,7 @@ public:
|
||||
IPA,
|
||||
OUT_R, // Emit vertex/primitive
|
||||
ISBERD,
|
||||
MEMBAR,
|
||||
VMAD,
|
||||
VSETP,
|
||||
FFMA_IMM, // Fused Multiply and Add
|
||||
@@ -1664,6 +1709,9 @@ public:
|
||||
ISCADD_C, // Scale and Add
|
||||
ISCADD_R,
|
||||
ISCADD_IMM,
|
||||
FLO_R,
|
||||
FLO_C,
|
||||
FLO_IMM,
|
||||
LEA_R1,
|
||||
LEA_R2,
|
||||
LEA_RZ,
|
||||
@@ -1727,6 +1775,10 @@ public:
|
||||
SHR_C,
|
||||
SHR_R,
|
||||
SHR_IMM,
|
||||
SHF_RIGHT_R,
|
||||
SHF_RIGHT_IMM,
|
||||
SHF_LEFT_R,
|
||||
SHF_LEFT_IMM,
|
||||
FMNMX_C,
|
||||
FMNMX_R,
|
||||
FMNMX_IMM,
|
||||
@@ -1894,7 +1946,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"),
|
||||
@@ -1921,9 +1973,11 @@ 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"),
|
||||
INST("11011110001110--", Id::TXD, Type::Texture, "TXD"),
|
||||
INST("11101011001-----", Id::SUST, Type::Image, "SUST"),
|
||||
INST("11101011000-----", Id::SULD, Type::Image, "SULD"),
|
||||
INST("1110101000------", Id::SUATOM, Type::Image, "SUATOM_D"),
|
||||
@@ -1931,6 +1985,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"),
|
||||
@@ -1965,6 +2020,9 @@ private:
|
||||
INST("010110110100----", Id::ICMP_R, Type::ArithmeticInteger, "ICMP_R"),
|
||||
INST("010010110100----", Id::ICMP_CR, Type::ArithmeticInteger, "ICMP_CR"),
|
||||
INST("0011011-0100----", Id::ICMP_IMM, Type::ArithmeticInteger, "ICMP_IMM"),
|
||||
INST("0101110000110---", Id::FLO_R, Type::ArithmeticInteger, "FLO_R"),
|
||||
INST("0100110000110---", Id::FLO_C, Type::ArithmeticInteger, "FLO_C"),
|
||||
INST("0011100-00110---", Id::FLO_IMM, Type::ArithmeticInteger, "FLO_IMM"),
|
||||
INST("0101101111011---", Id::LEA_R2, Type::ArithmeticInteger, "LEA_R2"),
|
||||
INST("0101101111010---", Id::LEA_R1, Type::ArithmeticInteger, "LEA_R1"),
|
||||
INST("001101101101----", Id::LEA_IMM, Type::ArithmeticInteger, "LEA_IMM"),
|
||||
@@ -2022,6 +2080,10 @@ private:
|
||||
INST("0100110000101---", Id::SHR_C, Type::Shift, "SHR_C"),
|
||||
INST("0101110000101---", Id::SHR_R, Type::Shift, "SHR_R"),
|
||||
INST("0011100-00101---", Id::SHR_IMM, Type::Shift, "SHR_IMM"),
|
||||
INST("0101110011111---", Id::SHF_RIGHT_R, Type::Shift, "SHF_RIGHT_R"),
|
||||
INST("0011100-11111---", Id::SHF_RIGHT_IMM, Type::Shift, "SHF_RIGHT_IMM"),
|
||||
INST("0101101111111---", Id::SHF_LEFT_R, Type::Shift, "SHF_LEFT_R"),
|
||||
INST("0011011-11111---", Id::SHF_LEFT_IMM, Type::Shift, "SHF_LEFT_IMM"),
|
||||
INST("0100110011100---", Id::I2I_C, Type::Conversion, "I2I_C"),
|
||||
INST("0101110011100---", Id::I2I_R, Type::Conversion, "I2I_R"),
|
||||
INST("0011101-11100---", Id::I2I_IMM, Type::Conversion, "I2I_IMM"),
|
||||
|
||||
@@ -52,7 +52,7 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) {
|
||||
const u64 aligned_size{Common::AlignUp(size, page_size)};
|
||||
const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)};
|
||||
|
||||
MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr);
|
||||
MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr);
|
||||
ASSERT(system.CurrentProcess()
|
||||
->VMManager()
|
||||
.SetMemoryAttribute(cpu_addr, size, Kernel::MemoryAttribute::DeviceMapped,
|
||||
@@ -67,7 +67,7 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size)
|
||||
|
||||
const u64 aligned_size{Common::AlignUp(size, page_size)};
|
||||
|
||||
MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr);
|
||||
MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr);
|
||||
ASSERT(system.CurrentProcess()
|
||||
->VMManager()
|
||||
.SetMemoryAttribute(cpu_addr, size, Kernel::MemoryAttribute::DeviceMapped,
|
||||
|
||||
@@ -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"
|
||||
@@ -22,7 +23,8 @@ constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
RasterizerAccelerated::RasterizerAccelerated() = default;
|
||||
RasterizerAccelerated::RasterizerAccelerated(Memory::Memory& cpu_memory_)
|
||||
: cpu_memory{cpu_memory_} {}
|
||||
|
||||
RasterizerAccelerated::~RasterizerAccelerated() = default;
|
||||
|
||||
@@ -47,9 +49,9 @@ void RasterizerAccelerated::UpdatePagesCachedCount(VAddr addr, u64 size, int del
|
||||
const u64 interval_size = interval_end_addr - interval_start_addr;
|
||||
|
||||
if (delta > 0 && count == delta) {
|
||||
Memory::RasterizerMarkRegionCached(interval_start_addr, interval_size, true);
|
||||
cpu_memory.RasterizerMarkRegionCached(interval_start_addr, interval_size, true);
|
||||
} else if (delta < 0 && count == -delta) {
|
||||
Memory::RasterizerMarkRegionCached(interval_start_addr, interval_size, false);
|
||||
cpu_memory.RasterizerMarkRegionCached(interval_start_addr, interval_size, false);
|
||||
} else {
|
||||
ASSERT(count >= 0);
|
||||
}
|
||||
|
||||
@@ -11,12 +11,16 @@
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
|
||||
namespace Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
/// Implements the shared part in GPU accelerated rasterizers in RasterizerInterface.
|
||||
class RasterizerAccelerated : public RasterizerInterface {
|
||||
public:
|
||||
explicit RasterizerAccelerated();
|
||||
explicit RasterizerAccelerated(Memory::Memory& cpu_memory_);
|
||||
~RasterizerAccelerated() override;
|
||||
|
||||
void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override;
|
||||
@@ -24,8 +28,9 @@ public:
|
||||
private:
|
||||
using CachedPageMap = boost::icl::interval_map<u64, int>;
|
||||
CachedPageMap cached_pages;
|
||||
|
||||
std::mutex pages_mutex;
|
||||
|
||||
Memory::Memory& cpu_memory;
|
||||
};
|
||||
|
||||
} // namespace VideoCore
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "common/cityhash.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/renderer_opengl/gl_framebuffer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
@@ -13,6 +16,7 @@
|
||||
namespace OpenGL {
|
||||
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
using VideoCore::Surface::SurfaceType;
|
||||
|
||||
FramebufferCacheOpenGL::FramebufferCacheOpenGL() = default;
|
||||
|
||||
@@ -35,36 +39,49 @@ OGLFramebuffer FramebufferCacheOpenGL::CreateFramebuffer(const FramebufferCacheK
|
||||
local_state.draw.draw_framebuffer = framebuffer.handle;
|
||||
local_state.ApplyFramebufferState();
|
||||
|
||||
for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
|
||||
if (key.colors[index]) {
|
||||
key.colors[index]->Attach(GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index),
|
||||
GL_DRAW_FRAMEBUFFER);
|
||||
}
|
||||
}
|
||||
if (key.colors_count) {
|
||||
glDrawBuffers(key.colors_count, key.color_attachments.data());
|
||||
} else {
|
||||
glDrawBuffer(GL_NONE);
|
||||
if (key.zeta) {
|
||||
const bool stencil = key.zeta->GetSurfaceParams().type == SurfaceType::DepthStencil;
|
||||
const GLenum attach_target = stencil ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT;
|
||||
key.zeta->Attach(attach_target, GL_DRAW_FRAMEBUFFER);
|
||||
}
|
||||
|
||||
if (key.zeta) {
|
||||
key.zeta->Attach(key.stencil_enable ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT,
|
||||
GL_DRAW_FRAMEBUFFER);
|
||||
std::size_t num_buffers = 0;
|
||||
std::array<GLenum, Maxwell::NumRenderTargets> targets;
|
||||
|
||||
for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
|
||||
if (!key.colors[index]) {
|
||||
targets[index] = GL_NONE;
|
||||
continue;
|
||||
}
|
||||
const GLenum attach_target = GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index);
|
||||
key.colors[index]->Attach(attach_target, GL_DRAW_FRAMEBUFFER);
|
||||
|
||||
const u32 attachment = (key.color_attachments >> (BitsPerAttachment * index)) & 0b1111;
|
||||
targets[index] = GL_COLOR_ATTACHMENT0 + attachment;
|
||||
num_buffers = index + 1;
|
||||
}
|
||||
|
||||
if (num_buffers > 0) {
|
||||
glDrawBuffers(static_cast<GLsizei>(num_buffers), std::data(targets));
|
||||
} else {
|
||||
glDrawBuffer(GL_NONE);
|
||||
}
|
||||
|
||||
return framebuffer;
|
||||
}
|
||||
|
||||
std::size_t FramebufferCacheKey::Hash() const {
|
||||
static_assert(sizeof(*this) % sizeof(u64) == 0, "Unaligned struct");
|
||||
return static_cast<std::size_t>(
|
||||
Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this)));
|
||||
std::size_t FramebufferCacheKey::Hash() const noexcept {
|
||||
std::size_t hash = std::hash<View>{}(zeta);
|
||||
for (const auto& color : colors) {
|
||||
hash ^= std::hash<View>{}(color);
|
||||
}
|
||||
hash ^= static_cast<std::size_t>(color_attachments) << 16;
|
||||
return hash;
|
||||
}
|
||||
|
||||
bool FramebufferCacheKey::operator==(const FramebufferCacheKey& rhs) const {
|
||||
return std::tie(stencil_enable, colors_count, color_attachments, colors, zeta) ==
|
||||
std::tie(rhs.stencil_enable, rhs.colors_count, rhs.color_attachments, rhs.colors,
|
||||
rhs.zeta);
|
||||
bool FramebufferCacheKey::operator==(const FramebufferCacheKey& rhs) const noexcept {
|
||||
return std::tie(colors, zeta, color_attachments) ==
|
||||
std::tie(rhs.colors, rhs.zeta, rhs.color_attachments);
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -18,21 +18,24 @@
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
struct alignas(sizeof(u64)) FramebufferCacheKey {
|
||||
bool stencil_enable = false;
|
||||
u16 colors_count = 0;
|
||||
constexpr std::size_t BitsPerAttachment = 4;
|
||||
|
||||
std::array<GLenum, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> color_attachments{};
|
||||
std::array<View, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> colors;
|
||||
struct FramebufferCacheKey {
|
||||
View zeta;
|
||||
std::array<View, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> colors;
|
||||
u32 color_attachments = 0;
|
||||
|
||||
std::size_t Hash() const;
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
bool operator==(const FramebufferCacheKey& rhs) const;
|
||||
bool operator==(const FramebufferCacheKey& rhs) const noexcept;
|
||||
|
||||
bool operator!=(const FramebufferCacheKey& rhs) const {
|
||||
bool operator!=(const FramebufferCacheKey& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
void SetAttachment(std::size_t index, u32 attachment) {
|
||||
color_attachments |= attachment << (BitsPerAttachment * index);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/settings.h"
|
||||
#include "video_core/engines/kepler_compute.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
@@ -86,12 +87,12 @@ std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer,
|
||||
|
||||
RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
|
||||
ScreenInfo& info)
|
||||
: texture_cache{system, *this, device}, shader_cache{*this, system, emu_window, device},
|
||||
system{system}, screen_info{info}, buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} {
|
||||
: RasterizerAccelerated{system.Memory()}, texture_cache{system, *this, device},
|
||||
shader_cache{*this, system, emu_window, device}, system{system}, screen_info{info},
|
||||
buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} {
|
||||
shader_program_manager = std::make_unique<GLShader::ProgramManager>();
|
||||
state.draw.shader_program = 0;
|
||||
state.Apply();
|
||||
clear_framebuffer.Create();
|
||||
|
||||
LOG_DEBUG(Render_OpenGL, "Sync fixed function OpenGL state here");
|
||||
CheckExtensions();
|
||||
@@ -276,6 +277,14 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
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
|
||||
@@ -371,78 +380,58 @@ void RasterizerOpenGL::ConfigureFramebuffers() {
|
||||
UNIMPLEMENTED_IF(regs.rt_separate_frag_data == 0);
|
||||
|
||||
// Bind the framebuffer surfaces
|
||||
FramebufferCacheKey fbkey;
|
||||
for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
|
||||
FramebufferCacheKey key;
|
||||
const auto colors_count = static_cast<std::size_t>(regs.rt_control.count);
|
||||
for (std::size_t index = 0; index < colors_count; ++index) {
|
||||
View color_surface{texture_cache.GetColorBufferSurface(index, true)};
|
||||
|
||||
if (color_surface) {
|
||||
// Assume that a surface will be written to if it is used as a framebuffer, even
|
||||
// if the shader doesn't actually write to it.
|
||||
texture_cache.MarkColorBufferInUse(index);
|
||||
if (!color_surface) {
|
||||
continue;
|
||||
}
|
||||
// Assume that a surface will be written to if it is used as a framebuffer, even
|
||||
// if the shader doesn't actually write to it.
|
||||
texture_cache.MarkColorBufferInUse(index);
|
||||
|
||||
fbkey.color_attachments[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index);
|
||||
fbkey.colors[index] = std::move(color_surface);
|
||||
key.SetAttachment(index, regs.rt_control.GetMap(index));
|
||||
key.colors[index] = std::move(color_surface);
|
||||
}
|
||||
fbkey.colors_count = static_cast<u16>(regs.rt_control.count);
|
||||
|
||||
if (depth_surface) {
|
||||
// Assume that a surface will be written to if it is used as a framebuffer, even if
|
||||
// the shader doesn't actually write to it.
|
||||
texture_cache.MarkDepthBufferInUse();
|
||||
|
||||
fbkey.stencil_enable = depth_surface->GetSurfaceParams().type == SurfaceType::DepthStencil;
|
||||
fbkey.zeta = std::move(depth_surface);
|
||||
key.zeta = std::move(depth_surface);
|
||||
}
|
||||
|
||||
texture_cache.GuardRenderTargets(false);
|
||||
|
||||
state.draw.draw_framebuffer = framebuffer_cache.GetFramebuffer(fbkey);
|
||||
state.draw.draw_framebuffer = framebuffer_cache.GetFramebuffer(key);
|
||||
SyncViewport(state);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::ConfigureClearFramebuffer(OpenGLState& current_state, bool using_color_fb,
|
||||
bool using_depth_fb, bool using_stencil_fb) {
|
||||
using VideoCore::Surface::SurfaceType;
|
||||
|
||||
auto& gpu = system.GPU().Maxwell3D();
|
||||
const auto& regs = gpu.regs;
|
||||
|
||||
texture_cache.GuardRenderTargets(true);
|
||||
View color_surface{};
|
||||
View color_surface;
|
||||
if (using_color_fb) {
|
||||
color_surface = texture_cache.GetColorBufferSurface(regs.clear_buffers.RT, false);
|
||||
}
|
||||
View depth_surface{};
|
||||
View depth_surface;
|
||||
if (using_depth_fb || using_stencil_fb) {
|
||||
depth_surface = texture_cache.GetDepthBufferSurface(false);
|
||||
}
|
||||
texture_cache.GuardRenderTargets(false);
|
||||
|
||||
current_state.draw.draw_framebuffer = clear_framebuffer.handle;
|
||||
FramebufferCacheKey key;
|
||||
key.colors[0] = color_surface;
|
||||
key.zeta = depth_surface;
|
||||
|
||||
current_state.draw.draw_framebuffer = framebuffer_cache.GetFramebuffer(key);
|
||||
current_state.ApplyFramebufferState();
|
||||
|
||||
if (color_surface) {
|
||||
color_surface->Attach(GL_COLOR_ATTACHMENT0, GL_DRAW_FRAMEBUFFER);
|
||||
} else {
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
}
|
||||
|
||||
if (depth_surface) {
|
||||
const auto& params = depth_surface->GetSurfaceParams();
|
||||
switch (params.type) {
|
||||
case VideoCore::Surface::SurfaceType::Depth:
|
||||
depth_surface->Attach(GL_DEPTH_ATTACHMENT, GL_DRAW_FRAMEBUFFER);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
break;
|
||||
case VideoCore::Surface::SurfaceType::DepthStencil:
|
||||
depth_surface->Attach(GL_DEPTH_STENCIL_ATTACHMENT, GL_DRAW_FRAMEBUFFER);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
} else {
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::Clear() {
|
||||
@@ -837,7 +826,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
|
||||
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
||||
|
||||
const auto surface{
|
||||
texture_cache.TryFindFramebufferSurface(Memory::GetPointer(framebuffer_addr))};
|
||||
texture_cache.TryFindFramebufferSurface(system.Memory().GetPointer(framebuffer_addr))};
|
||||
if (!surface) {
|
||||
return {};
|
||||
}
|
||||
@@ -1047,6 +1036,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(
|
||||
|
||||
@@ -223,8 +223,6 @@ private:
|
||||
|
||||
enum class AccelDraw { Disabled, Arrays, Indexed };
|
||||
AccelDraw accelerate_draw = AccelDraw::Disabled;
|
||||
|
||||
OGLFramebuffer clear_framebuffer;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -281,11 +281,11 @@ CachedProgram BuildShader(const Device& device, u64 unique_identifier, ShaderTyp
|
||||
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);
|
||||
source += fmt::format("shared uint smem[{}];\n", variant.shared_memory_size);
|
||||
}
|
||||
|
||||
if (variant.local_memory_size > 0) {
|
||||
source += fmt::format("#define LOCAL_MEMORY_SIZE {}",
|
||||
source += fmt::format("#define LOCAL_MEMORY_SIZE {}\n",
|
||||
Common::AlignUp(variant.local_memory_size, 4) / 4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,8 +49,9 @@ class ExprDecompiler;
|
||||
enum class Type { Void, Bool, Bool2, Float, Int, Uint, HalfFloat };
|
||||
|
||||
struct TextureAoffi {};
|
||||
struct TextureDerivates {};
|
||||
using TextureArgument = std::pair<Type, Node>;
|
||||
using TextureIR = std::variant<TextureAoffi, TextureArgument>;
|
||||
using TextureIR = std::variant<TextureAoffi, TextureDerivates, TextureArgument>;
|
||||
|
||||
constexpr u32 MAX_CONSTBUFFER_ELEMENTS =
|
||||
static_cast<u32>(Maxwell::MaxConstBufferSize) / (4 * sizeof(float));
|
||||
@@ -398,6 +399,7 @@ public:
|
||||
DeclareConstantBuffers();
|
||||
DeclareGlobalMemory();
|
||||
DeclareSamplers();
|
||||
DeclareImages();
|
||||
DeclarePhysicalAttributeReader();
|
||||
|
||||
code.AddLine("void execute_{}() {{", suffix);
|
||||
@@ -1075,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 sepparate_dc = false) {
|
||||
constexpr std::array coord_constructors = {"float", "vec2", "vec3", "vec4"};
|
||||
|
||||
const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
|
||||
@@ -1090,7 +1092,8 @@ private:
|
||||
expr += "Offset";
|
||||
}
|
||||
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 && !sepparate_dc ? 1 : 0) - 1);
|
||||
expr += '(';
|
||||
for (std::size_t i = 0; i < count; ++i) {
|
||||
expr += Visit(operation[i]).AsFloat();
|
||||
@@ -1103,15 +1106,22 @@ private:
|
||||
expr += ", float(" + Visit(meta->array).AsInt() + ')';
|
||||
}
|
||||
if (has_shadow) {
|
||||
expr += ", " + Visit(meta->depth_compare).AsFloat();
|
||||
if (sepparate_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<TextureDerivates>(variant)) {
|
||||
expr += GenerateTextureDerivates(meta->derivates);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -1181,6 +1191,36 @@ private:
|
||||
return expr;
|
||||
}
|
||||
|
||||
std::string GenerateTextureDerivates(const std::vector<Node>& derivates) {
|
||||
if (derivates.empty()) {
|
||||
return {};
|
||||
}
|
||||
constexpr std::array coord_constructors = {"float", "vec2", "vec3"};
|
||||
std::string expr = ", ";
|
||||
const std::size_t components = derivates.size() / 2;
|
||||
std::string dx = coord_constructors.at(components - 1);
|
||||
std::string dy = coord_constructors.at(components - 1);
|
||||
dx += '(';
|
||||
dy += '(';
|
||||
|
||||
for (std::size_t index = 0; index < components; ++index) {
|
||||
const auto operand_x{derivates.at(index * 2)};
|
||||
const auto operand_y{derivates.at(index * 2 + 1)};
|
||||
dx += Visit(operand_x).AsFloat();
|
||||
dy += Visit(operand_y).AsFloat();
|
||||
|
||||
if (index + 1 < components) {
|
||||
dx += ", ";
|
||||
dy += ", ";
|
||||
}
|
||||
}
|
||||
dx += ')';
|
||||
dy += ')';
|
||||
expr += dx + ", " + dy;
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
std::string BuildIntegerCoordinates(Operation operation) {
|
||||
constexpr std::array constructors{"int(", "ivec2(", "ivec3(", "ivec4("};
|
||||
const std::size_t coords_count{operation.GetOperandsCount()};
|
||||
@@ -1450,6 +1490,11 @@ private:
|
||||
return GenerateUnary(operation, "bitCount", type, type);
|
||||
}
|
||||
|
||||
template <Type type>
|
||||
Expression BitMSB(Operation operation) {
|
||||
return GenerateUnary(operation, "findMSB", type, type);
|
||||
}
|
||||
|
||||
Expression HNegate(Operation operation) {
|
||||
const auto GetNegate = [&](std::size_t index) {
|
||||
return VisitOperand(operation, index).AsBool() + " ? -1 : 1";
|
||||
@@ -1668,10 +1713,17 @@ private:
|
||||
ASSERT(meta);
|
||||
|
||||
const auto type = meta->sampler.IsShadow() ? Type::Float : Type::Int;
|
||||
return {GenerateTexture(operation, "Gather",
|
||||
{TextureAoffi{}, TextureArgument{type, meta->component}}) +
|
||||
GetSwizzle(meta->element),
|
||||
Type::Float};
|
||||
if (meta->sampler.IsShadow()) {
|
||||
return {GenerateTexture(operation, "Gather", {TextureAoffi{}}, true) +
|
||||
GetSwizzle(meta->element),
|
||||
Type::Float};
|
||||
} else {
|
||||
return {GenerateTexture(operation, "Gather",
|
||||
{TextureAoffi{}, TextureArgument{type, meta->component}},
|
||||
false) +
|
||||
GetSwizzle(meta->element),
|
||||
Type::Float};
|
||||
}
|
||||
}
|
||||
|
||||
Expression TextureQueryDimensions(Operation operation) {
|
||||
@@ -1738,6 +1790,14 @@ private:
|
||||
return {std::move(expr), Type::Float};
|
||||
}
|
||||
|
||||
Expression TextureGradient(Operation operation) {
|
||||
const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
|
||||
ASSERT(meta);
|
||||
|
||||
std::string expr = GenerateTexture(operation, "Grad", {TextureDerivates{}, TextureAoffi{}});
|
||||
return {std::move(expr) + GetSwizzle(meta->element), Type::Float};
|
||||
}
|
||||
|
||||
Expression ImageLoad(Operation operation) {
|
||||
if (!device.HasImageLoadFormatted()) {
|
||||
LOG_ERROR(Render_OpenGL,
|
||||
@@ -1869,6 +1929,10 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
Expression InvocationId(Operation operation) {
|
||||
return {"gl_InvocationID", Type::Int};
|
||||
}
|
||||
|
||||
Expression YNegate(Operation operation) {
|
||||
return {"y_direction", Type::Float};
|
||||
}
|
||||
@@ -1942,6 +2006,11 @@ private:
|
||||
return {fmt::format("readInvocationARB({}, {})", value, index), Type::Float};
|
||||
}
|
||||
|
||||
Expression MemoryBarrierGL(Operation) {
|
||||
code.AddLine("memoryBarrier();");
|
||||
return {};
|
||||
}
|
||||
|
||||
struct Func final {
|
||||
Func() = delete;
|
||||
~Func() = delete;
|
||||
@@ -2003,6 +2072,7 @@ private:
|
||||
&GLSLDecompiler::BitfieldInsert<Type::Int>,
|
||||
&GLSLDecompiler::BitfieldExtract<Type::Int>,
|
||||
&GLSLDecompiler::BitCount<Type::Int>,
|
||||
&GLSLDecompiler::BitMSB<Type::Int>,
|
||||
|
||||
&GLSLDecompiler::Add<Type::Uint>,
|
||||
&GLSLDecompiler::Mul<Type::Uint>,
|
||||
@@ -2021,6 +2091,7 @@ private:
|
||||
&GLSLDecompiler::BitfieldInsert<Type::Uint>,
|
||||
&GLSLDecompiler::BitfieldExtract<Type::Uint>,
|
||||
&GLSLDecompiler::BitCount<Type::Uint>,
|
||||
&GLSLDecompiler::BitMSB<Type::Uint>,
|
||||
|
||||
&GLSLDecompiler::Add<Type::HalfFloat>,
|
||||
&GLSLDecompiler::Mul<Type::HalfFloat>,
|
||||
@@ -2084,6 +2155,7 @@ private:
|
||||
&GLSLDecompiler::TextureQueryDimensions,
|
||||
&GLSLDecompiler::TextureQueryLod,
|
||||
&GLSLDecompiler::TexelFetch,
|
||||
&GLSLDecompiler::TextureGradient,
|
||||
|
||||
&GLSLDecompiler::ImageLoad,
|
||||
&GLSLDecompiler::ImageStore,
|
||||
@@ -2104,6 +2176,7 @@ private:
|
||||
&GLSLDecompiler::EmitVertex,
|
||||
&GLSLDecompiler::EndPrimitive,
|
||||
|
||||
&GLSLDecompiler::InvocationId,
|
||||
&GLSLDecompiler::YNegate,
|
||||
&GLSLDecompiler::LocalInvocationId<0>,
|
||||
&GLSLDecompiler::LocalInvocationId<1>,
|
||||
@@ -2119,6 +2192,8 @@ private:
|
||||
|
||||
&GLSLDecompiler::ThreadId,
|
||||
&GLSLDecompiler::ShuffleIndexed,
|
||||
|
||||
&GLSLDecompiler::MemoryBarrierGL,
|
||||
};
|
||||
static_assert(operation_decompilers.size() == static_cast<std::size_t>(OperationCode::Amount));
|
||||
|
||||
|
||||
@@ -411,8 +411,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -150,6 +150,7 @@ public:
|
||||
|
||||
struct {
|
||||
GLenum origin = GL_LOWER_LEFT;
|
||||
GLenum depth_mode = GL_NEGATIVE_ONE_TO_ONE;
|
||||
} clip_control;
|
||||
|
||||
OpenGLState();
|
||||
|
||||
@@ -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;
|
||||
@@ -158,7 +216,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
|
||||
VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)};
|
||||
const u32 bytes_per_pixel{VideoCore::Surface::GetBytesPerPixel(pixel_format)};
|
||||
const u64 size_in_bytes{framebuffer.stride * framebuffer.height * bytes_per_pixel};
|
||||
const auto host_ptr{Memory::GetPointer(framebuffer_addr)};
|
||||
u8* const host_ptr{system.Memory().GetPointer(framebuffer_addr)};
|
||||
rasterizer->FlushRegion(ToCacheAddr(host_ptr), size_in_bytes);
|
||||
|
||||
// TODO(Rodrigo): Read this from HLE
|
||||
@@ -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;
|
||||
|
||||
@@ -4,6 +4,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace vk {
|
||||
class DispatchLoaderDynamic;
|
||||
}
|
||||
|
||||
namespace Vulkan {
|
||||
constexpr vk::DispatchLoaderDynamic* dont_use_me_dld = nullptr;
|
||||
}
|
||||
|
||||
#define VULKAN_HPP_DEFAULT_DISPATCHER (*::Vulkan::dont_use_me_dld)
|
||||
#define VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL 0
|
||||
#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace Vulkan {
|
||||
@@ -41,5 +52,7 @@ using UniqueSemaphore = UniqueHandle<vk::Semaphore>;
|
||||
using UniqueShaderModule = UniqueHandle<vk::ShaderModule>;
|
||||
using UniqueSwapchainKHR = UniqueHandle<vk::SwapchainKHR>;
|
||||
using UniqueValidationCacheEXT = UniqueHandle<vk::ValidationCacheEXT>;
|
||||
using UniqueDebugReportCallbackEXT = UniqueHandle<vk::DebugReportCallbackEXT>;
|
||||
using UniqueDebugUtilsMessengerEXT = UniqueHandle<vk::DebugUtilsMessengerEXT>;
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user