Compare commits
162 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78a81614ee | ||
|
|
1a28f4fa8c | ||
|
|
b4bf099793 | ||
|
|
5e343edc9e | ||
|
|
d8f745382b | ||
|
|
c33abac275 | ||
|
|
d30110348b | ||
|
|
67bc2f5ecd | ||
|
|
666d53299c | ||
|
|
ac19e3d061 | ||
|
|
a6cab532f8 | ||
|
|
d4fb8a887c | ||
|
|
527ce12ce4 | ||
|
|
d89bfec5f5 | ||
|
|
1bfc0dc2db | ||
|
|
0a5832798a | ||
|
|
7504df52fc | ||
|
|
c1ccbf332f | ||
|
|
d732142b66 | ||
|
|
c2dbdefedf | ||
|
|
cd8bb6ea9b | ||
|
|
4369af6b7e | ||
|
|
3754e0fdfd | ||
|
|
15925b8293 | ||
|
|
0ee38e1363 | ||
|
|
0162a2d5cb | ||
|
|
33c0bf9dc5 | ||
|
|
ed2134784e | ||
|
|
8041d72a1f | ||
|
|
170ac3f9ee | ||
|
|
94c70693f9 | ||
|
|
1a9df83535 | ||
|
|
f934da0e43 | ||
|
|
010ea89013 | ||
|
|
4697025b73 | ||
|
|
56e2013c1f | ||
|
|
6afe9e0105 | ||
|
|
3357e8d9ba | ||
|
|
75da830c13 | ||
|
|
99d86deb1f | ||
|
|
b326369704 | ||
|
|
8c8da93693 | ||
|
|
e9315ace9f | ||
|
|
a0933d92fc | ||
|
|
692639e9b7 | ||
|
|
6f27edccb2 | ||
|
|
bde3e667be | ||
|
|
868f7f18b9 | ||
|
|
0ce52b1da2 | ||
|
|
2c785bd06c | ||
|
|
39e60cfeb1 | ||
|
|
025d111308 | ||
|
|
1c31e2b3d2 | ||
|
|
1ad97c75a0 | ||
|
|
77fd0d47e7 | ||
|
|
1b8d798835 | ||
|
|
71ebc3e90d | ||
|
|
46945b5c96 | ||
|
|
88c9608eac | ||
|
|
31816aac38 | ||
|
|
9b9de30086 | ||
|
|
a10baacf9e | ||
|
|
d561e4acc8 | ||
|
|
4ed54738fc | ||
|
|
a397a9e9a4 | ||
|
|
b7da9d5a54 | ||
|
|
0f8401906b | ||
|
|
054393917e | ||
|
|
b5f3e7951b | ||
|
|
68658ce4b0 | ||
|
|
fd0a7c0aaf | ||
|
|
7ce4a03188 | ||
|
|
4c06d55a81 | ||
|
|
b36b627d4d | ||
|
|
1a158dfcd6 | ||
|
|
11047d7fd5 | ||
|
|
cdf541fb5b | ||
|
|
ec4e1a3685 | ||
|
|
32fd57f0c8 | ||
|
|
5ded39f5d8 | ||
|
|
9ee33350de | ||
|
|
633abd5a94 | ||
|
|
c2c55e0811 | ||
|
|
e12c2cf8c6 | ||
|
|
a0b1235f82 | ||
|
|
f61b9f7338 | ||
|
|
63d3924b5b | ||
|
|
6ced80bb47 | ||
|
|
740310113b | ||
|
|
ee5f5a2c2d | ||
|
|
47cac816f6 | ||
|
|
8a250de987 | ||
|
|
bfe45774f1 | ||
|
|
c6362543d4 | ||
|
|
f707c2dac4 | ||
|
|
7c3a263839 | ||
|
|
3a6604e8fa | ||
|
|
656de23d93 | ||
|
|
4daf91fc69 | ||
|
|
8afdbf6a1f | ||
|
|
b5c03088bc | ||
|
|
95e747cd06 | ||
|
|
ec5ede68e7 | ||
|
|
2ab021da14 | ||
|
|
7fa3bf02ca | ||
|
|
34b733e70e | ||
|
|
3cdf6c536d | ||
|
|
eff3f60b73 | ||
|
|
4721ea4523 | ||
|
|
c450d264eb | ||
|
|
6673ed1274 | ||
|
|
ae28a52277 | ||
|
|
0b3ab30762 | ||
|
|
8c8de2efe9 | ||
|
|
dca7cfb9cf | ||
|
|
6e3222363c | ||
|
|
9c468e0c55 | ||
|
|
d7b1ebe4a8 | ||
|
|
0485ee499f | ||
|
|
21bac2d7d7 | ||
|
|
6d90d99d12 | ||
|
|
e1d7b9fc2c | ||
|
|
a9ba2c2000 | ||
|
|
fc44261dd1 | ||
|
|
f4700ccabf | ||
|
|
4bdb46e4c2 | ||
|
|
a3e10b1a72 | ||
|
|
db0cfb8e8b | ||
|
|
0e4b9cdde4 | ||
|
|
a1cf5020e6 | ||
|
|
6a0902e56d | ||
|
|
808704c78c | ||
|
|
c4ca802b9d | ||
|
|
7a27a11770 | ||
|
|
21d9519032 | ||
|
|
a90ab1dec7 | ||
|
|
1d8b6ad13b | ||
|
|
d16e08454d | ||
|
|
0e72d0d826 | ||
|
|
ef875d6a35 | ||
|
|
b2d7c92cae | ||
|
|
dcae0c9a4f | ||
|
|
cff7b29bba | ||
|
|
23a0d2d7b7 | ||
|
|
2dc3a56e96 | ||
|
|
2332a44b68 | ||
|
|
c1c92c30f9 | ||
|
|
c86af6939c | ||
|
|
2faa83ca13 | ||
|
|
019f1a0cf0 | ||
|
|
e353b9fb3d | ||
|
|
03156d0c9a | ||
|
|
6317a0b2ca | ||
|
|
7b6868e908 | ||
|
|
85d820b1b4 | ||
|
|
a64b936cbe | ||
|
|
46f9d4b4a3 | ||
|
|
aa586fa268 | ||
|
|
7ac8657432 | ||
|
|
29981fa2eb | ||
|
|
bc88cae0c7 | ||
|
|
c875a7984e |
39
.appveyor/UtilityFunctions.ps1
Normal file
39
.appveyor/UtilityFunctions.ps1
Normal file
@@ -0,0 +1,39 @@
|
||||
# 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
|
||||
}
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -18,7 +18,7 @@
|
||||
url = https://github.com/fmtlib/fmt.git
|
||||
[submodule "lz4"]
|
||||
path = externals/lz4
|
||||
url = http://github.com/lz4/lz4.git
|
||||
url = https://github.com/lz4/lz4.git
|
||||
[submodule "unicorn"]
|
||||
path = externals/unicorn
|
||||
url = https://github.com/yuzu-emu/unicorn
|
||||
|
||||
@@ -160,6 +160,9 @@ set_property(DIRECTORY APPEND PROPERTY
|
||||
COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
|
||||
|
||||
|
||||
math(EXPR EMU_ARCH_BITS ${CMAKE_SIZEOF_VOID_P}*8)
|
||||
add_definitions(-DEMU_ARCH_BITS=${EMU_ARCH_BITS})
|
||||
|
||||
# System imported libraries
|
||||
# ======================
|
||||
|
||||
|
||||
23
appveyor.yml
23
appveyor.yml
@@ -121,23 +121,16 @@ after_build:
|
||||
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 all the dll dependencies to the release folder
|
||||
# hardcoded list because we don't build static and determining the list of dlls from the binary is a pain.
|
||||
$MingwDLLs = "Qt5Core.dll","Qt5Widgets.dll","Qt5Gui.dll","Qt5OpenGL.dll",
|
||||
# QT dll dependencies
|
||||
"libbz2-*.dll","libicudt*.dll","libicuin*.dll","libicuuc*.dll","libffi-*.dll",
|
||||
"libfreetype-*.dll","libglib-*.dll","libgobject-*.dll","libgraphite2.dll","libiconv-*.dll",
|
||||
"libharfbuzz-*.dll","libintl-*.dll","libpcre-*.dll","libpcre2-16-*.dll","libpcre16-*.dll","libpng16-*.dll",
|
||||
# Runtime/Other dependencies
|
||||
"libgcc_s_seh-*.dll","libstdc++-*.dll","libwinpthread-*.dll","SDL2.dll","zlib1.dll"
|
||||
. "./.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"
|
||||
Write-Host "Detected the following dependencies:"
|
||||
Write-Host $MingwDLLs
|
||||
foreach ($file in $MingwDLLs) {
|
||||
Copy-Item -path "C:/msys64/mingw64/bin/$file" -force -destination "$RELEASE_DIST"
|
||||
}
|
||||
# the above list copies a few extra debug dlls that aren't needed (thanks globbing patterns!)
|
||||
# so we can remove them by hardcoding another list of extra dlls to remove
|
||||
$DebugDLLs = "libicudtd*.dll","libicuind*.dll","libicuucd*.dll"
|
||||
foreach ($file in $DebugDLLs) {
|
||||
Remove-Item -path "$RELEASE_DIST/$file"
|
||||
Copy-Item -path "$file" -force -destination "$RELEASE_DIST"
|
||||
}
|
||||
|
||||
# copy the qt windows plugin dll to platforms
|
||||
|
||||
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 6b4c6b06a9...12a102046c
2
externals/glad/Readme.md
vendored
2
externals/glad/Readme.md
vendored
@@ -1,5 +1,5 @@
|
||||
These files were generated by the [glad](https://github.com/Dav1dde/glad) OpenGL loader generator and have been checked in as-is. You can re-generate them using glad with the following command:
|
||||
|
||||
```
|
||||
python -m glad --profile core --out-path glad/ --api gl=3.3,gles=3.0
|
||||
python -m glad --profile core --out-path glad/ --api gl=3.3 --generator=c
|
||||
```
|
||||
|
||||
6
externals/glad/include/KHR/khrplatform.h
vendored
6
externals/glad/include/KHR/khrplatform.h
vendored
@@ -26,7 +26,7 @@
|
||||
|
||||
/* Khronos platform-specific types and definitions.
|
||||
*
|
||||
* $Revision: 23298 $ on $Date: 2013-09-30 17:07:13 -0700 (Mon, 30 Sep 2013) $
|
||||
* $Revision: 32517 $ on $Date: 2016-03-11 02:41:19 -0800 (Fri, 11 Mar 2016) $
|
||||
*
|
||||
* Adopters may modify this file to suit their platform. Adopters are
|
||||
* encouraged to submit platform specific modifications to the Khronos
|
||||
@@ -101,6 +101,8 @@
|
||||
# define KHRONOS_APICALL __declspec(dllimport)
|
||||
#elif defined (__SYMBIAN32__)
|
||||
# define KHRONOS_APICALL IMPORT_C
|
||||
#elif defined(__ANDROID__)
|
||||
# define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||
#else
|
||||
# define KHRONOS_APICALL
|
||||
#endif
|
||||
@@ -223,7 +225,7 @@ typedef signed short int khronos_int16_t;
|
||||
typedef unsigned short int khronos_uint16_t;
|
||||
|
||||
/*
|
||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||
* to be the only LLP64 architecture in current use.
|
||||
*/
|
||||
|
||||
13755
externals/glad/include/glad/glad.h
vendored
13755
externals/glad/include/glad/glad.h
vendored
File diff suppressed because one or more lines are too long
8704
externals/glad/src/glad.c
vendored
8704
externals/glad/src/glad.c
vendored
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Enforce citra's whitespace policy
|
||||
# Enforce yuzu's whitespace policy
|
||||
git config --local core.whitespace tab-in-indent,trailing-space
|
||||
|
||||
paths_to_check="src/ CMakeLists.txt"
|
||||
|
||||
@@ -58,7 +58,6 @@ add_library(common STATIC
|
||||
misc.cpp
|
||||
param_package.cpp
|
||||
param_package.h
|
||||
platform.h
|
||||
quaternion.h
|
||||
scm_rev.cpp
|
||||
scm_rev.h
|
||||
|
||||
@@ -121,7 +121,7 @@ void CopyDir(const std::string& source_path, const std::string& dest_path);
|
||||
// Set the current directory to given directory
|
||||
bool SetCurrentDir(const std::string& directory);
|
||||
|
||||
// Returns a pointer to a string with a Citra data dir in the user's home
|
||||
// Returns a pointer to a string with a yuzu data dir in the user's home
|
||||
// directory. To be used in "multi-user" mode (that is, installed).
|
||||
const std::string& GetUserPath(const unsigned int DirIDX, const std::string& newPath = "");
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace Log {
|
||||
SUB(Service, AM) \
|
||||
SUB(Service, AOC) \
|
||||
SUB(Service, APM) \
|
||||
SUB(Service, Fatal) \
|
||||
SUB(Service, Friend) \
|
||||
SUB(Service, FS) \
|
||||
SUB(Service, HID) \
|
||||
@@ -47,6 +48,8 @@ namespace Log {
|
||||
SUB(Service, PCTL) \
|
||||
SUB(Service, SET) \
|
||||
SUB(Service, SM) \
|
||||
SUB(Service, SPL) \
|
||||
SUB(Service, SSL) \
|
||||
SUB(Service, Time) \
|
||||
SUB(Service, VI) \
|
||||
CLS(HW) \
|
||||
|
||||
@@ -54,6 +54,7 @@ enum class Class : ClassType {
|
||||
Service_AOC, ///< The AOC (AddOn Content) service
|
||||
Service_APM, ///< The APM (Performance) service
|
||||
Service_Audio, ///< The Audio (Audio control) service
|
||||
Service_Fatal, ///< The Fatal service
|
||||
Service_Friend, ///< The friend service
|
||||
Service_FS, ///< The FS (Filesystem) service
|
||||
Service_HID, ///< The HID (Human interface device) service
|
||||
@@ -64,6 +65,8 @@ enum class Class : ClassType {
|
||||
Service_PCTL, ///< The PCTL (Parental control) service
|
||||
Service_SET, ///< The SET (Settings) service
|
||||
Service_SM, ///< The SM (Service manager) service
|
||||
Service_SPL, ///< The SPL service
|
||||
Service_SSL, ///< The SSL service
|
||||
Service_Time, ///< The time service
|
||||
Service_VI, ///< The VI (Video interface) service
|
||||
HW, ///< Low-level hardware emulation
|
||||
@@ -82,7 +85,7 @@ enum class Class : ClassType {
|
||||
Loader, ///< ROM loader
|
||||
Input, ///< Input emulation
|
||||
Network, ///< Network emulation
|
||||
WebService, ///< Interface to Citra Web Services
|
||||
WebService, ///< Interface to yuzu Web Services
|
||||
Count ///< Total number of logging classes
|
||||
};
|
||||
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2005-2012 Gekko Emulator
|
||||
*
|
||||
* @file platform.h
|
||||
* @author ShizZy <shizzy247@gmail.com>
|
||||
* @date 2012-02-11
|
||||
* @brief Platform detection macros for portable compilation
|
||||
*
|
||||
* @section LICENSE
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details at
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* Official project repository can be found at:
|
||||
* http://code.google.com/p/gekko-gc-emu/
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Platform detection
|
||||
|
||||
#if defined(ARCHITECTURE_x86_64) || defined(__aarch64__)
|
||||
#define EMU_ARCH_BITS 64
|
||||
#elif defined(__i386) || defined(_M_IX86) || defined(__arm__) || defined(_M_ARM)
|
||||
#define EMU_ARCH_BITS 32
|
||||
#endif
|
||||
@@ -54,7 +54,7 @@ static CPUCaps Detect() {
|
||||
caps.num_cores = std::thread::hardware_concurrency();
|
||||
|
||||
// Assumes the CPU supports the CPUID instruction. Those that don't would likely not support
|
||||
// Citra at all anyway
|
||||
// yuzu at all anyway
|
||||
|
||||
int cpu_id[4];
|
||||
memset(caps.brand_string, 0, sizeof(caps.brand_string));
|
||||
|
||||
@@ -22,6 +22,8 @@ add_library(core STATIC
|
||||
file_sys/romfs_filesystem.h
|
||||
file_sys/savedata_factory.cpp
|
||||
file_sys/savedata_factory.h
|
||||
file_sys/sdmc_factory.cpp
|
||||
file_sys/sdmc_factory.h
|
||||
file_sys/storage.h
|
||||
frontend/emu_window.cpp
|
||||
frontend/emu_window.h
|
||||
@@ -114,6 +116,12 @@ add_library(core STATIC
|
||||
hle/service/audio/audren_u.h
|
||||
hle/service/audio/codecctl.cpp
|
||||
hle/service/audio/codecctl.h
|
||||
hle/service/fatal/fatal.cpp
|
||||
hle/service/fatal/fatal.h
|
||||
hle/service/fatal/fatal_p.cpp
|
||||
hle/service/fatal/fatal_p.h
|
||||
hle/service/fatal/fatal_u.cpp
|
||||
hle/service/fatal/fatal_u.h
|
||||
hle/service/filesystem/filesystem.cpp
|
||||
hle/service/filesystem/filesystem.h
|
||||
hle/service/filesystem/fsp_srv.cpp
|
||||
@@ -181,12 +189,22 @@ add_library(core STATIC
|
||||
hle/service/sm/controller.h
|
||||
hle/service/sm/sm.cpp
|
||||
hle/service/sm/sm.h
|
||||
hle/service/sockets/bsd_u.cpp
|
||||
hle/service/sockets/bsd_u.h
|
||||
hle/service/sockets/bsd.cpp
|
||||
hle/service/sockets/bsd.h
|
||||
hle/service/sockets/nsd.cpp
|
||||
hle/service/sockets/nsd.h
|
||||
hle/service/sockets/sfdnsres.cpp
|
||||
hle/service/sockets/sfdnsres.h
|
||||
hle/service/sockets/sockets.cpp
|
||||
hle/service/sockets/sockets.h
|
||||
hle/service/spl/csrng.cpp
|
||||
hle/service/spl/csrng.h
|
||||
hle/service/spl/module.cpp
|
||||
hle/service/spl/module.h
|
||||
hle/service/spl/spl.cpp
|
||||
hle/service/spl/spl.h
|
||||
hle/service/ssl/ssl.cpp
|
||||
hle/service/ssl/ssl.h
|
||||
hle/service/time/time.cpp
|
||||
hle/service/time/time.h
|
||||
hle/service/time/time_s.cpp
|
||||
|
||||
@@ -25,19 +25,11 @@ public:
|
||||
VAddr tls_address;
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs the CPU for the given number of instructions
|
||||
* @param num_instructions Number of instructions to run
|
||||
*/
|
||||
void Run(int num_instructions) {
|
||||
ExecuteInstructions(num_instructions);
|
||||
this->num_instructions += num_instructions;
|
||||
}
|
||||
/// Runs the CPU until an event happens
|
||||
virtual void Run() = 0;
|
||||
|
||||
/// Step CPU by one instruction
|
||||
void Step() {
|
||||
Run(1);
|
||||
}
|
||||
virtual void Step() = 0;
|
||||
|
||||
/// Maps a backing memory region for the CPU
|
||||
virtual void MapBackingMemory(VAddr address, size_t size, u8* memory,
|
||||
@@ -126,19 +118,4 @@ public:
|
||||
|
||||
/// Prepare core for thread reschedule (if needed to correctly handle state)
|
||||
virtual void PrepareReschedule() = 0;
|
||||
|
||||
/// Getter for num_instructions
|
||||
u64 GetNumInstructions() const {
|
||||
return num_instructions;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Executes the given number of instructions
|
||||
* @param num_instructions Number of instructions to executes
|
||||
*/
|
||||
virtual void ExecuteInstructions(int num_instructions) = 0;
|
||||
|
||||
private:
|
||||
u64 num_instructions = 0; ///< Number of instructions executed
|
||||
};
|
||||
|
||||
@@ -86,21 +86,17 @@ public:
|
||||
}
|
||||
|
||||
void AddTicks(u64 ticks) override {
|
||||
if (ticks > ticks_remaining) {
|
||||
ticks_remaining = 0;
|
||||
return;
|
||||
}
|
||||
ticks -= ticks_remaining;
|
||||
CoreTiming::AddTicks(ticks - num_interpreted_instructions);
|
||||
num_interpreted_instructions = 0;
|
||||
}
|
||||
u64 GetTicksRemaining() override {
|
||||
return ticks_remaining;
|
||||
return std::max(CoreTiming::GetDowncount(), 0);
|
||||
}
|
||||
u64 GetCNTPCT() override {
|
||||
return CoreTiming::GetTicks();
|
||||
}
|
||||
|
||||
ARM_Dynarmic& parent;
|
||||
size_t ticks_remaining = 0;
|
||||
size_t num_interpreted_instructions = 0;
|
||||
u64 tpidrro_el0 = 0;
|
||||
u64 tpidr_el0 = 0;
|
||||
@@ -122,11 +118,22 @@ std::unique_ptr<Dynarmic::A64::Jit> MakeJit(const std::unique_ptr<ARM_Dynarmic_C
|
||||
return std::make_unique<Dynarmic::A64::Jit>(config);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::Run() {
|
||||
ASSERT(Memory::GetCurrentPageTable() == current_page_table);
|
||||
|
||||
jit->Run();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::Step() {
|
||||
cb->InterpreterFallback(jit->GetPC(), 1);
|
||||
}
|
||||
|
||||
ARM_Dynarmic::ARM_Dynarmic()
|
||||
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), jit(MakeJit(cb)) {
|
||||
ARM_Interface::ThreadContext ctx;
|
||||
inner_unicorn.SaveContext(ctx);
|
||||
LoadContext(ctx);
|
||||
PageTableChanged();
|
||||
}
|
||||
|
||||
ARM_Dynarmic::~ARM_Dynarmic() = default;
|
||||
@@ -189,13 +196,6 @@ void ARM_Dynarmic::SetTlsAddress(u64 address) {
|
||||
cb->tpidrro_el0 = address;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::ExecuteInstructions(int num_instructions) {
|
||||
cb->ticks_remaining = num_instructions;
|
||||
jit->Run();
|
||||
CoreTiming::AddTicks(num_instructions - cb->num_interpreted_instructions);
|
||||
cb->num_interpreted_instructions = 0;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
|
||||
ctx.cpu_registers = jit->GetRegisters();
|
||||
ctx.sp = jit->GetSP();
|
||||
@@ -228,4 +228,5 @@ void ARM_Dynarmic::ClearInstructionCache() {
|
||||
|
||||
void ARM_Dynarmic::PageTableChanged() {
|
||||
jit = MakeJit(cb);
|
||||
current_page_table = Memory::GetCurrentPageTable();
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ public:
|
||||
u32 GetVFPReg(int index) const override;
|
||||
void SetVFPReg(int index, u32 value) override;
|
||||
u32 GetCPSR() const override;
|
||||
void Run() override;
|
||||
void Step() override;
|
||||
void SetCPSR(u32 cpsr) override;
|
||||
VAddr GetTlsAddress() const override;
|
||||
void SetTlsAddress(VAddr address) override;
|
||||
@@ -37,7 +39,6 @@ public:
|
||||
void LoadContext(const ThreadContext& ctx) override;
|
||||
|
||||
void PrepareReschedule() override;
|
||||
void ExecuteInstructions(int num_instructions) override;
|
||||
|
||||
void ClearInstructionCache() override;
|
||||
void PageTableChanged() override;
|
||||
@@ -47,4 +48,6 @@ private:
|
||||
std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
|
||||
std::unique_ptr<Dynarmic::A64::Jit> jit;
|
||||
ARM_Unicorn inner_unicorn;
|
||||
|
||||
Memory::PageTable* current_page_table = nullptr;
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <unicorn/arm64.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/microprofile.h"
|
||||
@@ -52,7 +53,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
|
||||
void* user_data) {
|
||||
ARM_Interface::ThreadContext ctx{};
|
||||
Core::CPU().SaveContext(ctx);
|
||||
ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x%llx, pc=0x%llx, lr=0x%llx", addr,
|
||||
ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x%lx, pc=0x%lx, lr=0x%lx", addr,
|
||||
ctx.pc, ctx.cpu_registers[30]);
|
||||
return {};
|
||||
}
|
||||
@@ -153,6 +154,14 @@ void ARM_Unicorn::SetTlsAddress(VAddr base) {
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::Run() {
|
||||
ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::Step() {
|
||||
ExecuteInstructions(1);
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64));
|
||||
|
||||
void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
|
||||
|
||||
@@ -30,7 +30,9 @@ public:
|
||||
void SaveContext(ThreadContext& ctx) override;
|
||||
void LoadContext(const ThreadContext& ctx) override;
|
||||
void PrepareReschedule() override;
|
||||
void ExecuteInstructions(int num_instructions) override;
|
||||
void ExecuteInstructions(int num_instructions);
|
||||
void Run() override;
|
||||
void Step() override;
|
||||
void ClearInstructionCache() override;
|
||||
void PageTableChanged() override{};
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Core {
|
||||
|
||||
/*static*/ System System::s_instance;
|
||||
|
||||
System::ResultStatus System::RunLoop(int tight_loop) {
|
||||
System::ResultStatus System::RunLoop(bool tight_loop) {
|
||||
status = ResultStatus::Success;
|
||||
if (!cpu_core) {
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
@@ -40,7 +40,7 @@ System::ResultStatus System::RunLoop(int tight_loop) {
|
||||
if (GDBStub::GetCpuHaltFlag()) {
|
||||
if (GDBStub::GetCpuStepFlag()) {
|
||||
GDBStub::SetCpuStepFlag(false);
|
||||
tight_loop = 1;
|
||||
tight_loop = false;
|
||||
} else {
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
@@ -56,7 +56,11 @@ System::ResultStatus System::RunLoop(int tight_loop) {
|
||||
PrepareReschedule();
|
||||
} else {
|
||||
CoreTiming::Advance();
|
||||
cpu_core->Run(tight_loop);
|
||||
if (tight_loop) {
|
||||
cpu_core->Run();
|
||||
} else {
|
||||
cpu_core->Step();
|
||||
}
|
||||
}
|
||||
|
||||
HW::Update();
|
||||
@@ -66,7 +70,7 @@ System::ResultStatus System::RunLoop(int tight_loop) {
|
||||
}
|
||||
|
||||
System::ResultStatus System::SingleStep() {
|
||||
return RunLoop(1);
|
||||
return RunLoop(false);
|
||||
}
|
||||
|
||||
System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) {
|
||||
@@ -95,14 +99,15 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
|
||||
|
||||
ResultStatus init_result{Init(emu_window, system_mode.first.get())};
|
||||
if (init_result != ResultStatus::Success) {
|
||||
LOG_CRITICAL(Core, "Failed to initialize system (Error %i)!", init_result);
|
||||
LOG_CRITICAL(Core, "Failed to initialize system (Error %i)!",
|
||||
static_cast<int>(init_result));
|
||||
System::Shutdown();
|
||||
return init_result;
|
||||
}
|
||||
|
||||
const Loader::ResultStatus load_result{app_loader->Load(current_process)};
|
||||
if (Loader::ResultStatus::Success != load_result) {
|
||||
LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", load_result);
|
||||
LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", static_cast<int>(load_result));
|
||||
System::Shutdown();
|
||||
|
||||
switch (load_result) {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "core/memory.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "core/telemetry_session.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/gpu.h"
|
||||
|
||||
class EmuWindow;
|
||||
@@ -53,10 +54,10 @@ public:
|
||||
* is not required to do a full dispatch with each instruction. NOTE: the number of instructions
|
||||
* requested is not guaranteed to run, as this will be interrupted preemptively if a hardware
|
||||
* update is requested (e.g. on a thread switch).
|
||||
* @param tight_loop Number of instructions to execute.
|
||||
* @param tight_loop If false, the CPU single-steps.
|
||||
* @return Result status, indicating whether or not the operation succeeded.
|
||||
*/
|
||||
ResultStatus RunLoop(int tight_loop = 100000);
|
||||
ResultStatus RunLoop(bool tight_loop = true);
|
||||
|
||||
/**
|
||||
* Step the CPU one instruction
|
||||
@@ -135,6 +136,14 @@ public:
|
||||
return *app_loader;
|
||||
}
|
||||
|
||||
void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) {
|
||||
debug_context = std::move(context);
|
||||
}
|
||||
|
||||
std::shared_ptr<Tegra::DebugContext> GetGPUDebugContext() const {
|
||||
return debug_context;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Initialize the emulated system.
|
||||
@@ -154,6 +163,8 @@ private:
|
||||
std::unique_ptr<Kernel::Scheduler> scheduler;
|
||||
std::unique_ptr<Tegra::GPU> gpu_core;
|
||||
|
||||
std::shared_ptr<Tegra::DebugContext> debug_context;
|
||||
|
||||
Kernel::SharedPtr<Kernel::Process> current_process;
|
||||
|
||||
/// When true, signals that a reschedule should happen
|
||||
|
||||
@@ -6,34 +6,28 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/filesystem.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
// Structure of a directory entry, from http://3dbrew.org/wiki/FSDir:Read#Entry_format
|
||||
const size_t FILENAME_LENGTH = 0x20C / 2;
|
||||
// Structure of a directory entry, from
|
||||
// http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry
|
||||
const size_t FILENAME_LENGTH = 0x300;
|
||||
struct Entry {
|
||||
char16_t filename[FILENAME_LENGTH]; // Entry name (UTF-16, null-terminated)
|
||||
std::array<char, 9> short_name; // 8.3 file name ('longfilename' -> 'LONGFI~1', null-terminated)
|
||||
char unknown1; // unknown (observed values: 0x0A, 0x70, 0xFD)
|
||||
std::array<char, 4>
|
||||
extension; // 8.3 file extension (set to spaces for directories, null-terminated)
|
||||
char unknown2; // unknown (always 0x01)
|
||||
char unknown3; // unknown (0x00 or 0x08)
|
||||
char is_directory; // directory flag
|
||||
char is_hidden; // hidden flag
|
||||
char is_archive; // archive flag
|
||||
char is_read_only; // read-only flag
|
||||
u64 file_size; // file size (for files only)
|
||||
char filename[FILENAME_LENGTH];
|
||||
INSERT_PADDING_BYTES(4);
|
||||
EntryType type;
|
||||
INSERT_PADDING_BYTES(3);
|
||||
u64 file_size;
|
||||
};
|
||||
static_assert(sizeof(Entry) == 0x228, "Directory Entry struct isn't exactly 0x228 bytes long!");
|
||||
static_assert(offsetof(Entry, short_name) == 0x20C, "Wrong offset for short_name in Entry.");
|
||||
static_assert(offsetof(Entry, extension) == 0x216, "Wrong offset for extension in Entry.");
|
||||
static_assert(offsetof(Entry, is_archive) == 0x21E, "Wrong offset for is_archive in Entry.");
|
||||
static_assert(offsetof(Entry, file_size) == 0x220, "Wrong offset for file_size in Entry.");
|
||||
static_assert(sizeof(Entry) == 0x310, "Directory Entry struct isn't exactly 0x310 bytes long!");
|
||||
static_assert(offsetof(Entry, type) == 0x304, "Wrong offset for type in Entry.");
|
||||
static_assert(offsetof(Entry, file_size) == 0x308, "Wrong offset for file_size in Entry.");
|
||||
|
||||
class DirectoryBackend : NonCopyable {
|
||||
public:
|
||||
@@ -46,7 +40,10 @@ public:
|
||||
* @param entries Buffer to read data into
|
||||
* @return Number of entries listed
|
||||
*/
|
||||
virtual u32 Read(const u32 count, Entry* entries) = 0;
|
||||
virtual u64 Read(const u64 count, Entry* entries) = 0;
|
||||
|
||||
/// Returns the number of entries still left to read.
|
||||
virtual u64 GetEntryCount() const = 0;
|
||||
|
||||
/**
|
||||
* Close the directory
|
||||
|
||||
@@ -11,16 +11,43 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
static std::string ModeFlagsToString(Mode mode) {
|
||||
std::string mode_str;
|
||||
u32 mode_flags = static_cast<u32>(mode);
|
||||
|
||||
// Calculate the correct open mode for the file.
|
||||
if ((mode_flags & static_cast<u32>(Mode::Read)) &&
|
||||
(mode_flags & static_cast<u32>(Mode::Write))) {
|
||||
if (mode_flags & static_cast<u32>(Mode::Append))
|
||||
mode_str = "a+";
|
||||
else
|
||||
mode_str = "r+";
|
||||
} else {
|
||||
if (mode_flags & static_cast<u32>(Mode::Read))
|
||||
mode_str = "r";
|
||||
else if (mode_flags & static_cast<u32>(Mode::Append))
|
||||
mode_str = "a";
|
||||
else if (mode_flags & static_cast<u32>(Mode::Write))
|
||||
mode_str = "w";
|
||||
}
|
||||
|
||||
mode_str += "b";
|
||||
|
||||
return mode_str;
|
||||
}
|
||||
|
||||
std::string Disk_FileSystem::GetName() const {
|
||||
return "Disk";
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<StorageBackend>> Disk_FileSystem::OpenFile(const std::string& path,
|
||||
Mode mode) const {
|
||||
ASSERT_MSG(mode == Mode::Read || mode == Mode::Write, "Other file modes are not supported");
|
||||
|
||||
// Calculate the correct open mode for the file.
|
||||
std::string mode_str = ModeFlagsToString(mode);
|
||||
|
||||
std::string full_path = base_directory + path;
|
||||
auto file = std::make_shared<FileUtil::IOFile>(full_path, mode == Mode::Read ? "rb" : "wb");
|
||||
auto file = std::make_shared<FileUtil::IOFile>(full_path, mode_str.c_str());
|
||||
|
||||
if (!file->IsOpen()) {
|
||||
return ERROR_PATH_NOT_FOUND;
|
||||
@@ -75,8 +102,15 @@ ResultCode Disk_FileSystem::CreateFile(const std::string& path, u64 size) const
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode Disk_FileSystem::CreateDirectory(const Path& path) const {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
ResultCode Disk_FileSystem::CreateDirectory(const std::string& path) const {
|
||||
// TODO(Subv): Perform path validation to prevent escaping the emulator sandbox.
|
||||
std::string full_path = base_directory + path;
|
||||
|
||||
if (FileUtil::CreateDir(full_path)) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating %s", full_path.c_str());
|
||||
// TODO(wwylele): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
@@ -88,8 +122,17 @@ ResultCode Disk_FileSystem::RenameDirectory(const Path& src_path, const Path& de
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> Disk_FileSystem::OpenDirectory(
|
||||
const Path& path) const {
|
||||
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<Disk_Directory>());
|
||||
const std::string& path) const {
|
||||
|
||||
std::string full_path = base_directory + path;
|
||||
|
||||
if (!FileUtil::IsDirectory(full_path)) {
|
||||
// TODO(Subv): Find the correct error code for this.
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
auto directory = std::make_unique<Disk_Directory>(full_path);
|
||||
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory));
|
||||
}
|
||||
|
||||
u64 Disk_FileSystem::GetFreeSpaceSize() const {
|
||||
@@ -103,8 +146,10 @@ ResultVal<FileSys::EntryType> Disk_FileSystem::GetEntryType(const std::string& p
|
||||
return ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
|
||||
// TODO(Subv): Find out the EntryType values
|
||||
UNIMPLEMENTED_MSG("Unimplemented GetEntryType");
|
||||
if (FileUtil::IsDirectory(full_path))
|
||||
return MakeResult(EntryType::Directory);
|
||||
|
||||
return MakeResult(EntryType::File);
|
||||
}
|
||||
|
||||
ResultVal<size_t> Disk_Storage::Read(const u64 offset, const size_t length, u8* buffer) const {
|
||||
@@ -133,14 +178,50 @@ bool Disk_Storage::SetSize(const u64 size) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 Disk_Directory::Read(const u32 count, Entry* entries) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
return 0;
|
||||
Disk_Directory::Disk_Directory(const std::string& path) : directory() {
|
||||
unsigned size = FileUtil::ScanDirectoryTree(path, directory);
|
||||
directory.size = size;
|
||||
directory.isDirectory = true;
|
||||
children_iterator = directory.children.begin();
|
||||
}
|
||||
|
||||
bool Disk_Directory::Close() const {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
return true;
|
||||
u64 Disk_Directory::Read(const u64 count, Entry* entries) {
|
||||
u64 entries_read = 0;
|
||||
|
||||
while (entries_read < count && children_iterator != directory.children.cend()) {
|
||||
const FileUtil::FSTEntry& file = *children_iterator;
|
||||
const std::string& filename = file.virtualName;
|
||||
Entry& entry = entries[entries_read];
|
||||
|
||||
LOG_TRACE(Service_FS, "File %s: size=%llu dir=%d", filename.c_str(), file.size,
|
||||
file.isDirectory);
|
||||
|
||||
// TODO(Link Mauve): use a proper conversion to UTF-16.
|
||||
for (size_t j = 0; j < FILENAME_LENGTH; ++j) {
|
||||
entry.filename[j] = filename[j];
|
||||
if (!filename[j])
|
||||
break;
|
||||
}
|
||||
|
||||
if (file.isDirectory) {
|
||||
entry.file_size = 0;
|
||||
entry.type = EntryType::Directory;
|
||||
} else {
|
||||
entry.file_size = file.size;
|
||||
entry.type = EntryType::File;
|
||||
}
|
||||
|
||||
++entries_read;
|
||||
++children_iterator;
|
||||
}
|
||||
return entries_read;
|
||||
}
|
||||
|
||||
u64 Disk_Directory::GetEntryCount() const {
|
||||
// We convert the children iterator into a const_iterator to allow template argument deduction
|
||||
// in std::distance.
|
||||
std::vector<FileUtil::FSTEntry>::const_iterator current = children_iterator;
|
||||
return std::distance(current, directory.children.end());
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -30,9 +30,10 @@ public:
|
||||
ResultCode DeleteDirectory(const Path& path) const override;
|
||||
ResultCode DeleteDirectoryRecursively(const Path& path) const override;
|
||||
ResultCode CreateFile(const std::string& path, u64 size) const override;
|
||||
ResultCode CreateDirectory(const Path& path) const override;
|
||||
ResultCode CreateDirectory(const std::string& path) const override;
|
||||
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(
|
||||
const std::string& path) const override;
|
||||
u64 GetFreeSpaceSize() const override;
|
||||
ResultVal<EntryType> GetEntryType(const std::string& path) const override;
|
||||
|
||||
@@ -59,8 +60,26 @@ private:
|
||||
|
||||
class Disk_Directory : public DirectoryBackend {
|
||||
public:
|
||||
u32 Read(const u32 count, Entry* entries) override;
|
||||
bool Close() const override;
|
||||
Disk_Directory(const std::string& path);
|
||||
|
||||
~Disk_Directory() override {
|
||||
Close();
|
||||
}
|
||||
|
||||
u64 Read(const u64 count, Entry* entries) override;
|
||||
u64 GetEntryCount() const override;
|
||||
|
||||
bool Close() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
u32 total_entries_in_directory;
|
||||
FileUtil::FSTEntry directory;
|
||||
|
||||
// We need to remember the last entry we returned, so a subsequent call to Read will continue
|
||||
// from the next one. This iterator will always point to the next unread entry.
|
||||
std::vector<FileUtil::FSTEntry>::iterator children_iterator;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -27,7 +27,7 @@ enum LowPathType : u32 {
|
||||
Wchar = 4,
|
||||
};
|
||||
|
||||
enum EntryType : u32 {
|
||||
enum EntryType : u8 {
|
||||
Directory = 0,
|
||||
File = 1,
|
||||
};
|
||||
@@ -35,6 +35,7 @@ enum EntryType : u32 {
|
||||
enum class Mode : u32 {
|
||||
Read = 1,
|
||||
Write = 2,
|
||||
Append = 4,
|
||||
};
|
||||
|
||||
class Path {
|
||||
@@ -103,7 +104,7 @@ public:
|
||||
* @param path Path relative to the archive
|
||||
* @return Result of the operation
|
||||
*/
|
||||
virtual ResultCode CreateDirectory(const Path& path) const = 0;
|
||||
virtual ResultCode CreateDirectory(const std::string& path) const = 0;
|
||||
|
||||
/**
|
||||
* Delete a directory specified by its path
|
||||
@@ -149,7 +150,8 @@ public:
|
||||
* @param path Path relative to the archive
|
||||
* @return Opened directory, or error code
|
||||
*/
|
||||
virtual ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const = 0;
|
||||
virtual ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(
|
||||
const std::string& path) const = 0;
|
||||
|
||||
/**
|
||||
* Get the free space
|
||||
|
||||
@@ -55,7 +55,7 @@ ResultCode RomFS_FileSystem::CreateFile(const std::string& path, u64 size) const
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode RomFS_FileSystem::CreateDirectory(const Path& path) const {
|
||||
ResultCode RomFS_FileSystem::CreateDirectory(const std::string& path) const {
|
||||
LOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive (%s).",
|
||||
GetName().c_str());
|
||||
// TODO(wwylele): Use correct error code
|
||||
@@ -70,7 +70,8 @@ ResultCode RomFS_FileSystem::RenameDirectory(const Path& src_path, const Path& d
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> RomFS_FileSystem::OpenDirectory(
|
||||
const Path& path) const {
|
||||
const std::string& path) const {
|
||||
LOG_WARNING(Service_FS, "Opening Directory in a ROMFS archive");
|
||||
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<ROMFSDirectory>());
|
||||
}
|
||||
|
||||
|
||||
@@ -36,9 +36,10 @@ public:
|
||||
ResultCode DeleteDirectory(const Path& path) const override;
|
||||
ResultCode DeleteDirectoryRecursively(const Path& path) const override;
|
||||
ResultCode CreateFile(const std::string& path, u64 size) const override;
|
||||
ResultCode CreateDirectory(const Path& path) const override;
|
||||
ResultCode CreateDirectory(const std::string& path) const override;
|
||||
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(
|
||||
const std::string& path) const override;
|
||||
u64 GetFreeSpaceSize() const override;
|
||||
ResultVal<EntryType> GetEntryType(const std::string& path) const override;
|
||||
|
||||
@@ -70,7 +71,10 @@ private:
|
||||
|
||||
class ROMFSDirectory : public DirectoryBackend {
|
||||
public:
|
||||
u32 Read(const u32 count, Entry* entries) override {
|
||||
u64 Read(const u64 count, Entry* entries) override {
|
||||
return 0;
|
||||
}
|
||||
u64 GetEntryCount() const override {
|
||||
return 0;
|
||||
}
|
||||
bool Close() const override {
|
||||
|
||||
40
src/core/file_sys/sdmc_factory.cpp
Normal file
40
src/core/file_sys/sdmc_factory.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/disk_filesystem.h"
|
||||
#include "core/file_sys/sdmc_factory.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
SDMC_Factory::SDMC_Factory(std::string sd_directory) : sd_directory(std::move(sd_directory)) {}
|
||||
|
||||
ResultVal<std::unique_ptr<FileSystemBackend>> SDMC_Factory::Open(const Path& path) {
|
||||
// Create the SD Card directory if it doesn't already exist.
|
||||
if (!FileUtil::IsDirectory(sd_directory)) {
|
||||
FileUtil::CreateFullPath(sd_directory);
|
||||
}
|
||||
|
||||
auto archive = std::make_unique<Disk_FileSystem>(sd_directory);
|
||||
return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
|
||||
}
|
||||
|
||||
ResultCode SDMC_Factory::Format(const Path& path) {
|
||||
LOG_ERROR(Service_FS, "Unimplemented Format archive %s", GetName().c_str());
|
||||
// TODO(Subv): Find the right error code for this
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultVal<ArchiveFormatInfo> SDMC_Factory::GetFormatInfo(const Path& path) const {
|
||||
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
|
||||
// TODO(bunnei): Find the right error code for this
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
31
src/core/file_sys/sdmc_factory.h
Normal file
31
src/core/file_sys/sdmc_factory.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/filesystem.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
/// File system interface to the SDCard archive
|
||||
class SDMC_Factory final : public FileSystemFactory {
|
||||
public:
|
||||
explicit SDMC_Factory(std::string sd_directory);
|
||||
|
||||
std::string GetName() const override {
|
||||
return "SDMC_Factory";
|
||||
}
|
||||
ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override;
|
||||
ResultCode Format(const Path& path) override;
|
||||
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
|
||||
|
||||
private:
|
||||
std::string sd_directory;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
@@ -693,7 +693,7 @@ static void ReadMemory() {
|
||||
u64 len =
|
||||
HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset));
|
||||
|
||||
LOG_DEBUG(Debug_GDBStub, "gdb: addr: %016llx len: %016llx\n", addr, len);
|
||||
LOG_DEBUG(Debug_GDBStub, "gdb: addr: %016lx len: %016lx\n", addr, len);
|
||||
|
||||
if (len * 2 > sizeof(reply)) {
|
||||
SendReply("E01");
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
@@ -26,6 +27,32 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s
|
||||
boost::range::remove_erase(connected_sessions, server_session);
|
||||
}
|
||||
|
||||
SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
|
||||
const std::string& reason, u64 timeout,
|
||||
WakeupCallback&& callback) {
|
||||
|
||||
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
|
||||
thread->wakeup_callback =
|
||||
[context = *this, callback](ThreadWakeupReason reason, SharedPtr<Thread> thread,
|
||||
SharedPtr<WaitObject> object, size_t index) mutable -> bool {
|
||||
ASSERT(thread->status == THREADSTATUS_WAIT_HLE_EVENT);
|
||||
callback(thread, context, reason);
|
||||
context.WriteToOutgoingCommandBuffer(*thread);
|
||||
return true;
|
||||
};
|
||||
|
||||
auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
|
||||
thread->status = THREADSTATUS_WAIT_HLE_EVENT;
|
||||
thread->wait_objects = {event};
|
||||
event->AddWaitingThread(thread);
|
||||
|
||||
if (timeout > 0) {
|
||||
thread->WakeAfterDelay(timeout);
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session)
|
||||
: server_session(std::move(server_session)) {
|
||||
cmd_buf[0] = 0;
|
||||
@@ -35,7 +62,7 @@ HLERequestContext::~HLERequestContext() = default;
|
||||
|
||||
void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
IPC::RequestParser rp(src_cmdbuf);
|
||||
command_header = std::make_unique<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
|
||||
command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
|
||||
|
||||
if (command_header->type == IPC::CommandType::Close) {
|
||||
// Close does not populate the rest of the IPC header
|
||||
@@ -45,7 +72,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
// If handle descriptor is present, add size of it
|
||||
if (command_header->enable_handle_descriptor) {
|
||||
handle_descriptor_header =
|
||||
std::make_unique<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>());
|
||||
std::make_shared<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>());
|
||||
if (handle_descriptor_header->send_current_pid) {
|
||||
rp.Skip(2, false);
|
||||
}
|
||||
@@ -88,7 +115,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
// All outgoing domain messages have the domain header, if only incoming has it
|
||||
if (incoming || domain_message_header) {
|
||||
domain_message_header =
|
||||
std::make_unique<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>());
|
||||
std::make_shared<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>());
|
||||
} else {
|
||||
if (Session()->IsDomain())
|
||||
LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
|
||||
@@ -96,7 +123,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
}
|
||||
|
||||
data_payload_header =
|
||||
std::make_unique<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>());
|
||||
std::make_shared<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>());
|
||||
|
||||
data_payload_offset = rp.GetCurrentOffset();
|
||||
|
||||
@@ -159,8 +186,11 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,
|
||||
HandleTable& dst_table) {
|
||||
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
|
||||
Memory::ReadBlock(*thread.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.
|
||||
ParseCommandBuffer(cmd_buf.data(), false);
|
||||
@@ -171,7 +201,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
|
||||
if (domain_message_header)
|
||||
size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32);
|
||||
|
||||
std::copy_n(cmd_buf.begin(), size, dst_cmdbuf);
|
||||
std::copy_n(cmd_buf.begin(), size, dst_cmdbuf.data());
|
||||
|
||||
if (command_header->enable_handle_descriptor) {
|
||||
ASSERT_MSG(!move_objects.empty() || !copy_objects.empty(),
|
||||
@@ -213,6 +243,11 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
|
||||
dst_cmdbuf[domain_offset++] = static_cast<u32_le>(request_handlers.size());
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the translated command buffer back into the thread's command buffer area.
|
||||
Memory::WriteBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
dst_cmdbuf.size() * sizeof(u32));
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -234,7 +269,7 @@ std::vector<u8> HLERequestContext::ReadBuffer() const {
|
||||
size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size) const {
|
||||
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[0].Size()};
|
||||
|
||||
ASSERT_MSG(size <= GetWriteBufferSize(), "Size %d is too big", size);
|
||||
ASSERT_MSG(size <= GetWriteBufferSize(), "Size %lx is too big", size);
|
||||
|
||||
if (is_buffer_b) {
|
||||
Memory::WriteBlock(BufferDescriptorB()[0].Address(), buffer, size);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include "common/common_types.h"
|
||||
@@ -13,6 +14,7 @@
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Service {
|
||||
class ServiceFrameworkBase;
|
||||
@@ -24,6 +26,7 @@ class Domain;
|
||||
class HandleTable;
|
||||
class HLERequestContext;
|
||||
class Process;
|
||||
class Event;
|
||||
|
||||
/**
|
||||
* Interface implemented by HLE Session handlers.
|
||||
@@ -102,14 +105,31 @@ public:
|
||||
return server_session;
|
||||
}
|
||||
|
||||
using WakeupCallback = std::function<void(SharedPtr<Thread> thread, HLERequestContext& context,
|
||||
ThreadWakeupReason reason)>;
|
||||
|
||||
/**
|
||||
* Puts the specified guest thread to sleep until the returned event is signaled or until the
|
||||
* specified timeout expires.
|
||||
* @param thread Thread to be put to sleep.
|
||||
* @param reason Reason for pausing the thread, to be used for debugging purposes.
|
||||
* @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback
|
||||
* invoked with a Timeout reason.
|
||||
* @param callback Callback to be invoked when the thread is resumed. This callback must write
|
||||
* the entire command response once again, regardless of the state of it before this function
|
||||
* was called.
|
||||
* @returns Event that when signaled will resume the thread and call the callback function.
|
||||
*/
|
||||
SharedPtr<Event> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason,
|
||||
u64 timeout, WakeupCallback&& callback);
|
||||
|
||||
void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
|
||||
|
||||
/// Populates this context with data from the requesting process/thread.
|
||||
ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process,
|
||||
HandleTable& src_table);
|
||||
/// Writes data from this context back to the requesting process/thread.
|
||||
ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,
|
||||
HandleTable& dst_table);
|
||||
ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
|
||||
|
||||
u32_le GetCommand() const {
|
||||
return command;
|
||||
@@ -139,7 +159,7 @@ public:
|
||||
return buffer_c_desciptors;
|
||||
}
|
||||
|
||||
const std::unique_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const {
|
||||
const std::shared_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const {
|
||||
return domain_message_header;
|
||||
}
|
||||
|
||||
@@ -212,10 +232,10 @@ private:
|
||||
boost::container::small_vector<SharedPtr<Object>, 8> copy_objects;
|
||||
boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects;
|
||||
|
||||
std::unique_ptr<IPC::CommandHeader> command_header;
|
||||
std::unique_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header;
|
||||
std::unique_ptr<IPC::DataPayloadHeader> data_payload_header;
|
||||
std::unique_ptr<IPC::DomainMessageHeader> domain_message_header;
|
||||
std::shared_ptr<IPC::CommandHeader> command_header;
|
||||
std::shared_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header;
|
||||
std::shared_ptr<IPC::DataPayloadHeader> data_payload_header;
|
||||
std::shared_ptr<IPC::DomainMessageHeader> domain_message_header;
|
||||
std::vector<IPC::BufferDescriptorX> buffer_x_desciptors;
|
||||
std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors;
|
||||
std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors;
|
||||
|
||||
@@ -10,12 +10,12 @@ namespace Kernel {
|
||||
ObjectAddressTable g_object_address_table;
|
||||
|
||||
void ObjectAddressTable::Insert(VAddr addr, SharedPtr<Object> obj) {
|
||||
ASSERT_MSG(objects.find(addr) == objects.end(), "Object already exists with addr=0x%llx", addr);
|
||||
ASSERT_MSG(objects.find(addr) == objects.end(), "Object already exists with addr=0x%lx", addr);
|
||||
objects[addr] = obj;
|
||||
}
|
||||
|
||||
void ObjectAddressTable::Close(VAddr addr) {
|
||||
ASSERT_MSG(objects.find(addr) != objects.end(), "Object does not exist with addr=0x%llx", addr);
|
||||
ASSERT_MSG(objects.find(addr) != objects.end(), "Object does not exist with addr=0x%lx", addr);
|
||||
objects.erase(addr);
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,8 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con
|
||||
}
|
||||
}
|
||||
|
||||
LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value());
|
||||
LOG_CRITICAL(IPC, "Unknown domain command=%d",
|
||||
static_cast<int>(domain_message_header->command.Value()));
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
|
||||
|
||||
// Error out if the requested permissions don't match what the creator process allows.
|
||||
if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
|
||||
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, permissions don't match",
|
||||
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%lx name=%s, permissions don't match",
|
||||
GetObjectId(), address, name.c_str());
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
@@ -115,7 +115,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
|
||||
// Error out if the provided permissions are not compatible with what the creator process needs.
|
||||
if (other_permissions != MemoryPermission::DontCare &&
|
||||
static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) {
|
||||
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, permissions don't match",
|
||||
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%lx name=%s, permissions don't match",
|
||||
GetObjectId(), address, name.c_str());
|
||||
return ERR_WRONG_PERMISSION;
|
||||
}
|
||||
@@ -126,7 +126,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
|
||||
if (address != 0) {
|
||||
// TODO(shinyquagsire23): Check for virtual/mappable memory here too?
|
||||
if (address >= Memory::HEAP_VADDR && address < Memory::HEAP_VADDR_END) {
|
||||
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, invalid address",
|
||||
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%lx name=%s, invalid address",
|
||||
GetObjectId(), address, name.c_str());
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
@@ -143,10 +143,9 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
|
||||
auto result = target_process->vm_manager.MapMemoryBlock(
|
||||
target_address, backing_block, backing_block_offset, size, MemoryState::Shared);
|
||||
if (result.Failed()) {
|
||||
LOG_ERROR(
|
||||
Kernel,
|
||||
"cannot map id=%u, target_address=0x%llx name=%s, error mapping to virtual memory",
|
||||
GetObjectId(), target_address, name.c_str());
|
||||
LOG_ERROR(Kernel,
|
||||
"cannot map id=%u, target_address=0x%lx name=%s, error mapping to virtual memory",
|
||||
GetObjectId(), target_address, name.c_str());
|
||||
return result.Code();
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
|
||||
}
|
||||
|
||||
static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) {
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) called, addr=0x%llx", addr);
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) called, addr=0x%lx", addr);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -750,7 +750,7 @@ static ResultCode ResetSignal(Handle handle) {
|
||||
|
||||
/// Creates a TransferMemory object
|
||||
static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) {
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%llx, size=0x%llx, perms=%08X", addr, size,
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%lx, size=0x%lx, perms=%08X", addr, size,
|
||||
permissions);
|
||||
*handle = 0;
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
@@ -55,16 +55,6 @@ inline static u32 const NewThreadId() {
|
||||
Thread::Thread() {}
|
||||
Thread::~Thread() {}
|
||||
|
||||
/**
|
||||
* Check if the specified thread is waiting on the specified address to be arbitrated
|
||||
* @param thread The thread to test
|
||||
* @param wait_address The address to test against
|
||||
* @return True if the thread is waiting, false otherwise
|
||||
*/
|
||||
static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) {
|
||||
return thread->status == THREADSTATUS_WAIT_ARB && wait_address == thread->wait_address;
|
||||
}
|
||||
|
||||
void Thread::Stop() {
|
||||
// Cancel any outstanding wakeup events for this thread
|
||||
CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
|
||||
@@ -102,12 +92,6 @@ void WaitCurrentThread_Sleep() {
|
||||
thread->status = THREADSTATUS_WAIT_SLEEP;
|
||||
}
|
||||
|
||||
void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) {
|
||||
Thread* thread = GetCurrentThread();
|
||||
thread->wait_address = wait_address;
|
||||
thread->status = THREADSTATUS_WAIT_ARB;
|
||||
}
|
||||
|
||||
void ExitCurrentThread() {
|
||||
Thread* thread = GetCurrentThread();
|
||||
thread->Stop();
|
||||
@@ -129,7 +113,8 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
|
||||
bool resume = true;
|
||||
|
||||
if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
|
||||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) {
|
||||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL ||
|
||||
thread->status == THREADSTATUS_WAIT_HLE_EVENT) {
|
||||
|
||||
// Remove the thread from each of its waiting objects' waitlists
|
||||
for (auto& object : thread->wait_objects)
|
||||
@@ -163,7 +148,7 @@ void Thread::ResumeFromWait() {
|
||||
switch (status) {
|
||||
case THREADSTATUS_WAIT_SYNCH_ALL:
|
||||
case THREADSTATUS_WAIT_SYNCH_ANY:
|
||||
case THREADSTATUS_WAIT_ARB:
|
||||
case THREADSTATUS_WAIT_HLE_EVENT:
|
||||
case THREADSTATUS_WAIT_SLEEP:
|
||||
case THREADSTATUS_WAIT_IPC:
|
||||
break;
|
||||
|
||||
@@ -38,7 +38,7 @@ enum ThreadProcessorId : s32 {
|
||||
enum ThreadStatus {
|
||||
THREADSTATUS_RUNNING, ///< Currently running
|
||||
THREADSTATUS_READY, ///< Ready to run
|
||||
THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter
|
||||
THREADSTATUS_WAIT_HLE_EVENT, ///< Waiting for hle event to finish
|
||||
THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC
|
||||
THREADSTATUS_WAIT_IPC, ///< Waiting for the reply from an IPC request
|
||||
THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
|
||||
|
||||
@@ -39,7 +39,8 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
|
||||
for (const auto& thread : waiting_threads) {
|
||||
// The list of waiting threads must not contain threads that are not waiting to be awakened.
|
||||
ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
|
||||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL,
|
||||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL ||
|
||||
thread->status == THREADSTATUS_WAIT_HLE_EVENT,
|
||||
"Inconsistent thread statuses in waiting_threads");
|
||||
|
||||
if (thread->current_priority >= candidate_priority)
|
||||
|
||||
@@ -151,12 +151,52 @@ private:
|
||||
Kernel::SharedPtr<Kernel::Event> system_event;
|
||||
};
|
||||
|
||||
class IAudioDevice final : public ServiceFramework<IAudioDevice> {
|
||||
public:
|
||||
IAudioDevice() : ServiceFramework("IAudioDevice") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
|
||||
{0x1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"}};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
buffer_event =
|
||||
Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioOutBufferReleasedEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
void ListAudioDeviceName(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const std::string audio_interface = "AudioInterface";
|
||||
ctx.WriteBuffer(audio_interface.c_str(), audio_interface.size());
|
||||
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(1);
|
||||
}
|
||||
|
||||
void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
f32 volume = static_cast<f32>(rp.Pop<u32>());
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
|
||||
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> buffer_event;
|
||||
};
|
||||
|
||||
AudRenU::AudRenU() : ServiceFramework("audren:u") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
|
||||
{1, &AudRenU::GetAudioRendererWorkBufferSize, "GetAudioRendererWorkBufferSize"},
|
||||
{2, &AudRenU::GetAudioRenderersProcessMasterVolume, "GetAudioRenderersProcessMasterVolume"},
|
||||
{3, nullptr, "SetAudioRenderersProcessMasterVolume"},
|
||||
{2, &AudRenU::GetAudioDevice, "GetAudioDevice"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
@@ -179,12 +219,13 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void AudRenU::GetAudioRenderersProcessMasterVolume(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
void AudRenU::GetAudioDevice(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(100);
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
rb.PushIpcInterface<Audio::IAudioDevice>();
|
||||
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
}
|
||||
|
||||
} // namespace Audio
|
||||
|
||||
@@ -21,7 +21,7 @@ public:
|
||||
private:
|
||||
void OpenAudioRenderer(Kernel::HLERequestContext& ctx);
|
||||
void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx);
|
||||
void GetAudioRenderersProcessMasterVolume(Kernel::HLERequestContext& ctx);
|
||||
void GetAudioDevice(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
} // namespace Audio
|
||||
|
||||
38
src/core/hle/service/fatal/fatal.cpp
Normal file
38
src/core/hle/service/fatal/fatal.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/fatal/fatal.h"
|
||||
#include "core/hle/service/fatal/fatal_p.h"
|
||||
#include "core/hle/service/fatal/fatal_u.h"
|
||||
|
||||
namespace Service {
|
||||
namespace Fatal {
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)) {}
|
||||
|
||||
void Module::Interface::FatalSimple(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
u32 error_code = rp.Pop<u32>();
|
||||
LOG_WARNING(Service_Fatal, "(STUBBED) called, error_code=0x%X", error_code);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Module::Interface::TransitionToFatalError(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Fatal, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
auto module = std::make_shared<Module>();
|
||||
std::make_shared<Fatal_P>(module)->InstallAsService(service_manager);
|
||||
std::make_shared<Fatal_U>(module)->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace Fatal
|
||||
} // namespace Service
|
||||
29
src/core/hle/service/fatal/fatal.h
Normal file
29
src/core/hle/service/fatal/fatal.h
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service {
|
||||
namespace Fatal {
|
||||
|
||||
class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
Interface(std::shared_ptr<Module> module, const char* name);
|
||||
|
||||
void FatalSimple(Kernel::HLERequestContext& ctx);
|
||||
void TransitionToFatalError(Kernel::HLERequestContext& ctx);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
};
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
|
||||
} // namespace Fatal
|
||||
} // namespace Service
|
||||
14
src/core/hle/service/fatal/fatal_p.cpp
Normal file
14
src/core/hle/service/fatal/fatal_p.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/fatal/fatal_p.h"
|
||||
|
||||
namespace Service {
|
||||
namespace Fatal {
|
||||
|
||||
Fatal_P::Fatal_P(std::shared_ptr<Module> module)
|
||||
: Module::Interface(std::move(module), "fatal:p") {}
|
||||
|
||||
} // namespace Fatal
|
||||
} // namespace Service
|
||||
18
src/core/hle/service/fatal/fatal_p.h
Normal file
18
src/core/hle/service/fatal/fatal_p.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/fatal/fatal.h"
|
||||
|
||||
namespace Service {
|
||||
namespace Fatal {
|
||||
|
||||
class Fatal_P final : public Module::Interface {
|
||||
public:
|
||||
explicit Fatal_P(std::shared_ptr<Module> module);
|
||||
};
|
||||
|
||||
} // namespace Fatal
|
||||
} // namespace Service
|
||||
19
src/core/hle/service/fatal/fatal_u.cpp
Normal file
19
src/core/hle/service/fatal/fatal_u.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/fatal/fatal_u.h"
|
||||
|
||||
namespace Service {
|
||||
namespace Fatal {
|
||||
|
||||
Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, &Fatal_U::FatalSimple, "FatalSimple"},
|
||||
{2, &Fatal_U::TransitionToFatalError, "TransitionToFatalError"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
} // namespace Fatal
|
||||
} // namespace Service
|
||||
18
src/core/hle/service/fatal/fatal_u.h
Normal file
18
src/core/hle/service/fatal/fatal_u.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/fatal/fatal.h"
|
||||
|
||||
namespace Service {
|
||||
namespace Fatal {
|
||||
|
||||
class Fatal_U final : public Module::Interface {
|
||||
public:
|
||||
explicit Fatal_U(std::shared_ptr<Module> module);
|
||||
};
|
||||
|
||||
} // namespace Fatal
|
||||
} // namespace Service
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "common/file_util.h"
|
||||
#include "core/file_sys/filesystem.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/file_sys/sdmc_factory.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/filesystem/fsp_srv.h"
|
||||
|
||||
@@ -60,9 +61,13 @@ void RegisterFileSystems() {
|
||||
filesystem_map.clear();
|
||||
|
||||
std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
|
||||
std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX);
|
||||
|
||||
auto savedata = std::make_unique<FileSys::SaveData_Factory>(std::move(nand_directory));
|
||||
RegisterFileSystem(std::move(savedata), Type::SaveData);
|
||||
|
||||
auto sdcard = std::make_unique<FileSys::SDMC_Factory>(std::move(sd_directory));
|
||||
RegisterFileSystem(std::move(sdcard), Type::SDMC);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace FileSystem {
|
||||
enum class Type {
|
||||
RomFS = 1,
|
||||
SaveData = 2,
|
||||
SDMC = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <cinttypes>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/directory.h"
|
||||
#include "core/file_sys/filesystem.h"
|
||||
#include "core/file_sys/storage.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
@@ -35,7 +36,7 @@ private:
|
||||
const s64 offset = rp.Pop<s64>();
|
||||
const s64 length = rp.Pop<s64>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length);
|
||||
LOG_DEBUG(Service_FS, "called, offset=0x%ld, length=0x%ld", offset, length);
|
||||
|
||||
// Error checking
|
||||
if (length < 0) {
|
||||
@@ -86,7 +87,7 @@ private:
|
||||
const s64 offset = rp.Pop<s64>();
|
||||
const s64 length = rp.Pop<s64>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length);
|
||||
LOG_DEBUG(Service_FS, "called, offset=0x%ld, length=0x%ld", offset, length);
|
||||
|
||||
// Error checking
|
||||
if (length < 0) {
|
||||
@@ -123,7 +124,7 @@ private:
|
||||
const s64 offset = rp.Pop<s64>();
|
||||
const s64 length = rp.Pop<s64>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length);
|
||||
LOG_DEBUG(Service_FS, "called, offset=0x%ld, length=0x%ld", offset, length);
|
||||
|
||||
// Error checking
|
||||
if (length < 0) {
|
||||
@@ -151,14 +152,66 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
class IDirectory final : public ServiceFramework<IDirectory> {
|
||||
public:
|
||||
explicit IDirectory(std::unique_ptr<FileSys::DirectoryBackend>&& backend)
|
||||
: ServiceFramework("IDirectory"), backend(std::move(backend)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IDirectory::Read, "Read"},
|
||||
{1, &IDirectory::GetEntryCount, "GetEntryCount"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::DirectoryBackend> backend;
|
||||
|
||||
void Read(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 unk = rp.Pop<u64>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called, unk=0x%llx", unk);
|
||||
|
||||
// Calculate how many entries we can fit in the output buffer
|
||||
u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry);
|
||||
|
||||
// Read the data from the Directory backend
|
||||
std::vector<FileSys::Entry> entries(count_entries);
|
||||
u64 read_entries = backend->Read(count_entries, entries.data());
|
||||
|
||||
// Convert the data into a byte array
|
||||
std::vector<u8> output(entries.size() * sizeof(FileSys::Entry));
|
||||
std::memcpy(output.data(), entries.data(), output.size());
|
||||
|
||||
// Write the data to memory
|
||||
ctx.WriteBuffer(output);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(read_entries);
|
||||
}
|
||||
|
||||
void GetEntryCount(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
u64 count = backend->GetEntryCount();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(count);
|
||||
}
|
||||
};
|
||||
|
||||
class IFileSystem final : public ServiceFramework<IFileSystem> {
|
||||
public:
|
||||
explicit IFileSystem(std::unique_ptr<FileSys::FileSystemBackend>&& backend)
|
||||
: ServiceFramework("IFileSystem"), backend(std::move(backend)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IFileSystem::CreateFile, "CreateFile"},
|
||||
{2, &IFileSystem::CreateDirectory, "CreateDirectory"},
|
||||
{7, &IFileSystem::GetEntryType, "GetEntryType"},
|
||||
{8, &IFileSystem::OpenFile, "OpenFile"},
|
||||
{9, &IFileSystem::OpenDirectory, "OpenDirectory"},
|
||||
{10, &IFileSystem::Commit, "Commit"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
@@ -182,6 +235,20 @@ public:
|
||||
rb.Push(backend->CreateFile(name, size));
|
||||
}
|
||||
|
||||
void CreateDirectory(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
|
||||
|
||||
std::string name(file_buffer.begin(), end);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called directory %s", name.c_str());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend->CreateDirectory(name));
|
||||
}
|
||||
|
||||
void OpenFile(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
@@ -208,6 +275,33 @@ public:
|
||||
rb.PushIpcInterface<IFile>(std::move(file));
|
||||
}
|
||||
|
||||
void OpenDirectory(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
|
||||
|
||||
std::string name(file_buffer.begin(), end);
|
||||
|
||||
// TODO(Subv): Implement this filter.
|
||||
u32 filter_flags = rp.Pop<u32>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called directory %s filter %u", name.c_str(), filter_flags);
|
||||
|
||||
auto result = backend->OpenDirectory(name);
|
||||
if (result.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
auto directory = std::move(result.Unwrap());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IDirectory>(std::move(directory));
|
||||
}
|
||||
|
||||
void GetEntryType(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
@@ -274,10 +368,14 @@ void FSP_SRV::Initalize(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
FileSys::Path unused;
|
||||
auto filesystem = OpenFileSystem(Type::SDMC, unused).Unwrap();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
|
||||
}
|
||||
|
||||
void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -193,7 +193,7 @@ public:
|
||||
{121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
|
||||
{122, &Hid::SetNpadJoyAssignmentModeSingleByDefault,
|
||||
"SetNpadJoyAssignmentModeSingleByDefault"},
|
||||
{124, nullptr, "SetNpadJoyAssignmentModeDual"},
|
||||
{124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"},
|
||||
{128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"},
|
||||
{200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
|
||||
{201, &Hid::SendVibrationValue, "SendVibrationValue"},
|
||||
@@ -315,6 +315,12 @@ private:
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
@@ -33,6 +33,7 @@ enum class LoadState : u32 {
|
||||
|
||||
PL_U::PL_U() : ServiceFramework("pl:u") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &PL_U::RequestLoad, "RequestLoad"},
|
||||
{1, &PL_U::GetLoadState, "GetLoadState"},
|
||||
{2, &PL_U::GetSize, "GetSize"},
|
||||
{3, &PL_U::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"},
|
||||
@@ -54,6 +55,15 @@ PL_U::PL_U() : ServiceFramework("pl:u") {
|
||||
}
|
||||
}
|
||||
|
||||
void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u32 shared_font_type{rp.Pop<u32>()};
|
||||
|
||||
LOG_DEBUG(Service_NS, "called, shared_font_type=%d", shared_font_type);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u32 font_id{rp.Pop<u32>()};
|
||||
|
||||
@@ -17,6 +17,7 @@ public:
|
||||
~PL_U() = default;
|
||||
|
||||
private:
|
||||
void RequestLoad(Kernel::HLERequestContext& ctx);
|
||||
void GetLoadState(Kernel::HLERequestContext& ctx);
|
||||
void GetSize(Kernel::HLERequestContext& ctx);
|
||||
void GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -23,17 +23,16 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
|
||||
u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform) {
|
||||
VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
|
||||
LOG_WARNING(Service,
|
||||
"Drawing from address %llx offset %08X Width %u Height %u Stride %u Format %u",
|
||||
addr, offset, width, height, stride, format);
|
||||
"Drawing from address %lx offset %08X Width %u Height %u Stride %u Format %u", addr,
|
||||
offset, width, height, stride, format);
|
||||
|
||||
using PixelFormat = RendererBase::FramebufferInfo::PixelFormat;
|
||||
using Flags = NVFlinger::BufferQueue::BufferTransformFlags;
|
||||
const bool flip_vertical = static_cast<u32>(transform) & static_cast<u32>(Flags::FlipV);
|
||||
const RendererBase::FramebufferInfo framebuffer_info{
|
||||
addr, offset, width, height, stride, static_cast<PixelFormat>(format), flip_vertical};
|
||||
using PixelFormat = Tegra::FramebufferConfig::PixelFormat;
|
||||
const Tegra::FramebufferConfig framebuffer{
|
||||
addr, offset, width, height, stride, static_cast<PixelFormat>(format), transform};
|
||||
|
||||
Core::System::GetInstance().perf_stats.EndGameFrame();
|
||||
VideoCore::g_renderer->SwapBuffers(framebuffer_info);
|
||||
|
||||
VideoCore::g_renderer->SwapBuffers(framebuffer);
|
||||
}
|
||||
|
||||
} // namespace Devices
|
||||
|
||||
@@ -26,24 +26,30 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
|
||||
LOG_WARNING(Service, "Adding graphics buffer %u", slot);
|
||||
|
||||
queue.emplace_back(buffer);
|
||||
|
||||
if (buffer_wait_event) {
|
||||
buffer_wait_event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
u32 BufferQueue::DequeueBuffer(u32 pixel_format, u32 width, u32 height) {
|
||||
boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
|
||||
auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) {
|
||||
// Only consider free buffers. Buffers become free once again after they've been Acquired
|
||||
// and Released by the compositor, see the NVFlinger::Compose method.
|
||||
if (buffer.status != Buffer::Status::Free)
|
||||
if (buffer.status != Buffer::Status::Free) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure that the parameters match.
|
||||
return buffer.igbp_buffer.width == width && buffer.igbp_buffer.height == height;
|
||||
});
|
||||
|
||||
if (itr == queue.end()) {
|
||||
LOG_CRITICAL(Service_NVDRV, "no free buffers for pixel_format=%d, width=%d, height=%d",
|
||||
pixel_format, width, height);
|
||||
itr = queue.begin();
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
buffer_wait_event = nullptr;
|
||||
|
||||
itr->status = Buffer::Status::Dequeued;
|
||||
return itr->slot;
|
||||
}
|
||||
@@ -81,6 +87,10 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
|
||||
ASSERT(itr != queue.end());
|
||||
ASSERT(itr->status == Buffer::Status::Acquired);
|
||||
itr->status = Buffer::Status::Free;
|
||||
|
||||
if (buffer_wait_event) {
|
||||
buffer_wait_event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
u32 BufferQueue::Query(QueryType type) {
|
||||
@@ -96,5 +106,10 @@ u32 BufferQueue::Query(QueryType type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void BufferQueue::SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event) {
|
||||
ASSERT_MSG(!buffer_wait_event, "buffer_wait_event only supports a single waiting thread!");
|
||||
buffer_wait_event = std::move(wait_event);
|
||||
}
|
||||
|
||||
} // namespace NVFlinger
|
||||
} // namespace Service
|
||||
|
||||
@@ -47,6 +47,8 @@ public:
|
||||
~BufferQueue() = default;
|
||||
|
||||
enum class BufferTransformFlags : u32 {
|
||||
/// No transform flags are set
|
||||
Unset = 0x00,
|
||||
/// Flip source image horizontally (around the vertical axis)
|
||||
FlipH = 0x01,
|
||||
/// Flip source image vertically (around the horizontal axis)
|
||||
@@ -69,12 +71,13 @@ public:
|
||||
};
|
||||
|
||||
void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer);
|
||||
u32 DequeueBuffer(u32 pixel_format, u32 width, u32 height);
|
||||
boost::optional<u32> DequeueBuffer(u32 width, u32 height);
|
||||
const IGBPBuffer& RequestBuffer(u32 slot) const;
|
||||
void QueueBuffer(u32 slot, BufferTransformFlags transform);
|
||||
boost::optional<const Buffer&> AcquireBuffer();
|
||||
void ReleaseBuffer(u32 slot);
|
||||
u32 Query(QueryType type);
|
||||
void SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event);
|
||||
|
||||
u32 GetId() const {
|
||||
return id;
|
||||
@@ -90,6 +93,9 @@ private:
|
||||
|
||||
std::vector<Buffer> queue;
|
||||
Kernel::SharedPtr<Kernel::Event> native_handle;
|
||||
|
||||
/// Used to signal waiting thread when no buffers are available
|
||||
Kernel::SharedPtr<Kernel::Event> buffer_wait_event;
|
||||
};
|
||||
|
||||
} // namespace NVFlinger
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "core/hle/service/aoc/aoc_u.h"
|
||||
#include "core/hle/service/apm/apm.h"
|
||||
#include "core/hle/service/audio/audio.h"
|
||||
#include "core/hle/service/fatal/fatal.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/friend/friend.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
@@ -33,6 +34,8 @@
|
||||
#include "core/hle/service/sm/controller.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hle/service/sockets/sockets.h"
|
||||
#include "core/hle/service/spl/module.h"
|
||||
#include "core/hle/service/ssl/ssl.h"
|
||||
#include "core/hle/service/time/time.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
|
||||
@@ -149,11 +152,10 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("command_type=%d", context.GetCommandType());
|
||||
UNIMPLEMENTED_MSG("command_type=%d", static_cast<int>(context.GetCommandType()));
|
||||
}
|
||||
|
||||
u32* cmd_buf = (u32*)Memory::GetPointer(Kernel::GetCurrentThread()->GetTLSAddress());
|
||||
context.WriteToOutgoingCommandBuffer(cmd_buf, *Core::CurrentProcess(), Kernel::g_handle_table);
|
||||
context.WriteToOutgoingCommandBuffer(*Kernel::GetCurrentThread());
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@@ -180,6 +182,7 @@ void Init() {
|
||||
AOC::InstallInterfaces(*SM::g_service_manager);
|
||||
APM::InstallInterfaces(*SM::g_service_manager);
|
||||
Audio::InstallInterfaces(*SM::g_service_manager);
|
||||
Fatal::InstallInterfaces(*SM::g_service_manager);
|
||||
FileSystem::InstallInterfaces(*SM::g_service_manager);
|
||||
Friend::InstallInterfaces(*SM::g_service_manager);
|
||||
HID::InstallInterfaces(*SM::g_service_manager);
|
||||
@@ -189,6 +192,8 @@ void Init() {
|
||||
Nvidia::InstallInterfaces(*SM::g_service_manager);
|
||||
PCTL::InstallInterfaces(*SM::g_service_manager);
|
||||
Sockets::InstallInterfaces(*SM::g_service_manager);
|
||||
SPL::InstallInterfaces(*SM::g_service_manager);
|
||||
SSL::InstallInterfaces(*SM::g_service_manager);
|
||||
Time::InstallInterfaces(*SM::g_service_manager);
|
||||
VI::InstallInterfaces(*SM::g_service_manager, nv_flinger);
|
||||
Set::InstallInterfaces(*SM::g_service_manager);
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/sockets/bsd_u.h"
|
||||
#include "core/hle/service/sockets/bsd.h"
|
||||
|
||||
namespace Service {
|
||||
namespace Sockets {
|
||||
|
||||
void BSD_U::RegisterClient(Kernel::HLERequestContext& ctx) {
|
||||
void BSD::RegisterClient(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
@@ -17,7 +17,7 @@ void BSD_U::RegisterClient(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u32>(0); // bsd errno
|
||||
}
|
||||
|
||||
void BSD_U::StartMonitoring(Kernel::HLERequestContext& ctx) {
|
||||
void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
@@ -26,7 +26,7 @@ void BSD_U::StartMonitoring(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u32>(0); // bsd errno
|
||||
}
|
||||
|
||||
void BSD_U::Socket(Kernel::HLERequestContext& ctx) {
|
||||
void BSD::Socket(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
u32 domain = rp.Pop<u32>();
|
||||
@@ -44,7 +44,7 @@ void BSD_U::Socket(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u32>(0); // bsd errno
|
||||
}
|
||||
|
||||
void BSD_U::Connect(Kernel::HLERequestContext& ctx) {
|
||||
void BSD::Connect(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
@@ -54,7 +54,7 @@ void BSD_U::Connect(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u32>(0); // bsd errno
|
||||
}
|
||||
|
||||
void BSD_U::SendTo(Kernel::HLERequestContext& ctx) {
|
||||
void BSD::SendTo(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
@@ -64,7 +64,7 @@ void BSD_U::SendTo(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u32>(0); // bsd errno
|
||||
}
|
||||
|
||||
void BSD_U::Close(Kernel::HLERequestContext& ctx) {
|
||||
void BSD::Close(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
@@ -74,13 +74,15 @@ void BSD_U::Close(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u32>(0); // bsd errno
|
||||
}
|
||||
|
||||
BSD_U::BSD_U() : ServiceFramework("bsd:u") {
|
||||
static const FunctionInfo functions[] = {{0, &BSD_U::RegisterClient, "RegisterClient"},
|
||||
{1, &BSD_U::StartMonitoring, "StartMonitoring"},
|
||||
{2, &BSD_U::Socket, "Socket"},
|
||||
{11, &BSD_U::SendTo, "SendTo"},
|
||||
{14, &BSD_U::Connect, "Connect"},
|
||||
{26, &BSD_U::Close, "Close"}};
|
||||
BSD::BSD(const char* name) : ServiceFramework(name) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &BSD::RegisterClient, "RegisterClient"},
|
||||
{1, &BSD::StartMonitoring, "StartMonitoring"},
|
||||
{2, &BSD::Socket, "Socket"},
|
||||
{11, &BSD::SendTo, "SendTo"},
|
||||
{14, &BSD::Connect, "Connect"},
|
||||
{26, &BSD::Close, "Close"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
namespace Service {
|
||||
namespace Sockets {
|
||||
|
||||
class BSD_U final : public ServiceFramework<BSD_U> {
|
||||
class BSD final : public ServiceFramework<BSD> {
|
||||
public:
|
||||
BSD_U();
|
||||
~BSD_U() = default;
|
||||
explicit BSD(const char* name);
|
||||
~BSD() = default;
|
||||
|
||||
private:
|
||||
void RegisterClient(Kernel::HLERequestContext& ctx);
|
||||
34
src/core/hle/service/sockets/nsd.cpp
Normal file
34
src/core/hle/service/sockets/nsd.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/sockets/nsd.h"
|
||||
|
||||
namespace Service {
|
||||
namespace Sockets {
|
||||
|
||||
NSD::NSD(const char* name) : ServiceFramework(name) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{10, nullptr, "GetSettingName"},
|
||||
{11, nullptr, "GetEnvironmentIdentifier"},
|
||||
{12, nullptr, "GetDeviceId"},
|
||||
{13, nullptr, "DeleteSettings"},
|
||||
{14, nullptr, "ImportSettings"},
|
||||
{20, nullptr, "Resolve"},
|
||||
{21, nullptr, "ResolveEx"},
|
||||
{30, nullptr, "GetNasServiceSetting"},
|
||||
{31, nullptr, "GetNasServiceSettingEx"},
|
||||
{40, nullptr, "GetNasRequestFqdn"},
|
||||
{41, nullptr, "GetNasRequestFqdnEx"},
|
||||
{42, nullptr, "GetNasApiFqdn"},
|
||||
{43, nullptr, "GetNasApiFqdnEx"},
|
||||
{50, nullptr, "GetCurrentSetting"},
|
||||
{60, nullptr, "ReadSaveDataFromFsForTest"},
|
||||
{61, nullptr, "WriteSaveDataToFsForTest"},
|
||||
{62, nullptr, "DeleteSaveDataOfFsForTest"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
} // namespace Sockets
|
||||
} // namespace Service
|
||||
20
src/core/hle/service/sockets/nsd.h
Normal file
20
src/core/hle/service/sockets/nsd.h
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service {
|
||||
namespace Sockets {
|
||||
|
||||
class NSD final : public ServiceFramework<NSD> {
|
||||
public:
|
||||
explicit NSD(const char* name);
|
||||
~NSD() = default;
|
||||
};
|
||||
|
||||
} // namespace Sockets
|
||||
} // namespace Service
|
||||
@@ -19,16 +19,18 @@ void SFDNSRES::GetAddrInfo(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
SFDNSRES::SFDNSRES() : ServiceFramework("sfdnsres") {
|
||||
static const FunctionInfo functions[] = {{0, nullptr, "SetDnsAddressesPrivate"},
|
||||
{1, nullptr, "GetDnsAddressPrivate"},
|
||||
{2, nullptr, "GetHostByName"},
|
||||
{3, nullptr, "GetHostByAddr"},
|
||||
{4, nullptr, "GetHostStringError"},
|
||||
{5, nullptr, "GetGaiStringError"},
|
||||
{6, &SFDNSRES::GetAddrInfo, "GetAddrInfo"},
|
||||
{7, nullptr, "GetNameInfo"},
|
||||
{8, nullptr, "RequestCancelHandle"},
|
||||
{9, nullptr, "CancelSocketCall"}};
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "SetDnsAddressesPrivate"},
|
||||
{1, nullptr, "GetDnsAddressPrivate"},
|
||||
{2, nullptr, "GetHostByName"},
|
||||
{3, nullptr, "GetHostByAddr"},
|
||||
{4, nullptr, "GetHostStringError"},
|
||||
{5, nullptr, "GetGaiStringError"},
|
||||
{6, &SFDNSRES::GetAddrInfo, "GetAddrInfo"},
|
||||
{7, nullptr, "GetNameInfo"},
|
||||
{8, nullptr, "RequestCancelHandle"},
|
||||
{9, nullptr, "CancelSocketCall"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Sockets {
|
||||
|
||||
class SFDNSRES final : public ServiceFramework<SFDNSRES> {
|
||||
public:
|
||||
SFDNSRES();
|
||||
explicit SFDNSRES();
|
||||
~SFDNSRES() = default;
|
||||
|
||||
private:
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/sockets/bsd_u.h"
|
||||
#include "core/hle/service/sockets/bsd.h"
|
||||
#include "core/hle/service/sockets/nsd.h"
|
||||
#include "core/hle/service/sockets/sfdnsres.h"
|
||||
#include "core/hle/service/sockets/sockets.h"
|
||||
|
||||
@@ -10,7 +11,10 @@ namespace Service {
|
||||
namespace Sockets {
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
std::make_shared<BSD_U>()->InstallAsService(service_manager);
|
||||
std::make_shared<BSD>("bsd:s")->InstallAsService(service_manager);
|
||||
std::make_shared<BSD>("bsd:u")->InstallAsService(service_manager);
|
||||
std::make_shared<NSD>("nsd:a")->InstallAsService(service_manager);
|
||||
std::make_shared<NSD>("nsd:u")->InstallAsService(service_manager);
|
||||
std::make_shared<SFDNSRES>()->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
|
||||
18
src/core/hle/service/spl/csrng.cpp
Normal file
18
src/core/hle/service/spl/csrng.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/spl/csrng.h"
|
||||
|
||||
namespace Service {
|
||||
namespace SPL {
|
||||
|
||||
CSRNG::CSRNG(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "csrng") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &CSRNG::GetRandomBytes, "GetRandomBytes"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
} // namespace SPL
|
||||
} // namespace Service
|
||||
18
src/core/hle/service/spl/csrng.h
Normal file
18
src/core/hle/service/spl/csrng.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/spl/module.h"
|
||||
|
||||
namespace Service {
|
||||
namespace SPL {
|
||||
|
||||
class CSRNG final : public Module::Interface {
|
||||
public:
|
||||
explicit CSRNG(std::shared_ptr<Module> module);
|
||||
};
|
||||
|
||||
} // namespace SPL
|
||||
} // namespace Service
|
||||
42
src/core/hle/service/spl/module.cpp
Normal file
42
src/core/hle/service/spl/module.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/spl/csrng.h"
|
||||
#include "core/hle/service/spl/module.h"
|
||||
#include "core/hle/service/spl/spl.h"
|
||||
|
||||
namespace Service {
|
||||
namespace SPL {
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)) {}
|
||||
|
||||
void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
size_t size = ctx.GetWriteBufferSize();
|
||||
|
||||
std::vector<u8> data(size);
|
||||
std::generate(data.begin(), data.end(), std::rand);
|
||||
|
||||
ctx.WriteBuffer(data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_DEBUG(Service_SPL, "called");
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
auto module = std::make_shared<Module>();
|
||||
std::make_shared<CSRNG>(module)->InstallAsService(service_manager);
|
||||
std::make_shared<SPL>(module)->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace SPL
|
||||
} // namespace Service
|
||||
29
src/core/hle/service/spl/module.h
Normal file
29
src/core/hle/service/spl/module.h
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service {
|
||||
namespace SPL {
|
||||
|
||||
class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
Interface(std::shared_ptr<Module> module, const char* name);
|
||||
|
||||
void GetRandomBytes(Kernel::HLERequestContext& ctx);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
};
|
||||
};
|
||||
|
||||
/// Registers all SPL services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
|
||||
} // namespace SPL
|
||||
} // namespace Service
|
||||
41
src/core/hle/service/spl/spl.cpp
Normal file
41
src/core/hle/service/spl/spl.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/spl/spl.h"
|
||||
|
||||
namespace Service {
|
||||
namespace SPL {
|
||||
|
||||
SPL::SPL(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "spl:") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetConfig"},
|
||||
{1, nullptr, "UserExpMod"},
|
||||
{2, nullptr, "GenerateAesKek"},
|
||||
{3, nullptr, "LoadAesKey"},
|
||||
{4, nullptr, "GenerateAesKey"},
|
||||
{5, nullptr, "SetConfig"},
|
||||
{7, &SPL::GetRandomBytes, "GetRandomBytes"},
|
||||
{9, nullptr, "LoadSecureExpModKey"},
|
||||
{10, nullptr, "SecureExpMod"},
|
||||
{11, nullptr, "IsDevelopment"},
|
||||
{12, nullptr, "GenerateSpecificAesKey"},
|
||||
{13, nullptr, "DecryptPrivk"},
|
||||
{14, nullptr, "DecryptAesKey"},
|
||||
{15, nullptr, "DecryptAesCtr"},
|
||||
{16, nullptr, "ComputeCmac"},
|
||||
{17, nullptr, "LoadRsaOaepKey"},
|
||||
{18, nullptr, "UnwrapRsaOaepWrappedTitleKey"},
|
||||
{19, nullptr, "LoadTitleKey"},
|
||||
{20, nullptr, "UnwrapAesWrappedTitleKey"},
|
||||
{21, nullptr, "LockAesEngine"},
|
||||
{22, nullptr, "UnlockAesEngine"},
|
||||
{23, nullptr, "GetSplWaitEvent"},
|
||||
{24, nullptr, "SetSharedData"},
|
||||
{25, nullptr, "GetSharedData"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
} // namespace SPL
|
||||
} // namespace Service
|
||||
18
src/core/hle/service/spl/spl.h
Normal file
18
src/core/hle/service/spl/spl.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/spl/module.h"
|
||||
|
||||
namespace Service {
|
||||
namespace SPL {
|
||||
|
||||
class SPL final : public Module::Interface {
|
||||
public:
|
||||
explicit SPL(std::shared_ptr<Module> module);
|
||||
};
|
||||
|
||||
} // namespace SPL
|
||||
} // namespace Service
|
||||
17
src/core/hle/service/ssl/ssl.cpp
Normal file
17
src/core/hle/service/ssl/ssl.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/ssl/ssl.h"
|
||||
|
||||
namespace Service {
|
||||
namespace SSL {
|
||||
|
||||
SSL::SSL() : ServiceFramework("ssl") {}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
std::make_shared<SSL>()->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace SSL
|
||||
} // namespace Service
|
||||
22
src/core/hle/service/ssl/ssl.h
Normal file
22
src/core/hle/service/ssl/ssl.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service {
|
||||
namespace SSL {
|
||||
|
||||
class SSL final : public ServiceFramework<SSL> {
|
||||
public:
|
||||
explicit SSL();
|
||||
~SSL() = default;
|
||||
};
|
||||
|
||||
/// Registers all SSL services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
|
||||
} // namespace SSL
|
||||
} // namespace Service
|
||||
@@ -107,7 +107,7 @@ private:
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 posix_time = rp.Pop<u64>();
|
||||
|
||||
LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x%016llX", posix_time);
|
||||
LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x%016lX", posix_time);
|
||||
|
||||
CalendarTime calendar_time{2018, 1, 1, 0, 0, 0};
|
||||
CalendarAdditionalInfo additional_info{};
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <boost/optional.hpp>
|
||||
#include "common/alignment.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
@@ -471,7 +474,7 @@ private:
|
||||
u32 flags = rp.Pop<u32>();
|
||||
auto buffer_queue = nv_flinger->GetBufferQueue(id);
|
||||
|
||||
LOG_DEBUG(Service_VI, "called, transaction=%x", transaction);
|
||||
LOG_DEBUG(Service_VI, "called, transaction=%x", static_cast<u32>(transaction));
|
||||
|
||||
if (transaction == TransactionId::Connect) {
|
||||
IGBPConnectRequestParcel request{ctx.ReadBuffer()};
|
||||
@@ -486,12 +489,30 @@ private:
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
} else if (transaction == TransactionId::DequeueBuffer) {
|
||||
IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
|
||||
const u32 width{request.data.width};
|
||||
const u32 height{request.data.height};
|
||||
boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
|
||||
|
||||
u32 slot = buffer_queue->DequeueBuffer(request.data.pixel_format, request.data.width,
|
||||
request.data.height);
|
||||
|
||||
IGBPDequeueBufferResponseParcel response{slot};
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
if (slot != boost::none) {
|
||||
// Buffer is available
|
||||
IGBPDequeueBufferResponseParcel response{*slot};
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
} else {
|
||||
// Wait the current thread until a buffer becomes available
|
||||
auto wait_event = ctx.SleepClientThread(
|
||||
Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1,
|
||||
[=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
|
||||
ThreadWakeupReason reason) {
|
||||
// Repeat TransactParcel DequeueBuffer when a buffer is available
|
||||
auto buffer_queue = nv_flinger->GetBufferQueue(id);
|
||||
boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
|
||||
IGBPDequeueBufferResponseParcel response{*slot};
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
});
|
||||
buffer_queue->SetBufferWaitEvent(std::move(wait_event));
|
||||
}
|
||||
} else if (transaction == TransactionId::RequestBuffer) {
|
||||
IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
|
||||
|
||||
@@ -628,144 +649,152 @@ private:
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
};
|
||||
|
||||
void IApplicationDisplayService::GetRelayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
|
||||
public:
|
||||
IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
~IApplicationDisplayService() = default;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger);
|
||||
}
|
||||
private:
|
||||
void GetRelayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
void IApplicationDisplayService::GetSystemDisplayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISystemDisplayService>();
|
||||
}
|
||||
void GetSystemDisplayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
void IApplicationDisplayService::GetManagerDisplayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISystemDisplayService>();
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IManagerDisplayService>(nv_flinger);
|
||||
}
|
||||
void GetManagerDisplayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
void IApplicationDisplayService::GetIndirectDisplayTransactionService(
|
||||
Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IManagerDisplayService>(nv_flinger);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger);
|
||||
}
|
||||
void GetIndirectDisplayTransactionService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
void IApplicationDisplayService::OpenDisplay(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
|
||||
auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger);
|
||||
}
|
||||
|
||||
std::string name(name_buf.begin(), end);
|
||||
void OpenDisplay(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
|
||||
auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
|
||||
|
||||
ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
|
||||
std::string name(name_buf.begin(), end);
|
||||
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(nv_flinger->OpenDisplay(name));
|
||||
}
|
||||
ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
|
||||
|
||||
void IApplicationDisplayService::CloseDisplay(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 display_id = rp.Pop<u64>();
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(nv_flinger->OpenDisplay(name));
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
void CloseDisplay(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 display_id = rp.Pop<u64>();
|
||||
|
||||
void IApplicationDisplayService::OpenLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
|
||||
auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
std::string display_name(name_buf.begin(), end);
|
||||
void SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 scaling_mode = rp.Pop<u32>();
|
||||
u64 unknown = rp.Pop<u64>();
|
||||
|
||||
u64 layer_id = rp.Pop<u64>();
|
||||
u64 aruid = rp.Pop<u64>();
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
u64 display_id = nv_flinger->OpenDisplay(display_name);
|
||||
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
|
||||
void ListDisplays(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
DisplayInfo display_info;
|
||||
ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(1);
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
}
|
||||
|
||||
NativeWindow native_window{buffer_queue_id};
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
|
||||
}
|
||||
void OpenLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
|
||||
auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
|
||||
|
||||
void IApplicationDisplayService::CreateStrayLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
std::string display_name(name_buf.begin(), end);
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 flags = rp.Pop<u32>();
|
||||
rp.Pop<u32>(); // padding
|
||||
u64 display_id = rp.Pop<u64>();
|
||||
u64 layer_id = rp.Pop<u64>();
|
||||
u64 aruid = rp.Pop<u64>();
|
||||
|
||||
// TODO(Subv): What's the difference between a Stray and a Managed layer?
|
||||
u64 display_id = nv_flinger->OpenDisplay(display_name);
|
||||
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
|
||||
|
||||
u64 layer_id = nv_flinger->CreateLayer(display_id);
|
||||
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
|
||||
NativeWindow native_window{buffer_queue_id};
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
|
||||
}
|
||||
|
||||
NativeWindow native_window{buffer_queue_id};
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(layer_id);
|
||||
rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
|
||||
}
|
||||
void CreateStrayLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_VI, "called");
|
||||
|
||||
void IApplicationDisplayService::DestroyStrayLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 flags = rp.Pop<u32>();
|
||||
rp.Pop<u32>(); // padding
|
||||
u64 display_id = rp.Pop<u64>();
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 layer_id = rp.Pop<u64>();
|
||||
// TODO(Subv): What's the difference between a Stray and a Managed layer?
|
||||
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
u64 layer_id = nv_flinger->CreateLayer(display_id);
|
||||
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
|
||||
|
||||
void IApplicationDisplayService::SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 scaling_mode = rp.Pop<u32>();
|
||||
u64 unknown = rp.Pop<u64>();
|
||||
NativeWindow native_window{buffer_queue_id};
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(layer_id);
|
||||
rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
void DestroyStrayLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
void IApplicationDisplayService::ListDisplays(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
DisplayInfo display_info;
|
||||
ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(1);
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
}
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 layer_id = rp.Pop<u64>();
|
||||
|
||||
void IApplicationDisplayService::GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 display_id = rp.Pop<u64>();
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
auto vsync_event = nv_flinger->GetVsyncEvent(display_id);
|
||||
void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 display_id = rp.Pop<u64>();
|
||||
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 1, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(vsync_event);
|
||||
}
|
||||
auto vsync_event = nv_flinger->GetVsyncEvent(display_id);
|
||||
|
||||
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 1, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(vsync_event);
|
||||
}
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
};
|
||||
|
||||
IApplicationDisplayService::IApplicationDisplayService(
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
@@ -788,11 +817,24 @@ IApplicationDisplayService::IApplicationDisplayService(
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework(name), module(std::move(module)), nv_flinger(std::move(nv_flinger)) {}
|
||||
|
||||
void Module::Interface::GetDisplayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) {
|
||||
std::make_shared<VI_M>(nv_flinger)->InstallAsService(service_manager);
|
||||
std::make_shared<VI_S>(nv_flinger)->InstallAsService(service_manager);
|
||||
std::make_shared<VI_U>(nv_flinger)->InstallAsService(service_manager);
|
||||
auto module = std::make_shared<Module>();
|
||||
std::make_shared<VI_M>(module, nv_flinger)->InstallAsService(service_manager);
|
||||
std::make_shared<VI_S>(module, nv_flinger)->InstallAsService(service_manager);
|
||||
std::make_shared<VI_U>(module, nv_flinger)->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace VI
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <boost/optional.hpp>
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
@@ -17,26 +14,19 @@ struct EventType;
|
||||
namespace Service {
|
||||
namespace VI {
|
||||
|
||||
class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
|
||||
class Module final {
|
||||
public:
|
||||
IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
~IApplicationDisplayService() = default;
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
Interface(std::shared_ptr<Module> module, const char* name,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
|
||||
private:
|
||||
void GetRelayService(Kernel::HLERequestContext& ctx);
|
||||
void GetSystemDisplayService(Kernel::HLERequestContext& ctx);
|
||||
void GetManagerDisplayService(Kernel::HLERequestContext& ctx);
|
||||
void GetIndirectDisplayTransactionService(Kernel::HLERequestContext& ctx);
|
||||
void OpenDisplay(Kernel::HLERequestContext& ctx);
|
||||
void CloseDisplay(Kernel::HLERequestContext& ctx);
|
||||
void SetLayerScalingMode(Kernel::HLERequestContext& ctx);
|
||||
void ListDisplays(Kernel::HLERequestContext& ctx);
|
||||
void OpenLayer(Kernel::HLERequestContext& ctx);
|
||||
void CreateStrayLayer(Kernel::HLERequestContext& ctx);
|
||||
void DestroyStrayLayer(Kernel::HLERequestContext& ctx);
|
||||
void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx);
|
||||
void GetDisplayService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
};
|
||||
};
|
||||
|
||||
/// Registers all VI services with the specified service manager.
|
||||
|
||||
@@ -2,24 +2,13 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include "core/hle/service/vi/vi_m.h"
|
||||
|
||||
namespace Service {
|
||||
namespace VI {
|
||||
|
||||
void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
|
||||
}
|
||||
|
||||
VI_M::VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework("vi:m"), nv_flinger(std::move(nv_flinger)) {
|
||||
VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: Module::Interface(std::move(module), "vi:m", std::move(nv_flinger)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{2, &VI_M::GetDisplayService, "GetDisplayService"},
|
||||
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
|
||||
|
||||
@@ -4,25 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NVFlinger {
|
||||
class NVFlinger;
|
||||
}
|
||||
|
||||
namespace VI {
|
||||
|
||||
class VI_M final : public ServiceFramework<VI_M> {
|
||||
class VI_M final : public Module::Interface {
|
||||
public:
|
||||
VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
~VI_M() = default;
|
||||
|
||||
private:
|
||||
void GetDisplayService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
explicit VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
};
|
||||
|
||||
} // namespace VI
|
||||
|
||||
@@ -2,24 +2,13 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include "core/hle/service/vi/vi_s.h"
|
||||
|
||||
namespace Service {
|
||||
namespace VI {
|
||||
|
||||
void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
|
||||
}
|
||||
|
||||
VI_S::VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework("vi:s"), nv_flinger(std::move(nv_flinger)) {
|
||||
VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: Module::Interface(std::move(module), "vi:s", std::move(nv_flinger)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, &VI_S::GetDisplayService, "GetDisplayService"},
|
||||
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
|
||||
|
||||
@@ -4,25 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NVFlinger {
|
||||
class NVFlinger;
|
||||
}
|
||||
|
||||
namespace VI {
|
||||
|
||||
class VI_S final : public ServiceFramework<VI_S> {
|
||||
class VI_S final : public Module::Interface {
|
||||
public:
|
||||
VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
~VI_S() = default;
|
||||
|
||||
private:
|
||||
void GetDisplayService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
explicit VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
};
|
||||
|
||||
} // namespace VI
|
||||
|
||||
@@ -2,24 +2,13 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include "core/hle/service/vi/vi_u.h"
|
||||
|
||||
namespace Service {
|
||||
namespace VI {
|
||||
|
||||
void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
|
||||
}
|
||||
|
||||
VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework("vi:u"), nv_flinger(std::move(nv_flinger)) {
|
||||
VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: Module::Interface(std::move(module), "vi:u", std::move(nv_flinger)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &VI_U::GetDisplayService, "GetDisplayService"},
|
||||
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
|
||||
|
||||
@@ -4,25 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NVFlinger {
|
||||
class NVFlinger;
|
||||
}
|
||||
|
||||
namespace VI {
|
||||
|
||||
class VI_U final : public ServiceFramework<VI_U> {
|
||||
class VI_U final : public Module::Interface {
|
||||
public:
|
||||
VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
~VI_U() = default;
|
||||
|
||||
private:
|
||||
void GetDisplayService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
explicit VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
};
|
||||
|
||||
} // namespace VI
|
||||
|
||||
@@ -84,7 +84,7 @@ void Linker::WriteRelocations(std::vector<u8>& program_image, const std::vector<
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_CRITICAL(Loader, "Unknown relocation type: %d", rela.type);
|
||||
LOG_CRITICAL(Loader, "Unknown relocation type: %d", static_cast<int>(rela.type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/memory.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/memory_setup.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
@@ -23,7 +24,6 @@
|
||||
namespace Memory {
|
||||
|
||||
static std::array<u8, Memory::VRAM_SIZE> vram;
|
||||
static std::array<u8, Memory::N3DS_EXTRA_RAM_SIZE> n3ds_extra_ram;
|
||||
|
||||
static PageTable* current_page_table = nullptr;
|
||||
|
||||
@@ -42,6 +42,9 @@ static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, Pa
|
||||
LOG_DEBUG(HW_Memory, "Mapping %p onto %016" PRIX64 "-%016" PRIX64, memory, base * PAGE_SIZE,
|
||||
(base + size) * PAGE_SIZE);
|
||||
|
||||
RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE,
|
||||
FlushMode::FlushAndInvalidate);
|
||||
|
||||
VAddr end = base + size;
|
||||
while (base != end) {
|
||||
ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %016" PRIX64, base);
|
||||
@@ -113,91 +116,120 @@ static std::set<MemoryHookPointer> GetSpecialHandlers(VAddr vaddr, u64 size) {
|
||||
return GetSpecialHandlers(page_table, vaddr, size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
boost::optional<T> ReadSpecial(VAddr addr);
|
||||
/**
|
||||
* Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
|
||||
* using a VMA from the current process
|
||||
*/
|
||||
static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) {
|
||||
u8* direct_pointer = nullptr;
|
||||
|
||||
auto& vm_manager = process.vm_manager;
|
||||
|
||||
auto it = vm_manager.FindVMA(vaddr);
|
||||
ASSERT(it != vm_manager.vma_map.end());
|
||||
|
||||
auto& vma = it->second;
|
||||
switch (vma.type) {
|
||||
case Kernel::VMAType::AllocatedMemoryBlock:
|
||||
direct_pointer = vma.backing_block->data() + vma.offset;
|
||||
break;
|
||||
case Kernel::VMAType::BackingMemory:
|
||||
direct_pointer = vma.backing_memory;
|
||||
break;
|
||||
case Kernel::VMAType::Free:
|
||||
return nullptr;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return direct_pointer + (vaddr - vma.base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
|
||||
* using a VMA from the current process.
|
||||
*/
|
||||
static u8* GetPointerFromVMA(VAddr vaddr) {
|
||||
return GetPointerFromVMA(*Core::CurrentProcess(), vaddr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T Read(const VAddr vaddr) {
|
||||
if ((vaddr >> PAGE_BITS) >= PAGE_TABLE_NUM_ENTRIES) {
|
||||
LOG_ERROR(HW_Memory, "Read%lu after page table @ 0x%016" PRIX64, sizeof(T) * 8, vaddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
|
||||
switch (type) {
|
||||
case PageType::Unmapped:
|
||||
LOG_ERROR(HW_Memory, "unmapped Read%zu @ 0x%016" PRIX64, sizeof(T) * 8, vaddr);
|
||||
return 0;
|
||||
case PageType::Special: {
|
||||
if (auto result = ReadSpecial<T>(vaddr))
|
||||
return *result;
|
||||
[[fallthrough]];
|
||||
}
|
||||
case PageType::Memory: {
|
||||
const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
|
||||
ASSERT_MSG(page_pointer, "Mapped memory page without a pointer @ %016" PRIX64, vaddr);
|
||||
|
||||
const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
|
||||
if (page_pointer) {
|
||||
// NOTE: Avoid adding any extra logic to this fast-path block
|
||||
T value;
|
||||
std::memcpy(&value, &page_pointer[vaddr & PAGE_MASK], sizeof(T));
|
||||
return value;
|
||||
}
|
||||
|
||||
// The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state
|
||||
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
|
||||
|
||||
PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
|
||||
switch (type) {
|
||||
case PageType::Unmapped:
|
||||
LOG_ERROR(HW_Memory, "unmapped Read%lu @ 0x%08X", sizeof(T) * 8, vaddr);
|
||||
return 0;
|
||||
case PageType::Memory:
|
||||
ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr);
|
||||
break;
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
RasterizerFlushVirtualRegion(vaddr, sizeof(T), FlushMode::Flush);
|
||||
|
||||
T value;
|
||||
std::memcpy(&value, GetPointerFromVMA(vaddr), sizeof(T));
|
||||
return value;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool WriteSpecial(VAddr addr, const T data);
|
||||
|
||||
template <typename T>
|
||||
void Write(const VAddr vaddr, const T data) {
|
||||
if ((vaddr >> PAGE_BITS) >= PAGE_TABLE_NUM_ENTRIES) {
|
||||
LOG_ERROR(HW_Memory, "Write%lu after page table 0x%08X @ 0x%016" PRIX64, sizeof(data) * 8,
|
||||
(u32)data, vaddr);
|
||||
return;
|
||||
}
|
||||
|
||||
const PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
|
||||
switch (type) {
|
||||
case PageType::Unmapped:
|
||||
LOG_ERROR(HW_Memory, "unmapped Write%zu 0x%08X @ 0x%016" PRIX64, sizeof(data) * 8,
|
||||
static_cast<u32>(data), vaddr);
|
||||
return;
|
||||
case PageType::Special: {
|
||||
if (WriteSpecial<T>(vaddr, data))
|
||||
return;
|
||||
[[fallthrough]];
|
||||
}
|
||||
case PageType::Memory: {
|
||||
u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
|
||||
ASSERT_MSG(page_pointer, "Mapped memory page without a pointer @ %016" PRIX64, vaddr);
|
||||
u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
|
||||
if (page_pointer) {
|
||||
// NOTE: Avoid adding any extra logic to this fast-path block
|
||||
std::memcpy(&page_pointer[vaddr & PAGE_MASK], &data, sizeof(T));
|
||||
return;
|
||||
}
|
||||
|
||||
// The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state
|
||||
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
|
||||
|
||||
PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
|
||||
switch (type) {
|
||||
case PageType::Unmapped:
|
||||
LOG_ERROR(HW_Memory, "unmapped Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data,
|
||||
vaddr);
|
||||
return;
|
||||
case PageType::Memory:
|
||||
ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr);
|
||||
break;
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
RasterizerFlushVirtualRegion(vaddr, sizeof(T), FlushMode::Invalidate);
|
||||
std::memcpy(GetPointerFromVMA(vaddr), &data, sizeof(T));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) {
|
||||
auto& page_table = process.vm_manager.page_table;
|
||||
|
||||
if ((vaddr >> PAGE_BITS) >= PAGE_TABLE_NUM_ENTRIES)
|
||||
const u8* page_pointer = page_table.pointers[vaddr >> PAGE_BITS];
|
||||
if (page_pointer)
|
||||
return true;
|
||||
|
||||
if (page_table.attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory)
|
||||
return true;
|
||||
|
||||
if (page_table.attributes[vaddr >> PAGE_BITS] != PageType::Special)
|
||||
return false;
|
||||
|
||||
const PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
|
||||
switch (type) {
|
||||
case PageType::Unmapped:
|
||||
return false;
|
||||
case PageType::Memory:
|
||||
return true;
|
||||
case PageType::Special: {
|
||||
for (auto handler : GetSpecialHandlers(page_table, vaddr, 1))
|
||||
if (auto result = handler->IsValidAddress(vaddr))
|
||||
return *result;
|
||||
return current_page_table->pointers[vaddr >> PAGE_BITS] != nullptr;
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -215,7 +247,11 @@ u8* GetPointer(const VAddr vaddr) {
|
||||
return page_pointer + (vaddr & PAGE_MASK);
|
||||
}
|
||||
|
||||
LOG_ERROR(HW_Memory, "unknown GetPointer @ 0x%016" PRIx64, vaddr);
|
||||
if (current_page_table->attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory) {
|
||||
return GetPointerFromVMA(vaddr);
|
||||
}
|
||||
|
||||
LOG_ERROR(HW_Memory, "unknown GetPointer @ 0x%08x", vaddr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -244,7 +280,6 @@ u8* GetPhysicalPointer(PAddr address) {
|
||||
{IO_AREA_PADDR, IO_AREA_SIZE},
|
||||
{DSP_RAM_PADDR, DSP_RAM_SIZE},
|
||||
{FCRAM_PADDR, FCRAM_N3DS_SIZE},
|
||||
{N3DS_EXTRA_RAM_PADDR, N3DS_EXTRA_RAM_SIZE},
|
||||
};
|
||||
|
||||
const auto area =
|
||||
@@ -283,9 +318,6 @@ u8* GetPhysicalPointer(PAddr address) {
|
||||
}
|
||||
ASSERT_MSG(target_pointer != nullptr, "Invalid FCRAM address");
|
||||
break;
|
||||
case N3DS_EXTRA_RAM_PADDR:
|
||||
target_pointer = n3ds_extra_ram.data() + offset_into_region;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -293,6 +325,95 @@ u8* GetPhysicalPointer(PAddr address) {
|
||||
return target_pointer;
|
||||
}
|
||||
|
||||
void RasterizerMarkRegionCached(VAddr start, u64 size, bool cached) {
|
||||
if (start == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
u64 num_pages = ((start + size - 1) >> PAGE_BITS) - (start >> PAGE_BITS) + 1;
|
||||
VAddr vaddr = start;
|
||||
|
||||
for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
|
||||
PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
|
||||
|
||||
if (cached) {
|
||||
// Switch page type to cached if now cached
|
||||
switch (page_type) {
|
||||
case PageType::Unmapped:
|
||||
// It is not necessary for a process to have this region mapped into its address
|
||||
// space, for example, a system module need not have a VRAM mapping.
|
||||
break;
|
||||
case PageType::Memory:
|
||||
page_type = PageType::RasterizerCachedMemory;
|
||||
current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
} else {
|
||||
// Switch page type to uncached if now uncached
|
||||
switch (page_type) {
|
||||
case PageType::Unmapped:
|
||||
// It is not necessary for a process to have this region mapped into its address
|
||||
// space, for example, a system module need not have a VRAM mapping.
|
||||
break;
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK);
|
||||
if (pointer == nullptr) {
|
||||
// It's possible that this function has been called while updating the pagetable
|
||||
// after unmapping a VMA. In that case the underlying VMA will no longer exist,
|
||||
// and we should just leave the pagetable entry blank.
|
||||
page_type = PageType::Unmapped;
|
||||
} else {
|
||||
page_type = PageType::Memory;
|
||||
current_page_table->pointers[vaddr >> PAGE_BITS] = pointer;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
|
||||
// Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
|
||||
// null here
|
||||
if (VideoCore::g_renderer == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
VAddr end = start + size;
|
||||
|
||||
auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
|
||||
if (start >= region_end || end <= region_start) {
|
||||
// No overlap with region
|
||||
return;
|
||||
}
|
||||
|
||||
VAddr overlap_start = std::max(start, region_start);
|
||||
VAddr overlap_end = std::min(end, region_end);
|
||||
u64 overlap_size = overlap_end - overlap_start;
|
||||
|
||||
auto* rasterizer = VideoCore::g_renderer->Rasterizer();
|
||||
switch (mode) {
|
||||
case FlushMode::Flush:
|
||||
rasterizer->FlushRegion(overlap_start, overlap_size);
|
||||
break;
|
||||
case FlushMode::Invalidate:
|
||||
rasterizer->InvalidateRegion(overlap_start, overlap_size);
|
||||
break;
|
||||
case FlushMode::FlushAndInvalidate:
|
||||
rasterizer->FlushAndInvalidateRegion(overlap_start, overlap_size);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
CheckRegion(PROCESS_IMAGE_VADDR, PROCESS_IMAGE_VADDR_END);
|
||||
CheckRegion(HEAP_VADDR, HEAP_VADDR_END);
|
||||
}
|
||||
|
||||
u8 Read8(const VAddr addr) {
|
||||
return Read<u8>(addr);
|
||||
}
|
||||
@@ -309,17 +430,6 @@ u64 Read64(const VAddr addr) {
|
||||
return Read<u64_le>(addr);
|
||||
}
|
||||
|
||||
static bool ReadSpecialBlock(const Kernel::Process& process, const VAddr src_addr,
|
||||
void* dest_buffer, const size_t size) {
|
||||
auto& page_table = process.vm_manager.page_table;
|
||||
for (const auto& handler : GetSpecialHandlers(page_table, src_addr, size)) {
|
||||
if (handler->ReadBlock(src_addr, dest_buffer, size)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
|
||||
const size_t size) {
|
||||
auto& page_table = process.vm_manager.page_table;
|
||||
@@ -329,21 +439,16 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_
|
||||
size_t page_offset = src_addr & PAGE_MASK;
|
||||
|
||||
while (remaining_size > 0) {
|
||||
const size_t copy_amount = std::min<size_t>(PAGE_SIZE - page_offset, remaining_size);
|
||||
const size_t copy_amount =
|
||||
std::min(static_cast<size_t>(PAGE_SIZE) - page_offset, remaining_size);
|
||||
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
||||
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case PageType::Unmapped:
|
||||
LOG_ERROR(HW_Memory,
|
||||
"unmapped ReadBlock @ 0x%016" PRIX64 " (start address = 0x%" PRIx64
|
||||
", size = %zu)",
|
||||
case PageType::Unmapped: {
|
||||
LOG_ERROR(HW_Memory, "unmapped ReadBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
|
||||
current_vaddr, src_addr, size);
|
||||
std::memset(dest_buffer, 0, copy_amount);
|
||||
break;
|
||||
case PageType::Special: {
|
||||
if (ReadSpecialBlock(process, current_vaddr, dest_buffer, copy_amount))
|
||||
break;
|
||||
[[fallthrough]];
|
||||
}
|
||||
case PageType::Memory: {
|
||||
DEBUG_ASSERT(page_table.pointers[page_index]);
|
||||
@@ -352,6 +457,12 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_
|
||||
std::memcpy(dest_buffer, src_ptr, copy_amount);
|
||||
break;
|
||||
}
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||
FlushMode::Flush);
|
||||
std::memcpy(dest_buffer, GetPointerFromVMA(process, current_vaddr), copy_amount);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -383,17 +494,6 @@ void Write64(const VAddr addr, const u64 data) {
|
||||
Write<u64_le>(addr, data);
|
||||
}
|
||||
|
||||
static bool WriteSpecialBlock(const Kernel::Process& process, const VAddr dest_addr,
|
||||
const void* src_buffer, const size_t size) {
|
||||
auto& page_table = process.vm_manager.page_table;
|
||||
for (const auto& handler : GetSpecialHandlers(page_table, dest_addr, size)) {
|
||||
if (handler->WriteBlock(dest_addr, src_buffer, size)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,
|
||||
const size_t size) {
|
||||
auto& page_table = process.vm_manager.page_table;
|
||||
@@ -402,20 +502,17 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi
|
||||
size_t page_offset = dest_addr & PAGE_MASK;
|
||||
|
||||
while (remaining_size > 0) {
|
||||
const size_t copy_amount = std::min<size_t>(PAGE_SIZE - page_offset, remaining_size);
|
||||
const size_t copy_amount =
|
||||
std::min(static_cast<size_t>(PAGE_SIZE) - page_offset, remaining_size);
|
||||
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
||||
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case PageType::Unmapped:
|
||||
case PageType::Unmapped: {
|
||||
LOG_ERROR(HW_Memory,
|
||||
"unmapped WriteBlock @ 0x%016" PRIX64 " (start address = 0x%016" PRIX64
|
||||
", size = %zu)",
|
||||
"unmapped WriteBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
|
||||
current_vaddr, dest_addr, size);
|
||||
break;
|
||||
case PageType::Special:
|
||||
if (WriteSpecialBlock(process, current_vaddr, src_buffer, copy_amount))
|
||||
break;
|
||||
[[fallthrough]];
|
||||
}
|
||||
case PageType::Memory: {
|
||||
DEBUG_ASSERT(page_table.pointers[page_index]);
|
||||
|
||||
@@ -423,6 +520,12 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi
|
||||
std::memcpy(dest_ptr, src_buffer, copy_amount);
|
||||
break;
|
||||
}
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||
FlushMode::Invalidate);
|
||||
std::memcpy(GetPointerFromVMA(process, current_vaddr), src_buffer, copy_amount);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -438,9 +541,8 @@ void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size
|
||||
WriteBlock(*Core::CurrentProcess(), dest_addr, src_buffer, size);
|
||||
}
|
||||
|
||||
void ZeroBlock(const VAddr dest_addr, const size_t size) {
|
||||
const auto& process = *Core::CurrentProcess();
|
||||
|
||||
void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const size_t size) {
|
||||
auto& page_table = process.vm_manager.page_table;
|
||||
size_t remaining_size = size;
|
||||
size_t page_index = dest_addr >> PAGE_BITS;
|
||||
size_t page_offset = dest_addr & PAGE_MASK;
|
||||
@@ -448,27 +550,29 @@ void ZeroBlock(const VAddr dest_addr, const size_t size) {
|
||||
static const std::array<u8, PAGE_SIZE> zeros = {};
|
||||
|
||||
while (remaining_size > 0) {
|
||||
const size_t copy_amount = std::min<size_t>(PAGE_SIZE - page_offset, remaining_size);
|
||||
const size_t copy_amount =
|
||||
std::min(static_cast<size_t>(PAGE_SIZE) - page_offset, remaining_size);
|
||||
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
||||
|
||||
switch (current_page_table->attributes[page_index]) {
|
||||
case PageType::Unmapped:
|
||||
LOG_ERROR(HW_Memory,
|
||||
"unmapped ZeroBlock @ 0x%016" PRIX64 " (start address = 0x%016" PRIX64
|
||||
", size = %zu)",
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case PageType::Unmapped: {
|
||||
LOG_ERROR(HW_Memory, "unmapped ZeroBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
|
||||
current_vaddr, dest_addr, size);
|
||||
break;
|
||||
case PageType::Special:
|
||||
if (WriteSpecialBlock(process, current_vaddr, zeros.data(), copy_amount))
|
||||
break;
|
||||
[[fallthrough]];
|
||||
}
|
||||
case PageType::Memory: {
|
||||
DEBUG_ASSERT(current_page_table->pointers[page_index]);
|
||||
DEBUG_ASSERT(page_table.pointers[page_index]);
|
||||
|
||||
u8* dest_ptr = current_page_table->pointers[page_index] + page_offset;
|
||||
u8* dest_ptr = page_table.pointers[page_index] + page_offset;
|
||||
std::memset(dest_ptr, 0, copy_amount);
|
||||
break;
|
||||
}
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||
FlushMode::Invalidate);
|
||||
std::memset(GetPointerFromVMA(process, current_vaddr), 0, copy_amount);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -479,37 +583,34 @@ void ZeroBlock(const VAddr dest_addr, const size_t size) {
|
||||
}
|
||||
}
|
||||
|
||||
void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) {
|
||||
const auto& process = *Core::CurrentProcess();
|
||||
|
||||
void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, const size_t size) {
|
||||
auto& page_table = process.vm_manager.page_table;
|
||||
size_t remaining_size = size;
|
||||
size_t page_index = src_addr >> PAGE_BITS;
|
||||
size_t page_offset = src_addr & PAGE_MASK;
|
||||
|
||||
while (remaining_size > 0) {
|
||||
const size_t copy_amount = std::min<size_t>(PAGE_SIZE - page_offset, remaining_size);
|
||||
const size_t copy_amount =
|
||||
std::min(static_cast<size_t>(PAGE_SIZE) - page_offset, remaining_size);
|
||||
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
||||
|
||||
switch (current_page_table->attributes[page_index]) {
|
||||
case PageType::Unmapped:
|
||||
LOG_ERROR(HW_Memory,
|
||||
"unmapped CopyBlock @ 0x%016" PRIX64 " (start address = 0x%016" PRIX64
|
||||
", size = %zu)",
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case PageType::Unmapped: {
|
||||
LOG_ERROR(HW_Memory, "unmapped CopyBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
|
||||
current_vaddr, src_addr, size);
|
||||
ZeroBlock(dest_addr, copy_amount);
|
||||
ZeroBlock(process, dest_addr, copy_amount);
|
||||
break;
|
||||
case PageType::Special: {
|
||||
std::vector<u8> buffer(copy_amount);
|
||||
if (ReadSpecialBlock(process, current_vaddr, buffer.data(), buffer.size())) {
|
||||
WriteBlock(dest_addr, buffer.data(), buffer.size());
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
}
|
||||
case PageType::Memory: {
|
||||
DEBUG_ASSERT(current_page_table->pointers[page_index]);
|
||||
const u8* src_ptr = current_page_table->pointers[page_index] + page_offset;
|
||||
WriteBlock(dest_addr, src_ptr, copy_amount);
|
||||
DEBUG_ASSERT(page_table.pointers[page_index]);
|
||||
const u8* src_ptr = page_table.pointers[page_index] + page_offset;
|
||||
WriteBlock(process, dest_addr, src_ptr, copy_amount);
|
||||
break;
|
||||
}
|
||||
case PageType::RasterizerCachedMemory: {
|
||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||
FlushMode::Flush);
|
||||
WriteBlock(process, dest_addr, GetPointerFromVMA(process, current_vaddr), copy_amount);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -524,78 +625,6 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) {
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
boost::optional<u8> ReadSpecial<u8>(VAddr addr) {
|
||||
const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
|
||||
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u8)))
|
||||
if (auto result = handler->Read8(addr))
|
||||
return *result;
|
||||
return {};
|
||||
}
|
||||
|
||||
template <>
|
||||
boost::optional<u16> ReadSpecial<u16>(VAddr addr) {
|
||||
const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
|
||||
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u16)))
|
||||
if (auto result = handler->Read16(addr))
|
||||
return *result;
|
||||
return {};
|
||||
}
|
||||
|
||||
template <>
|
||||
boost::optional<u32> ReadSpecial<u32>(VAddr addr) {
|
||||
const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
|
||||
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u32)))
|
||||
if (auto result = handler->Read32(addr))
|
||||
return *result;
|
||||
return {};
|
||||
}
|
||||
|
||||
template <>
|
||||
boost::optional<u64> ReadSpecial<u64>(VAddr addr) {
|
||||
const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
|
||||
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u64)))
|
||||
if (auto result = handler->Read64(addr))
|
||||
return *result;
|
||||
return {};
|
||||
}
|
||||
|
||||
template <>
|
||||
bool WriteSpecial<u8>(VAddr addr, const u8 data) {
|
||||
const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
|
||||
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u8)))
|
||||
if (handler->Write8(addr, data))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool WriteSpecial<u16>(VAddr addr, const u16 data) {
|
||||
const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
|
||||
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u16)))
|
||||
if (handler->Write16(addr, data))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool WriteSpecial<u32>(VAddr addr, const u32 data) {
|
||||
const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
|
||||
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u32)))
|
||||
if (handler->Write32(addr, data))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool WriteSpecial<u64>(VAddr addr, const u64 data) {
|
||||
const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
|
||||
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u64)))
|
||||
if (handler->Write64(addr, data))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
boost::optional<PAddr> TryVirtualToPhysicalAddress(const VAddr addr) {
|
||||
if (addr == 0) {
|
||||
return 0;
|
||||
@@ -609,8 +638,6 @@ boost::optional<PAddr> TryVirtualToPhysicalAddress(const VAddr addr) {
|
||||
return addr - DSP_RAM_VADDR + DSP_RAM_PADDR;
|
||||
} else if (addr >= IO_AREA_VADDR && addr < IO_AREA_VADDR_END) {
|
||||
return addr - IO_AREA_VADDR + IO_AREA_PADDR;
|
||||
} else if (addr >= N3DS_EXTRA_RAM_VADDR && addr < N3DS_EXTRA_RAM_VADDR_END) {
|
||||
return addr - N3DS_EXTRA_RAM_VADDR + N3DS_EXTRA_RAM_PADDR;
|
||||
}
|
||||
|
||||
return boost::none;
|
||||
@@ -637,8 +664,6 @@ boost::optional<VAddr> PhysicalToVirtualAddress(const PAddr addr) {
|
||||
return addr - DSP_RAM_PADDR + DSP_RAM_VADDR;
|
||||
} else if (addr >= IO_AREA_PADDR && addr < IO_AREA_PADDR_END) {
|
||||
return addr - IO_AREA_PADDR + IO_AREA_VADDR;
|
||||
} else if (addr >= N3DS_EXTRA_RAM_PADDR && addr < N3DS_EXTRA_RAM_PADDR_END) {
|
||||
return addr - N3DS_EXTRA_RAM_PADDR + N3DS_EXTRA_RAM_VADDR;
|
||||
}
|
||||
|
||||
return boost::none;
|
||||
|
||||
@@ -36,7 +36,10 @@ enum class PageType : u8 {
|
||||
Unmapped,
|
||||
/// Page is mapped to regular memory. This is the only type you can get pointers to.
|
||||
Memory,
|
||||
/// Page is mapped to a memory hook, which intercepts read and write requests.
|
||||
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
||||
/// invalidation
|
||||
RasterizerCachedMemory,
|
||||
/// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
|
||||
Special,
|
||||
};
|
||||
|
||||
@@ -98,12 +101,6 @@ enum : PAddr {
|
||||
VRAM_SIZE = 0x00600000, ///< VRAM size (6MB)
|
||||
VRAM_PADDR_END = VRAM_PADDR + VRAM_SIZE,
|
||||
|
||||
/// New 3DS additional memory. Supposedly faster than regular FCRAM. Part of it can be used by
|
||||
/// applications and system modules if mapped via the ExHeader.
|
||||
N3DS_EXTRA_RAM_PADDR = 0x1F000000,
|
||||
N3DS_EXTRA_RAM_SIZE = 0x00400000, ///< New 3DS additional memory size (4MB)
|
||||
N3DS_EXTRA_RAM_PADDR_END = N3DS_EXTRA_RAM_PADDR + N3DS_EXTRA_RAM_SIZE,
|
||||
|
||||
/// DSP memory
|
||||
DSP_RAM_PADDR = 0x1FF00000,
|
||||
DSP_RAM_SIZE = 0x00080000, ///< DSP memory size (512KB)
|
||||
@@ -119,7 +116,6 @@ enum : PAddr {
|
||||
FCRAM_SIZE = 0x08000000, ///< FCRAM size on the Old 3DS (128MB)
|
||||
FCRAM_N3DS_SIZE = 0x10000000, ///< FCRAM size on the New 3DS (256MB)
|
||||
FCRAM_PADDR_END = FCRAM_PADDR + FCRAM_SIZE,
|
||||
FCRAM_N3DS_PADDR_END = FCRAM_PADDR + FCRAM_N3DS_SIZE,
|
||||
};
|
||||
|
||||
/// Virtual user-space memory regions
|
||||
@@ -135,10 +131,6 @@ enum : VAddr {
|
||||
LINEAR_HEAP_SIZE = 0x08000000,
|
||||
LINEAR_HEAP_VADDR_END = LINEAR_HEAP_VADDR + LINEAR_HEAP_SIZE,
|
||||
|
||||
/// Maps 1:1 to New 3DS additional memory
|
||||
N3DS_EXTRA_RAM_VADDR = 0x1E800000,
|
||||
N3DS_EXTRA_RAM_VADDR_END = N3DS_EXTRA_RAM_VADDR + N3DS_EXTRA_RAM_SIZE,
|
||||
|
||||
/// Maps 1:1 to the IO register area.
|
||||
IO_AREA_VADDR = 0x1EC00000,
|
||||
IO_AREA_VADDR_END = IO_AREA_VADDR + IO_AREA_SIZE,
|
||||
@@ -253,4 +245,24 @@ boost::optional<VAddr> PhysicalToVirtualAddress(PAddr addr);
|
||||
*/
|
||||
u8* GetPhysicalPointer(PAddr address);
|
||||
|
||||
enum class FlushMode {
|
||||
/// Write back modified surfaces to RAM
|
||||
Flush,
|
||||
/// Remove region from the cache
|
||||
Invalidate,
|
||||
/// Write back modified surfaces to RAM, and also remove them from the cache
|
||||
FlushAndInvalidate,
|
||||
};
|
||||
|
||||
/**
|
||||
* Mark each page touching the region as cached.
|
||||
*/
|
||||
void RasterizerMarkRegionCached(VAddr start, u64 size, bool cached);
|
||||
|
||||
/**
|
||||
* Flushes and invalidates any externally cached rasterizer resources touching the given virtual
|
||||
* address region.
|
||||
*/
|
||||
void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode);
|
||||
|
||||
} // namespace Memory
|
||||
|
||||
@@ -1,24 +1,42 @@
|
||||
add_library(video_core STATIC
|
||||
command_processor.cpp
|
||||
command_processor.h
|
||||
debug_utils/debug_utils.cpp
|
||||
debug_utils/debug_utils.h
|
||||
engines/fermi_2d.cpp
|
||||
engines/fermi_2d.h
|
||||
engines/maxwell_3d.cpp
|
||||
engines/maxwell_3d.h
|
||||
engines/maxwell_compute.cpp
|
||||
engines/maxwell_compute.h
|
||||
gpu.cpp
|
||||
gpu.h
|
||||
memory_manager.cpp
|
||||
memory_manager.h
|
||||
rasterizer_interface.h
|
||||
renderer_base.cpp
|
||||
renderer_base.h
|
||||
renderer_opengl/gl_rasterizer.cpp
|
||||
renderer_opengl/gl_rasterizer.h
|
||||
renderer_opengl/gl_rasterizer_cache.cpp
|
||||
renderer_opengl/gl_rasterizer_cache.h
|
||||
renderer_opengl/gl_resource_manager.h
|
||||
renderer_opengl/gl_shader_decompiler.cpp
|
||||
renderer_opengl/gl_shader_decompiler.h
|
||||
renderer_opengl/gl_shader_gen.cpp
|
||||
renderer_opengl/gl_shader_gen.h
|
||||
renderer_opengl/gl_shader_util.cpp
|
||||
renderer_opengl/gl_shader_util.h
|
||||
renderer_opengl/gl_state.cpp
|
||||
renderer_opengl/gl_state.h
|
||||
renderer_opengl/gl_stream_buffer.cpp
|
||||
renderer_opengl/gl_stream_buffer.h
|
||||
renderer_opengl/maxwell_to_gl.h
|
||||
renderer_opengl/renderer_opengl.cpp
|
||||
renderer_opengl/renderer_opengl.h
|
||||
textures/decoders.cpp
|
||||
textures/decoders.h
|
||||
textures/texture.h
|
||||
utils.h
|
||||
video_core.cpp
|
||||
video_core.h
|
||||
|
||||
@@ -24,12 +24,37 @@ namespace Tegra {
|
||||
|
||||
enum class BufferMethods {
|
||||
BindObject = 0,
|
||||
SetGraphMacroCode = 0x45,
|
||||
SetGraphMacroCodeArg = 0x46,
|
||||
SetGraphMacroEntry = 0x47,
|
||||
CountBufferMethods = 0x100,
|
||||
};
|
||||
|
||||
void GPU::WriteReg(u32 method, u32 subchannel, u32 value) {
|
||||
LOG_WARNING(HW_GPU, "Processing method %08X on subchannel %u value %08X", method, subchannel,
|
||||
value);
|
||||
void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params) {
|
||||
LOG_WARNING(HW_GPU, "Processing method %08X on subchannel %u value %08X remaining params %u",
|
||||
method, subchannel, value, remaining_params);
|
||||
|
||||
if (method == static_cast<u32>(BufferMethods::SetGraphMacroEntry)) {
|
||||
// Prepare to upload a new macro, reset the upload counter.
|
||||
LOG_DEBUG(HW_GPU, "Uploading GPU macro %08X", value);
|
||||
current_macro_entry = value;
|
||||
current_macro_code.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == static_cast<u32>(BufferMethods::SetGraphMacroCodeArg)) {
|
||||
// Append a new code word to the current macro.
|
||||
current_macro_code.push_back(value);
|
||||
|
||||
// There are no more params remaining, submit the code to the 3D engine.
|
||||
if (remaining_params == 0) {
|
||||
maxwell_3d->SubmitMacroCode(current_macro_entry, std::move(current_macro_code));
|
||||
current_macro_entry = InvalidGraphMacroEntry;
|
||||
current_macro_code.clear();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (method == static_cast<u32>(BufferMethods::BindObject)) {
|
||||
// Bind the current subchannel to the desired engine id.
|
||||
@@ -54,7 +79,7 @@ void GPU::WriteReg(u32 method, u32 subchannel, u32 value) {
|
||||
fermi_2d->WriteReg(method, value);
|
||||
break;
|
||||
case EngineID::MAXWELL_B:
|
||||
maxwell_3d->WriteReg(method, value);
|
||||
maxwell_3d->WriteReg(method, value, remaining_params);
|
||||
break;
|
||||
case EngineID::MAXWELL_COMPUTE_B:
|
||||
maxwell_compute->WriteReg(method, value);
|
||||
@@ -64,35 +89,6 @@ void GPU::WriteReg(u32 method, u32 subchannel, u32 value) {
|
||||
}
|
||||
}
|
||||
|
||||
void GPU::CallMethod(u32 method, u32 subchannel, const std::vector<u32>& parameters) {
|
||||
LOG_WARNING(HW_GPU, "Processing method %08X on subchannel %u num params %zu", method,
|
||||
subchannel, parameters.size());
|
||||
|
||||
if (method < static_cast<u32>(BufferMethods::CountBufferMethods)) {
|
||||
// TODO(Subv): Research and implement these methods.
|
||||
LOG_ERROR(HW_GPU, "Special buffer methods other than Bind are not implemented");
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(bound_engines.find(subchannel) != bound_engines.end());
|
||||
|
||||
const EngineID engine = bound_engines[subchannel];
|
||||
|
||||
switch (engine) {
|
||||
case EngineID::FERMI_TWOD_A:
|
||||
fermi_2d->CallMethod(method, parameters);
|
||||
break;
|
||||
case EngineID::MAXWELL_B:
|
||||
maxwell_3d->CallMethod(method, parameters);
|
||||
break;
|
||||
case EngineID::MAXWELL_COMPUTE_B:
|
||||
maxwell_compute->CallMethod(method, parameters);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
}
|
||||
|
||||
void GPU::ProcessCommandList(GPUVAddr address, u32 size) {
|
||||
// TODO(Subv): PhysicalToVirtualAddress is a misnomer, it converts a GPU VAddr into an
|
||||
// application VAddr.
|
||||
@@ -107,7 +103,8 @@ void GPU::ProcessCommandList(GPUVAddr address, u32 size) {
|
||||
case SubmissionMode::Increasing: {
|
||||
// Increase the method value with each argument.
|
||||
for (unsigned i = 0; i < header.arg_count; ++i) {
|
||||
WriteReg(header.method + i, header.subchannel, Memory::Read32(current_addr));
|
||||
WriteReg(header.method + i, header.subchannel, Memory::Read32(current_addr),
|
||||
header.arg_count - i - 1);
|
||||
current_addr += sizeof(u32);
|
||||
}
|
||||
break;
|
||||
@@ -116,31 +113,31 @@ void GPU::ProcessCommandList(GPUVAddr address, u32 size) {
|
||||
case SubmissionMode::NonIncreasing: {
|
||||
// Use the same method value for all arguments.
|
||||
for (unsigned i = 0; i < header.arg_count; ++i) {
|
||||
WriteReg(header.method, header.subchannel, Memory::Read32(current_addr));
|
||||
WriteReg(header.method, header.subchannel, Memory::Read32(current_addr),
|
||||
header.arg_count - i - 1);
|
||||
current_addr += sizeof(u32);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SubmissionMode::IncreaseOnce: {
|
||||
ASSERT(header.arg_count.Value() >= 1);
|
||||
|
||||
// Use the original method for the first argument and then the next method for all other
|
||||
// arguments.
|
||||
WriteReg(header.method, header.subchannel, Memory::Read32(current_addr),
|
||||
header.arg_count - 1);
|
||||
current_addr += sizeof(u32);
|
||||
|
||||
// Process this command as a method call instead of a register write. Gather
|
||||
// all the parameters first and then pass them at once to the CallMethod function.
|
||||
std::vector<u32> parameters(header.arg_count);
|
||||
|
||||
for (unsigned i = 0; i < header.arg_count; ++i) {
|
||||
parameters[i] = Memory::Read32(current_addr);
|
||||
for (unsigned i = 1; i < header.arg_count; ++i) {
|
||||
WriteReg(header.method + 1, header.subchannel, Memory::Read32(current_addr),
|
||||
header.arg_count - i - 1);
|
||||
current_addr += sizeof(u32);
|
||||
}
|
||||
|
||||
CallMethod(header.method, header.subchannel, parameters);
|
||||
break;
|
||||
}
|
||||
case SubmissionMode::Inline: {
|
||||
// The register value is stored in the bits 16-28 as an immediate
|
||||
WriteReg(header.method, header.subchannel, header.inline_data);
|
||||
WriteReg(header.method, header.subchannel, header.inline_data, 0);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
64
src/video_core/debug_utils/debug_utils.cpp
Normal file
64
src/video_core/debug_utils/debug_utils.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_field.h"
|
||||
#include "common/color.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
void DebugContext::DoOnEvent(Event event, void* data) {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(breakpoint_mutex);
|
||||
|
||||
// TODO(Subv): Commit the rasterizer's caches so framebuffers, render targets, etc. will
|
||||
// show on debug widgets
|
||||
|
||||
// TODO: Should stop the CPU thread here once we multithread emulation.
|
||||
|
||||
active_breakpoint = event;
|
||||
at_breakpoint = true;
|
||||
|
||||
// Tell all observers that we hit a breakpoint
|
||||
for (auto& breakpoint_observer : breakpoint_observers) {
|
||||
breakpoint_observer->OnMaxwellBreakPointHit(event, data);
|
||||
}
|
||||
|
||||
// Wait until another thread tells us to Resume()
|
||||
resume_from_breakpoint.wait(lock, [&] { return !at_breakpoint; });
|
||||
}
|
||||
}
|
||||
|
||||
void DebugContext::Resume() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(breakpoint_mutex);
|
||||
|
||||
// Tell all observers that we are about to resume
|
||||
for (auto& breakpoint_observer : breakpoint_observers) {
|
||||
breakpoint_observer->OnMaxwellResume();
|
||||
}
|
||||
|
||||
// Resume the waiting thread (i.e. OnEvent())
|
||||
at_breakpoint = false;
|
||||
}
|
||||
|
||||
resume_from_breakpoint.notify_one();
|
||||
}
|
||||
|
||||
} // namespace Tegra
|
||||
163
src/video_core/debug_utils/debug_utils.h
Normal file
163
src/video_core/debug_utils/debug_utils.h
Normal file
@@ -0,0 +1,163 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <condition_variable>
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/vector_math.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
class DebugContext {
|
||||
public:
|
||||
enum class Event {
|
||||
FirstEvent = 0,
|
||||
|
||||
MaxwellCommandLoaded = FirstEvent,
|
||||
MaxwellCommandProcessed,
|
||||
IncomingPrimitiveBatch,
|
||||
FinishedPrimitiveBatch,
|
||||
|
||||
NumEvents
|
||||
};
|
||||
|
||||
/**
|
||||
* Inherit from this class to be notified of events registered to some debug context.
|
||||
* Most importantly this is used for our debugger GUI.
|
||||
*
|
||||
* To implement event handling, override the OnMaxwellBreakPointHit and OnMaxwellResume methods.
|
||||
* @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state
|
||||
* access
|
||||
* @todo Evaluate an alternative interface, in which there is only one managing observer and
|
||||
* multiple child observers running (by design) on the same thread.
|
||||
*/
|
||||
class BreakPointObserver {
|
||||
public:
|
||||
/// Constructs the object such that it observes events of the given DebugContext.
|
||||
BreakPointObserver(std::shared_ptr<DebugContext> debug_context)
|
||||
: context_weak(debug_context) {
|
||||
std::unique_lock<std::mutex> lock(debug_context->breakpoint_mutex);
|
||||
debug_context->breakpoint_observers.push_back(this);
|
||||
}
|
||||
|
||||
virtual ~BreakPointObserver() {
|
||||
auto context = context_weak.lock();
|
||||
if (context) {
|
||||
std::unique_lock<std::mutex> lock(context->breakpoint_mutex);
|
||||
context->breakpoint_observers.remove(this);
|
||||
|
||||
// If we are the last observer to be destroyed, tell the debugger context that
|
||||
// it is free to continue. In particular, this is required for a proper yuzu
|
||||
// shutdown, when the emulation thread is waiting at a breakpoint.
|
||||
if (context->breakpoint_observers.empty())
|
||||
context->Resume();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to perform when a breakpoint was reached.
|
||||
* @param event Type of event which triggered the breakpoint
|
||||
* @param data Optional data pointer (if unused, this is a nullptr)
|
||||
* @note This function will perform nothing unless it is overridden in the child class.
|
||||
*/
|
||||
virtual void OnMaxwellBreakPointHit(Event event, void* data) {}
|
||||
|
||||
/**
|
||||
* Action to perform when emulation is resumed from a breakpoint.
|
||||
* @note This function will perform nothing unless it is overridden in the child class.
|
||||
*/
|
||||
virtual void OnMaxwellResume() {}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Weak context pointer. This need not be valid, so when requesting a shared_ptr via
|
||||
* context_weak.lock(), always compare the result against nullptr.
|
||||
*/
|
||||
std::weak_ptr<DebugContext> context_weak;
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple structure defining a breakpoint state
|
||||
*/
|
||||
struct BreakPoint {
|
||||
bool enabled = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Static constructor used to create a shared_ptr of a DebugContext.
|
||||
*/
|
||||
static std::shared_ptr<DebugContext> Construct() {
|
||||
return std::shared_ptr<DebugContext>(new DebugContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the emulation core when a given event has happened. If a breakpoint has been set
|
||||
* for this event, OnEvent calls the event handlers of the registered breakpoint observers.
|
||||
* The current thread then is halted until Resume() is called from another thread (or until
|
||||
* emulation is stopped).
|
||||
* @param event Event which has happened
|
||||
* @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until
|
||||
* Resume() is called.
|
||||
*/
|
||||
void OnEvent(Event event, void* data) {
|
||||
// This check is left in the header to allow the compiler to inline it.
|
||||
if (!breakpoints[(int)event].enabled)
|
||||
return;
|
||||
// For the rest of event handling, call a separate function.
|
||||
DoOnEvent(event, data);
|
||||
}
|
||||
|
||||
void DoOnEvent(Event event, void* data);
|
||||
|
||||
/**
|
||||
* Resume from the current breakpoint.
|
||||
* @warning Calling this from the same thread that OnEvent was called in will cause a deadlock.
|
||||
* Calling from any other thread is safe.
|
||||
*/
|
||||
void Resume();
|
||||
|
||||
/**
|
||||
* Delete all set breakpoints and resume emulation.
|
||||
*/
|
||||
void ClearBreakpoints() {
|
||||
for (auto& bp : breakpoints) {
|
||||
bp.enabled = false;
|
||||
}
|
||||
Resume();
|
||||
}
|
||||
|
||||
// TODO: Evaluate if access to these members should be hidden behind a public interface.
|
||||
std::array<BreakPoint, (int)Event::NumEvents> breakpoints;
|
||||
Event active_breakpoint;
|
||||
bool at_breakpoint = false;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Private default constructor to make sure people always construct this through Construct()
|
||||
* instead.
|
||||
*/
|
||||
DebugContext() = default;
|
||||
|
||||
/// Mutex protecting current breakpoint state and the observer list.
|
||||
std::mutex breakpoint_mutex;
|
||||
|
||||
/// Used by OnEvent to wait for resumption.
|
||||
std::condition_variable resume_from_breakpoint;
|
||||
|
||||
/// List of registered observers
|
||||
std::list<BreakPointObserver*> breakpoint_observers;
|
||||
};
|
||||
|
||||
} // namespace Tegra
|
||||
@@ -8,7 +8,6 @@ namespace Tegra {
|
||||
namespace Engines {
|
||||
|
||||
void Fermi2D::WriteReg(u32 method, u32 value) {}
|
||||
void Fermi2D::CallMethod(u32 method, const std::vector<u32>& parameters) {}
|
||||
|
||||
} // namespace Engines
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Tegra {
|
||||
@@ -17,13 +16,6 @@ public:
|
||||
|
||||
/// Write the value to the register identified by method.
|
||||
void WriteReg(u32 method, u32 value);
|
||||
|
||||
/**
|
||||
* Handles a method call to this engine.
|
||||
* @param method Method to call
|
||||
* @param parameters Arguments to the method call
|
||||
*/
|
||||
void CallMethod(u32 method, const std::vector<u32>& parameters);
|
||||
};
|
||||
|
||||
} // namespace Engines
|
||||
|
||||
@@ -2,33 +2,89 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cinttypes>
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/textures/decoders.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace Tegra {
|
||||
namespace Engines {
|
||||
|
||||
/// First register id that is actually a Macro call.
|
||||
constexpr u32 MacroRegistersStart = 0xE00;
|
||||
|
||||
const std::unordered_map<u32, Maxwell3D::MethodInfo> Maxwell3D::method_handlers = {
|
||||
{0xE1A, {"BindTextureInfoBuffer", 1, &Maxwell3D::BindTextureInfoBuffer}},
|
||||
{0xE24, {"SetShader", 5, &Maxwell3D::SetShader}},
|
||||
{0xE2A, {"BindStorageBuffer", 1, &Maxwell3D::BindStorageBuffer}},
|
||||
};
|
||||
|
||||
Maxwell3D::Maxwell3D(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
|
||||
|
||||
void Maxwell3D::CallMethod(u32 method, const std::vector<u32>& parameters) {
|
||||
void Maxwell3D::SubmitMacroCode(u32 entry, std::vector<u32> code) {
|
||||
uploaded_macros[entry * 2 + MacroRegistersStart] = std::move(code);
|
||||
}
|
||||
|
||||
void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) {
|
||||
// TODO(Subv): Write an interpreter for the macros uploaded via registers 0x45 and 0x47
|
||||
|
||||
// The requested macro must have been uploaded already.
|
||||
ASSERT_MSG(uploaded_macros.find(method) != uploaded_macros.end(), "Macro %08X was not uploaded",
|
||||
method);
|
||||
|
||||
auto itr = method_handlers.find(method);
|
||||
if (itr == method_handlers.end()) {
|
||||
LOG_ERROR(HW_GPU, "Unhandled method call %08X", method);
|
||||
ASSERT_MSG(itr != method_handlers.end(), "Unhandled method call %08X", method);
|
||||
|
||||
ASSERT(itr->second.arguments == parameters.size());
|
||||
|
||||
(this->*itr->second.handler)(parameters);
|
||||
|
||||
// Reset the current macro and its parameters.
|
||||
executing_macro = 0;
|
||||
macro_params.clear();
|
||||
}
|
||||
|
||||
void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
"Invalid Maxwell3D register, increase the size of the Regs structure");
|
||||
|
||||
auto debug_context = Core::System::GetInstance().GetGPUDebugContext();
|
||||
|
||||
// It is an error to write to a register other than the current macro's ARG register before it
|
||||
// has finished execution.
|
||||
if (executing_macro != 0) {
|
||||
ASSERT(method == executing_macro + 1);
|
||||
}
|
||||
|
||||
// Methods after 0xE00 are special, they're actually triggers for some microcode that was
|
||||
// uploaded to the GPU during initialization.
|
||||
if (method >= MacroRegistersStart) {
|
||||
// We're trying to execute a macro
|
||||
if (executing_macro == 0) {
|
||||
// A macro call must begin by writing the macro method's register, not its argument.
|
||||
ASSERT_MSG((method % 2) == 0,
|
||||
"Can't start macro execution by writing to the ARGS register");
|
||||
executing_macro = method;
|
||||
}
|
||||
|
||||
macro_params.push_back(value);
|
||||
|
||||
// Call the macro when there are no more parameters in the command buffer
|
||||
if (remaining_params == 0) {
|
||||
CallMacroMethod(executing_macro, macro_params);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(itr->second.arguments == parameters.size());
|
||||
(this->*itr->second.handler)(parameters);
|
||||
}
|
||||
|
||||
void Maxwell3D::WriteReg(u32 method, u32 value) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
"Invalid Maxwell3D register, increase the size of the Regs structure");
|
||||
if (debug_context) {
|
||||
debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr);
|
||||
}
|
||||
|
||||
regs.reg_array[method] = value;
|
||||
|
||||
@@ -43,6 +99,25 @@ void Maxwell3D::WriteReg(u32 method, u32 value) {
|
||||
ASSERT_MSG(regs.code_address.CodeAddress() == 0, "Unexpected CODE_ADDRESS register value.");
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[3]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[4]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[5]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[6]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[7]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[8]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[9]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[10]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[11]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[12]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]): {
|
||||
ProcessCBData(value);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(cb_bind[0].raw_config): {
|
||||
ProcessCBBind(Regs::ShaderStage::Vertex);
|
||||
break;
|
||||
@@ -76,6 +151,10 @@ void Maxwell3D::WriteReg(u32 method, u32 value) {
|
||||
}
|
||||
|
||||
#undef MAXWELL3D_REG_INDEX
|
||||
|
||||
if (debug_context) {
|
||||
debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessQueryGet() {
|
||||
@@ -92,12 +171,43 @@ void Maxwell3D::ProcessQueryGet() {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Query mode %u not implemented", regs.query.query_get.mode.Value());
|
||||
UNIMPLEMENTED_MSG("Query mode %u not implemented",
|
||||
static_cast<u32>(regs.query.query_get.mode.Value()));
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::DrawArrays() {
|
||||
LOG_WARNING(HW_GPU, "Game requested a DrawArrays, ignoring");
|
||||
LOG_DEBUG(HW_GPU, "called, topology=%d, count=%d", regs.draw.topology.Value(),
|
||||
regs.vertex_buffer.count);
|
||||
|
||||
auto debug_context = Core::System::GetInstance().GetGPUDebugContext();
|
||||
|
||||
if (debug_context) {
|
||||
debug_context->OnEvent(Tegra::DebugContext::Event::IncomingPrimitiveBatch, nullptr);
|
||||
}
|
||||
|
||||
if (debug_context) {
|
||||
debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr);
|
||||
}
|
||||
|
||||
VideoCore::g_renderer->Rasterizer()->AccelerateDrawBatch(false /*is_indexed*/);
|
||||
}
|
||||
|
||||
void Maxwell3D::BindTextureInfoBuffer(const std::vector<u32>& parameters) {
|
||||
/**
|
||||
* Parameters description:
|
||||
* [0] = Shader stage, usually 4 for FragmentShader
|
||||
*/
|
||||
|
||||
u32 stage = parameters[0];
|
||||
|
||||
// Perform the same operations as the real macro code.
|
||||
GPUVAddr address = static_cast<GPUVAddr>(regs.tex_info_buffers.address[stage]) << 8;
|
||||
u32 size = regs.tex_info_buffers.size[stage];
|
||||
|
||||
regs.const_buffer.cb_size = size;
|
||||
regs.const_buffer.cb_address_high = address >> 32;
|
||||
regs.const_buffer.cb_address_low = address & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
void Maxwell3D::SetShader(const std::vector<u32>& parameters) {
|
||||
@@ -141,6 +251,26 @@ void Maxwell3D::SetShader(const std::vector<u32>& parameters) {
|
||||
ProcessCBBind(shader_stage);
|
||||
}
|
||||
|
||||
void Maxwell3D::BindStorageBuffer(const std::vector<u32>& parameters) {
|
||||
/**
|
||||
* Parameters description:
|
||||
* [0] = Buffer offset >> 2
|
||||
*/
|
||||
|
||||
u32 buffer_offset = parameters[0] << 2;
|
||||
|
||||
// Perform the same operations as the real macro code.
|
||||
// Note: This value is hardcoded in the macro's code.
|
||||
static constexpr u32 DefaultCBSize = 0x5F00;
|
||||
regs.const_buffer.cb_size = DefaultCBSize;
|
||||
|
||||
GPUVAddr address = regs.ssbo_info.BufferAddress();
|
||||
regs.const_buffer.cb_address_high = address >> 32;
|
||||
regs.const_buffer.cb_address_low = address & 0xFFFFFFFF;
|
||||
|
||||
regs.const_buffer.cb_pos = buffer_offset;
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) {
|
||||
// Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage.
|
||||
auto& shader = state.shader_stages[static_cast<size_t>(stage)];
|
||||
@@ -154,5 +284,107 @@ void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) {
|
||||
buffer.size = regs.const_buffer.cb_size;
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessCBData(u32 value) {
|
||||
// Write the input value to the current const buffer at the current position.
|
||||
GPUVAddr buffer_address = regs.const_buffer.BufferAddress();
|
||||
ASSERT(buffer_address != 0);
|
||||
|
||||
// Don't allow writing past the end of the buffer.
|
||||
ASSERT(regs.const_buffer.cb_pos + sizeof(u32) <= regs.const_buffer.cb_size);
|
||||
|
||||
VAddr address =
|
||||
memory_manager.PhysicalToVirtualAddress(buffer_address + regs.const_buffer.cb_pos);
|
||||
|
||||
Memory::Write32(address, value);
|
||||
|
||||
// Increment the current buffer position.
|
||||
regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4;
|
||||
}
|
||||
|
||||
Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
|
||||
GPUVAddr tic_base_address = regs.tic.TICAddress();
|
||||
|
||||
GPUVAddr tic_address_gpu = tic_base_address + tic_index * sizeof(Texture::TICEntry);
|
||||
VAddr tic_address_cpu = memory_manager.PhysicalToVirtualAddress(tic_address_gpu);
|
||||
|
||||
Texture::TICEntry tic_entry;
|
||||
Memory::ReadBlock(tic_address_cpu, &tic_entry, sizeof(Texture::TICEntry));
|
||||
|
||||
ASSERT_MSG(tic_entry.header_version == Texture::TICHeaderVersion::BlockLinear,
|
||||
"TIC versions other than BlockLinear are unimplemented");
|
||||
|
||||
ASSERT_MSG(tic_entry.texture_type == Texture::TextureType::Texture2D,
|
||||
"Texture types other than Texture2D are unimplemented");
|
||||
|
||||
auto r_type = tic_entry.r_type.Value();
|
||||
auto g_type = tic_entry.g_type.Value();
|
||||
auto b_type = tic_entry.b_type.Value();
|
||||
auto a_type = tic_entry.a_type.Value();
|
||||
|
||||
// TODO(Subv): Different data types for separate components are not supported
|
||||
ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
|
||||
|
||||
return tic_entry;
|
||||
}
|
||||
|
||||
Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const {
|
||||
GPUVAddr tsc_base_address = regs.tsc.TSCAddress();
|
||||
|
||||
GPUVAddr tsc_address_gpu = tsc_base_address + tsc_index * sizeof(Texture::TSCEntry);
|
||||
VAddr tsc_address_cpu = memory_manager.PhysicalToVirtualAddress(tsc_address_gpu);
|
||||
|
||||
Texture::TSCEntry tsc_entry;
|
||||
Memory::ReadBlock(tsc_address_cpu, &tsc_entry, sizeof(Texture::TSCEntry));
|
||||
return tsc_entry;
|
||||
}
|
||||
|
||||
std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderStage stage) const {
|
||||
std::vector<Texture::FullTextureInfo> textures;
|
||||
|
||||
auto& fragment_shader = state.shader_stages[static_cast<size_t>(stage)];
|
||||
auto& tex_info_buffer = fragment_shader.const_buffers[regs.tex_cb_index];
|
||||
ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0);
|
||||
|
||||
GPUVAddr tic_base_address = regs.tic.TICAddress();
|
||||
|
||||
GPUVAddr tex_info_buffer_end = tex_info_buffer.address + tex_info_buffer.size;
|
||||
|
||||
// Offset into the texture constbuffer where the texture info begins.
|
||||
static constexpr size_t TextureInfoOffset = 0x20;
|
||||
|
||||
for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset;
|
||||
current_texture < tex_info_buffer_end; current_texture += sizeof(Texture::TextureHandle)) {
|
||||
|
||||
Texture::TextureHandle tex_handle{
|
||||
Memory::Read32(memory_manager.PhysicalToVirtualAddress(current_texture))};
|
||||
|
||||
Texture::FullTextureInfo tex_info{};
|
||||
// TODO(Subv): Use the shader to determine which textures are actually accessed.
|
||||
tex_info.index = (current_texture - tex_info_buffer.address - TextureInfoOffset) /
|
||||
sizeof(Texture::TextureHandle);
|
||||
|
||||
// Load the TIC data.
|
||||
if (tex_handle.tic_id != 0) {
|
||||
tex_info.enabled = true;
|
||||
|
||||
auto tic_entry = GetTICEntry(tex_handle.tic_id);
|
||||
// TODO(Subv): Workaround for BitField's move constructor being deleted.
|
||||
std::memcpy(&tex_info.tic, &tic_entry, sizeof(tic_entry));
|
||||
}
|
||||
|
||||
// Load the TSC data
|
||||
if (tex_handle.tsc_id != 0) {
|
||||
auto tsc_entry = GetTSCEntry(tex_handle.tsc_id);
|
||||
// TODO(Subv): Workaround for BitField's move constructor being deleted.
|
||||
std::memcpy(&tex_info.tsc, &tsc_entry, sizeof(tsc_entry));
|
||||
}
|
||||
|
||||
if (tex_info.enabled)
|
||||
textures.push_back(tex_info);
|
||||
}
|
||||
|
||||
return textures;
|
||||
}
|
||||
|
||||
} // namespace Engines
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -7,10 +7,14 @@
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
|
||||
namespace Tegra {
|
||||
namespace Engines {
|
||||
@@ -20,23 +24,16 @@ public:
|
||||
explicit Maxwell3D(MemoryManager& memory_manager);
|
||||
~Maxwell3D() = default;
|
||||
|
||||
/// Write the value to the register identified by method.
|
||||
void WriteReg(u32 method, u32 value);
|
||||
|
||||
/**
|
||||
* Handles a method call to this engine.
|
||||
* @param method Method to call
|
||||
* @param parameters Arguments to the method call
|
||||
*/
|
||||
void CallMethod(u32 method, const std::vector<u32>& parameters);
|
||||
|
||||
/// Register structure of the Maxwell3D engine.
|
||||
/// TODO(Subv): This structure will need to be made bigger as more registers are discovered.
|
||||
struct Regs {
|
||||
static constexpr size_t NUM_REGS = 0xE36;
|
||||
|
||||
static constexpr size_t NumRenderTargets = 8;
|
||||
static constexpr size_t NumViewports = 16;
|
||||
static constexpr size_t NumCBData = 16;
|
||||
static constexpr size_t NumVertexArrays = 32;
|
||||
static constexpr size_t NumVertexAttributes = 32;
|
||||
static constexpr size_t MaxShaderProgram = 6;
|
||||
static constexpr size_t MaxShaderStage = 5;
|
||||
// Maximum number of const buffers per shader stage.
|
||||
@@ -64,9 +61,300 @@ public:
|
||||
Fragment = 4,
|
||||
};
|
||||
|
||||
struct VertexAttribute {
|
||||
enum class Size : u32 {
|
||||
Size_32_32_32_32 = 0x01,
|
||||
Size_32_32_32 = 0x02,
|
||||
Size_16_16_16_16 = 0x03,
|
||||
Size_32_32 = 0x04,
|
||||
Size_16_16_16 = 0x05,
|
||||
Size_8_8_8_8 = 0x0a,
|
||||
Size_16_16 = 0x0f,
|
||||
Size_32 = 0x12,
|
||||
Size_8_8_8 = 0x13,
|
||||
Size_8_8 = 0x18,
|
||||
Size_16 = 0x1b,
|
||||
Size_8 = 0x1d,
|
||||
Size_10_10_10_2 = 0x30,
|
||||
Size_11_11_10 = 0x31,
|
||||
};
|
||||
|
||||
enum class Type : u32 {
|
||||
SignedNorm = 1,
|
||||
UnsignedNorm = 2,
|
||||
SignedInt = 3,
|
||||
UnsignedInt = 4,
|
||||
UnsignedScaled = 5,
|
||||
SignedScaled = 6,
|
||||
Float = 7,
|
||||
};
|
||||
|
||||
union {
|
||||
BitField<0, 5, u32> buffer;
|
||||
BitField<6, 1, u32> constant;
|
||||
BitField<7, 14, u32> offset;
|
||||
BitField<21, 6, Size> size;
|
||||
BitField<27, 3, Type> type;
|
||||
BitField<31, 1, u32> bgra;
|
||||
};
|
||||
|
||||
u32 ComponentCount() const {
|
||||
switch (size) {
|
||||
case Size::Size_32_32_32_32:
|
||||
return 4;
|
||||
case Size::Size_32_32_32:
|
||||
return 3;
|
||||
case Size::Size_16_16_16_16:
|
||||
return 4;
|
||||
case Size::Size_32_32:
|
||||
return 2;
|
||||
case Size::Size_16_16_16:
|
||||
return 3;
|
||||
case Size::Size_8_8_8_8:
|
||||
return 4;
|
||||
case Size::Size_16_16:
|
||||
return 2;
|
||||
case Size::Size_32:
|
||||
return 1;
|
||||
case Size::Size_8_8_8:
|
||||
return 3;
|
||||
case Size::Size_8_8:
|
||||
return 2;
|
||||
case Size::Size_16:
|
||||
return 1;
|
||||
case Size::Size_8:
|
||||
return 1;
|
||||
case Size::Size_10_10_10_2:
|
||||
return 4;
|
||||
case Size::Size_11_11_10:
|
||||
return 3;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
u32 SizeInBytes() const {
|
||||
switch (size) {
|
||||
case Size::Size_32_32_32_32:
|
||||
return 16;
|
||||
case Size::Size_32_32_32:
|
||||
return 12;
|
||||
case Size::Size_16_16_16_16:
|
||||
return 8;
|
||||
case Size::Size_32_32:
|
||||
return 8;
|
||||
case Size::Size_16_16_16:
|
||||
return 6;
|
||||
case Size::Size_8_8_8_8:
|
||||
return 4;
|
||||
case Size::Size_16_16:
|
||||
return 4;
|
||||
case Size::Size_32:
|
||||
return 4;
|
||||
case Size::Size_8_8_8:
|
||||
return 3;
|
||||
case Size::Size_8_8:
|
||||
return 2;
|
||||
case Size::Size_16:
|
||||
return 2;
|
||||
case Size::Size_8:
|
||||
return 1;
|
||||
case Size::Size_10_10_10_2:
|
||||
return 4;
|
||||
case Size::Size_11_11_10:
|
||||
return 4;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
std::string SizeString() const {
|
||||
switch (size) {
|
||||
case Size::Size_32_32_32_32:
|
||||
return "32_32_32_32";
|
||||
case Size::Size_32_32_32:
|
||||
return "32_32_32";
|
||||
case Size::Size_16_16_16_16:
|
||||
return "16_16_16_16";
|
||||
case Size::Size_32_32:
|
||||
return "32_32";
|
||||
case Size::Size_16_16_16:
|
||||
return "16_16_16";
|
||||
case Size::Size_8_8_8_8:
|
||||
return "8_8_8_8";
|
||||
case Size::Size_16_16:
|
||||
return "16_16";
|
||||
case Size::Size_32:
|
||||
return "32";
|
||||
case Size::Size_8_8_8:
|
||||
return "8_8_8";
|
||||
case Size::Size_8_8:
|
||||
return "8_8";
|
||||
case Size::Size_16:
|
||||
return "16";
|
||||
case Size::Size_8:
|
||||
return "8";
|
||||
case Size::Size_10_10_10_2:
|
||||
return "10_10_10_2";
|
||||
case Size::Size_11_11_10:
|
||||
return "11_11_10";
|
||||
}
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string TypeString() const {
|
||||
switch (type) {
|
||||
case Type::SignedNorm:
|
||||
return "SNORM";
|
||||
case Type::UnsignedNorm:
|
||||
return "UNORM";
|
||||
case Type::SignedInt:
|
||||
return "SINT";
|
||||
case Type::UnsignedInt:
|
||||
return "UINT";
|
||||
case Type::UnsignedScaled:
|
||||
return "USCALED";
|
||||
case Type::SignedScaled:
|
||||
return "SSCALED";
|
||||
case Type::Float:
|
||||
return "FLOAT";
|
||||
}
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
}
|
||||
|
||||
bool IsNormalized() const {
|
||||
return (type == Type::SignedNorm) || (type == Type::UnsignedNorm);
|
||||
}
|
||||
};
|
||||
|
||||
enum class PrimitiveTopology : u32 {
|
||||
Points = 0x0,
|
||||
Lines = 0x1,
|
||||
LineLoop = 0x2,
|
||||
LineStrip = 0x3,
|
||||
Triangles = 0x4,
|
||||
TriangleStrip = 0x5,
|
||||
TriangleFan = 0x6,
|
||||
Quads = 0x7,
|
||||
QuadStrip = 0x8,
|
||||
Polygon = 0x9,
|
||||
LinesAdjacency = 0xa,
|
||||
LineStripAdjacency = 0xb,
|
||||
TrianglesAdjacency = 0xc,
|
||||
TriangleStripAdjacency = 0xd,
|
||||
Patches = 0xe,
|
||||
};
|
||||
|
||||
union {
|
||||
struct {
|
||||
INSERT_PADDING_WORDS(0x582);
|
||||
INSERT_PADDING_WORDS(0x200);
|
||||
|
||||
struct {
|
||||
u32 address_high;
|
||||
u32 address_low;
|
||||
u32 width;
|
||||
u32 height;
|
||||
Tegra::RenderTargetFormat format;
|
||||
u32 block_dimensions;
|
||||
u32 array_mode;
|
||||
u32 layer_stride;
|
||||
u32 base_layer;
|
||||
INSERT_PADDING_WORDS(7);
|
||||
|
||||
GPUVAddr Address() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
|
||||
address_low);
|
||||
}
|
||||
} rt[NumRenderTargets];
|
||||
|
||||
INSERT_PADDING_WORDS(0x80);
|
||||
|
||||
struct {
|
||||
union {
|
||||
BitField<0, 16, u32> x;
|
||||
BitField<16, 16, u32> width;
|
||||
};
|
||||
union {
|
||||
BitField<0, 16, u32> y;
|
||||
BitField<16, 16, u32> height;
|
||||
};
|
||||
float depth_range_near;
|
||||
float depth_range_far;
|
||||
|
||||
MathUtil::Rectangle<s32> GetRect() const {
|
||||
return {
|
||||
static_cast<s32>(x), // left
|
||||
static_cast<s32>(y + height), // top
|
||||
static_cast<s32>(x + width), // right
|
||||
static_cast<s32>(y) // bottom
|
||||
};
|
||||
};
|
||||
} viewport[NumViewports];
|
||||
|
||||
INSERT_PADDING_WORDS(0x1D);
|
||||
|
||||
struct {
|
||||
u32 first;
|
||||
u32 count;
|
||||
} vertex_buffer;
|
||||
|
||||
INSERT_PADDING_WORDS(0x99);
|
||||
|
||||
struct {
|
||||
u32 address_high;
|
||||
u32 address_low;
|
||||
u32 format;
|
||||
u32 block_dimensions;
|
||||
u32 layer_stride;
|
||||
|
||||
GPUVAddr Address() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
|
||||
address_low);
|
||||
}
|
||||
} zeta;
|
||||
|
||||
INSERT_PADDING_WORDS(0x5B);
|
||||
|
||||
VertexAttribute vertex_attrib_format[NumVertexAttributes];
|
||||
|
||||
INSERT_PADDING_WORDS(0xF);
|
||||
|
||||
struct {
|
||||
union {
|
||||
BitField<0, 4, u32> count;
|
||||
};
|
||||
} rt_control;
|
||||
|
||||
INSERT_PADDING_WORDS(0xCF);
|
||||
|
||||
struct {
|
||||
u32 tsc_address_high;
|
||||
u32 tsc_address_low;
|
||||
u32 tsc_limit;
|
||||
|
||||
GPUVAddr TSCAddress() const {
|
||||
return static_cast<GPUVAddr>(
|
||||
(static_cast<GPUVAddr>(tsc_address_high) << 32) | tsc_address_low);
|
||||
}
|
||||
} tsc;
|
||||
|
||||
INSERT_PADDING_WORDS(0x3);
|
||||
|
||||
struct {
|
||||
u32 tic_address_high;
|
||||
u32 tic_address_low;
|
||||
u32 tic_limit;
|
||||
|
||||
GPUVAddr TICAddress() const {
|
||||
return static_cast<GPUVAddr>(
|
||||
(static_cast<GPUVAddr>(tic_address_high) << 32) | tic_address_low);
|
||||
}
|
||||
} tic;
|
||||
|
||||
INSERT_PADDING_WORDS(0x22);
|
||||
|
||||
struct {
|
||||
u32 code_address_high;
|
||||
u32 code_address_low;
|
||||
@@ -77,10 +365,15 @@ public:
|
||||
}
|
||||
} code_address;
|
||||
INSERT_PADDING_WORDS(1);
|
||||
|
||||
struct {
|
||||
u32 vertex_end_gl;
|
||||
u32 vertex_begin_gl;
|
||||
union {
|
||||
u32 vertex_begin_gl;
|
||||
BitField<0, 16, PrimitiveTopology> topology;
|
||||
};
|
||||
} draw;
|
||||
|
||||
INSERT_PADDING_WORDS(0x139);
|
||||
struct {
|
||||
u32 query_address_high;
|
||||
@@ -166,7 +459,30 @@ public:
|
||||
INSERT_PADDING_WORDS(7);
|
||||
} cb_bind[MaxShaderStage];
|
||||
|
||||
INSERT_PADDING_WORDS(0x50A);
|
||||
INSERT_PADDING_WORDS(0x56);
|
||||
|
||||
u32 tex_cb_index;
|
||||
|
||||
INSERT_PADDING_WORDS(0x395);
|
||||
|
||||
struct {
|
||||
/// Compressed address of a buffer that holds information about bound SSBOs.
|
||||
/// This address is usually bound to c0 in the shaders.
|
||||
u32 buffer_address;
|
||||
|
||||
GPUVAddr BufferAddress() const {
|
||||
return static_cast<GPUVAddr>(buffer_address) << 8;
|
||||
}
|
||||
} ssbo_info;
|
||||
|
||||
INSERT_PADDING_WORDS(0x11);
|
||||
|
||||
struct {
|
||||
u32 address[MaxShaderStage];
|
||||
u32 size[MaxShaderStage];
|
||||
} tex_info_buffers;
|
||||
|
||||
INSERT_PADDING_WORDS(0x102);
|
||||
};
|
||||
std::array<u32, NUM_REGS> reg_array;
|
||||
};
|
||||
@@ -198,12 +514,44 @@ public:
|
||||
|
||||
State state{};
|
||||
|
||||
/// Write the value to the register identified by method.
|
||||
void WriteReg(u32 method, u32 value, u32 remaining_params);
|
||||
|
||||
/// Uploads the code for a GPU macro program associated with the specified entry.
|
||||
void SubmitMacroCode(u32 entry, std::vector<u32> code);
|
||||
|
||||
/// Returns a list of enabled textures for the specified shader stage.
|
||||
std::vector<Texture::FullTextureInfo> GetStageTextures(Regs::ShaderStage stage) const;
|
||||
|
||||
private:
|
||||
MemoryManager& memory_manager;
|
||||
|
||||
std::unordered_map<u32, std::vector<u32>> uploaded_macros;
|
||||
|
||||
/// Macro method that is currently being executed / being fed parameters.
|
||||
u32 executing_macro = 0;
|
||||
/// Parameters that have been submitted to the macro call so far.
|
||||
std::vector<u32> macro_params;
|
||||
|
||||
/// Retrieves information about a specific TIC entry from the TIC buffer.
|
||||
Texture::TICEntry GetTICEntry(u32 tic_index) const;
|
||||
|
||||
/// Retrieves information about a specific TSC entry from the TSC buffer.
|
||||
Texture::TSCEntry GetTSCEntry(u32 tsc_index) const;
|
||||
|
||||
/**
|
||||
* Call a macro on this engine.
|
||||
* @param method Method to call
|
||||
* @param parameters Arguments to the method call
|
||||
*/
|
||||
void CallMacroMethod(u32 method, const std::vector<u32>& parameters);
|
||||
|
||||
/// Handles a write to the QUERY_GET register.
|
||||
void ProcessQueryGet();
|
||||
|
||||
/// Handles a write to the CB_DATA[i] register.
|
||||
void ProcessCBData(u32 value);
|
||||
|
||||
/// Handles a write to the CB_BIND register.
|
||||
void ProcessCBBind(Regs::ShaderStage stage);
|
||||
|
||||
@@ -211,7 +559,9 @@ private:
|
||||
void DrawArrays();
|
||||
|
||||
/// Method call handlers
|
||||
void BindTextureInfoBuffer(const std::vector<u32>& parameters);
|
||||
void SetShader(const std::vector<u32>& parameters);
|
||||
void BindStorageBuffer(const std::vector<u32>& parameters);
|
||||
|
||||
struct MethodInfo {
|
||||
const char* name;
|
||||
@@ -226,6 +576,14 @@ private:
|
||||
static_assert(offsetof(Maxwell3D::Regs, field_name) == position * 4, \
|
||||
"Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_REG_POSITION(rt, 0x200);
|
||||
ASSERT_REG_POSITION(viewport, 0x300);
|
||||
ASSERT_REG_POSITION(vertex_buffer, 0x35D);
|
||||
ASSERT_REG_POSITION(zeta, 0x3F8);
|
||||
ASSERT_REG_POSITION(vertex_attrib_format[0], 0x458);
|
||||
ASSERT_REG_POSITION(rt_control, 0x487);
|
||||
ASSERT_REG_POSITION(tsc, 0x557);
|
||||
ASSERT_REG_POSITION(tic, 0x55D);
|
||||
ASSERT_REG_POSITION(code_address, 0x582);
|
||||
ASSERT_REG_POSITION(draw, 0x585);
|
||||
ASSERT_REG_POSITION(query, 0x6C0);
|
||||
@@ -234,6 +592,10 @@ ASSERT_REG_POSITION(vertex_array_limit[0], 0x7C0);
|
||||
ASSERT_REG_POSITION(shader_config[0], 0x800);
|
||||
ASSERT_REG_POSITION(const_buffer, 0x8E0);
|
||||
ASSERT_REG_POSITION(cb_bind[0], 0x904);
|
||||
ASSERT_REG_POSITION(tex_cb_index, 0x982);
|
||||
ASSERT_REG_POSITION(ssbo_info, 0xD18);
|
||||
ASSERT_REG_POSITION(tex_info_buffers.address[0], 0xD2A);
|
||||
ASSERT_REG_POSITION(tex_info_buffers.size[0], 0xD2F);
|
||||
|
||||
#undef ASSERT_REG_POSITION
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ namespace Tegra {
|
||||
namespace Engines {
|
||||
|
||||
void MaxwellCompute::WriteReg(u32 method, u32 value) {}
|
||||
void MaxwellCompute::CallMethod(u32 method, const std::vector<u32>& parameters) {}
|
||||
|
||||
} // namespace Engines
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Tegra {
|
||||
@@ -17,13 +16,6 @@ public:
|
||||
|
||||
/// Write the value to the register identified by method.
|
||||
void WriteReg(u32 method, u32 value);
|
||||
|
||||
/**
|
||||
* Handles a method call to this engine.
|
||||
* @param method Method to call
|
||||
* @param parameters Arguments to the method call
|
||||
*/
|
||||
void CallMethod(u32 method, const std::vector<u32>& parameters);
|
||||
};
|
||||
|
||||
} // namespace Engines
|
||||
|
||||
25
src/video_core/gpu.cpp
Normal file
25
src/video_core/gpu.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/maxwell_compute.h"
|
||||
#include "video_core/gpu.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
GPU::GPU() {
|
||||
memory_manager = std::make_unique<MemoryManager>();
|
||||
maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager);
|
||||
fermi_2d = std::make_unique<Engines::Fermi2D>();
|
||||
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
|
||||
}
|
||||
|
||||
GPU::~GPU() = default;
|
||||
|
||||
const Tegra::Engines::Maxwell3D& GPU::Get3DEngine() const {
|
||||
return *maxwell_3d;
|
||||
}
|
||||
|
||||
} // namespace Tegra
|
||||
@@ -6,14 +6,57 @@
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/maxwell_compute.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
enum class RenderTargetFormat : u32 {
|
||||
NONE = 0x0,
|
||||
RGBA8_UNORM = 0xD5,
|
||||
};
|
||||
|
||||
class DebugContext;
|
||||
|
||||
/**
|
||||
* Struct describing framebuffer configuration
|
||||
*/
|
||||
struct FramebufferConfig {
|
||||
enum class PixelFormat : u32 {
|
||||
ABGR8 = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the number of bytes per pixel.
|
||||
*/
|
||||
static u32 BytesPerPixel(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::ABGR8:
|
||||
return 4;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
VAddr address;
|
||||
u32 offset;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 stride;
|
||||
PixelFormat pixel_format;
|
||||
|
||||
using TransformFlags = Service::NVFlinger::BufferQueue::BufferTransformFlags;
|
||||
TransformFlags transform_flags;
|
||||
};
|
||||
|
||||
namespace Engines {
|
||||
class Fermi2D;
|
||||
class Maxwell3D;
|
||||
class MaxwellCompute;
|
||||
} // namespace Engines
|
||||
|
||||
enum class EngineID {
|
||||
FERMI_TWOD_A = 0x902D, // 2D Engine
|
||||
MAXWELL_B = 0xB197, // 3D Engine
|
||||
@@ -24,25 +67,26 @@ enum class EngineID {
|
||||
|
||||
class GPU final {
|
||||
public:
|
||||
GPU() {
|
||||
memory_manager = std::make_unique<MemoryManager>();
|
||||
maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager);
|
||||
fermi_2d = std::make_unique<Engines::Fermi2D>();
|
||||
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
|
||||
}
|
||||
~GPU() = default;
|
||||
GPU();
|
||||
~GPU();
|
||||
|
||||
/// Processes a command list stored at the specified address in GPU memory.
|
||||
void ProcessCommandList(GPUVAddr address, u32 size);
|
||||
|
||||
/// Returns a reference to the Maxwell3D GPU engine.
|
||||
const Engines::Maxwell3D& Get3DEngine() const;
|
||||
|
||||
std::unique_ptr<MemoryManager> memory_manager;
|
||||
|
||||
private:
|
||||
/// Writes a single register in the engine bound to the specified subchannel
|
||||
void WriteReg(u32 method, u32 subchannel, u32 value);
|
||||
Engines::Maxwell3D& Maxwell3D() {
|
||||
return *maxwell_3d;
|
||||
}
|
||||
|
||||
/// Calls a method in the engine bound to the specified subchannel with the input parameters.
|
||||
void CallMethod(u32 method, u32 subchannel, const std::vector<u32>& parameters);
|
||||
private:
|
||||
static constexpr u32 InvalidGraphMacroEntry = 0xFFFFFFFF;
|
||||
|
||||
/// Writes a single register in the engine bound to the specified subchannel
|
||||
void WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params);
|
||||
|
||||
/// Mapping of command subchannels to their bound engine ids.
|
||||
std::unordered_map<u32, EngineID> bound_engines;
|
||||
@@ -53,6 +97,11 @@ private:
|
||||
std::unique_ptr<Engines::Fermi2D> fermi_2d;
|
||||
/// Compute engine
|
||||
std::unique_ptr<Engines::MaxwellCompute> maxwell_compute;
|
||||
|
||||
/// Entry of the macro that is currently being uploaded
|
||||
u32 current_macro_entry = InvalidGraphMacroEntry;
|
||||
/// Code being uploaded for the current macro
|
||||
std::vector<u32> current_macro_code;
|
||||
};
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
63
src/video_core/rasterizer_interface.h
Normal file
63
src/video_core/rasterizer_interface.h
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/gpu.h"
|
||||
|
||||
struct ScreenInfo;
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
class RasterizerInterface {
|
||||
public:
|
||||
virtual ~RasterizerInterface() {}
|
||||
|
||||
/// Draw the current batch of vertex arrays
|
||||
virtual void DrawArrays() = 0;
|
||||
|
||||
/// Notify rasterizer that the specified Maxwell register has been changed
|
||||
virtual void NotifyMaxwellRegisterChanged(u32 id) = 0;
|
||||
|
||||
/// Notify rasterizer that all caches should be flushed to 3DS memory
|
||||
virtual void FlushAll() = 0;
|
||||
|
||||
/// Notify rasterizer that any caches of the specified region should be flushed to 3DS memory
|
||||
virtual void FlushRegion(VAddr addr, u64 size) = 0;
|
||||
|
||||
/// Notify rasterizer that any caches of the specified region should be invalidated
|
||||
virtual void InvalidateRegion(VAddr addr, u64 size) = 0;
|
||||
|
||||
/// Notify rasterizer that any caches of the specified region should be flushed to 3DS memory
|
||||
/// and invalidated
|
||||
virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0;
|
||||
|
||||
/// Attempt to use a faster method to perform a display transfer with is_texture_copy = 0
|
||||
virtual bool AccelerateDisplayTransfer(const void* config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Attempt to use a faster method to perform a display transfer with is_texture_copy = 1
|
||||
virtual bool AccelerateTextureCopy(const void* config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Attempt to use a faster method to fill a region
|
||||
virtual bool AccelerateFill(const void* config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Attempt to use a faster method to display the framebuffer to screen
|
||||
virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& framebuffer,
|
||||
VAddr framebuffer_addr, u32 pixel_stride,
|
||||
ScreenInfo& screen_info) {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool AccelerateDrawBatch(bool is_indexed) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
} // namespace VideoCore
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user