Compare commits
289 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c06d6b27f3 | ||
|
|
c3e95086b6 | ||
|
|
a3d82ef5d9 | ||
|
|
be1f5dedfb | ||
|
|
7a0bb406d5 | ||
|
|
0d8ae773f1 | ||
|
|
1ab133d7fa | ||
|
|
38989bef43 | ||
|
|
eab7457c00 | ||
|
|
0e13d9cb7b | ||
|
|
c11cfaa705 | ||
|
|
4ac4b308e4 | ||
|
|
ea080501fb | ||
|
|
bf4e2b2f0b | ||
|
|
7c7f4a9be2 | ||
|
|
61779fa072 | ||
|
|
d2277b825e | ||
|
|
fe906fff36 | ||
|
|
f9af74201c | ||
|
|
afdd657d30 | ||
|
|
5673ce39c7 | ||
|
|
3c43ea5c68 | ||
|
|
d383043e07 | ||
|
|
fb5bd0920d | ||
|
|
46cbb6b090 | ||
|
|
55c49d5bf4 | ||
|
|
61f9d9c4ab | ||
|
|
acc8fe5a2a | ||
|
|
f969ddb54e | ||
|
|
9f8fbce35b | ||
|
|
94d27b1717 | ||
|
|
ac88d3e89f | ||
|
|
a353322b58 | ||
|
|
50153a1cb2 | ||
|
|
17f3590d59 | ||
|
|
7786f41cc0 | ||
|
|
019d7208c8 | ||
|
|
2015a1b180 | ||
|
|
db0497b808 | ||
|
|
987a170665 | ||
|
|
33dbf24b56 | ||
|
|
2dc8b5c224 | ||
|
|
5f3d6c85db | ||
|
|
2f9c0e7c7e | ||
|
|
09b8a16414 | ||
|
|
004b1b3830 | ||
|
|
281fd881a0 | ||
|
|
b366b885a1 | ||
|
|
3cb753eeb1 | ||
|
|
d81aaa3ed3 | ||
|
|
e2176dc7ce | ||
|
|
174c22e5f6 | ||
|
|
5440b9c634 | ||
|
|
abec5f82e2 | ||
|
|
bbc4f369ed | ||
|
|
79e9c2e237 | ||
|
|
83517cb53a | ||
|
|
9949e4d508 | ||
|
|
c116b220e9 | ||
|
|
c011b6f67e | ||
|
|
c712dafaee | ||
|
|
a931cf9e8b | ||
|
|
a941a94148 | ||
|
|
8d9534d830 | ||
|
|
47dc5e0dab | ||
|
|
f3885845fc | ||
|
|
c0d3e2da4e | ||
|
|
517112f549 | ||
|
|
6324d86c71 | ||
|
|
5aff2d38a9 | ||
|
|
ee318d4015 | ||
|
|
86146ef819 | ||
|
|
a2efb1dd48 | ||
|
|
ee1eb8cfdf | ||
|
|
0639e03055 | ||
|
|
930487c7fb | ||
|
|
92209f905f | ||
|
|
f22e090b86 | ||
|
|
218a08df93 | ||
|
|
0cb7ce71e0 | ||
|
|
9f21f20d7c | ||
|
|
128aeba0f3 | ||
|
|
03f877919d | ||
|
|
37f50c8773 | ||
|
|
4732e1f064 | ||
|
|
00c830405b | ||
|
|
4b114e1b8a | ||
|
|
0a49c46353 | ||
|
|
47629c89a8 | ||
|
|
89e81a9be2 | ||
|
|
0ff2929644 | ||
|
|
cfc9effa6c | ||
|
|
4669f15f8b | ||
|
|
4112aa68a6 | ||
|
|
6e386a334b | ||
|
|
dbfc39d214 | ||
|
|
61fbf5c8e6 | ||
|
|
be09dfeed9 | ||
|
|
2f842a86fe | ||
|
|
ce026332a5 | ||
|
|
fa220dd709 | ||
|
|
a776464a55 | ||
|
|
9a85277d83 | ||
|
|
05dc93399b | ||
|
|
39fb3e362c | ||
|
|
566f97b580 | ||
|
|
bf0543af23 | ||
|
|
c5684411a0 | ||
|
|
2abe5e39fc | ||
|
|
adf47cd59a | ||
|
|
c531a92eda | ||
|
|
14afc704d4 | ||
|
|
8d70d1ea45 | ||
|
|
5fb99e6a16 | ||
|
|
6e8e1a4110 | ||
|
|
9232fbdf34 | ||
|
|
38eb33f150 | ||
|
|
b54a72afc0 | ||
|
|
62cd19e4ae | ||
|
|
7e3d746b06 | ||
|
|
e7dfcdde74 | ||
|
|
4b89348c00 | ||
|
|
8c99dd055c | ||
|
|
799e632ccb | ||
|
|
c23c30c76f | ||
|
|
00749f5ab3 | ||
|
|
6ea1576513 | ||
|
|
81a16c073a | ||
|
|
23b1e6eded | ||
|
|
438a9b70cc | ||
|
|
e8bfff7b4b | ||
|
|
f564822e78 | ||
|
|
6cf6fa2842 | ||
|
|
d27279092f | ||
|
|
37fd4e6d9b | ||
|
|
cdd92dc692 | ||
|
|
38d25a4cb2 | ||
|
|
2933521a08 | ||
|
|
f6679ce422 | ||
|
|
5d55403f94 | ||
|
|
0a0233f39f | ||
|
|
9936d1b9e2 | ||
|
|
4fad069870 | ||
|
|
0c688b421c | ||
|
|
cb47abecc6 | ||
|
|
fbef849c04 | ||
|
|
0641950f9a | ||
|
|
b7c64f0ded | ||
|
|
90cddf1996 | ||
|
|
7c181fd4f4 | ||
|
|
d16f83fda3 | ||
|
|
5c82400ef8 | ||
|
|
bb081dd1d2 | ||
|
|
019778707d | ||
|
|
afdd2f4cad | ||
|
|
df4336a85e | ||
|
|
51d8a2c322 | ||
|
|
049ce242a4 | ||
|
|
b481d8a00d | ||
|
|
06c72b4fcf | ||
|
|
876b805e50 | ||
|
|
2dcb98226b | ||
|
|
9fedfbe141 | ||
|
|
d73c22bf4d | ||
|
|
ba117854f9 | ||
|
|
527c098ff6 | ||
|
|
d57333406d | ||
|
|
1efcba346a | ||
|
|
bb9d39b8fe | ||
|
|
27c0f9e02d | ||
|
|
41faeeeb03 | ||
|
|
63270e588b | ||
|
|
e54ea773fc | ||
|
|
0d64ddc6dd | ||
|
|
9cd87a6352 | ||
|
|
99f9d47d16 | ||
|
|
bbbe34429e | ||
|
|
11568c2ea3 | ||
|
|
888eb345c0 | ||
|
|
4c727d0ba8 | ||
|
|
bdd68fc210 | ||
|
|
f1bded1270 | ||
|
|
49309b5848 | ||
|
|
c02d7c8ce7 | ||
|
|
3957b0c34e | ||
|
|
ca5a4a704b | ||
|
|
15086a22be | ||
|
|
94fecef137 | ||
|
|
d1f9c750a6 | ||
|
|
99f12b05fa | ||
|
|
8df011a57f | ||
|
|
9a273bb23b | ||
|
|
6fcc7e9c36 | ||
|
|
c8e1383fa9 | ||
|
|
68937a662d | ||
|
|
734106dcb9 | ||
|
|
6306655665 | ||
|
|
0658973a4e | ||
|
|
0d843eaba6 | ||
|
|
5a763e8a5a | ||
|
|
220d4672df | ||
|
|
7757cc1a7f | ||
|
|
2abf39ea4a | ||
|
|
d809f65827 | ||
|
|
7f155ba713 | ||
|
|
7029daa32e | ||
|
|
15c388e0d6 | ||
|
|
9e30f5574f | ||
|
|
7ddc872b52 | ||
|
|
6138075df0 | ||
|
|
0d681f7a7a | ||
|
|
c23ce3365d | ||
|
|
ee53688ca7 | ||
|
|
b16e5c6a81 | ||
|
|
87f21657f8 | ||
|
|
e6df4b37db | ||
|
|
aee356bd10 | ||
|
|
cd7665218d | ||
|
|
e2cdf54177 | ||
|
|
e3a92b09ba | ||
|
|
e2db7a83f6 | ||
|
|
126270d963 | ||
|
|
e6a87428ae | ||
|
|
55e6296e71 | ||
|
|
1ce7942dc2 | ||
|
|
6b6287dda0 | ||
|
|
c74d24f841 | ||
|
|
4cb92b776c | ||
|
|
a55f112cb1 | ||
|
|
5cdc277dd2 | ||
|
|
74efdd6928 | ||
|
|
3825b703fa | ||
|
|
1efb81a61d | ||
|
|
3c26b7179d | ||
|
|
8c648b59cd | ||
|
|
f217d6c66f | ||
|
|
58d9078742 | ||
|
|
58857b9f46 | ||
|
|
c6eaf0b2cf | ||
|
|
693f78e6c2 | ||
|
|
898f0fa029 | ||
|
|
ff54287a73 | ||
|
|
882111c4f2 | ||
|
|
6486544e09 | ||
|
|
2dbfcd32d7 | ||
|
|
8440cef223 | ||
|
|
fd500d3da6 | ||
|
|
525492428d | ||
|
|
72b5c448cf | ||
|
|
03388c3071 | ||
|
|
353e1dd7e4 | ||
|
|
a215f63235 | ||
|
|
dc26601860 | ||
|
|
2a35a36251 | ||
|
|
c74f2555b6 | ||
|
|
fab3dd98fe | ||
|
|
c50393e066 | ||
|
|
a056d5ad8c | ||
|
|
98b143c2d6 | ||
|
|
370ab5df9b | ||
|
|
21959ddfef | ||
|
|
abe79b2724 | ||
|
|
536cfb13e6 | ||
|
|
e35cfc1b03 | ||
|
|
fd86cdb2e2 | ||
|
|
0984e9d601 | ||
|
|
1b5c02fc37 | ||
|
|
e07218906d | ||
|
|
e7b0e8a3cc | ||
|
|
811dae12f9 | ||
|
|
46ec9a9bc9 | ||
|
|
edc52250b8 | ||
|
|
fbd7afefaa | ||
|
|
91af2f94e8 | ||
|
|
e6671190a5 | ||
|
|
4822765fef | ||
|
|
8aa5d25f82 | ||
|
|
d6e3cd9a17 | ||
|
|
6ea8b3ef60 | ||
|
|
1c36f2a798 | ||
|
|
6a890023e9 | ||
|
|
5c0421ebd8 | ||
|
|
9bf2a428f9 | ||
|
|
cba69fdcd4 | ||
|
|
a434fdcb10 | ||
|
|
9776ff9179 | ||
|
|
5590245930 | ||
|
|
5e9c547952 | ||
|
|
266703b50e |
@@ -42,3 +42,7 @@ notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://api.yuzu-emu.org/code/travis/notify
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.ccache
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
docker run -v $(pwd):/yuzu ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh
|
||||
docker run -e CCACHE_DIR=/ccache -v $HOME/.ccache:/ccache -v $(pwd):/yuzu ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
apt-get update
|
||||
apt-get install -y build-essential git libqt5opengl5-dev libsdl2-dev libssl-dev python qtbase5-dev wget
|
||||
|
||||
# Get a recent version of CMake
|
||||
wget https://cmake.org/files/v3.10/cmake-3.10.1-Linux-x86_64.sh
|
||||
sh cmake-3.10.1-Linux-x86_64.sh --exclude-subdir --prefix=/ --skip-license
|
||||
apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev libsdl2-dev libssl-dev python qtbase5-dev wget cmake ninja-build ccache
|
||||
|
||||
cd /yuzu
|
||||
|
||||
export PATH=/usr/lib/ccache:$PATH
|
||||
ln -sf /usr/bin/ccache /usr/lib/ccache/cc
|
||||
ln -sf /usr/bin/ccache /usr/lib/ccache/c++
|
||||
mkdir build && cd build
|
||||
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release
|
||||
make -j4
|
||||
ccache --show-stats > ccache_before
|
||||
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -G Ninja
|
||||
ninja
|
||||
ccache --show-stats > ccache_after
|
||||
diff -U100 ccache_before ccache_after || true
|
||||
|
||||
ctest -VV -C Release
|
||||
|
||||
@@ -7,8 +7,12 @@ export Qt5_DIR=$(brew --prefix)/opt/qt5
|
||||
export UNICORNDIR=$(pwd)/externals/unicorn
|
||||
|
||||
mkdir build && cd build
|
||||
export PATH=/usr/local/opt/ccache/libexec:$PATH
|
||||
ccache --show-stats > ccache_before
|
||||
cmake --version
|
||||
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release
|
||||
make -j4
|
||||
ccache --show-stats > ccache_after
|
||||
diff -U100 ccache_before ccache_after || true
|
||||
|
||||
ctest -VV -C Release
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh -ex
|
||||
|
||||
brew update
|
||||
brew install dylibbundler p7zip qt5 sdl2
|
||||
brew install dylibbundler p7zip qt5 sdl2 ccache
|
||||
brew outdated cmake || brew upgrade cmake
|
||||
|
||||
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 5e20e5a44a...990a569b7a
@@ -35,11 +35,13 @@ namespace Log {
|
||||
SUB(Service, AM) \
|
||||
SUB(Service, AOC) \
|
||||
SUB(Service, APM) \
|
||||
SUB(Service, BCAT) \
|
||||
SUB(Service, Fatal) \
|
||||
SUB(Service, Friend) \
|
||||
SUB(Service, FS) \
|
||||
SUB(Service, HID) \
|
||||
SUB(Service, LM) \
|
||||
SUB(Service, MM) \
|
||||
SUB(Service, NFP) \
|
||||
SUB(Service, NIFM) \
|
||||
SUB(Service, NS) \
|
||||
|
||||
@@ -55,11 +55,13 @@ 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_BCAT, ///< The BCAT 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
|
||||
Service_LM, ///< The LM (Logger) service
|
||||
Service_MM, ///< The MM (Multimedia) service
|
||||
Service_NFP, ///< The NFP service
|
||||
Service_NIFM, ///< The NIFM (Network interface) service
|
||||
Service_NS, ///< The NS services
|
||||
|
||||
@@ -64,6 +64,10 @@ std::string ArrayToString(const u8* data, size_t size, int line_len, bool spaces
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string StringFromBuffer(const std::vector<u8>& data) {
|
||||
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
||||
}
|
||||
|
||||
// Turns " hej " into "hej". Also handles tabs.
|
||||
std::string StripSpaces(const std::string& str) {
|
||||
const size_t s = str.find_first_not_of(" \t\r\n");
|
||||
|
||||
@@ -21,6 +21,8 @@ std::string ToUpper(std::string str);
|
||||
|
||||
std::string ArrayToString(const u8* data, size_t size, int line_len = 20, bool spaces = true);
|
||||
|
||||
std::string StringFromBuffer(const std::vector<u8>& data);
|
||||
|
||||
std::string StripSpaces(const std::string& s);
|
||||
std::string StripQuotes(const std::string& s);
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ add_library(core STATIC
|
||||
arm/unicorn/arm_unicorn.h
|
||||
core.cpp
|
||||
core.h
|
||||
core_cpu.cpp
|
||||
core_cpu.h
|
||||
core_timing.cpp
|
||||
core_timing.h
|
||||
file_sys/directory.h
|
||||
@@ -122,6 +124,10 @@ add_library(core STATIC
|
||||
hle/service/audio/audren_u.h
|
||||
hle/service/audio/codecctl.cpp
|
||||
hle/service/audio/codecctl.h
|
||||
hle/service/bcat/module.cpp
|
||||
hle/service/bcat/module.h
|
||||
hle/service/bcat/bcat.cpp
|
||||
hle/service/bcat/bcat.h
|
||||
hle/service/fatal/fatal.cpp
|
||||
hle/service/fatal/fatal.h
|
||||
hle/service/fatal/fatal_p.cpp
|
||||
@@ -142,6 +148,8 @@ add_library(core STATIC
|
||||
hle/service/hid/hid.h
|
||||
hle/service/lm/lm.cpp
|
||||
hle/service/lm/lm.h
|
||||
hle/service/mm/mm_u.cpp
|
||||
hle/service/mm/mm_u.h
|
||||
hle/service/nifm/nifm.cpp
|
||||
hle/service/nifm/nifm.h
|
||||
hle/service/nifm/nifm_a.cpp
|
||||
@@ -169,6 +177,8 @@ add_library(core STATIC
|
||||
hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
|
||||
hle/service/nvdrv/devices/nvhost_gpu.cpp
|
||||
hle/service/nvdrv/devices/nvhost_gpu.h
|
||||
hle/service/nvdrv/devices/nvhost_nvdec.cpp
|
||||
hle/service/nvdrv/devices/nvhost_nvdec.h
|
||||
hle/service/nvdrv/devices/nvmap.cpp
|
||||
hle/service/nvdrv/devices/nvmap.h
|
||||
hle/service/nvdrv/interface.cpp
|
||||
|
||||
@@ -35,6 +35,17 @@ LoadDll LoadDll::g_load_dll;
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_data) {
|
||||
GDBStub::BreakpointAddress bkpt =
|
||||
GDBStub::GetNextBreakpointFromAddress(address, GDBStub::BreakpointType::Execute);
|
||||
if (GDBStub::IsMemoryBreak() ||
|
||||
(bkpt.type != GDBStub::BreakpointType::None && address == bkpt.address)) {
|
||||
auto core = static_cast<ARM_Unicorn*>(user_data);
|
||||
core->RecordBreak(bkpt);
|
||||
uc_emu_stop(uc);
|
||||
}
|
||||
}
|
||||
|
||||
static void InterruptHook(uc_engine* uc, u32 intNo, void* user_data) {
|
||||
u32 esr{};
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
|
||||
@@ -52,7 +63,7 @@ static void InterruptHook(uc_engine* uc, u32 intNo, void* user_data) {
|
||||
static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value,
|
||||
void* user_data) {
|
||||
ARM_Interface::ThreadContext ctx{};
|
||||
Core::CPU().SaveContext(ctx);
|
||||
Core::CurrentArmInterface().SaveContext(ctx);
|
||||
ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x{:X}, pc=0x{:X}, lr=0x{:X}", addr,
|
||||
ctx.pc, ctx.cpu_registers[30]);
|
||||
return {};
|
||||
@@ -67,6 +78,10 @@ ARM_Unicorn::ARM_Unicorn() {
|
||||
uc_hook hook{};
|
||||
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_INTR, (void*)InterruptHook, this, 0, -1));
|
||||
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, this, 0, -1));
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_CODE, (void*)CodeHook, this, 0, -1));
|
||||
last_bkpt_hit = false;
|
||||
}
|
||||
}
|
||||
|
||||
ARM_Unicorn::~ARM_Unicorn() {
|
||||
@@ -155,7 +170,11 @@ void ARM_Unicorn::SetTlsAddress(VAddr base) {
|
||||
}
|
||||
|
||||
void ARM_Unicorn::Run() {
|
||||
ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0));
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
ExecuteInstructions(std::max(4000000, 0));
|
||||
} else {
|
||||
ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0));
|
||||
}
|
||||
}
|
||||
|
||||
void ARM_Unicorn::Step() {
|
||||
@@ -168,6 +187,18 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
|
||||
MICROPROFILE_SCOPE(ARM_Jit);
|
||||
CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
|
||||
CoreTiming::AddTicks(num_instructions);
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
if (last_bkpt_hit) {
|
||||
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
|
||||
}
|
||||
Kernel::Thread* thread = Kernel::GetCurrentThread();
|
||||
SaveContext(thread->context);
|
||||
if (last_bkpt_hit) {
|
||||
last_bkpt_hit = false;
|
||||
GDBStub::Break();
|
||||
}
|
||||
GDBStub::SendTrap(thread, 5);
|
||||
}
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SaveContext(ARM_Interface::ThreadContext& ctx) {
|
||||
@@ -233,3 +264,8 @@ void ARM_Unicorn::PrepareReschedule() {
|
||||
}
|
||||
|
||||
void ARM_Unicorn::ClearInstructionCache() {}
|
||||
|
||||
void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {
|
||||
last_bkpt = bkpt;
|
||||
last_bkpt_hit = true;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <unicorn/unicorn.h>
|
||||
#include "common/common_types.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
|
||||
class ARM_Unicorn final : public ARM_Interface {
|
||||
public:
|
||||
@@ -35,7 +36,10 @@ public:
|
||||
void Step() override;
|
||||
void ClearInstructionCache() override;
|
||||
void PageTableChanged() override{};
|
||||
void RecordBreak(GDBStub::BreakpointAddress bkpt);
|
||||
|
||||
private:
|
||||
uc_engine* uc{};
|
||||
GDBStub::BreakpointAddress last_bkpt{};
|
||||
bool last_bkpt_hit;
|
||||
};
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include "common/logging/log.h"
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||
#endif
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
@@ -31,11 +27,31 @@ namespace Core {
|
||||
|
||||
System::~System() = default;
|
||||
|
||||
/// Runs a CPU core while the system is powered on
|
||||
static void RunCpuCore(std::shared_ptr<Cpu> cpu_state) {
|
||||
while (Core::System().GetInstance().IsPoweredOn()) {
|
||||
cpu_state->RunLoop(true);
|
||||
}
|
||||
}
|
||||
|
||||
Cpu& System::CurrentCpuCore() {
|
||||
// If multicore is enabled, use host thread to figure out the current CPU core
|
||||
if (Settings::values.use_multi_core) {
|
||||
const auto& search = thread_to_cpu.find(std::this_thread::get_id());
|
||||
ASSERT(search != thread_to_cpu.end());
|
||||
ASSERT(search->second);
|
||||
return *search->second;
|
||||
}
|
||||
|
||||
// Otherwise, use single-threaded mode active_core variable
|
||||
return *cpu_cores[active_core];
|
||||
}
|
||||
|
||||
System::ResultStatus System::RunLoop(bool tight_loop) {
|
||||
status = ResultStatus::Success;
|
||||
if (!cpu_core) {
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
}
|
||||
|
||||
// Update thread_to_cpu in case Core 0 is run from a different host thread
|
||||
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
|
||||
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
GDBStub::HandlePacket();
|
||||
@@ -52,25 +68,14 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't have a currently active thread then don't execute instructions,
|
||||
// instead advance to the next event and try to yield to the next thread
|
||||
if (Kernel::GetCurrentThread() == nullptr) {
|
||||
NGLOG_TRACE(Core_ARM, "Idling");
|
||||
CoreTiming::Idle();
|
||||
CoreTiming::Advance();
|
||||
PrepareReschedule();
|
||||
} else {
|
||||
CoreTiming::Advance();
|
||||
if (tight_loop) {
|
||||
cpu_core->Run();
|
||||
} else {
|
||||
cpu_core->Step();
|
||||
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
|
||||
cpu_cores[active_core]->RunLoop(tight_loop);
|
||||
if (Settings::values.use_multi_core) {
|
||||
// Cores 1-3 are run on other threads in this mode
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
HW::Update();
|
||||
Reschedule();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -133,21 +138,26 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
|
||||
}
|
||||
|
||||
void System::PrepareReschedule() {
|
||||
cpu_core->PrepareReschedule();
|
||||
reschedule_pending = true;
|
||||
CurrentCpuCore().PrepareReschedule();
|
||||
}
|
||||
|
||||
PerfStats::Results System::GetAndResetPerfStats() {
|
||||
return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs());
|
||||
}
|
||||
|
||||
void System::Reschedule() {
|
||||
if (!reschedule_pending) {
|
||||
return;
|
||||
}
|
||||
const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(size_t core_index) {
|
||||
ASSERT(core_index < NUM_CPU_CORES);
|
||||
return cpu_cores[core_index]->Scheduler();
|
||||
}
|
||||
|
||||
reschedule_pending = false;
|
||||
Core::System::GetInstance().Scheduler().Reschedule();
|
||||
ARM_Interface& System::ArmInterface(size_t core_index) {
|
||||
ASSERT(core_index < NUM_CPU_CORES);
|
||||
return cpu_cores[core_index]->ArmInterface();
|
||||
}
|
||||
|
||||
Cpu& System::CpuCore(size_t core_index) {
|
||||
ASSERT(core_index < NUM_CPU_CORES);
|
||||
return *cpu_cores[core_index];
|
||||
}
|
||||
|
||||
System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
|
||||
@@ -157,26 +167,17 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
|
||||
|
||||
current_process = Kernel::Process::Create("main");
|
||||
|
||||
if (Settings::values.use_cpu_jit) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
cpu_core = std::make_shared<ARM_Dynarmic>();
|
||||
#else
|
||||
cpu_core = std::make_shared<ARM_Unicorn>();
|
||||
NGLOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
|
||||
#endif
|
||||
} else {
|
||||
cpu_core = std::make_shared<ARM_Unicorn>();
|
||||
cpu_barrier = std::make_shared<CpuBarrier>();
|
||||
for (size_t index = 0; index < cpu_cores.size(); ++index) {
|
||||
cpu_cores[index] = std::make_shared<Cpu>(cpu_barrier, index);
|
||||
}
|
||||
|
||||
gpu_core = std::make_unique<Tegra::GPU>();
|
||||
|
||||
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
||||
|
||||
service_manager = std::make_shared<Service::SM::ServiceManager>();
|
||||
|
||||
HW::Init();
|
||||
Kernel::Init(system_mode);
|
||||
scheduler = std::make_unique<Kernel::Scheduler>(cpu_core.get());
|
||||
Service::Init(service_manager);
|
||||
GDBStub::Init();
|
||||
|
||||
@@ -184,6 +185,17 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
|
||||
return ResultStatus::ErrorVideoCore;
|
||||
}
|
||||
|
||||
// Create threads for CPU cores 1-3, and build thread_to_cpu map
|
||||
// CPU core 0 is run on the main thread
|
||||
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
|
||||
if (Settings::values.use_multi_core) {
|
||||
for (size_t index = 0; index < cpu_core_threads.size(); ++index) {
|
||||
cpu_core_threads[index] =
|
||||
std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]);
|
||||
thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1];
|
||||
}
|
||||
}
|
||||
|
||||
NGLOG_DEBUG(Core, "Initialized OK");
|
||||
|
||||
// Reset counters and set time origin to current frame
|
||||
@@ -207,15 +219,30 @@ void System::Shutdown() {
|
||||
VideoCore::Shutdown();
|
||||
GDBStub::Shutdown();
|
||||
Service::Shutdown();
|
||||
scheduler.reset();
|
||||
Kernel::Shutdown();
|
||||
HW::Shutdown();
|
||||
service_manager.reset();
|
||||
telemetry_session.reset();
|
||||
gpu_core.reset();
|
||||
cpu_core.reset();
|
||||
|
||||
// Close all CPU/threading state
|
||||
cpu_barrier->NotifyEnd();
|
||||
if (Settings::values.use_multi_core) {
|
||||
for (auto& thread : cpu_core_threads) {
|
||||
thread->join();
|
||||
thread.reset();
|
||||
}
|
||||
}
|
||||
thread_to_cpu.clear();
|
||||
for (auto& cpu_core : cpu_cores) {
|
||||
cpu_core.reset();
|
||||
}
|
||||
cpu_barrier.reset();
|
||||
|
||||
// Close core timing
|
||||
CoreTiming::Shutdown();
|
||||
|
||||
// Close app loader
|
||||
app_loader.reset();
|
||||
|
||||
NGLOG_DEBUG(Core, "Shutdown OK");
|
||||
|
||||
@@ -4,9 +4,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include "common/common_types.h"
|
||||
#include "core/core_cpu.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/loader/loader.h"
|
||||
@@ -89,7 +92,7 @@ public:
|
||||
* @returns True if the emulated system is powered on, otherwise false.
|
||||
*/
|
||||
bool IsPoweredOn() const {
|
||||
return cpu_core != nullptr;
|
||||
return cpu_barrier && cpu_barrier->IsAlive();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,24 +106,34 @@ public:
|
||||
/// Prepare the core emulation for a reschedule
|
||||
void PrepareReschedule();
|
||||
|
||||
/// Gets and resets core performance statistics
|
||||
PerfStats::Results GetAndResetPerfStats();
|
||||
|
||||
/**
|
||||
* Gets a reference to the emulated CPU.
|
||||
* @returns A reference to the emulated CPU.
|
||||
*/
|
||||
ARM_Interface& CPU() {
|
||||
return *cpu_core;
|
||||
/// Gets an ARM interface to the CPU core that is currently running
|
||||
ARM_Interface& CurrentArmInterface() {
|
||||
return CurrentCpuCore().ArmInterface();
|
||||
}
|
||||
|
||||
/// Gets an ARM interface to the CPU core with the specified index
|
||||
ARM_Interface& ArmInterface(size_t core_index);
|
||||
|
||||
/// Gets a CPU interface to the CPU core with the specified index
|
||||
Cpu& CpuCore(size_t core_index);
|
||||
|
||||
/// Gets the GPU interface
|
||||
Tegra::GPU& GPU() {
|
||||
return *gpu_core;
|
||||
}
|
||||
|
||||
Kernel::Scheduler& Scheduler() {
|
||||
return *scheduler;
|
||||
/// Gets the scheduler for the CPU core that is currently running
|
||||
Kernel::Scheduler& CurrentScheduler() {
|
||||
return *CurrentCpuCore().Scheduler();
|
||||
}
|
||||
|
||||
/// Gets the scheduler for the CPU core with the specified index
|
||||
const std::shared_ptr<Kernel::Scheduler>& Scheduler(size_t core_index);
|
||||
|
||||
/// Gets the current process
|
||||
Kernel::SharedPtr<Kernel::Process>& CurrentProcess() {
|
||||
return current_process;
|
||||
}
|
||||
@@ -155,6 +168,9 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
/// Returns the currently running CPU core
|
||||
Cpu& CurrentCpuCore();
|
||||
|
||||
/**
|
||||
* Initialize the emulated system.
|
||||
* @param emu_window Pointer to the host-system window used for video output and keyboard input.
|
||||
@@ -163,22 +179,15 @@ private:
|
||||
*/
|
||||
ResultStatus Init(EmuWindow* emu_window, u32 system_mode);
|
||||
|
||||
/// Reschedule the core emulation
|
||||
void Reschedule();
|
||||
|
||||
/// AppLoader used to load the current executing application
|
||||
std::unique_ptr<Loader::AppLoader> app_loader;
|
||||
|
||||
std::shared_ptr<ARM_Interface> cpu_core;
|
||||
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
|
||||
bool reschedule_pending{};
|
||||
std::shared_ptr<CpuBarrier> cpu_barrier;
|
||||
std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
|
||||
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
|
||||
size_t active_core{}; ///< Active core, only used in single thread mode
|
||||
|
||||
/// Service manager
|
||||
std::shared_ptr<Service::SM::ServiceManager> service_manager;
|
||||
@@ -190,10 +199,13 @@ private:
|
||||
|
||||
ResultStatus status = ResultStatus::Success;
|
||||
std::string status_details = "";
|
||||
|
||||
/// Map of guest threads to CPU cores
|
||||
std::map<std::thread::id, std::shared_ptr<Cpu>> thread_to_cpu;
|
||||
};
|
||||
|
||||
inline ARM_Interface& CPU() {
|
||||
return System::GetInstance().CPU();
|
||||
inline ARM_Interface& CurrentArmInterface() {
|
||||
return System::GetInstance().CurrentArmInterface();
|
||||
}
|
||||
|
||||
inline TelemetrySession& Telemetry() {
|
||||
|
||||
119
src/core/core_cpu.cpp
Normal file
119
src/core/core_cpu.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||
#endif
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
#include "core/core_cpu.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
void CpuBarrier::NotifyEnd() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
end = true;
|
||||
condition.notify_all();
|
||||
}
|
||||
|
||||
bool CpuBarrier::Rendezvous() {
|
||||
if (!Settings::values.use_multi_core) {
|
||||
// Meaningless when running in single-core mode
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!end) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
|
||||
--cores_waiting;
|
||||
if (!cores_waiting) {
|
||||
cores_waiting = NUM_CPU_CORES;
|
||||
condition.notify_all();
|
||||
return true;
|
||||
}
|
||||
|
||||
condition.wait(lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Cpu::Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index)
|
||||
: cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} {
|
||||
|
||||
if (Settings::values.use_cpu_jit) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
arm_interface = std::make_shared<ARM_Dynarmic>();
|
||||
#else
|
||||
cpu_core = std::make_shared<ARM_Unicorn>();
|
||||
NGLOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
|
||||
#endif
|
||||
} else {
|
||||
arm_interface = std::make_shared<ARM_Unicorn>();
|
||||
}
|
||||
|
||||
scheduler = std::make_shared<Kernel::Scheduler>(arm_interface.get());
|
||||
}
|
||||
|
||||
void Cpu::RunLoop(bool tight_loop) {
|
||||
// Wait for all other CPU cores to complete the previous slice, such that they run in lock-step
|
||||
if (!cpu_barrier->Rendezvous()) {
|
||||
// If rendezvous failed, session has been killed
|
||||
return;
|
||||
}
|
||||
|
||||
// If we don't have a currently active thread then don't execute instructions,
|
||||
// instead advance to the next event and try to yield to the next thread
|
||||
if (Kernel::GetCurrentThread() == nullptr) {
|
||||
NGLOG_TRACE(Core, "Core-{} idling", core_index);
|
||||
|
||||
if (IsMainCore()) {
|
||||
CoreTiming::Idle();
|
||||
CoreTiming::Advance();
|
||||
}
|
||||
|
||||
PrepareReschedule();
|
||||
} else {
|
||||
if (IsMainCore()) {
|
||||
CoreTiming::Advance();
|
||||
}
|
||||
|
||||
if (tight_loop) {
|
||||
arm_interface->Run();
|
||||
} else {
|
||||
arm_interface->Step();
|
||||
}
|
||||
}
|
||||
|
||||
Reschedule();
|
||||
}
|
||||
|
||||
void Cpu::SingleStep() {
|
||||
return RunLoop(false);
|
||||
}
|
||||
|
||||
void Cpu::PrepareReschedule() {
|
||||
arm_interface->PrepareReschedule();
|
||||
reschedule_pending = true;
|
||||
}
|
||||
|
||||
void Cpu::Reschedule() {
|
||||
if (!reschedule_pending) {
|
||||
return;
|
||||
}
|
||||
|
||||
reschedule_pending = false;
|
||||
scheduler->Reschedule();
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
78
src/core/core_cpu.h
Normal file
78
src/core/core_cpu.h
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
|
||||
class ARM_Interface;
|
||||
|
||||
namespace Kernel {
|
||||
class Scheduler;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
constexpr unsigned NUM_CPU_CORES{4};
|
||||
|
||||
class CpuBarrier {
|
||||
public:
|
||||
bool IsAlive() const {
|
||||
return !end;
|
||||
}
|
||||
|
||||
void NotifyEnd();
|
||||
|
||||
bool Rendezvous();
|
||||
|
||||
private:
|
||||
unsigned cores_waiting{NUM_CPU_CORES};
|
||||
std::mutex mutex;
|
||||
std::condition_variable condition;
|
||||
std::atomic<bool> end{};
|
||||
};
|
||||
|
||||
class Cpu {
|
||||
public:
|
||||
Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index);
|
||||
|
||||
void RunLoop(bool tight_loop = true);
|
||||
|
||||
void SingleStep();
|
||||
|
||||
void PrepareReschedule();
|
||||
|
||||
ARM_Interface& ArmInterface() {
|
||||
return *arm_interface;
|
||||
}
|
||||
|
||||
const ARM_Interface& ArmInterface() const {
|
||||
return *arm_interface;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Kernel::Scheduler>& Scheduler() const {
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
bool IsMainCore() const {
|
||||
return core_index == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void Reschedule();
|
||||
|
||||
std::shared_ptr<ARM_Interface> arm_interface;
|
||||
std::shared_ptr<CpuBarrier> cpu_barrier;
|
||||
std::shared_ptr<Kernel::Scheduler> scheduler;
|
||||
|
||||
bool reschedule_pending{};
|
||||
size_t core_index;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
@@ -73,21 +73,21 @@ u32 PartitionFilesystem::GetNumEntries() const {
|
||||
return pfs_header.num_entries;
|
||||
}
|
||||
|
||||
u64 PartitionFilesystem::GetEntryOffset(int index) const {
|
||||
u64 PartitionFilesystem::GetEntryOffset(u32 index) const {
|
||||
if (index > GetNumEntries())
|
||||
return 0;
|
||||
|
||||
return content_offset + pfs_entries[index].fs_entry.offset;
|
||||
}
|
||||
|
||||
u64 PartitionFilesystem::GetEntrySize(int index) const {
|
||||
u64 PartitionFilesystem::GetEntrySize(u32 index) const {
|
||||
if (index > GetNumEntries())
|
||||
return 0;
|
||||
|
||||
return pfs_entries[index].fs_entry.size;
|
||||
}
|
||||
|
||||
std::string PartitionFilesystem::GetEntryName(int index) const {
|
||||
std::string PartitionFilesystem::GetEntryName(u32 index) const {
|
||||
if (index > GetNumEntries())
|
||||
return "";
|
||||
|
||||
|
||||
@@ -27,9 +27,9 @@ public:
|
||||
Loader::ResultStatus Load(const std::vector<u8>& file_data, size_t offset = 0);
|
||||
|
||||
u32 GetNumEntries() const;
|
||||
u64 GetEntryOffset(int index) const;
|
||||
u64 GetEntrySize(int index) const;
|
||||
std::string GetEntryName(int index) const;
|
||||
u64 GetEntryOffset(u32 index) const;
|
||||
u64 GetEntrySize(u32 index) const;
|
||||
std::string GetEntryName(u32 index) const;
|
||||
u64 GetFileOffset(const std::string& name) const;
|
||||
u64 GetFileSize(const std::string& name) const;
|
||||
|
||||
|
||||
@@ -32,9 +32,13 @@
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_cpu.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
@@ -137,15 +141,17 @@ static u8 command_buffer[GDB_BUFFER_SIZE];
|
||||
static u32 command_length;
|
||||
|
||||
static u32 latest_signal = 0;
|
||||
static bool step_break = false;
|
||||
static bool memory_break = false;
|
||||
|
||||
static Kernel::Thread* current_thread = nullptr;
|
||||
|
||||
// Binding to a port within the reserved ports range (0-1023) requires root permissions,
|
||||
// so default to a port outside of that range.
|
||||
static u16 gdbstub_port = 24689;
|
||||
|
||||
static bool halt_loop = true;
|
||||
static bool step_loop = false;
|
||||
static bool send_trap = false;
|
||||
|
||||
// If set to false, the server will never be started and no
|
||||
// gdbstub-related functions will be executed.
|
||||
@@ -165,6 +171,53 @@ static std::map<u64, Breakpoint> breakpoints_execute;
|
||||
static std::map<u64, Breakpoint> breakpoints_read;
|
||||
static std::map<u64, Breakpoint> breakpoints_write;
|
||||
|
||||
static Kernel::Thread* FindThreadById(int id) {
|
||||
for (int core = 0; core < Core::NUM_CPU_CORES; core++) {
|
||||
auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
|
||||
for (auto thread : threads) {
|
||||
if (thread->GetThreadId() == id) {
|
||||
current_thread = thread.get();
|
||||
return current_thread;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static u64 RegRead(int id, Kernel::Thread* thread = nullptr) {
|
||||
if (!thread) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (id < SP_REGISTER) {
|
||||
return thread->context.cpu_registers[id];
|
||||
} else if (id == SP_REGISTER) {
|
||||
return thread->context.sp;
|
||||
} else if (id == PC_REGISTER) {
|
||||
return thread->context.pc;
|
||||
} else if (id == CPSR_REGISTER) {
|
||||
return thread->context.cpsr;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void RegWrite(int id, u64 val, Kernel::Thread* thread = nullptr) {
|
||||
if (!thread) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (id < SP_REGISTER) {
|
||||
thread->context.cpu_registers[id] = val;
|
||||
} else if (id == SP_REGISTER) {
|
||||
thread->context.sp = val;
|
||||
} else if (id == PC_REGISTER) {
|
||||
thread->context.pc = val;
|
||||
} else if (id == CPSR_REGISTER) {
|
||||
thread->context.cpsr = val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns hex string character into the equivalent byte.
|
||||
*
|
||||
@@ -193,7 +246,7 @@ static u8 NibbleToHex(u8 n) {
|
||||
if (n < 0xA) {
|
||||
return '0' + n;
|
||||
} else {
|
||||
return 'A' + n - 0xA;
|
||||
return 'a' + n - 0xA;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -439,6 +492,8 @@ static void SendReply(const char* reply) {
|
||||
return;
|
||||
}
|
||||
|
||||
NGLOG_DEBUG(Debug_GDBStub, "Reply: {}", reply);
|
||||
|
||||
memset(command_buffer, 0, sizeof(command_buffer));
|
||||
|
||||
command_length = static_cast<u32>(strlen(reply));
|
||||
@@ -483,6 +538,22 @@ static void HandleQuery() {
|
||||
} else if (strncmp(query, "Xfer:features:read:target.xml:",
|
||||
strlen("Xfer:features:read:target.xml:")) == 0) {
|
||||
SendReply(target_xml);
|
||||
} else if (strncmp(query, "Offsets", strlen("Offsets")) == 0) {
|
||||
std::string buffer = fmt::format("TextSeg={:0x}", Memory::PROCESS_IMAGE_VADDR);
|
||||
SendReply(buffer.c_str());
|
||||
} else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
|
||||
std::string val = "m";
|
||||
for (int core = 0; core < Core::NUM_CPU_CORES; core++) {
|
||||
auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
|
||||
for (auto thread : threads) {
|
||||
val += fmt::format("{:x}", thread->GetThreadId());
|
||||
val += ",";
|
||||
}
|
||||
}
|
||||
val.pop_back();
|
||||
SendReply(val.c_str());
|
||||
} else if (strncmp(query, "sThreadInfo", strlen("sThreadInfo")) == 0) {
|
||||
SendReply("l");
|
||||
} else {
|
||||
SendReply("");
|
||||
}
|
||||
@@ -490,11 +561,40 @@ static void HandleQuery() {
|
||||
|
||||
/// Handle set thread command from gdb client.
|
||||
static void HandleSetThread() {
|
||||
if (memcmp(command_buffer, "Hg0", 3) == 0 || memcmp(command_buffer, "Hc-1", 4) == 0 ||
|
||||
memcmp(command_buffer, "Hc0", 4) == 0 || memcmp(command_buffer, "Hc1", 4) == 0) {
|
||||
return SendReply("OK");
|
||||
if (memcmp(command_buffer, "Hc", 2) == 0 || memcmp(command_buffer, "Hg", 2) == 0) {
|
||||
int thread_id = -1;
|
||||
if (command_buffer[2] != '-') {
|
||||
thread_id = static_cast<int>(HexToInt(
|
||||
command_buffer + 2,
|
||||
command_length - 2 /*strlen(reinterpret_cast<char*>(command_buffer) + 2)*/));
|
||||
}
|
||||
if (thread_id >= 1) {
|
||||
current_thread = FindThreadById(thread_id);
|
||||
}
|
||||
if (!current_thread) {
|
||||
thread_id = 1;
|
||||
current_thread = FindThreadById(thread_id);
|
||||
}
|
||||
if (current_thread) {
|
||||
SendReply("OK");
|
||||
return;
|
||||
}
|
||||
}
|
||||
SendReply("E01");
|
||||
}
|
||||
|
||||
/// Handle thread alive command from gdb client.
|
||||
static void HandleThreadAlive() {
|
||||
int thread_id = static_cast<int>(
|
||||
HexToInt(command_buffer + 1,
|
||||
command_length - 1 /*strlen(reinterpret_cast<char*>(command_buffer) + 1)*/));
|
||||
if (thread_id == 0) {
|
||||
thread_id = 1;
|
||||
}
|
||||
if (FindThreadById(thread_id)) {
|
||||
SendReply("OK");
|
||||
return;
|
||||
}
|
||||
SendReply("E01");
|
||||
}
|
||||
|
||||
@@ -503,15 +603,24 @@ static void HandleSetThread() {
|
||||
*
|
||||
* @param signal Signal to be sent to client.
|
||||
*/
|
||||
static void SendSignal(u32 signal) {
|
||||
static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) {
|
||||
if (gdbserver_socket == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
latest_signal = signal;
|
||||
|
||||
std::string buffer = fmt::format("T{:02x}", latest_signal);
|
||||
NGLOG_DEBUG(Debug_GDBStub, "Response: {}", buffer);
|
||||
std::string buffer;
|
||||
if (full) {
|
||||
buffer = fmt::format("T{:02x}{:02x}:{:016x};{:02x}:{:016x};", latest_signal, PC_REGISTER,
|
||||
Common::swap64(RegRead(PC_REGISTER, thread)), SP_REGISTER,
|
||||
Common::swap64(RegRead(SP_REGISTER, thread)));
|
||||
} else {
|
||||
buffer = fmt::format("T{:02x};", latest_signal);
|
||||
}
|
||||
|
||||
buffer += fmt::format("thread:{:x};", thread->GetThreadId());
|
||||
|
||||
SendReply(buffer.c_str());
|
||||
}
|
||||
|
||||
@@ -527,7 +636,7 @@ static void ReadCommand() {
|
||||
} else if (c == 0x03) {
|
||||
NGLOG_INFO(Debug_GDBStub, "gdb: found break command");
|
||||
halt_loop = true;
|
||||
SendSignal(SIGTRAP);
|
||||
SendSignal(current_thread, SIGTRAP);
|
||||
return;
|
||||
} else if (c != GDB_STUB_START) {
|
||||
NGLOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte {:02X}", c);
|
||||
@@ -598,11 +707,11 @@ static void ReadRegister() {
|
||||
}
|
||||
|
||||
if (id <= SP_REGISTER) {
|
||||
LongToGdbHex(reply, Core::CPU().GetReg(static_cast<int>(id)));
|
||||
LongToGdbHex(reply, RegRead(id, current_thread));
|
||||
} else if (id == PC_REGISTER) {
|
||||
LongToGdbHex(reply, Core::CPU().GetPC());
|
||||
LongToGdbHex(reply, RegRead(id, current_thread));
|
||||
} else if (id == CPSR_REGISTER) {
|
||||
IntToGdbHex(reply, Core::CPU().GetCPSR());
|
||||
IntToGdbHex(reply, (u32)RegRead(id, current_thread));
|
||||
} else {
|
||||
return SendReply("E01");
|
||||
}
|
||||
@@ -618,16 +727,16 @@ static void ReadRegisters() {
|
||||
u8* bufptr = buffer;
|
||||
|
||||
for (int reg = 0; reg <= SP_REGISTER; reg++) {
|
||||
LongToGdbHex(bufptr + reg * 16, Core::CPU().GetReg(reg));
|
||||
LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread));
|
||||
}
|
||||
|
||||
bufptr += (32 * 16);
|
||||
|
||||
LongToGdbHex(bufptr, Core::CPU().GetPC());
|
||||
LongToGdbHex(bufptr, RegRead(PC_REGISTER, current_thread));
|
||||
|
||||
bufptr += 16;
|
||||
|
||||
IntToGdbHex(bufptr, Core::CPU().GetCPSR());
|
||||
IntToGdbHex(bufptr, (u32)RegRead(CPSR_REGISTER, current_thread));
|
||||
|
||||
bufptr += 8;
|
||||
|
||||
@@ -646,11 +755,11 @@ static void WriteRegister() {
|
||||
}
|
||||
|
||||
if (id <= SP_REGISTER) {
|
||||
Core::CPU().SetReg(id, GdbHexToLong(buffer_ptr));
|
||||
RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
|
||||
} else if (id == PC_REGISTER) {
|
||||
Core::CPU().SetPC(GdbHexToLong(buffer_ptr));
|
||||
RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
|
||||
} else if (id == CPSR_REGISTER) {
|
||||
Core::CPU().SetCPSR(GdbHexToInt(buffer_ptr));
|
||||
RegWrite(id, GdbHexToInt(buffer_ptr), current_thread);
|
||||
} else {
|
||||
return SendReply("E01");
|
||||
}
|
||||
@@ -667,11 +776,11 @@ static void WriteRegisters() {
|
||||
|
||||
for (int i = 0, reg = 0; reg <= CPSR_REGISTER; i++, reg++) {
|
||||
if (reg <= SP_REGISTER) {
|
||||
Core::CPU().SetReg(reg, GdbHexToLong(buffer_ptr + i * 16));
|
||||
RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
|
||||
} else if (reg == PC_REGISTER) {
|
||||
Core::CPU().SetPC(GdbHexToLong(buffer_ptr + i * 16));
|
||||
RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
|
||||
} else if (reg == CPSR_REGISTER) {
|
||||
Core::CPU().SetCPSR(GdbHexToInt(buffer_ptr + i * 16));
|
||||
RegWrite(CPSR_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread);
|
||||
} else {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
@@ -734,7 +843,7 @@ static void WriteMemory() {
|
||||
void Break(bool is_memory_break) {
|
||||
if (!halt_loop) {
|
||||
halt_loop = true;
|
||||
SendSignal(SIGTRAP);
|
||||
send_trap = true;
|
||||
}
|
||||
|
||||
memory_break = is_memory_break;
|
||||
@@ -744,10 +853,10 @@ void Break(bool is_memory_break) {
|
||||
static void Step() {
|
||||
step_loop = true;
|
||||
halt_loop = true;
|
||||
step_break = true;
|
||||
SendSignal(SIGTRAP);
|
||||
send_trap = true;
|
||||
}
|
||||
|
||||
/// Tell the CPU if we hit a memory breakpoint.
|
||||
bool IsMemoryBreak() {
|
||||
if (IsConnected()) {
|
||||
return false;
|
||||
@@ -759,7 +868,6 @@ bool IsMemoryBreak() {
|
||||
/// Tell the CPU to continue executing.
|
||||
static void Continue() {
|
||||
memory_break = false;
|
||||
step_break = false;
|
||||
step_loop = false;
|
||||
halt_loop = false;
|
||||
}
|
||||
@@ -898,7 +1006,7 @@ void HandlePacket() {
|
||||
HandleSetThread();
|
||||
break;
|
||||
case '?':
|
||||
SendSignal(latest_signal);
|
||||
SendSignal(current_thread, latest_signal);
|
||||
break;
|
||||
case 'k':
|
||||
Shutdown();
|
||||
@@ -935,6 +1043,9 @@ void HandlePacket() {
|
||||
case 'Z':
|
||||
AddBreakpoint();
|
||||
break;
|
||||
case 'T':
|
||||
HandleThreadAlive();
|
||||
break;
|
||||
default:
|
||||
SendReply("");
|
||||
break;
|
||||
@@ -1079,4 +1190,11 @@ bool GetCpuStepFlag() {
|
||||
void SetCpuStepFlag(bool is_step) {
|
||||
step_loop = is_step;
|
||||
}
|
||||
|
||||
void SendTrap(Kernel::Thread* thread, int trap) {
|
||||
if (send_trap) {
|
||||
send_trap = false;
|
||||
SendSignal(thread, trap);
|
||||
}
|
||||
}
|
||||
}; // namespace GDBStub
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace GDBStub {
|
||||
|
||||
@@ -91,4 +92,12 @@ bool GetCpuStepFlag();
|
||||
* @param is_step
|
||||
*/
|
||||
void SetCpuStepFlag(bool is_step);
|
||||
|
||||
/**
|
||||
* Send trap signal from thread back to the gdbstub server.
|
||||
*
|
||||
* @param thread Sending thread.
|
||||
* @param trap Trap no.
|
||||
*/
|
||||
void SendTrap(Kernel::Thread* thread, int trap);
|
||||
} // namespace GDBStub
|
||||
|
||||
@@ -29,9 +29,14 @@ enum class ControlCommand : u32 {
|
||||
};
|
||||
|
||||
enum class CommandType : u32 {
|
||||
Invalid = 0,
|
||||
LegacyRequest = 1,
|
||||
Close = 2,
|
||||
LegacyControl = 3,
|
||||
Request = 4,
|
||||
Control = 5,
|
||||
RequestWithContext = 6,
|
||||
ControlWithContext = 7,
|
||||
Unspecified,
|
||||
};
|
||||
|
||||
|
||||
@@ -21,7 +21,9 @@ enum {
|
||||
|
||||
// Confirmed Switch OS error codes
|
||||
MisalignedAddress = 102,
|
||||
InvalidProcessorId = 113,
|
||||
InvalidHandle = 114,
|
||||
InvalidCombination = 116,
|
||||
Timeout = 117,
|
||||
SynchronizationCanceled = 118,
|
||||
TooLarge = 119,
|
||||
|
||||
@@ -110,7 +110,9 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
// Padding to align to 16 bytes
|
||||
rp.AlignWithPadding();
|
||||
|
||||
if (Session()->IsDomain() && (command_header->type == IPC::CommandType::Request || !incoming)) {
|
||||
if (Session()->IsDomain() && ((command_header->type == IPC::CommandType::Request ||
|
||||
command_header->type == IPC::CommandType::RequestWithContext) ||
|
||||
!incoming)) {
|
||||
// If this is an incoming message, only CommandType "Request" has a domain header
|
||||
// All outgoing domain messages have the domain header, if only incoming has it
|
||||
if (incoming || domain_message_header) {
|
||||
|
||||
@@ -104,7 +104,6 @@ ResultCode Mutex::Release(VAddr address) {
|
||||
|
||||
// There are no more threads waiting for the mutex, release it completely.
|
||||
if (thread == nullptr) {
|
||||
ASSERT(GetCurrentThread()->wait_mutex_threads.empty());
|
||||
Memory::Write32(address, 0);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
std::mutex Scheduler::scheduler_mutex;
|
||||
|
||||
Scheduler::Scheduler(ARM_Interface* cpu_core) : cpu_core(cpu_core) {}
|
||||
|
||||
Scheduler::~Scheduler() {
|
||||
@@ -18,6 +20,7 @@ Scheduler::~Scheduler() {
|
||||
}
|
||||
|
||||
bool Scheduler::HaveReadyThreads() {
|
||||
std::lock_guard<std::mutex> lock(scheduler_mutex);
|
||||
return ready_queue.get_first() != nullptr;
|
||||
}
|
||||
|
||||
@@ -90,6 +93,8 @@ void Scheduler::SwitchContext(Thread* new_thread) {
|
||||
}
|
||||
|
||||
void Scheduler::Reschedule() {
|
||||
std::lock_guard<std::mutex> lock(scheduler_mutex);
|
||||
|
||||
Thread* cur = GetCurrentThread();
|
||||
Thread* next = PopNextReadyThread();
|
||||
|
||||
@@ -105,26 +110,36 @@ void Scheduler::Reschedule() {
|
||||
}
|
||||
|
||||
void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) {
|
||||
std::lock_guard<std::mutex> lock(scheduler_mutex);
|
||||
|
||||
thread_list.push_back(thread);
|
||||
ready_queue.prepare(priority);
|
||||
}
|
||||
|
||||
void Scheduler::RemoveThread(Thread* thread) {
|
||||
std::lock_guard<std::mutex> lock(scheduler_mutex);
|
||||
|
||||
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
|
||||
thread_list.end());
|
||||
}
|
||||
|
||||
void Scheduler::ScheduleThread(Thread* thread, u32 priority) {
|
||||
std::lock_guard<std::mutex> lock(scheduler_mutex);
|
||||
|
||||
ASSERT(thread->status == THREADSTATUS_READY);
|
||||
ready_queue.push_back(priority, thread);
|
||||
}
|
||||
|
||||
void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
|
||||
std::lock_guard<std::mutex> lock(scheduler_mutex);
|
||||
|
||||
ASSERT(thread->status == THREADSTATUS_READY);
|
||||
ready_queue.remove(priority, thread);
|
||||
}
|
||||
|
||||
void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
|
||||
std::lock_guard<std::mutex> lock(scheduler_mutex);
|
||||
|
||||
// If thread was ready, adjust queues
|
||||
if (thread->status == THREADSTATUS_READY)
|
||||
ready_queue.move(thread, thread->current_priority, priority);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/thread_queue_list.h"
|
||||
@@ -68,6 +69,8 @@ private:
|
||||
SharedPtr<Thread> current_thread = nullptr;
|
||||
|
||||
ARM_Interface* cpu_core;
|
||||
|
||||
static std::mutex scheduler_mutex;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -145,36 +145,6 @@ static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thr
|
||||
return true;
|
||||
};
|
||||
|
||||
/// Wait for a kernel object to synchronize, timeout after the specified nanoseconds
|
||||
static ResultCode WaitSynchronization1(
|
||||
SharedPtr<WaitObject> object, Thread* thread, s64 nano_seconds = -1,
|
||||
std::function<Thread::WakeupCallback> wakeup_callback = DefaultThreadWakeupCallback) {
|
||||
|
||||
if (!object) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (object->ShouldWait(thread)) {
|
||||
if (nano_seconds == 0) {
|
||||
return RESULT_TIMEOUT;
|
||||
}
|
||||
|
||||
thread->wait_objects = {object};
|
||||
object->AddWaitingThread(thread);
|
||||
thread->status = THREADSTATUS_WAIT_SYNCH_ANY;
|
||||
|
||||
// Create an event to wake the thread up after the specified nanosecond delay has passed
|
||||
thread->WakeAfterDelay(nano_seconds);
|
||||
thread->wakeup_callback = wakeup_callback;
|
||||
|
||||
Core::System::GetInstance().PrepareReschedule();
|
||||
} else {
|
||||
object->Acquire(thread);
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
|
||||
static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 handle_count,
|
||||
s64 nano_seconds) {
|
||||
@@ -232,7 +202,7 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
|
||||
thread->WakeAfterDelay(nano_seconds);
|
||||
thread->wakeup_callback = DefaultThreadWakeupCallback;
|
||||
|
||||
Core::System::GetInstance().PrepareReschedule();
|
||||
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
|
||||
|
||||
return RESULT_TIMEOUT;
|
||||
}
|
||||
@@ -346,6 +316,11 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
"(STUBBED) Attempted to query privileged process id bounds, returned 0");
|
||||
*result = 0;
|
||||
break;
|
||||
case GetInfoType::UserExceptionContextAddr:
|
||||
NGLOG_WARNING(Kernel_SVC,
|
||||
"(STUBBED) Attempted to query user exception context address, returned 0");
|
||||
*result = 0;
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
@@ -395,14 +370,14 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
|
||||
|
||||
thread->SetPriority(priority);
|
||||
|
||||
Core::System::GetInstance().PrepareReschedule();
|
||||
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// Get which CPU core is executing the current thread
|
||||
static u32 GetCurrentProcessorNumber() {
|
||||
NGLOG_WARNING(Kernel_SVC, "(STUBBED) called, defaulting to processor 0");
|
||||
return 0;
|
||||
NGLOG_TRACE(Kernel_SVC, "called");
|
||||
return GetCurrentThread()->processor_id;
|
||||
}
|
||||
|
||||
static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size,
|
||||
@@ -485,22 +460,28 @@ static void ExitProcess() {
|
||||
|
||||
Core::CurrentProcess()->status = ProcessStatus::Exited;
|
||||
|
||||
// Stop all the process threads that are currently waiting for objects.
|
||||
auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList();
|
||||
for (auto& thread : thread_list) {
|
||||
if (thread->owner_process != Core::CurrentProcess())
|
||||
continue;
|
||||
auto stop_threads = [](const std::vector<SharedPtr<Thread>>& thread_list) {
|
||||
for (auto& thread : thread_list) {
|
||||
if (thread->owner_process != Core::CurrentProcess())
|
||||
continue;
|
||||
|
||||
if (thread == GetCurrentThread())
|
||||
continue;
|
||||
if (thread == GetCurrentThread())
|
||||
continue;
|
||||
|
||||
// TODO(Subv): When are the other running/ready threads terminated?
|
||||
ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
|
||||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL,
|
||||
"Exiting processes with non-waiting threads is currently unimplemented");
|
||||
// TODO(Subv): When are the other running/ready threads terminated?
|
||||
ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
|
||||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL,
|
||||
"Exiting processes with non-waiting threads is currently unimplemented");
|
||||
|
||||
thread->Stop();
|
||||
}
|
||||
thread->Stop();
|
||||
}
|
||||
};
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
stop_threads(system.Scheduler(0)->GetThreadList());
|
||||
stop_threads(system.Scheduler(1)->GetThreadList());
|
||||
stop_threads(system.Scheduler(2)->GetThreadList());
|
||||
stop_threads(system.Scheduler(3)->GetThreadList());
|
||||
|
||||
// Kill the current thread
|
||||
GetCurrentThread()->Stop();
|
||||
@@ -530,14 +511,9 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
|
||||
|
||||
switch (processor_id) {
|
||||
case THREADPROCESSORID_0:
|
||||
break;
|
||||
case THREADPROCESSORID_1:
|
||||
case THREADPROCESSORID_2:
|
||||
case THREADPROCESSORID_3:
|
||||
// TODO(bunnei): Implement support for other processor IDs
|
||||
NGLOG_ERROR(Kernel_SVC,
|
||||
"Newly created thread must run in another thread ({}), unimplemented.",
|
||||
processor_id);
|
||||
break;
|
||||
default:
|
||||
ASSERT_MSG(false, "Unsupported thread processor ID: {}", processor_id);
|
||||
@@ -551,6 +527,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
|
||||
*out_handle = thread->guest_handle;
|
||||
|
||||
Core::System::GetInstance().PrepareReschedule();
|
||||
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
|
||||
|
||||
NGLOG_TRACE(Kernel_SVC,
|
||||
"called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, "
|
||||
@@ -569,14 +546,17 @@ static ResultCode StartThread(Handle thread_handle) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
ASSERT(thread->status == THREADSTATUS_DORMANT);
|
||||
|
||||
thread->ResumeFromWait();
|
||||
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// Called when a thread exits
|
||||
static void ExitThread() {
|
||||
NGLOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", Core::CPU().GetPC());
|
||||
NGLOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", Core::CurrentArmInterface().GetPC());
|
||||
|
||||
ExitCurrentThread();
|
||||
Core::System::GetInstance().PrepareReschedule();
|
||||
@@ -588,7 +568,7 @@ static void SleepThread(s64 nanoseconds) {
|
||||
|
||||
// Don't attempt to yield execution if there are no available threads to run,
|
||||
// this way we avoid a useless reschedule to the idle thread.
|
||||
if (nanoseconds == 0 && !Core::System::GetInstance().Scheduler().HaveReadyThreads())
|
||||
if (nanoseconds == 0 && !Core::System::GetInstance().CurrentScheduler().HaveReadyThreads())
|
||||
return;
|
||||
|
||||
// Sleep current thread and check for next thread to schedule
|
||||
@@ -624,7 +604,7 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
|
||||
|
||||
// Note: Deliberately don't attempt to inherit the lock owner's priority.
|
||||
|
||||
Core::System::GetInstance().PrepareReschedule();
|
||||
Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule();
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -633,17 +613,43 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
|
||||
NGLOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}",
|
||||
condition_variable_addr, target);
|
||||
|
||||
u32 processed = 0;
|
||||
auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList();
|
||||
auto RetrieveWaitingThreads =
|
||||
[](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr condvar_addr) {
|
||||
const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
|
||||
auto& thread_list = scheduler->GetThreadList();
|
||||
|
||||
for (auto& thread : thread_list) {
|
||||
if (thread->condvar_wait_address != condition_variable_addr)
|
||||
continue;
|
||||
for (auto& thread : thread_list) {
|
||||
if (thread->condvar_wait_address == condvar_addr)
|
||||
waiting_threads.push_back(thread);
|
||||
}
|
||||
};
|
||||
|
||||
// Only process up to 'target' threads, unless 'target' is -1, in which case process
|
||||
// them all.
|
||||
if (target != -1 && processed >= target)
|
||||
break;
|
||||
// Retrieve a list of all threads that are waiting for this condition variable.
|
||||
std::vector<SharedPtr<Thread>> waiting_threads;
|
||||
RetrieveWaitingThreads(0, waiting_threads, condition_variable_addr);
|
||||
RetrieveWaitingThreads(1, waiting_threads, condition_variable_addr);
|
||||
RetrieveWaitingThreads(2, waiting_threads, condition_variable_addr);
|
||||
RetrieveWaitingThreads(3, waiting_threads, condition_variable_addr);
|
||||
// Sort them by priority, such that the highest priority ones come first.
|
||||
std::sort(waiting_threads.begin(), waiting_threads.end(),
|
||||
[](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
|
||||
return lhs->current_priority < rhs->current_priority;
|
||||
});
|
||||
|
||||
// Only process up to 'target' threads, unless 'target' is -1, in which case process
|
||||
// them all.
|
||||
size_t last = waiting_threads.size();
|
||||
if (target != -1)
|
||||
last = target;
|
||||
|
||||
// If there are no threads waiting on this condition variable, just exit
|
||||
if (last > waiting_threads.size())
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
for (size_t index = 0; index < last; ++index) {
|
||||
auto& thread = waiting_threads[index];
|
||||
|
||||
ASSERT(thread->condvar_wait_address == condition_variable_addr);
|
||||
|
||||
// If the mutex is not yet acquired, acquire it.
|
||||
u32 mutex_val = Memory::Read32(thread->mutex_wait_address);
|
||||
@@ -676,10 +682,8 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
|
||||
|
||||
owner->AddMutexWaiter(thread);
|
||||
|
||||
Core::System::GetInstance().PrepareReschedule();
|
||||
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
|
||||
}
|
||||
|
||||
++processed;
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
@@ -718,16 +722,56 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode GetThreadCoreMask(Handle handle, u32* mask, u64* unknown) {
|
||||
NGLOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}", handle);
|
||||
*mask = 0x0;
|
||||
*unknown = 0xf;
|
||||
static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) {
|
||||
NGLOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
|
||||
|
||||
const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
|
||||
if (!thread) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
*core = thread->ideal_core;
|
||||
*mask = thread->affinity_mask;
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode SetThreadCoreMask(Handle handle, u32 mask, u64 unknown) {
|
||||
NGLOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, mask=0x{:08X}, unknown=0x{:X}",
|
||||
handle, mask, unknown);
|
||||
static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
|
||||
NGLOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle,
|
||||
mask, core);
|
||||
|
||||
const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
|
||||
if (!thread) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (core == THREADPROCESSORID_DEFAULT) {
|
||||
ASSERT(thread->owner_process->ideal_processor != THREADPROCESSORID_DEFAULT);
|
||||
// Set the target CPU to the one specified in the process' exheader.
|
||||
core = thread->owner_process->ideal_processor;
|
||||
mask = 1ull << core;
|
||||
}
|
||||
|
||||
if (mask == 0) {
|
||||
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination);
|
||||
}
|
||||
|
||||
/// This value is used to only change the affinity mask without changing the current ideal core.
|
||||
static constexpr u32 OnlyChangeMask = static_cast<u32>(-3);
|
||||
|
||||
if (core == OnlyChangeMask) {
|
||||
core = thread->ideal_core;
|
||||
} else if (core >= Core::NUM_CPU_CORES && core != -1) {
|
||||
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
|
||||
}
|
||||
|
||||
// Error out if the input core isn't enabled in the input mask.
|
||||
if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) {
|
||||
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination);
|
||||
}
|
||||
|
||||
thread->ChangeCore(core, mask);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,9 +47,12 @@ enum class GetInfoType : u64 {
|
||||
NewMapRegionSize = 15,
|
||||
// 3.0.0+
|
||||
IsVirtualAddressMemoryEnabled = 16,
|
||||
PersonalMmHeapUsage = 17,
|
||||
TitleId = 18,
|
||||
// 4.0.0+
|
||||
PrivilegedProcessId = 19,
|
||||
// 5.0.0+
|
||||
UserExceptionContextAddr = 20,
|
||||
};
|
||||
|
||||
void CallSVC(u32 immediate);
|
||||
|
||||
@@ -13,14 +13,14 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
#define PARAM(n) Core::CPU().GetReg(n)
|
||||
#define PARAM(n) Core::CurrentArmInterface().GetReg(n)
|
||||
|
||||
/**
|
||||
* HLE a function return from the current ARM userland process
|
||||
* @param res Result to return
|
||||
*/
|
||||
static inline void FuncReturn(u64 res) {
|
||||
Core::CPU().SetReg(0, res);
|
||||
Core::CurrentArmInterface().SetReg(0, res);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -45,7 +45,7 @@ template <ResultCode func(u32*, u32)>
|
||||
void SvcWrap() {
|
||||
u32 param_1 = 0;
|
||||
u32 retval = func(¶m_1, (u32)PARAM(1)).raw;
|
||||
Core::CPU().SetReg(1, param_1);
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(retval);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ template <ResultCode func(u32*, u64)>
|
||||
void SvcWrap() {
|
||||
u32 param_1 = 0;
|
||||
u32 retval = func(¶m_1, PARAM(1)).raw;
|
||||
Core::CPU().SetReg(1, param_1);
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(retval);
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ template <ResultCode func(u64*, u64)>
|
||||
void SvcWrap() {
|
||||
u64 param_1 = 0;
|
||||
u32 retval = func(¶m_1, PARAM(1)).raw;
|
||||
Core::CPU().SetReg(1, param_1);
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(retval);
|
||||
}
|
||||
|
||||
@@ -85,8 +85,8 @@ void SvcWrap() {
|
||||
u32 param_1 = 0;
|
||||
u64 param_2 = 0;
|
||||
ResultCode retval = func((u32)(PARAM(2) & 0xFFFFFFFF), ¶m_1, ¶m_2);
|
||||
Core::CPU().SetReg(1, param_1);
|
||||
Core::CPU().SetReg(2, param_2);
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
Core::CurrentArmInterface().SetReg(2, param_2);
|
||||
FuncReturn(retval.raw);
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ template <ResultCode func(u32*, u64, u64, s64)>
|
||||
void SvcWrap() {
|
||||
u32 param_1 = 0;
|
||||
ResultCode retval = func(¶m_1, PARAM(1), (u32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3));
|
||||
Core::CPU().SetReg(1, param_1);
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(retval.raw);
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ template <ResultCode func(u64*, u64, u64, u64)>
|
||||
void SvcWrap() {
|
||||
u64 param_1 = 0;
|
||||
u32 retval = func(¶m_1, PARAM(1), PARAM(2), PARAM(3)).raw;
|
||||
Core::CPU().SetReg(1, param_1);
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(retval);
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ void SvcWrap() {
|
||||
u32 retval =
|
||||
func(¶m_1, PARAM(1), PARAM(2), PARAM(3), (u32)PARAM(4), (s32)(PARAM(5) & 0xFFFFFFFF))
|
||||
.raw;
|
||||
Core::CPU().SetReg(1, param_1);
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(retval);
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ template <ResultCode func(u32*, u64, u64, u32)>
|
||||
void SvcWrap() {
|
||||
u32 param_1 = 0;
|
||||
u32 retval = func(¶m_1, PARAM(1), PARAM(2), (u32)(PARAM(3) & 0xFFFFFFFF)).raw;
|
||||
Core::CPU().SetReg(1, param_1);
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(retval);
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ void SvcWrap() {
|
||||
u32 param_1 = 0;
|
||||
u32 retval =
|
||||
func(¶m_1, PARAM(1), (u32)(PARAM(2) & 0xFFFFFFFF), (u32)(PARAM(3) & 0xFFFFFFFF)).raw;
|
||||
Core::CPU().SetReg(1, param_1);
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(retval);
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ void Thread::Stop() {
|
||||
// Clean up thread from ready queue
|
||||
// This is only needed when the thread is termintated forcefully (SVC TerminateProcess)
|
||||
if (status == THREADSTATUS_READY) {
|
||||
Core::System::GetInstance().Scheduler().UnscheduleThread(this, current_priority);
|
||||
scheduler->UnscheduleThread(this, current_priority);
|
||||
}
|
||||
|
||||
status = THREADSTATUS_DEAD;
|
||||
@@ -92,7 +92,7 @@ void WaitCurrentThread_Sleep() {
|
||||
void ExitCurrentThread() {
|
||||
Thread* thread = GetCurrentThread();
|
||||
thread->Stop();
|
||||
Core::System::GetInstance().Scheduler().RemoveThread(thread);
|
||||
Core::System::GetInstance().CurrentScheduler().RemoveThread(thread);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,8 +133,11 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
|
||||
|
||||
auto lock_owner = thread->lock_owner;
|
||||
// Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
|
||||
// and don't have a lock owner.
|
||||
ASSERT(lock_owner == nullptr);
|
||||
// and don't have a lock owner unless SignalProcessWideKey was called first and the thread
|
||||
// wasn't awakened due to the mutex already being acquired.
|
||||
if (lock_owner) {
|
||||
lock_owner->RemoveMutexWaiter(thread);
|
||||
}
|
||||
}
|
||||
|
||||
if (resume)
|
||||
@@ -154,6 +157,18 @@ void Thread::CancelWakeupTimer() {
|
||||
CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
|
||||
}
|
||||
|
||||
static boost::optional<s32> GetNextProcessorId(u64 mask) {
|
||||
for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) {
|
||||
if (mask & (1ULL << index)) {
|
||||
if (!Core::System().GetInstance().Scheduler(index)->GetCurrentThread()) {
|
||||
// Core is enabled and not running any threads, use this one
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void Thread::ResumeFromWait() {
|
||||
ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects");
|
||||
|
||||
@@ -188,8 +203,37 @@ void Thread::ResumeFromWait() {
|
||||
wakeup_callback = nullptr;
|
||||
|
||||
status = THREADSTATUS_READY;
|
||||
Core::System::GetInstance().Scheduler().ScheduleThread(this, current_priority);
|
||||
Core::System::GetInstance().PrepareReschedule();
|
||||
|
||||
boost::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask);
|
||||
if (!new_processor_id) {
|
||||
new_processor_id = processor_id;
|
||||
}
|
||||
if (ideal_core != -1 &&
|
||||
Core::System().GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) {
|
||||
new_processor_id = ideal_core;
|
||||
}
|
||||
|
||||
ASSERT(*new_processor_id < 4);
|
||||
|
||||
// Add thread to new core's scheduler
|
||||
auto& next_scheduler = Core::System().GetInstance().Scheduler(*new_processor_id);
|
||||
|
||||
if (*new_processor_id != processor_id) {
|
||||
// Remove thread from previous core's scheduler
|
||||
scheduler->RemoveThread(this);
|
||||
next_scheduler->AddThread(this, current_priority);
|
||||
}
|
||||
|
||||
processor_id = *new_processor_id;
|
||||
|
||||
// If the thread was ready, unschedule from the previous core and schedule on the new core
|
||||
scheduler->UnscheduleThread(this, current_priority);
|
||||
next_scheduler->ScheduleThread(this, current_priority);
|
||||
|
||||
// Change thread's scheduler
|
||||
scheduler = next_scheduler;
|
||||
|
||||
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,8 +303,6 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
|
||||
|
||||
SharedPtr<Thread> thread(new Thread);
|
||||
|
||||
Core::System::GetInstance().Scheduler().AddThread(thread, priority);
|
||||
|
||||
thread->thread_id = NewThreadId();
|
||||
thread->status = THREADSTATUS_DORMANT;
|
||||
thread->entry_point = entry_point;
|
||||
@@ -268,6 +310,8 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
|
||||
thread->nominal_priority = thread->current_priority = priority;
|
||||
thread->last_running_ticks = CoreTiming::GetTicks();
|
||||
thread->processor_id = processor_id;
|
||||
thread->ideal_core = processor_id;
|
||||
thread->affinity_mask = 1ULL << processor_id;
|
||||
thread->wait_objects.clear();
|
||||
thread->mutex_wait_address = 0;
|
||||
thread->condvar_wait_address = 0;
|
||||
@@ -275,6 +319,8 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
|
||||
thread->name = std::move(name);
|
||||
thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap();
|
||||
thread->owner_process = owner_process;
|
||||
thread->scheduler = Core::System().GetInstance().Scheduler(processor_id);
|
||||
thread->scheduler->AddThread(thread, priority);
|
||||
|
||||
// Find the next available TLS index, and mark it as used
|
||||
auto& tls_slots = owner_process->tls_slots;
|
||||
@@ -337,7 +383,7 @@ void Thread::SetPriority(u32 priority) {
|
||||
}
|
||||
|
||||
void Thread::BoostPriority(u32 priority) {
|
||||
Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority);
|
||||
scheduler->SetThreadPriority(this, priority);
|
||||
current_priority = priority;
|
||||
}
|
||||
|
||||
@@ -406,7 +452,7 @@ void Thread::UpdatePriority() {
|
||||
if (new_priority == current_priority)
|
||||
return;
|
||||
|
||||
Core::System::GetInstance().Scheduler().SetThreadPriority(this, new_priority);
|
||||
scheduler->SetThreadPriority(this, new_priority);
|
||||
|
||||
current_priority = new_priority;
|
||||
|
||||
@@ -415,13 +461,54 @@ void Thread::UpdatePriority() {
|
||||
lock_owner->UpdatePriority();
|
||||
}
|
||||
|
||||
void Thread::ChangeCore(u32 core, u64 mask) {
|
||||
ideal_core = core;
|
||||
affinity_mask = mask;
|
||||
|
||||
if (status != THREADSTATUS_READY) {
|
||||
return;
|
||||
}
|
||||
|
||||
boost::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
|
||||
|
||||
if (!new_processor_id) {
|
||||
new_processor_id = processor_id;
|
||||
}
|
||||
if (ideal_core != -1 &&
|
||||
Core::System().GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) {
|
||||
new_processor_id = ideal_core;
|
||||
}
|
||||
|
||||
ASSERT(*new_processor_id < 4);
|
||||
|
||||
// Add thread to new core's scheduler
|
||||
auto& next_scheduler = Core::System().GetInstance().Scheduler(*new_processor_id);
|
||||
|
||||
if (*new_processor_id != processor_id) {
|
||||
// Remove thread from previous core's scheduler
|
||||
scheduler->RemoveThread(this);
|
||||
next_scheduler->AddThread(this, current_priority);
|
||||
}
|
||||
|
||||
processor_id = *new_processor_id;
|
||||
|
||||
// If the thread was ready, unschedule from the previous core and schedule on the new core
|
||||
scheduler->UnscheduleThread(this, current_priority);
|
||||
next_scheduler->ScheduleThread(this, current_priority);
|
||||
|
||||
// Change thread's scheduler
|
||||
scheduler = next_scheduler;
|
||||
|
||||
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Gets the current thread
|
||||
*/
|
||||
Thread* GetCurrentThread() {
|
||||
return Core::System::GetInstance().Scheduler().GetCurrentThread();
|
||||
return Core::System::GetInstance().CurrentScheduler().GetCurrentThread();
|
||||
}
|
||||
|
||||
void ThreadingInit() {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
@@ -56,6 +57,7 @@ enum class ThreadWakeupReason {
|
||||
namespace Kernel {
|
||||
|
||||
class Process;
|
||||
class Scheduler;
|
||||
|
||||
class Thread final : public WaitObject {
|
||||
public:
|
||||
@@ -118,6 +120,9 @@ public:
|
||||
/// Recalculates the current priority taking into account priority inheritance.
|
||||
void UpdatePriority();
|
||||
|
||||
/// Changes the core that the thread is running or scheduled to run on.
|
||||
void ChangeCore(u32 core, u64 mask);
|
||||
|
||||
/**
|
||||
* Gets the thread's thread ID
|
||||
* @return The thread's ID
|
||||
@@ -240,6 +245,11 @@ public:
|
||||
// available. In case of a timeout, the object will be nullptr.
|
||||
std::function<WakeupCallback> wakeup_callback;
|
||||
|
||||
std::shared_ptr<Scheduler> scheduler;
|
||||
|
||||
u32 ideal_core{0xFFFFFFFF};
|
||||
u64 affinity_mask{0x1};
|
||||
|
||||
private:
|
||||
Thread();
|
||||
~Thread() override;
|
||||
|
||||
@@ -104,8 +104,15 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
|
||||
VirtualMemoryArea& final_vma = vma_handle->second;
|
||||
ASSERT(final_vma.size == size);
|
||||
|
||||
Core::CPU().MapBackingMemory(target, size, block->data() + offset,
|
||||
VMAPermission::ReadWriteExecute);
|
||||
auto& system = Core::System::GetInstance();
|
||||
system.ArmInterface(0).MapBackingMemory(target, size, block->data() + offset,
|
||||
VMAPermission::ReadWriteExecute);
|
||||
system.ArmInterface(1).MapBackingMemory(target, size, block->data() + offset,
|
||||
VMAPermission::ReadWriteExecute);
|
||||
system.ArmInterface(2).MapBackingMemory(target, size, block->data() + offset,
|
||||
VMAPermission::ReadWriteExecute);
|
||||
system.ArmInterface(3).MapBackingMemory(target, size, block->data() + offset,
|
||||
VMAPermission::ReadWriteExecute);
|
||||
|
||||
final_vma.type = VMAType::AllocatedMemoryBlock;
|
||||
final_vma.permissions = VMAPermission::ReadWrite;
|
||||
@@ -126,7 +133,11 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
|
||||
VirtualMemoryArea& final_vma = vma_handle->second;
|
||||
ASSERT(final_vma.size == size);
|
||||
|
||||
Core::CPU().MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
|
||||
auto& system = Core::System::GetInstance();
|
||||
system.ArmInterface(0).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
|
||||
system.ArmInterface(1).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
|
||||
system.ArmInterface(2).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
|
||||
system.ArmInterface(3).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
|
||||
|
||||
final_vma.type = VMAType::BackingMemory;
|
||||
final_vma.permissions = VMAPermission::ReadWrite;
|
||||
@@ -184,7 +195,11 @@ ResultCode VMManager::UnmapRange(VAddr target, u64 size) {
|
||||
|
||||
ASSERT(FindVMA(target)->second.size >= size);
|
||||
|
||||
Core::CPU().UnmapMemory(target, size);
|
||||
auto& system = Core::System::GetInstance();
|
||||
system.ArmInterface(0).UnmapMemory(target, size);
|
||||
system.ArmInterface(1).UnmapMemory(target, size);
|
||||
system.ArmInterface(2).UnmapMemory(target, size);
|
||||
system.ArmInterface(3).UnmapMemory(target, size);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ enum class ErrorModule : u32 {
|
||||
Common = 0,
|
||||
Kernel = 1,
|
||||
FS = 2,
|
||||
NvidiaTransferMemory = 3,
|
||||
OS = 3, // used for Memory, Thread, Mutex, Nvidia
|
||||
HTCS = 4,
|
||||
NCM = 5,
|
||||
DD = 6,
|
||||
LR = 8,
|
||||
@@ -42,41 +43,80 @@ enum class ErrorModule : u32 {
|
||||
PM = 15,
|
||||
NS = 16,
|
||||
HTC = 18,
|
||||
NCMContent = 20,
|
||||
SM = 21,
|
||||
RO = 22,
|
||||
SDMMC = 24,
|
||||
OVLN = 25,
|
||||
SPL = 26,
|
||||
ETHC = 100,
|
||||
I2C = 101,
|
||||
GPIO = 102,
|
||||
UART = 103,
|
||||
Settings = 105,
|
||||
WLAN = 107,
|
||||
XCD = 108,
|
||||
NIFM = 110,
|
||||
Display = 114,
|
||||
NTC = 116,
|
||||
Hwopus = 111,
|
||||
Bluetooth = 113,
|
||||
VI = 114,
|
||||
NFP = 115,
|
||||
Time = 116,
|
||||
FGM = 117,
|
||||
PCIE = 120,
|
||||
OE = 118,
|
||||
PCIe = 120,
|
||||
Friends = 121,
|
||||
BCAT = 122,
|
||||
SSL = 123,
|
||||
Account = 124,
|
||||
News = 125,
|
||||
Mii = 126,
|
||||
NFC = 127,
|
||||
AM = 128,
|
||||
PlayReport = 129,
|
||||
AHID = 130,
|
||||
Qlaunch = 132,
|
||||
PCV = 133,
|
||||
OMM = 134,
|
||||
BPC = 135,
|
||||
PSM = 136,
|
||||
NIM = 137,
|
||||
PSC = 138,
|
||||
TC = 139,
|
||||
USB = 140,
|
||||
NSD = 141,
|
||||
PCTL = 142,
|
||||
BTM = 143,
|
||||
ETicket = 145,
|
||||
NGC = 146,
|
||||
ERPT = 147,
|
||||
APM = 148,
|
||||
Profiler = 150,
|
||||
ErrorUpload = 151,
|
||||
Audio = 153,
|
||||
NPNS = 154,
|
||||
NPNSHTTPSTREAM = 155,
|
||||
ARP = 157,
|
||||
BOOT = 158,
|
||||
NFC = 161,
|
||||
SWKBD = 158,
|
||||
BOOT = 159,
|
||||
NFCMifare = 161,
|
||||
UserlandAssert = 162,
|
||||
Fatal = 163,
|
||||
NIMShop = 164,
|
||||
SPSM = 165,
|
||||
BGTC = 167,
|
||||
UserlandCrash = 168,
|
||||
HID = 203,
|
||||
SREPO = 180,
|
||||
Dauth = 181,
|
||||
HID = 202,
|
||||
LDN = 203,
|
||||
Irsensor = 205,
|
||||
Capture = 206,
|
||||
TC = 651,
|
||||
Manu = 208,
|
||||
ATK = 209,
|
||||
GRC = 212,
|
||||
Migration = 216,
|
||||
MigrationLdcServ = 217,
|
||||
GeneralWebApplet = 800,
|
||||
WifiWebAuthApplet = 809,
|
||||
WhitelistedApplet = 810,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cinttypes>
|
||||
#include <stack>
|
||||
#include "core/file_sys/filesystem.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
@@ -74,7 +75,40 @@ void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestCo
|
||||
rb.Push(volume);
|
||||
}
|
||||
|
||||
IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {}
|
||||
IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetLastForegroundCaptureImage"},
|
||||
{1, nullptr, "UpdateLastForegroundCaptureImage"},
|
||||
{2, nullptr, "GetLastApplicationCaptureImage"},
|
||||
{3, nullptr, "GetCallerAppletCaptureImage"},
|
||||
{4, nullptr, "UpdateCallerAppletCaptureImage"},
|
||||
{5, nullptr, "GetLastForegroundCaptureImageEx"},
|
||||
{6, nullptr, "GetLastApplicationCaptureImageEx"},
|
||||
{7, nullptr, "GetCallerAppletCaptureImageEx"},
|
||||
{8, nullptr, "TakeScreenShotOfOwnLayer"}, // 2.0.0+
|
||||
{9, nullptr, "CopyBetweenCaptureBuffers"}, // 5.0.0+
|
||||
{10, nullptr, "AcquireLastApplicationCaptureBuffer"},
|
||||
{11, nullptr, "ReleaseLastApplicationCaptureBuffer"},
|
||||
{12, nullptr, "AcquireLastForegroundCaptureBuffer"},
|
||||
{13, nullptr, "ReleaseLastForegroundCaptureBuffer"},
|
||||
{14, nullptr, "AcquireCallerAppletCaptureBuffer"},
|
||||
{15, nullptr, "ReleaseCallerAppletCaptureBuffer"},
|
||||
{16, nullptr, "AcquireLastApplicationCaptureBufferEx"},
|
||||
{17, nullptr, "AcquireLastForegroundCaptureBufferEx"},
|
||||
{18, nullptr, "AcquireCallerAppletCaptureBufferEx"},
|
||||
// 2.0.0+
|
||||
{20, nullptr, "ClearCaptureBuffer"},
|
||||
{21, nullptr, "ClearAppletTransitionBuffer"},
|
||||
// 4.0.0+
|
||||
{22, nullptr, "AcquireLastApplicationCaptureSharedBuffer"},
|
||||
{23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"},
|
||||
{24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"},
|
||||
{25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"},
|
||||
{26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"},
|
||||
{27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IDebugFunctions::IDebugFunctions() : ServiceFramework("IDebugFunctions") {}
|
||||
|
||||
@@ -103,7 +137,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
|
||||
{40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
|
||||
{41, nullptr, "IsSystemBufferSharingEnabled"},
|
||||
{42, nullptr, "GetSystemSharedLayerHandle"},
|
||||
{50, nullptr, "SetHandlesRequestToDisplay"},
|
||||
{50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
|
||||
{51, nullptr, "ApproveToDisplay"},
|
||||
{60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
|
||||
{61, nullptr, "SetMediaPlaybackState"},
|
||||
@@ -121,7 +155,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
|
||||
RegisterHandlers(functions);
|
||||
|
||||
launchable_event =
|
||||
Kernel::Event::Create(Kernel::ResetType::OneShot, "ISelfController:LaunchableEvent");
|
||||
Kernel::Event::Create(Kernel::ResetType::Sticky, "ISelfController:LaunchableEvent");
|
||||
}
|
||||
|
||||
void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
|
||||
@@ -229,6 +263,13 @@ void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx)
|
||||
NGLOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void ISelfController::SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
NGLOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
|
||||
@@ -308,77 +349,13 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
|
||||
public:
|
||||
explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
|
||||
{1, nullptr, "IsCompleted"},
|
||||
{10, nullptr, "Start"},
|
||||
{20, nullptr, "RequestExit"},
|
||||
{25, nullptr, "Terminate"},
|
||||
{30, nullptr, "GetResult"},
|
||||
{50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
|
||||
{100, nullptr, "PushInData"},
|
||||
{101, nullptr, "PopOutData"},
|
||||
{102, nullptr, "PushExtraStorage"},
|
||||
{103, nullptr, "PushInteractiveInData"},
|
||||
{104, nullptr, "PopInteractiveOutData"},
|
||||
{105, nullptr, "GetPopOutDataEvent"},
|
||||
{106, nullptr, "GetPopInteractiveOutDataEvent"},
|
||||
{110, nullptr, "NeedsToExitProcess"},
|
||||
{120, nullptr, "GetLibraryAppletInfo"},
|
||||
{150, nullptr, "RequestForAppletToGetForeground"},
|
||||
{160, nullptr, "GetIndirectLayerConsumerHandle"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
state_changed_event = Kernel::Event::Create(Kernel::ResetType::OneShot,
|
||||
"ILibraryAppletAccessor:StateChangedEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) {
|
||||
state_changed_event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(state_changed_event);
|
||||
|
||||
NGLOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> state_changed_event;
|
||||
};
|
||||
|
||||
ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"},
|
||||
{1, nullptr, "TerminateAllLibraryApplets"},
|
||||
{2, nullptr, "AreAnyLibraryAppletsLeft"},
|
||||
{10, nullptr, "CreateStorage"},
|
||||
{11, nullptr, "CreateTransferMemoryStorage"},
|
||||
{12, nullptr, "CreateHandleStorage"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<AM::ILibraryAppletAccessor>();
|
||||
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
|
||||
public:
|
||||
explicit IStorageAccessor(std::vector<u8> buffer)
|
||||
: ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorageAccessor::GetSize, "GetSize"},
|
||||
{10, nullptr, "Write"},
|
||||
{10, &IStorageAccessor::Write, "Write"},
|
||||
{11, &IStorageAccessor::Read, "Read"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
@@ -396,22 +373,36 @@ private:
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void Write(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const u64 offset{rp.Pop<u64>()};
|
||||
const std::vector<u8> data{ctx.ReadBuffer()};
|
||||
|
||||
ASSERT(offset + data.size() <= buffer.size());
|
||||
|
||||
std::memcpy(&buffer[offset], data.data(), data.size());
|
||||
|
||||
IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
NGLOG_DEBUG(Service_AM, "called, offset={}", offset);
|
||||
}
|
||||
|
||||
void Read(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
u64 offset = rp.Pop<u64>();
|
||||
|
||||
const u64 offset{rp.Pop<u64>()};
|
||||
const size_t size{ctx.GetWriteBufferSize()};
|
||||
|
||||
ASSERT(offset + size <= buffer.size());
|
||||
|
||||
ctx.WriteBuffer(buffer.data() + offset, size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
||||
IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
NGLOG_DEBUG(Service_AM, "called, offset={}", offset);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -439,17 +430,129 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
|
||||
public:
|
||||
explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
|
||||
{1, nullptr, "IsCompleted"},
|
||||
{10, &ILibraryAppletAccessor::Start, "Start"},
|
||||
{20, nullptr, "RequestExit"},
|
||||
{25, nullptr, "Terminate"},
|
||||
{30, &ILibraryAppletAccessor::GetResult, "GetResult"},
|
||||
{50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
|
||||
{100, &ILibraryAppletAccessor::PushInData, "PushInData"},
|
||||
{101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
|
||||
{102, nullptr, "PushExtraStorage"},
|
||||
{103, nullptr, "PushInteractiveInData"},
|
||||
{104, nullptr, "PopInteractiveOutData"},
|
||||
{105, nullptr, "GetPopOutDataEvent"},
|
||||
{106, nullptr, "GetPopInteractiveOutDataEvent"},
|
||||
{110, nullptr, "NeedsToExitProcess"},
|
||||
{120, nullptr, "GetLibraryAppletInfo"},
|
||||
{150, nullptr, "RequestForAppletToGetForeground"},
|
||||
{160, nullptr, "GetIndirectLayerConsumerHandle"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
state_changed_event = Kernel::Event::Create(Kernel::ResetType::OneShot,
|
||||
"ILibraryAppletAccessor:StateChangedEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) {
|
||||
state_changed_event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(state_changed_event);
|
||||
|
||||
NGLOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void GetResult(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
NGLOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void Start(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
NGLOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void PushInData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
storage_stack.push(rp.PopIpcInterface<AM::IStorage>());
|
||||
|
||||
IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void PopOutData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<AM::IStorage>(std::move(storage_stack.top()));
|
||||
|
||||
storage_stack.pop();
|
||||
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
std::stack<std::shared_ptr<AM::IStorage>> storage_stack;
|
||||
Kernel::SharedPtr<Kernel::Event> state_changed_event;
|
||||
};
|
||||
|
||||
ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"},
|
||||
{1, nullptr, "TerminateAllLibraryApplets"},
|
||||
{2, nullptr, "AreAnyLibraryAppletsLeft"},
|
||||
{10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"},
|
||||
{11, nullptr, "CreateTransferMemoryStorage"},
|
||||
{12, nullptr, "CreateHandleStorage"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<AM::ILibraryAppletAccessor>();
|
||||
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 size{rp.Pop<u64>()};
|
||||
std::vector<u8> buffer(size);
|
||||
|
||||
IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 1)};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<AM::IStorage>(std::move(buffer));
|
||||
|
||||
NGLOG_DEBUG(Service_AM, "called, size={}", size);
|
||||
}
|
||||
|
||||
IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
|
||||
{10, nullptr, "CreateApplicationAndPushAndRequestToStart"},
|
||||
{11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"},
|
||||
{12, nullptr, "CreateApplicationAndRequestToStart"},
|
||||
{13, nullptr, "CreateApplicationAndRequestToStartForQuest"},
|
||||
{13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest,
|
||||
"CreateApplicationAndRequestToStartForQuest"},
|
||||
{20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"},
|
||||
{21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"},
|
||||
{22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
|
||||
{23, nullptr, "GetDisplayVersion"},
|
||||
{23, &IApplicationFunctions::GetDisplayVersion, "GetDisplayVersion"},
|
||||
{24, nullptr, "GetLaunchStorageInfoForDebug"},
|
||||
{25, nullptr, "ExtendSaveData"},
|
||||
{26, nullptr, "GetSaveDataSize"},
|
||||
@@ -458,7 +561,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
|
||||
{32, nullptr, "BeginBlockingHomeButton"},
|
||||
{33, nullptr, "EndBlockingHomeButton"},
|
||||
{40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
|
||||
{50, nullptr, "GetPseudoDeviceId"},
|
||||
{50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
|
||||
{60, nullptr, "SetMediaPlaybackStateForApplication"},
|
||||
{65, nullptr, "IsGamePlayRecordingSupported"},
|
||||
{66, &IApplicationFunctions::InitializeGamePlayRecording, "InitializeGamePlayRecording"},
|
||||
@@ -502,6 +605,13 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(
|
||||
Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
NGLOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u128 uid = rp.PopRaw<u128>();
|
||||
@@ -537,6 +647,14 @@ void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_WARNING(Service_AM, "(STUBBED) called, result=0x{:08X}", result);
|
||||
}
|
||||
|
||||
void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(1);
|
||||
rb.Push<u64>(0);
|
||||
NGLOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
|
||||
// TODO(bunnei): This should be configurable
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
@@ -566,10 +684,81 @@ void IApplicationFunctions::NotifyRunning(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
// Returns a 128-bit UUID
|
||||
rb.Push<u64>(0);
|
||||
rb.Push<u64>(0);
|
||||
|
||||
NGLOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
|
||||
std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager);
|
||||
std::make_shared<AppletOE>(nvflinger)->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"},
|
||||
{11, nullptr, "LockForeground"},
|
||||
{12, nullptr, "UnlockForeground"},
|
||||
{20, nullptr, "PopFromGeneralChannel"},
|
||||
{21, nullptr, "GetPopFromGeneralChannelEvent"},
|
||||
{30, nullptr, "GetHomeButtonWriterLockAccessor"},
|
||||
{31, nullptr, "GetWriterLockAccessorEx"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
NGLOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "RequestToEnterSleep"},
|
||||
{1, nullptr, "EnterSleep"},
|
||||
{2, nullptr, "StartSleepSequence"},
|
||||
{3, nullptr, "StartShutdownSequence"},
|
||||
{4, nullptr, "StartRebootSequence"},
|
||||
{10, nullptr, "LoadAndApplyIdlePolicySettings"},
|
||||
{11, nullptr, "NotifyCecSettingsChanged"},
|
||||
{12, nullptr, "SetDefaultHomeButtonLongPressTime"},
|
||||
{13, nullptr, "UpdateDefaultDisplayResolution"},
|
||||
{14, nullptr, "ShouldSleepOnBoot"},
|
||||
{15, nullptr, "GetHdcpAuthenticationFailedEvent"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreator") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CreateApplication"},
|
||||
{1, nullptr, "PopLaunchRequestedApplication"},
|
||||
{10, nullptr, "CreateSystemApplication"},
|
||||
{100, nullptr, "PopFloatingApplicationForDevelopment"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IProcessWindingController::IProcessWindingController()
|
||||
: ServiceFramework("IProcessWindingController") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetLaunchReason"},
|
||||
{11, nullptr, "OpenCallingLibraryApplet"},
|
||||
{21, nullptr, "PushContext"},
|
||||
{22, nullptr, "PopContext"},
|
||||
{23, nullptr, "CancelWindingReservation"},
|
||||
{30, nullptr, "WindAndDoReserved"},
|
||||
{40, nullptr, "ReserveToStartAndWaitAndUnwindThis"},
|
||||
{41, nullptr, "ReserveToStartAndWait"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -18,10 +18,25 @@ class NVFlinger;
|
||||
|
||||
namespace AM {
|
||||
|
||||
// TODO: Add more languages
|
||||
enum SystemLanguage {
|
||||
Japanese = 0,
|
||||
English = 1,
|
||||
English = 1, // en-US
|
||||
French = 2,
|
||||
German = 3,
|
||||
Italian = 4,
|
||||
Spanish = 5,
|
||||
Chinese = 6,
|
||||
Korean = 7,
|
||||
Dutch = 8,
|
||||
Portuguese = 9,
|
||||
Russian = 10,
|
||||
Taiwanese = 11,
|
||||
BritishEnglish = 12, // en-GB
|
||||
CanadianFrench = 13,
|
||||
LatinAmericanSpanish = 14, // es-419
|
||||
// 4.0.0+
|
||||
SimplifiedChinese = 15,
|
||||
TraditionalChinese = 16,
|
||||
};
|
||||
|
||||
class IWindowController final : public ServiceFramework<IWindowController> {
|
||||
@@ -70,6 +85,7 @@ private:
|
||||
void GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx);
|
||||
void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx);
|
||||
void SetScreenShotPermission(Kernel::HLERequestContext& ctx);
|
||||
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
Kernel::SharedPtr<Kernel::Event> launchable_event;
|
||||
@@ -105,6 +121,7 @@ public:
|
||||
|
||||
private:
|
||||
void CreateLibraryApplet(Kernel::HLERequestContext& ctx);
|
||||
void CreateStorage(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
|
||||
@@ -113,12 +130,38 @@ public:
|
||||
|
||||
private:
|
||||
void PopLaunchParameter(Kernel::HLERequestContext& ctx);
|
||||
void CreateApplicationAndRequestToStartForQuest(Kernel::HLERequestContext& ctx);
|
||||
void EnsureSaveData(Kernel::HLERequestContext& ctx);
|
||||
void SetTerminateResult(Kernel::HLERequestContext& ctx);
|
||||
void GetDisplayVersion(Kernel::HLERequestContext& ctx);
|
||||
void GetDesiredLanguage(Kernel::HLERequestContext& ctx);
|
||||
void InitializeGamePlayRecording(Kernel::HLERequestContext& ctx);
|
||||
void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
|
||||
void NotifyRunning(Kernel::HLERequestContext& ctx);
|
||||
void GetPseudoDeviceId(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
|
||||
public:
|
||||
IHomeMenuFunctions();
|
||||
|
||||
private:
|
||||
void RequestToGetForeground(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
class IGlobalStateController final : public ServiceFramework<IGlobalStateController> {
|
||||
public:
|
||||
IGlobalStateController();
|
||||
};
|
||||
|
||||
class IApplicationCreator final : public ServiceFramework<IApplicationCreator> {
|
||||
public:
|
||||
IApplicationCreator();
|
||||
};
|
||||
|
||||
class IProcessWindingController final : public ServiceFramework<IProcessWindingController> {
|
||||
public:
|
||||
IProcessWindingController();
|
||||
};
|
||||
|
||||
/// Registers all AM services with the specified service manager.
|
||||
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
{2, &ILibraryAppletProxy::GetWindowController, "GetWindowController"},
|
||||
{3, &ILibraryAppletProxy::GetAudioController, "GetAudioController"},
|
||||
{4, &ILibraryAppletProxy::GetDisplayController, "GetDisplayController"},
|
||||
{10, nullptr, "GetProcessWindingController"},
|
||||
{10, &ILibraryAppletProxy::GetProcessWindingController, "GetProcessWindingController"},
|
||||
{11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
|
||||
{20, &ILibraryAppletProxy::GetApplicationFunctions, "GetApplicationFunctions"},
|
||||
{1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
|
||||
@@ -28,6 +28,93 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ICommonStateGetter>();
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void GetSelfController(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISelfController>(nvflinger);
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void GetWindowController(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IWindowController>();
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void GetAudioController(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IAudioController>();
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void GetDisplayController(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IDisplayController>();
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void GetProcessWindingController(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IProcessWindingController>();
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void GetDebugFunctions(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IDebugFunctions>();
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ILibraryAppletCreator>();
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void GetApplicationFunctions(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IApplicationFunctions>();
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
};
|
||||
|
||||
class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
|
||||
public:
|
||||
explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
|
||||
{1, &ISystemAppletProxy::GetSelfController, "GetSelfController"},
|
||||
{2, &ISystemAppletProxy::GetWindowController, "GetWindowController"},
|
||||
{3, &ISystemAppletProxy::GetAudioController, "GetAudioController"},
|
||||
{4, &ISystemAppletProxy::GetDisplayController, "GetDisplayController"},
|
||||
{10, nullptr, "GetProcessWindingController"},
|
||||
{11, &ISystemAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
|
||||
{20, &ISystemAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"},
|
||||
{21, &ISystemAppletProxy::GetGlobalStateController, "GetGlobalStateController"},
|
||||
{22, &ISystemAppletProxy::GetApplicationCreator, "GetApplicationCreator"},
|
||||
{1000, &ISystemAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
@@ -78,16 +165,43 @@ private:
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void GetApplicationFunctions(Kernel::HLERequestContext& ctx) {
|
||||
void GetHomeMenuFunctions(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IApplicationFunctions>();
|
||||
rb.PushIpcInterface<IHomeMenuFunctions>();
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void GetGlobalStateController(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IGlobalStateController>();
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void GetApplicationCreator(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IApplicationCreator>();
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
};
|
||||
|
||||
void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISystemAppletProxy>(nvflinger);
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger);
|
||||
NGLOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -98,9 +212,9 @@ void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
|
||||
AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{100, nullptr, "OpenSystemAppletProxy"},
|
||||
{100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
|
||||
{200, &AppletAE::OpenLibraryAppletProxyOld, "OpenLibraryAppletProxyOld"},
|
||||
{201, nullptr, "OpenLibraryAppletProxy"},
|
||||
{201, &AppletAE::OpenLibraryAppletProxy, "OpenLibraryAppletProxy"},
|
||||
{300, nullptr, "OpenOverlayAppletProxy"},
|
||||
{350, nullptr, "OpenSystemApplicationProxy"},
|
||||
{400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"},
|
||||
|
||||
@@ -21,6 +21,8 @@ public:
|
||||
~AppletAE() = default;
|
||||
|
||||
private:
|
||||
void OpenSystemAppletProxy(Kernel::HLERequestContext& ctx);
|
||||
void OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx);
|
||||
void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
|
||||
@@ -98,7 +98,7 @@ void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
|
||||
AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x00000000, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
|
||||
{0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -35,10 +35,8 @@ public:
|
||||
|
||||
AudInU::AudInU() : ServiceFramework("audin:u") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "ListAudioIns"},
|
||||
{1, nullptr, "OpenAudioIn"},
|
||||
{3, nullptr, "OpenAudioInAuto"},
|
||||
{4, nullptr, "ListAudioInsAuto"},
|
||||
{0, nullptr, "ListAudioIns"}, {1, nullptr, "OpenAudioIn"}, {2, nullptr, "Unknown"},
|
||||
{3, nullptr, "OpenAudioInAuto"}, {4, nullptr, "ListAudioInsAuto"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -196,10 +196,10 @@ void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
AudOutU::AudOutU() : ServiceFramework("audout:u") {
|
||||
static const FunctionInfo functions[] = {{0x00000000, &AudOutU::ListAudioOuts, "ListAudioOuts"},
|
||||
{0x00000001, &AudOutU::OpenAudioOut, "OpenAudioOut"},
|
||||
{0x00000002, nullptr, "ListAudioOutsAuto"},
|
||||
{0x00000003, nullptr, "OpenAudioOutAuto"}};
|
||||
static const FunctionInfo functions[] = {{0, &AudOutU::ListAudioOuts, "ListAudioOuts"},
|
||||
{1, &AudOutU::OpenAudioOut, "OpenAudioOut"},
|
||||
{2, nullptr, "ListAudioOutsAuto"},
|
||||
{3, nullptr, "OpenAudioOutAuto"}};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ public:
|
||||
{4, nullptr, "RegisterBufferEvent"},
|
||||
{5, nullptr, "GetReleasedFinalOutputRecorderBuffer"},
|
||||
{6, nullptr, "ContainsFinalOutputRecorderBuffer"},
|
||||
{7, nullptr, "Unknown"},
|
||||
{8, nullptr, "AppendFinalOutputRecorderBufferAuto"},
|
||||
{9, nullptr, "GetReleasedFinalOutputRecorderBufferAuto"},
|
||||
};
|
||||
@@ -30,7 +31,7 @@ public:
|
||||
|
||||
AudRecU::AudRecU() : ServiceFramework("audrec:u") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x00000000, nullptr, "OpenFinalOutputRecorder"},
|
||||
{0, nullptr, "OpenFinalOutputRecorder"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
@@ -56,27 +57,26 @@ private:
|
||||
}
|
||||
|
||||
void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_DEBUG(Service_Audio, "{}", ctx.Description());
|
||||
AudioRendererResponseData response_data{};
|
||||
AudioRendererConfig config;
|
||||
auto buf = ctx.ReadBuffer();
|
||||
std::memcpy(&config, buf.data(), sizeof(AudioRendererConfig));
|
||||
|
||||
response_data.section_0_size =
|
||||
static_cast<u32>(response_data.state_entries.size() * sizeof(AudioRendererStateEntry));
|
||||
response_data.section_1_size = static_cast<u32>(response_data.section_1.size());
|
||||
response_data.section_2_size = static_cast<u32>(response_data.section_2.size());
|
||||
response_data.section_3_size = static_cast<u32>(response_data.section_3.size());
|
||||
response_data.section_4_size = static_cast<u32>(response_data.section_4.size());
|
||||
response_data.section_5_size = static_cast<u32>(response_data.section_5.size());
|
||||
response_data.total_size = sizeof(AudioRendererResponseData);
|
||||
AudioRendererResponse response_data{config};
|
||||
|
||||
for (unsigned i = 0; i < response_data.state_entries.size(); i++) {
|
||||
// 4 = Busy and 5 = Ready?
|
||||
response_data.state_entries[i].state = 5;
|
||||
ASSERT(ctx.GetWriteBufferSize() == response_data.total_size);
|
||||
|
||||
std::vector<u8> output(response_data.total_size);
|
||||
std::memcpy(output.data(), &response_data, sizeof(AudioRendererResponse));
|
||||
std::vector<MemoryPoolEntry> memory_pool(config.memory_pools_size / 0x20);
|
||||
for (auto& entry : memory_pool) {
|
||||
entry.state = 5;
|
||||
}
|
||||
std::memcpy(output.data() + sizeof(AudioRendererResponse), memory_pool.data(),
|
||||
response_data.memory_pools_size);
|
||||
|
||||
ctx.WriteBuffer(&response_data, response_data.total_size);
|
||||
ctx.WriteBuffer(output);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
NGLOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
@@ -108,43 +108,55 @@ private:
|
||||
NGLOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
}
|
||||
|
||||
struct AudioRendererStateEntry {
|
||||
struct MemoryPoolEntry {
|
||||
u32_le state;
|
||||
u32_le unknown_4;
|
||||
u32_le unknown_8;
|
||||
u32_le unknown_c;
|
||||
};
|
||||
static_assert(sizeof(AudioRendererStateEntry) == 0x10,
|
||||
"AudioRendererStateEntry has wrong size");
|
||||
static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
|
||||
|
||||
struct AudioRendererResponseData {
|
||||
u32_le unknown_0;
|
||||
u32_le section_5_size;
|
||||
u32_le section_0_size;
|
||||
u32_le section_1_size;
|
||||
u32_le unknown_10;
|
||||
u32_le section_2_size;
|
||||
u32_le unknown_18;
|
||||
u32_le section_3_size;
|
||||
u32_le section_4_size;
|
||||
u32_le unknown_24;
|
||||
u32_le unknown_28;
|
||||
u32_le unknown_2c;
|
||||
u32_le unknown_30;
|
||||
u32_le unknown_34;
|
||||
u32_le unknown_38;
|
||||
u32_le total_size;
|
||||
|
||||
std::array<AudioRendererStateEntry, 0x18e> state_entries;
|
||||
|
||||
std::array<u8, 0x600> section_1;
|
||||
std::array<u8, 0xe0> section_2;
|
||||
std::array<u8, 0x20> section_3;
|
||||
std::array<u8, 0x10> section_4;
|
||||
std::array<u8, 0xb0> section_5;
|
||||
struct AudioRendererConfig {
|
||||
u32 revision;
|
||||
u32 behavior_size;
|
||||
u32 memory_pools_size;
|
||||
u32 voices_size;
|
||||
u32 voice_resource_size;
|
||||
u32 effects_size;
|
||||
u32 mixes_size;
|
||||
u32 sinks_size;
|
||||
u32 performance_buffer_size;
|
||||
INSERT_PADDING_WORDS(6);
|
||||
u32 total_size;
|
||||
};
|
||||
static_assert(sizeof(AudioRendererResponseData) == 0x20e0,
|
||||
"AudioRendererResponseData has wrong size");
|
||||
static_assert(sizeof(AudioRendererConfig) == 0x40, "AudioRendererConfig has wrong size");
|
||||
|
||||
struct AudioRendererResponse {
|
||||
AudioRendererResponse(const AudioRendererConfig& config) {
|
||||
revision = config.revision;
|
||||
error_info_size = 0xb0;
|
||||
memory_pools_size = (config.memory_pools_size / 0x20) * 0x10;
|
||||
voices_size = (config.voices_size / 0x170) * 0x10;
|
||||
effects_size = (config.effects_size / 0xC0) * 0x10;
|
||||
sinks_size = (config.sinks_size / 0x140) * 0x20;
|
||||
performance_manager_size = 0x10;
|
||||
total_size = sizeof(AudioRendererResponse) + error_info_size + memory_pools_size +
|
||||
voices_size + effects_size + sinks_size + performance_manager_size;
|
||||
}
|
||||
|
||||
u32_le revision;
|
||||
u32_le error_info_size;
|
||||
u32_le memory_pools_size;
|
||||
u32_le voices_size;
|
||||
u32_le unknown_10;
|
||||
u32_le effects_size;
|
||||
u32_le unknown_18;
|
||||
u32_le sinks_size;
|
||||
u32_le performance_manager_size;
|
||||
INSERT_PADDING_WORDS(6);
|
||||
u32_le total_size;
|
||||
};
|
||||
static_assert(sizeof(AudioRendererResponse) == 0x40, "AudioRendererResponse has wrong size");
|
||||
|
||||
/// This is used to trigger the audio event callback.
|
||||
CoreTiming::EventType* audio_event;
|
||||
@@ -156,19 +168,20 @@ class IAudioDevice final : public ServiceFramework<IAudioDevice> {
|
||||
public:
|
||||
IAudioDevice() : ServiceFramework("IAudioDevice") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
|
||||
{0x1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"},
|
||||
{0x2, nullptr, "GetAudioDeviceOutputVolume"},
|
||||
{0x3, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceName"},
|
||||
{0x4, &IAudioDevice::QueryAudioDeviceSystemEvent, "QueryAudioDeviceSystemEvent"},
|
||||
{0x5, &IAudioDevice::GetActiveChannelCount, "GetActiveChannelCount"},
|
||||
{0x6, &IAudioDevice::ListAudioDeviceName,
|
||||
{0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
|
||||
{1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"},
|
||||
{2, nullptr, "GetAudioDeviceOutputVolume"},
|
||||
{3, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceName"},
|
||||
{4, &IAudioDevice::QueryAudioDeviceSystemEvent, "QueryAudioDeviceSystemEvent"},
|
||||
{5, &IAudioDevice::GetActiveChannelCount, "GetActiveChannelCount"},
|
||||
{6, &IAudioDevice::ListAudioDeviceName,
|
||||
"ListAudioDeviceNameAuto"}, // TODO(ogniK): Confirm if autos are identical to non auto
|
||||
{0x7, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolumeAuto"},
|
||||
{0x8, nullptr, "GetAudioDeviceOutputVolumeAuto"},
|
||||
{0xa, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
|
||||
{0xb, nullptr, "QueryAudioDeviceInputEvent"},
|
||||
{0xc, nullptr, "QueryAudioDeviceOutputEvent"}};
|
||||
{7, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolumeAuto"},
|
||||
{8, nullptr, "GetAudioDeviceOutputVolumeAuto"},
|
||||
{10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
|
||||
{11, nullptr, "QueryAudioDeviceInputEvent"},
|
||||
{12, nullptr, "QueryAudioDeviceOutputEvent"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
buffer_event =
|
||||
@@ -255,12 +268,62 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto params = rp.PopRaw<AudioRendererParameters>();
|
||||
|
||||
u64 buffer_sz = Common::AlignUp(4 * params.unknown8, 0x40);
|
||||
buffer_sz += params.unknownC * 1024;
|
||||
buffer_sz += 0x940 * (params.unknownC + 1);
|
||||
buffer_sz += 0x3F0 * params.voice_count;
|
||||
buffer_sz += Common::AlignUp(8 * (params.unknownC + 1), 0x10);
|
||||
buffer_sz += Common::AlignUp(8 * params.voice_count, 0x10);
|
||||
buffer_sz +=
|
||||
Common::AlignUp((0x3C0 * (params.sink_count + params.unknownC) + 4 * params.sample_count) *
|
||||
(params.unknown8 + 6),
|
||||
0x40);
|
||||
|
||||
if (IsFeatureSupported(AudioFeatures::Splitter, params.magic)) {
|
||||
u32 count = params.unknownC + 1;
|
||||
u64 node_count = Common::AlignUp(count, 0x40);
|
||||
u64 node_state_buffer_sz =
|
||||
4 * (node_count * node_count) + 0xC * node_count + 2 * (node_count / 8);
|
||||
u64 edge_matrix_buffer_sz = 0;
|
||||
node_count = Common::AlignUp(count * count, 0x40);
|
||||
if (node_count >> 31 != 0) {
|
||||
edge_matrix_buffer_sz = (node_count | 7) / 8;
|
||||
} else {
|
||||
edge_matrix_buffer_sz = node_count / 8;
|
||||
}
|
||||
buffer_sz += Common::AlignUp(node_state_buffer_sz + edge_matrix_buffer_sz, 0x10);
|
||||
}
|
||||
|
||||
buffer_sz += 0x20 * (params.effect_count + 4 * params.voice_count) + 0x50;
|
||||
if (IsFeatureSupported(AudioFeatures::Splitter, params.magic)) {
|
||||
buffer_sz += 0xE0 * params.unknown2c;
|
||||
buffer_sz += 0x20 * params.splitter_count;
|
||||
buffer_sz += Common::AlignUp(4 * params.unknown2c, 0x10);
|
||||
}
|
||||
buffer_sz = Common::AlignUp(buffer_sz, 0x40) + 0x170 * params.sink_count;
|
||||
u64 output_sz = buffer_sz + 0x280 * params.sink_count + 0x4B0 * params.effect_count +
|
||||
((params.voice_count * 256) | 0x40);
|
||||
|
||||
if (params.unknown1c >= 1) {
|
||||
output_sz = Common::AlignUp(((16 * params.sink_count + 16 * params.effect_count +
|
||||
16 * params.voice_count + 16) +
|
||||
0x658) *
|
||||
(params.unknown1c + 1) +
|
||||
0xc0,
|
||||
0x40) +
|
||||
output_sz;
|
||||
}
|
||||
output_sz = Common::AlignUp(output_sz + 0x1807e, 0x1000);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(0x4000);
|
||||
rb.Push<u64>(output_sz);
|
||||
|
||||
NGLOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
NGLOG_DEBUG(Service_Audio, "called, buffer_size=0x{:X}", output_sz);
|
||||
}
|
||||
|
||||
void AudRenU::GetAudioDevice(Kernel::HLERequestContext& ctx) {
|
||||
@@ -272,4 +335,14 @@ void AudRenU::GetAudioDevice(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_DEBUG(Service_Audio, "called");
|
||||
}
|
||||
|
||||
bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const {
|
||||
u32_be version_num = (revision - Common::MakeMagic('R', 'E', 'V', '0')); // Byte swap
|
||||
switch (feature) {
|
||||
case AudioFeatures::Splitter:
|
||||
return version_num >= 2u;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
||||
@@ -21,6 +21,31 @@ private:
|
||||
void OpenAudioRenderer(Kernel::HLERequestContext& ctx);
|
||||
void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx);
|
||||
void GetAudioDevice(Kernel::HLERequestContext& ctx);
|
||||
|
||||
struct AudioRendererParameters {
|
||||
u32_le sample_rate;
|
||||
u32_le sample_count;
|
||||
u32_le unknown8;
|
||||
u32_le unknownC;
|
||||
u32_le voice_count;
|
||||
u32_le sink_count;
|
||||
u32_le effect_count;
|
||||
u32_le unknown1c;
|
||||
u8 unknown20;
|
||||
u8 padding1[3];
|
||||
u32_le splitter_count;
|
||||
u32_le unknown2c;
|
||||
u8 padding2[4];
|
||||
u32_le magic;
|
||||
};
|
||||
static_assert(sizeof(AudioRendererParameters) == 52,
|
||||
"AudioRendererParameters is an invalid size");
|
||||
|
||||
enum class AudioFeatures : u32 {
|
||||
Splitter,
|
||||
};
|
||||
|
||||
bool IsFeatureSupported(AudioFeatures feature, u32_le revision) const;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
||||
@@ -11,19 +11,19 @@ namespace Service::Audio {
|
||||
|
||||
CodecCtl::CodecCtl() : ServiceFramework("codecctl") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x00000000, nullptr, "InitializeCodecController"},
|
||||
{0x00000001, nullptr, "FinalizeCodecController"},
|
||||
{0x00000002, nullptr, "SleepCodecController"},
|
||||
{0x00000003, nullptr, "WakeCodecController"},
|
||||
{0x00000004, nullptr, "SetCodecVolume"},
|
||||
{0x00000005, nullptr, "GetCodecVolumeMax"},
|
||||
{0x00000006, nullptr, "GetCodecVolumeMin"},
|
||||
{0x00000007, nullptr, "SetCodecActiveTarget"},
|
||||
{0x00000008, nullptr, "Unknown"},
|
||||
{0x00000009, nullptr, "BindCodecHeadphoneMicJackInterrupt"},
|
||||
{0x00000010, nullptr, "IsCodecHeadphoneMicJackInserted"},
|
||||
{0x00000011, nullptr, "ClearCodecHeadphoneMicJackInterrupt"},
|
||||
{0x00000012, nullptr, "IsCodecDeviceRequested"},
|
||||
{0, nullptr, "InitializeCodecController"},
|
||||
{1, nullptr, "FinalizeCodecController"},
|
||||
{2, nullptr, "SleepCodecController"},
|
||||
{3, nullptr, "WakeCodecController"},
|
||||
{4, nullptr, "SetCodecVolume"},
|
||||
{5, nullptr, "GetCodecVolumeMax"},
|
||||
{6, nullptr, "GetCodecVolumeMin"},
|
||||
{7, nullptr, "SetCodecActiveTarget"},
|
||||
{8, nullptr, "GetCodecActiveTarget"},
|
||||
{9, nullptr, "BindCodecHeadphoneMicJackInterrupt"},
|
||||
{10, nullptr, "IsCodecHeadphoneMicJackInserted"},
|
||||
{11, nullptr, "ClearCodecHeadphoneMicJackInterrupt"},
|
||||
{12, nullptr, "IsCodecDeviceRequested"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
16
src/core/hle/service/bcat/bcat.cpp
Normal file
16
src/core/hle/service/bcat/bcat.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/bcat/bcat.h"
|
||||
|
||||
namespace Service::BCAT {
|
||||
|
||||
BCAT::BCAT(std::shared_ptr<Module> module, const char* name)
|
||||
: Module::Interface(std::move(module), name) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &BCAT::CreateBcatService, "CreateBcatService"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
} // namespace Service::BCAT
|
||||
16
src/core/hle/service/bcat/bcat.h
Normal file
16
src/core/hle/service/bcat/bcat.h
Normal file
@@ -0,0 +1,16 @@
|
||||
// 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/bcat/module.h"
|
||||
|
||||
namespace Service::BCAT {
|
||||
|
||||
class BCAT final : public Module::Interface {
|
||||
public:
|
||||
explicit BCAT(std::shared_ptr<Module> module, const char* name);
|
||||
};
|
||||
|
||||
} // namespace Service::BCAT
|
||||
53
src/core/hle/service/bcat/module.cpp
Normal file
53
src/core/hle/service/bcat/module.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
// 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/bcat/bcat.h"
|
||||
#include "core/hle/service/bcat/module.h"
|
||||
|
||||
namespace Service::BCAT {
|
||||
|
||||
class IBcatService final : public ServiceFramework<IBcatService> {
|
||||
public:
|
||||
IBcatService() : ServiceFramework("IBcatService") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{10100, nullptr, "RequestSyncDeliveryCache"},
|
||||
{10101, nullptr, "RequestSyncDeliveryCacheWithDirectoryName"},
|
||||
{10200, nullptr, "CancelSyncDeliveryCacheRequest"},
|
||||
{20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"},
|
||||
{20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"},
|
||||
{30100, nullptr, "SetPassphrase"},
|
||||
{30200, nullptr, "RegisterBackgroundDeliveryTask"},
|
||||
{30201, nullptr, "UnregisterBackgroundDeliveryTask"},
|
||||
{30202, nullptr, "BlockDeliveryTask"},
|
||||
{30203, nullptr, "UnblockDeliveryTask"},
|
||||
{90100, nullptr, "EnumerateBackgroundDeliveryTask"},
|
||||
{90200, nullptr, "GetDeliveryList"},
|
||||
{90201, nullptr, "ClearDeliveryCacheStorage"},
|
||||
{90300, nullptr, "GetPushNotificationLog"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IBcatService>();
|
||||
NGLOG_DEBUG(Service_BCAT, "called");
|
||||
}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)) {}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
auto module = std::make_shared<Module>();
|
||||
std::make_shared<BCAT>(module, "bcat:a")->InstallAsService(service_manager);
|
||||
std::make_shared<BCAT>(module, "bcat:m")->InstallAsService(service_manager);
|
||||
std::make_shared<BCAT>(module, "bcat:u")->InstallAsService(service_manager);
|
||||
std::make_shared<BCAT>(module, "bcat:s")->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace Service::BCAT
|
||||
27
src/core/hle/service/bcat/module.h
Normal file
27
src/core/hle/service/bcat/module.h
Normal file
@@ -0,0 +1,27 @@
|
||||
// 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::BCAT {
|
||||
|
||||
class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
Interface(std::shared_ptr<Module> module, const char* name);
|
||||
|
||||
void CreateBcatService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
};
|
||||
};
|
||||
|
||||
/// Registers all BCAT services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
|
||||
} // namespace Service::BCAT
|
||||
@@ -13,7 +13,7 @@ namespace Service::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) {
|
||||
void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
u32 error_code = rp.Pop<u32>();
|
||||
NGLOG_WARNING(Service_Fatal, "(STUBBED) called, error_code=0x{:X}", error_code);
|
||||
@@ -21,7 +21,7 @@ void Module::Interface::FatalSimple(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Module::Interface::TransitionToFatalError(Kernel::HLERequestContext& ctx) {
|
||||
void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_WARNING(Service_Fatal, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
@@ -14,8 +14,8 @@ public:
|
||||
public:
|
||||
Interface(std::shared_ptr<Module> module, const char* name);
|
||||
|
||||
void FatalSimple(Kernel::HLERequestContext& ctx);
|
||||
void TransitionToFatalError(Kernel::HLERequestContext& ctx);
|
||||
void ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx);
|
||||
void ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
|
||||
@@ -8,8 +8,9 @@ namespace Service::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"},
|
||||
{0, nullptr, "ThrowFatal"},
|
||||
{1, &Fatal_U::ThrowFatalWithPolicy, "ThrowFatalWithPolicy"},
|
||||
{2, &Fatal_U::ThrowFatalWithCpuContext, "ThrowFatalWithCpuContext"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <cinttypes>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/directory.h"
|
||||
#include "core/file_sys/filesystem.h"
|
||||
@@ -258,9 +259,7 @@ public:
|
||||
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);
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
u64 mode = rp.Pop<u64>();
|
||||
u32 size = rp.Pop<u32>();
|
||||
@@ -275,9 +274,7 @@ public:
|
||||
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);
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
NGLOG_DEBUG(Service_FS, "called file {}", name);
|
||||
|
||||
@@ -289,9 +286,7 @@ public:
|
||||
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);
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
NGLOG_DEBUG(Service_FS, "called directory {}", name);
|
||||
|
||||
@@ -305,13 +300,11 @@ public:
|
||||
std::vector<u8> buffer;
|
||||
buffer.resize(ctx.BufferDescriptorX()[0].Size());
|
||||
Memory::ReadBlock(ctx.BufferDescriptorX()[0].Address(), buffer.data(), buffer.size());
|
||||
auto end = std::find(buffer.begin(), buffer.end(), '\0');
|
||||
std::string src_name(buffer.begin(), end);
|
||||
std::string src_name = Common::StringFromBuffer(buffer);
|
||||
|
||||
buffer.resize(ctx.BufferDescriptorX()[1].Size());
|
||||
Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size());
|
||||
end = std::find(buffer.begin(), buffer.end(), '\0');
|
||||
std::string dst_name(buffer.begin(), end);
|
||||
std::string dst_name = Common::StringFromBuffer(buffer);
|
||||
|
||||
NGLOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name);
|
||||
|
||||
@@ -323,9 +316,7 @@ public:
|
||||
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);
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>());
|
||||
|
||||
@@ -349,9 +340,7 @@ public:
|
||||
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);
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
// TODO(Subv): Implement this filter.
|
||||
u32 filter_flags = rp.Pop<u32>();
|
||||
@@ -376,9 +365,7 @@ public:
|
||||
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);
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
NGLOG_DEBUG(Service_FS, "called file {}", name);
|
||||
|
||||
|
||||
@@ -84,6 +84,10 @@ private:
|
||||
|
||||
for (size_t controller = 0; controller < mem.controllers.size(); controller++) {
|
||||
for (int index = 0; index < HID_NUM_LAYOUTS; index++) {
|
||||
// TODO(DarkLordZach): Is this layout/controller config actually invalid?
|
||||
if (controller == Controller_Handheld && index == Layout_Single)
|
||||
continue;
|
||||
|
||||
ControllerLayout& layout = mem.controllers[controller].layouts[index];
|
||||
layout.header.num_entries = HID_NUM_ENTRIES;
|
||||
layout.header.max_entry_index = HID_NUM_ENTRIES - 1;
|
||||
@@ -94,7 +98,6 @@ private:
|
||||
layout.header.latest_entry = (layout.header.latest_entry + 1) % HID_NUM_ENTRIES;
|
||||
|
||||
ControllerInputEntry& entry = layout.entries[layout.header.latest_entry];
|
||||
entry.connection_state = ConnectionState_Connected | ConnectionState_Wired;
|
||||
entry.timestamp++;
|
||||
// TODO(shinyquagsire23): Is this always identical to timestamp?
|
||||
entry.timestamp_2++;
|
||||
@@ -103,6 +106,8 @@ private:
|
||||
if (controller != Controller_Handheld)
|
||||
continue;
|
||||
|
||||
entry.connection_state = ConnectionState_Connected | ConnectionState_Wired;
|
||||
|
||||
// TODO(shinyquagsire23): Set up some LUTs for each layout mapping in the future?
|
||||
// For now everything is just the default handheld layout, but split Joy-Con will
|
||||
// rotate the face buttons and directions for certain layouts.
|
||||
@@ -329,6 +334,7 @@ public:
|
||||
{130, nullptr, "SwapNpadAssignment"},
|
||||
{131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"},
|
||||
{132, nullptr, "EnableUnintendedHomeButtonInputProtection"},
|
||||
{133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"},
|
||||
{200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
|
||||
{201, &Hid::SendVibrationValue, "SendVibrationValue"},
|
||||
{202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"},
|
||||
@@ -336,12 +342,41 @@ public:
|
||||
{204, nullptr, "PermitVibration"},
|
||||
{205, nullptr, "IsVibrationPermitted"},
|
||||
{206, &Hid::SendVibrationValues, "SendVibrationValues"},
|
||||
{207, nullptr, "SendVibrationGcErmCommand"},
|
||||
{208, nullptr, "GetActualVibrationGcErmCommand"},
|
||||
{209, nullptr, "BeginPermitVibrationSession"},
|
||||
{210, nullptr, "EndPermitVibrationSession"},
|
||||
{300, nullptr, "ActivateConsoleSixAxisSensor"},
|
||||
{301, nullptr, "StartConsoleSixAxisSensor"},
|
||||
{302, nullptr, "StopConsoleSixAxisSensor"},
|
||||
{303, nullptr, "ActivateSevenSixAxisSensor"},
|
||||
{304, nullptr, "StartSevenSixAxisSensor"},
|
||||
{305, nullptr, "StopSevenSixAxisSensor"},
|
||||
{306, nullptr, "InitializeSevenSixAxisSensor"},
|
||||
{307, nullptr, "FinalizeSevenSixAxisSensor"},
|
||||
{308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
|
||||
{309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
|
||||
{400, nullptr, "IsUsbFullKeyControllerEnabled"},
|
||||
{401, nullptr, "EnableUsbFullKeyController"},
|
||||
{402, nullptr, "IsUsbFullKeyControllerConnected"},
|
||||
{403, nullptr, "HasBattery"},
|
||||
{404, nullptr, "HasLeftRightBattery"},
|
||||
{405, nullptr, "GetNpadInterfaceType"},
|
||||
{406, nullptr, "GetNpadLeftRightInterfaceType"},
|
||||
{500, nullptr, "GetPalmaConnectionHandle"},
|
||||
{501, nullptr, "InitializePalma"},
|
||||
{502, nullptr, "AcquirePalmaOperationCompleteEvent"},
|
||||
{503, nullptr, "GetPalmaOperationInfo"},
|
||||
{504, nullptr, "PlayPalmaActivity"},
|
||||
{505, nullptr, "SetPalmaFrModeType"},
|
||||
{506, nullptr, "ReadPalmaStep"},
|
||||
{507, nullptr, "EnablePalmaStep"},
|
||||
{508, nullptr, "SuspendPalmaStep"},
|
||||
{509, nullptr, "ResetPalmaStep"},
|
||||
{510, nullptr, "ReadPalmaApplicationSection"},
|
||||
{511, nullptr, "WritePalmaApplicationSection"},
|
||||
{512, nullptr, "ReadPalmaUniqueCode"},
|
||||
{513, nullptr, "SetPalmaUniqueCodeInvalid"},
|
||||
{1000, nullptr, "SetNpadCommunicationMode"},
|
||||
{1001, nullptr, "GetNpadCommunicationMode"},
|
||||
};
|
||||
|
||||
50
src/core/hle/service/mm/mm_u.cpp
Normal file
50
src/core/hle/service/mm/mm_u.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
// 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/kernel/client_session.h"
|
||||
#include "core/hle/service/mm/mm_u.h"
|
||||
|
||||
namespace Service::MM {
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
std::make_shared<MM_U>()->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
void MM_U::Initialize(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_WARNING(Service_MM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void MM_U::SetAndWait(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
min = rp.Pop<u32>();
|
||||
max = rp.Pop<u32>();
|
||||
current = min;
|
||||
|
||||
NGLOG_WARNING(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void MM_U::Get(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_WARNING(Service_MM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(current);
|
||||
}
|
||||
|
||||
MM_U::MM_U() : ServiceFramework("mm:u") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "InitializeOld"}, {1, nullptr, "FinalizeOld"},
|
||||
{2, nullptr, "SetAndWaitOld"}, {3, nullptr, "GetOld"},
|
||||
{4, &MM_U::Initialize, "Initialize"}, {5, nullptr, "Finalize"},
|
||||
{6, &MM_U::SetAndWait, "SetAndWait"}, {7, &MM_U::Get, "Get"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
} // namespace Service::MM
|
||||
29
src/core/hle/service/mm/mm_u.h
Normal file
29
src/core/hle/service/mm/mm_u.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::MM {
|
||||
|
||||
class MM_U final : public ServiceFramework<MM_U> {
|
||||
public:
|
||||
MM_U();
|
||||
~MM_U() = default;
|
||||
|
||||
private:
|
||||
void Initialize(Kernel::HLERequestContext& ctx);
|
||||
void SetAndWait(Kernel::HLERequestContext& ctx);
|
||||
void Get(Kernel::HLERequestContext& ctx);
|
||||
|
||||
u32 min{0};
|
||||
u32 max{0};
|
||||
u32 current{0};
|
||||
};
|
||||
|
||||
/// Registers all MM services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
|
||||
} // namespace Service::MM
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/hle/service/nfp/nfp.h"
|
||||
#include "core/hle/service/nfp/nfp_user.h"
|
||||
|
||||
@@ -17,40 +19,132 @@ public:
|
||||
IUser() : ServiceFramework("IUser") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IUser::Initialize, "Initialize"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
{9, nullptr, "Unknown9"},
|
||||
{10, nullptr, "Unknown10"},
|
||||
{11, nullptr, "Unknown11"},
|
||||
{12, nullptr, "Unknown12"},
|
||||
{13, nullptr, "Unknown13"},
|
||||
{14, nullptr, "Unknown14"},
|
||||
{15, nullptr, "Unknown15"},
|
||||
{16, nullptr, "Unknown16"},
|
||||
{17, nullptr, "Unknown17"},
|
||||
{18, nullptr, "Unknown18"},
|
||||
{19, nullptr, "Unknown19"},
|
||||
{20, nullptr, "Unknown20"},
|
||||
{21, nullptr, "Unknown21"},
|
||||
{22, nullptr, "Unknown22"},
|
||||
{23, nullptr, "Unknown23"},
|
||||
{24, nullptr, "Unknown24"},
|
||||
{1, nullptr, "Finalize"},
|
||||
{2, &IUser::ListDevices, "ListDevices"},
|
||||
{3, nullptr, "StartDetection"},
|
||||
{4, nullptr, "StopDetection"},
|
||||
{5, nullptr, "Mount"},
|
||||
{6, nullptr, "Unmount"},
|
||||
{7, nullptr, "OpenApplicationArea"},
|
||||
{8, nullptr, "GetApplicationArea"},
|
||||
{9, nullptr, "SetApplicationArea"},
|
||||
{10, nullptr, "Flush"},
|
||||
{11, nullptr, "Restore"},
|
||||
{12, nullptr, "CreateApplicationArea"},
|
||||
{13, nullptr, "GetTagInfo"},
|
||||
{14, nullptr, "GetRegisterInfo"},
|
||||
{15, nullptr, "GetCommonInfo"},
|
||||
{16, nullptr, "GetModelInfo"},
|
||||
{17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
|
||||
{18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
|
||||
{19, &IUser::GetState, "GetState"},
|
||||
{20, &IUser::GetDeviceState, "GetDeviceState"},
|
||||
{21, &IUser::GetNpadId, "GetNpadId"},
|
||||
{22, nullptr, "GetApplicationArea2"},
|
||||
{23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
|
||||
{24, nullptr, "RecreateApplicationArea"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
activate_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "IUser:ActivateEvent");
|
||||
deactivate_event =
|
||||
Kernel::Event::Create(Kernel::ResetType::OneShot, "IUser:DeactivateEvent");
|
||||
availability_change_event =
|
||||
Kernel::Event::Create(Kernel::ResetType::OneShot, "IUser:AvailabilityChangeEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
enum class State : u32 {
|
||||
NonInitialized = 0,
|
||||
Initialized = 1,
|
||||
};
|
||||
|
||||
enum class DeviceState : u32 {
|
||||
Initialized = 0,
|
||||
};
|
||||
|
||||
void Initialize(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_WARNING(Service_NFP, "(STUBBED) called");
|
||||
|
||||
state = State::Initialized;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ListDevices(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u32 array_size = rp.Pop<u32>();
|
||||
|
||||
ctx.WriteBuffer(&device_handle, sizeof(device_handle));
|
||||
|
||||
NGLOG_WARNING(Service_NFP, "(STUBBED) called, array_size={}", array_size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(0);
|
||||
}
|
||||
|
||||
void AttachActivateEvent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 dev_handle = rp.Pop<u64>();
|
||||
NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(activate_event);
|
||||
}
|
||||
|
||||
void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 dev_handle = rp.Pop<u64>();
|
||||
NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(deactivate_event);
|
||||
}
|
||||
|
||||
void GetState(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_WARNING(Service_NFP, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(static_cast<u32>(state));
|
||||
}
|
||||
|
||||
void GetDeviceState(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_WARNING(Service_NFP, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(static_cast<u32>(device_state));
|
||||
}
|
||||
|
||||
void GetNpadId(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 dev_handle = rp.Pop<u64>();
|
||||
NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(npad_id);
|
||||
}
|
||||
|
||||
void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 dev_handle = rp.Pop<u64>();
|
||||
NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(availability_change_event);
|
||||
}
|
||||
|
||||
const u64 device_handle{0xDEAD};
|
||||
const HID::ControllerID npad_id{HID::Controller_Player1};
|
||||
State state{State::NonInitialized};
|
||||
DeviceState device_state{DeviceState::Initialized};
|
||||
Kernel::SharedPtr<Kernel::Event> activate_event;
|
||||
Kernel::SharedPtr<Kernel::Event> deactivate_event;
|
||||
Kernel::SharedPtr<Kernel::Event> availability_change_event;
|
||||
};
|
||||
|
||||
void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
{8, nullptr, "SetPriority"},
|
||||
{9, nullptr, "SetNetworkProfileId"},
|
||||
{10, nullptr, "SetRejectable"},
|
||||
{11, nullptr, "SetConnectionConfirmationOption"},
|
||||
{11, &IRequest::SetConnectionConfirmationOption, "SetConnectionConfirmationOption"},
|
||||
{12, nullptr, "SetPersistent"},
|
||||
{13, nullptr, "SetInstant"},
|
||||
{14, nullptr, "SetSustainable"},
|
||||
@@ -67,23 +67,32 @@ private:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(0);
|
||||
}
|
||||
|
||||
void GetResult(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(event1, event2);
|
||||
}
|
||||
|
||||
void Cancel(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void SetConnectionConfirmationOption(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> event1, event2;
|
||||
};
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vecto
|
||||
return BindChannel(input, output);
|
||||
case IoctlCommand::IocGetVaRegionsCommand:
|
||||
return GetVARegions(input, output);
|
||||
case IoctlCommand::IocUnmapBufferCommand:
|
||||
return UnmapBuffer(input, output);
|
||||
}
|
||||
|
||||
if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand)
|
||||
@@ -125,6 +127,37 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
|
||||
params.offset = gpu.memory_manager->MapBufferEx(object->addr, object->size);
|
||||
}
|
||||
|
||||
// Create a new mapping entry for this operation.
|
||||
ASSERT_MSG(buffer_mappings.find(params.offset) == buffer_mappings.end(),
|
||||
"Offset is already mapped");
|
||||
|
||||
BufferMapping mapping{};
|
||||
mapping.nvmap_handle = params.nvmap_handle;
|
||||
mapping.offset = params.offset;
|
||||
mapping.size = object->size;
|
||||
|
||||
buffer_mappings[params.offset] = mapping;
|
||||
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlUnmapBuffer params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
|
||||
NGLOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
|
||||
|
||||
auto& gpu = Core::System::GetInstance().GPU();
|
||||
|
||||
auto itr = buffer_mappings.find(params.offset);
|
||||
|
||||
ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping");
|
||||
|
||||
params.offset = gpu.memory_manager->UnmapBuffer(params.offset, itr->second.size);
|
||||
|
||||
buffer_mappings.erase(itr->second.offset);
|
||||
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
@@ -30,6 +31,7 @@ private:
|
||||
IocMapBufferExCommand = 0xC0284106,
|
||||
IocBindChannelCommand = 0x40044101,
|
||||
IocGetVaRegionsCommand = 0xC0404108,
|
||||
IocUnmapBufferCommand = 0xC0084105,
|
||||
};
|
||||
|
||||
struct IoctlInitalizeEx {
|
||||
@@ -76,6 +78,11 @@ private:
|
||||
};
|
||||
static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size");
|
||||
|
||||
struct IoctlUnmapBuffer {
|
||||
u64_le offset;
|
||||
};
|
||||
static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size");
|
||||
|
||||
struct IoctlBindChannel {
|
||||
u32_le fd;
|
||||
};
|
||||
@@ -98,12 +105,22 @@ private:
|
||||
static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2,
|
||||
"IoctlGetVaRegions is incorrect size");
|
||||
|
||||
struct BufferMapping {
|
||||
u64 offset;
|
||||
u64 size;
|
||||
u32 nvmap_handle;
|
||||
};
|
||||
|
||||
/// Map containing the nvmap object mappings in GPU memory.
|
||||
std::unordered_map<u64, BufferMapping> buffer_mappings;
|
||||
|
||||
u32 channel{};
|
||||
|
||||
u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 Remap(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
|
||||
@@ -16,7 +16,11 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<
|
||||
case IoctlCommand::IocGetConfigCommand:
|
||||
return NvOsGetConfigU32(input, output);
|
||||
case IoctlCommand::IocCtrlEventWaitCommand:
|
||||
return IocCtrlEventWait(input, output);
|
||||
return IocCtrlEventWait(input, output, false);
|
||||
case IoctlCommand::IocCtrlEventWaitAsyncCommand:
|
||||
return IocCtrlEventWait(input, output, true);
|
||||
case IoctlCommand::IocCtrlEventRegisterCommand:
|
||||
return IocCtrlEventRegister(input, output);
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl");
|
||||
return 0;
|
||||
@@ -36,7 +40,7 @@ u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>&
|
||||
} else if (!strcmp(params.param_str.data(), "NVRM_GPU_PREVENT_USE")) {
|
||||
params.config_str[0] = '0';
|
||||
} else {
|
||||
params.config_str[0] = '0';
|
||||
params.config_str[0] = '\0';
|
||||
}
|
||||
} else {
|
||||
UNIMPLEMENTED(); // unknown domain? Only nv has been seen so far on hardware
|
||||
@@ -45,11 +49,13 @@ u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>&
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
|
||||
bool is_async) {
|
||||
IocCtrlEventWaitParams params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, syncpt_id={} threshold={} timeout={}",
|
||||
params.syncpt_id, params.threshold, params.timeout);
|
||||
NGLOG_WARNING(Service_NVDRV,
|
||||
"(STUBBED) called, syncpt_id={}, threshold={}, timeout={}, is_async={}",
|
||||
params.syncpt_id, params.threshold, params.timeout, is_async);
|
||||
|
||||
// TODO(Subv): Implement actual syncpt waiting.
|
||||
params.value = 0;
|
||||
@@ -57,4 +63,10 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called");
|
||||
// TODO(bunnei): Implement this.
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -26,12 +26,64 @@ private:
|
||||
IocSyncptIncrCommand = 0x40040015,
|
||||
IocSyncptWaitCommand = 0xC00C0016,
|
||||
IocModuleMutexCommand = 0x40080017,
|
||||
IocModuleRegRDWRCommand = 0xC008010E,
|
||||
IocModuleRegRDWRCommand = 0xC0180018,
|
||||
IocSyncptWaitexCommand = 0xC0100019,
|
||||
IocSyncptReadMaxCommand = 0xC008001A,
|
||||
IocCtrlEventWaitCommand = 0xC010001D,
|
||||
IocGetConfigCommand = 0xC183001B,
|
||||
IocCtrlEventSignalCommand = 0xC004001C,
|
||||
IocCtrlEventWaitCommand = 0xC010001D,
|
||||
IocCtrlEventWaitAsyncCommand = 0xC010001E,
|
||||
IocCtrlEventRegisterCommand = 0xC004001F,
|
||||
IocCtrlEventUnregisterCommand = 0xC0040020,
|
||||
IocCtrlEventKillCommand = 0x40080021,
|
||||
};
|
||||
struct IocSyncptReadParams {
|
||||
u32_le id;
|
||||
u32_le value;
|
||||
};
|
||||
static_assert(sizeof(IocSyncptReadParams) == 8, "IocSyncptReadParams is incorrect size");
|
||||
|
||||
struct IocSyncptIncrParams {
|
||||
u32_le id;
|
||||
};
|
||||
static_assert(sizeof(IocSyncptIncrParams) == 4, "IocSyncptIncrParams is incorrect size");
|
||||
|
||||
struct IocSyncptWaitParams {
|
||||
u32_le id;
|
||||
u32_le thresh;
|
||||
s32_le timeout;
|
||||
};
|
||||
static_assert(sizeof(IocSyncptWaitParams) == 12, "IocSyncptWaitParams is incorrect size");
|
||||
|
||||
struct IocModuleMutexParams {
|
||||
u32_le id;
|
||||
u32_le lock; // (0 = unlock and 1 = lock)
|
||||
};
|
||||
static_assert(sizeof(IocModuleMutexParams) == 8, "IocModuleMutexParams is incorrect size");
|
||||
|
||||
struct IocModuleRegRDWRParams {
|
||||
u32_le id;
|
||||
u32_le num_offsets;
|
||||
u32_le block_size;
|
||||
u32_le offsets;
|
||||
u32_le values;
|
||||
u32_le write;
|
||||
};
|
||||
static_assert(sizeof(IocModuleRegRDWRParams) == 24, "IocModuleRegRDWRParams is incorrect size");
|
||||
|
||||
struct IocSyncptWaitexParams {
|
||||
u32_le id;
|
||||
u32_le thresh;
|
||||
s32_le timeout;
|
||||
u32_le value;
|
||||
};
|
||||
static_assert(sizeof(IocSyncptWaitexParams) == 16, "IocSyncptWaitexParams is incorrect size");
|
||||
|
||||
struct IocSyncptReadMaxParams {
|
||||
u32_le id;
|
||||
u32_le value;
|
||||
};
|
||||
static_assert(sizeof(IocSyncptReadMaxParams) == 8, "IocSyncptReadMaxParams is incorrect size");
|
||||
|
||||
struct IocGetConfigParams {
|
||||
std::array<char, 0x41> domain_str;
|
||||
@@ -40,6 +92,12 @@ private:
|
||||
};
|
||||
static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size");
|
||||
|
||||
struct IocCtrlEventSignalParams {
|
||||
u32_le user_event_id;
|
||||
};
|
||||
static_assert(sizeof(IocCtrlEventSignalParams) == 4,
|
||||
"IocCtrlEventSignalParams is incorrect size");
|
||||
|
||||
struct IocCtrlEventWaitParams {
|
||||
u32_le syncpt_id;
|
||||
u32_le threshold;
|
||||
@@ -48,9 +106,37 @@ private:
|
||||
};
|
||||
static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size");
|
||||
|
||||
struct IocCtrlEventWaitAsyncParams {
|
||||
u32_le syncpt_id;
|
||||
u32_le threshold;
|
||||
u32_le timeout;
|
||||
u32_le value;
|
||||
};
|
||||
static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16,
|
||||
"IocCtrlEventWaitAsyncParams is incorrect size");
|
||||
|
||||
struct IocCtrlEventRegisterParams {
|
||||
u32_le user_event_id;
|
||||
};
|
||||
static_assert(sizeof(IocCtrlEventRegisterParams) == 4,
|
||||
"IocCtrlEventRegisterParams is incorrect size");
|
||||
|
||||
struct IocCtrlEventUnregisterParams {
|
||||
u32_le user_event_id;
|
||||
};
|
||||
static_assert(sizeof(IocCtrlEventUnregisterParams) == 4,
|
||||
"IocCtrlEventUnregisterParams is incorrect size");
|
||||
|
||||
struct IocCtrlEventKill {
|
||||
u64_le user_events;
|
||||
};
|
||||
static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size");
|
||||
|
||||
u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async);
|
||||
|
||||
u32 IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -24,6 +24,12 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec
|
||||
return ZCullGetCtxSize(input, output);
|
||||
case IoctlCommand::IocZcullGetInfo:
|
||||
return ZCullGetInfo(input, output);
|
||||
case IoctlCommand::IocZbcSetTable:
|
||||
return ZBCSetTable(input, output);
|
||||
case IoctlCommand::IocZbcQueryTable:
|
||||
return ZBCQueryTable(input, output);
|
||||
case IoctlCommand::IocFlushL2:
|
||||
return FlushL2(input, output);
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl");
|
||||
return 0;
|
||||
@@ -77,9 +83,13 @@ u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vecto
|
||||
u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlGpuGetTpcMasksArgs params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, mask=0x{:X}, mask_buf_addr=0x{:X}",
|
||||
params.mask_buf_size, params.mask_buf_addr);
|
||||
params.unk = 0xcafe; // TODO(ogniK): Needs to be non 0, what does this actually do?
|
||||
NGLOG_INFO(Service_NVDRV, "called, mask=0x{:X}, mask_buf_addr=0x{:X}", params.mask_buf_size,
|
||||
params.mask_buf_addr);
|
||||
// TODO(ogniK): Confirm value on hardware
|
||||
if (params.mask_buf_size)
|
||||
params.tpc_mask_size = 4 * 1; // 4 * num_gpc
|
||||
else
|
||||
params.tpc_mask_size = 0;
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
return 0;
|
||||
}
|
||||
@@ -121,4 +131,31 @@ u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>&
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called");
|
||||
IoctlZbcSetTable params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
// TODO(ogniK): What does this even actually do?
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called");
|
||||
IoctlZbcQueryTable params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
// TODO : To implement properly
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called");
|
||||
IoctlFlushL2 params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
// TODO : To implement properly
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -25,6 +25,19 @@ private:
|
||||
IocGetActiveSlotMaskCommand = 0x80084714,
|
||||
IocZcullGetCtxSizeCommand = 0x80044701,
|
||||
IocZcullGetInfo = 0x80284702,
|
||||
IocZbcSetTable = 0x402C4703,
|
||||
IocZbcQueryTable = 0xC0344704,
|
||||
IocFlushL2 = 0x40084707,
|
||||
IocInvalICache = 0x4008470D,
|
||||
IocSetMmudebugMode = 0x4008470E,
|
||||
IocSetSmDebugMode = 0x4010470F,
|
||||
IocWaitForPause = 0xC0084710,
|
||||
IocGetTcpExceptionEnStatus = 0x80084711,
|
||||
IocNumVsms = 0x80084712,
|
||||
IocVsmsMapping = 0xC0044713,
|
||||
IocGetErrorChannelUserData = 0xC008471B,
|
||||
IocGetGpuTime = 0xC010471C,
|
||||
IocGetCpuTimeCorrelationInfo = 0xC108471D,
|
||||
};
|
||||
|
||||
struct IoctlGpuCharacteristics {
|
||||
@@ -86,7 +99,7 @@ private:
|
||||
/// [in] pointer to TPC mask buffer. It will receive one 32-bit TPC mask per GPC or 0 if
|
||||
/// GPC is not enabled or not present. This parameter is ignored if mask_buf_size is 0.
|
||||
u64_le mask_buf_addr;
|
||||
u64_le unk; // Nintendo add this?
|
||||
u64_le tpc_mask_size; // Nintendo add this?
|
||||
};
|
||||
static_assert(sizeof(IoctlGpuGetTpcMasksArgs) == 24,
|
||||
"IoctlGpuGetTpcMasksArgs is incorrect size");
|
||||
@@ -117,11 +130,40 @@ private:
|
||||
static_assert(sizeof(IoctlNvgpuGpuZcullGetInfoArgs) == 40,
|
||||
"IoctlNvgpuGpuZcullGetInfoArgs is incorrect size");
|
||||
|
||||
struct IoctlZbcSetTable {
|
||||
u32_le color_ds[4];
|
||||
u32_le color_l2[4];
|
||||
u32_le depth;
|
||||
u32_le format;
|
||||
u32_le type;
|
||||
};
|
||||
static_assert(sizeof(IoctlZbcSetTable) == 44, "IoctlZbcSetTable is incorrect size");
|
||||
|
||||
struct IoctlZbcQueryTable {
|
||||
u32_le color_ds[4];
|
||||
u32_le color_l2[4];
|
||||
u32_le depth;
|
||||
u32_le ref_cnt;
|
||||
u32_le format;
|
||||
u32_le type;
|
||||
u32_le index_size;
|
||||
};
|
||||
static_assert(sizeof(IoctlZbcQueryTable) == 52, "IoctlZbcQueryTable is incorrect size");
|
||||
|
||||
struct IoctlFlushL2 {
|
||||
u32_le flush; // l2_flush | l2_invalidate << 1 | fb_flush << 2
|
||||
u32_le reserved;
|
||||
};
|
||||
static_assert(sizeof(IoctlFlushL2) == 8, "IoctlFlushL2 is incorrect size");
|
||||
|
||||
u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -32,6 +32,10 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u
|
||||
return AllocGPFIFOEx2(input, output);
|
||||
case IoctlCommand::IocAllocObjCtxCommand:
|
||||
return AllocateObjectContext(input, output);
|
||||
case IoctlCommand::IocChannelGetWaitbaseCommand:
|
||||
return GetWaitbase(input, output);
|
||||
case IoctlCommand::IocChannelSetTimeoutCommand:
|
||||
return ChannelSetTimeout(input, output);
|
||||
}
|
||||
|
||||
if (command.group == NVGPU_IOCTL_MAGIC) {
|
||||
@@ -117,8 +121,9 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
|
||||
}
|
||||
|
||||
u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
if (input.size() < sizeof(IoctlSubmitGpfifo))
|
||||
if (input.size() < sizeof(IoctlSubmitGpfifo)) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
IoctlSubmitGpfifo params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo));
|
||||
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
|
||||
@@ -138,4 +143,20 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlGetWaitbase params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase));
|
||||
NGLOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
|
||||
params.value = 0; // Seems to be hard coded at 0
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlChannelSetTimeout params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlChannelSetTimeout));
|
||||
NGLOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -26,13 +26,23 @@ public:
|
||||
private:
|
||||
enum class IoctlCommand : u32_le {
|
||||
IocSetNVMAPfdCommand = 0x40044801,
|
||||
IocAllocGPFIFOCommand = 0x40084805,
|
||||
IocSetClientDataCommand = 0x40084714,
|
||||
IocGetClientDataCommand = 0x80084715,
|
||||
IocZCullBind = 0xc010480b,
|
||||
IocSetErrorNotifierCommand = 0xC018480C,
|
||||
IocChannelSetPriorityCommand = 0x4004480D,
|
||||
IocEnableCommand = 0x0000480E,
|
||||
IocDisableCommand = 0x0000480F,
|
||||
IocPreemptCommand = 0x00004810,
|
||||
IocForceResetCommand = 0x00004811,
|
||||
IocEventIdControlCommand = 0x40084812,
|
||||
IocGetErrorNotificationCommand = 0xC0104817,
|
||||
IocAllocGPFIFOExCommand = 0x40204818,
|
||||
IocAllocGPFIFOEx2Command = 0xC020481A,
|
||||
IocAllocObjCtxCommand = 0xC0104809,
|
||||
IocChannelGetWaitbaseCommand = 0xC0080003,
|
||||
IocChannelSetTimeoutCommand = 0x40044803,
|
||||
};
|
||||
|
||||
enum class CtxObjects : u32_le {
|
||||
@@ -49,6 +59,17 @@ private:
|
||||
};
|
||||
static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
|
||||
|
||||
struct IoctlChannelSetTimeout {
|
||||
u32_le timeout;
|
||||
};
|
||||
static_assert(sizeof(IoctlChannelSetTimeout) == 4, "IoctlChannelSetTimeout is incorrect size");
|
||||
|
||||
struct IoctlAllocGPFIFO {
|
||||
u32_le num_entries;
|
||||
u32_le flags;
|
||||
};
|
||||
static_assert(sizeof(IoctlAllocGPFIFO) == 8, "IoctlAllocGPFIFO is incorrect size");
|
||||
|
||||
struct IoctlClientData {
|
||||
u64_le data;
|
||||
};
|
||||
@@ -69,12 +90,45 @@ private:
|
||||
};
|
||||
static_assert(sizeof(IoctlSetErrorNotifier) == 24, "IoctlSetErrorNotifier is incorrect size");
|
||||
|
||||
struct IoctlChannelSetPriority {
|
||||
u32_le priority;
|
||||
};
|
||||
static_assert(sizeof(IoctlChannelSetPriority) == 4,
|
||||
"IoctlChannelSetPriority is incorrect size");
|
||||
|
||||
struct IoctlEventIdControl {
|
||||
u32_le cmd; // 0=disable, 1=enable, 2=clear
|
||||
u32_le id;
|
||||
};
|
||||
static_assert(sizeof(IoctlEventIdControl) == 8, "IoctlEventIdControl is incorrect size");
|
||||
|
||||
struct IoctlGetErrorNotification {
|
||||
u64_le timestamp;
|
||||
u32_le info32;
|
||||
u16_le info16;
|
||||
u16_le status; // always 0xFFFF
|
||||
};
|
||||
static_assert(sizeof(IoctlGetErrorNotification) == 16,
|
||||
"IoctlGetErrorNotification is incorrect size");
|
||||
|
||||
struct IoctlFence {
|
||||
u32_le id;
|
||||
u32_le value;
|
||||
};
|
||||
static_assert(sizeof(IoctlFence) == 8, "IoctlFence is incorrect size");
|
||||
|
||||
struct IoctlAllocGpfifoEx {
|
||||
u32_le num_entries;
|
||||
u32_le flags;
|
||||
u32_le unk0;
|
||||
u32_le unk1;
|
||||
u32_le unk2;
|
||||
u32_le unk3;
|
||||
u32_le unk4;
|
||||
u32_le unk5;
|
||||
};
|
||||
static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size");
|
||||
|
||||
struct IoctlAllocGpfifoEx2 {
|
||||
u32_le num_entries; // in
|
||||
u32_le flags; // in
|
||||
@@ -117,7 +171,13 @@ private:
|
||||
IoctlFence fence_out; // returned new fence object for others to wait on
|
||||
};
|
||||
static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(IoctlFence),
|
||||
"submit_gpfifo is incorrect size");
|
||||
"IoctlSubmitGpfifo is incorrect size");
|
||||
|
||||
struct IoctlGetWaitbase {
|
||||
u32 unknown; // seems to be ignored? Nintendo added this
|
||||
u32 value;
|
||||
};
|
||||
static_assert(sizeof(IoctlGetWaitbase) == 8, "IoctlGetWaitbase is incorrect size");
|
||||
|
||||
u32_le nvmap_fd{};
|
||||
u64_le user_data{};
|
||||
@@ -133,6 +193,8 @@ private:
|
||||
u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
std::shared_ptr<nvmap> nvmap_dev;
|
||||
};
|
||||
|
||||
32
src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
Normal file
32
src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
NGLOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
|
||||
command.raw, input.size(), output.size());
|
||||
|
||||
switch (static_cast<IoctlCommand>(command.raw)) {
|
||||
case IoctlCommand::IocSetNVMAPfdCommand:
|
||||
return SetNVMAPfd(input, output);
|
||||
}
|
||||
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl");
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_nvdec::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlSetNvmapFD params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
NGLOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
|
||||
nvmap_fd = params.nvmap_fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
38
src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
Normal file
38
src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdevice.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
class nvhost_nvdec final : public nvdevice {
|
||||
public:
|
||||
nvhost_nvdec() = default;
|
||||
~nvhost_nvdec() override = default;
|
||||
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||
|
||||
private:
|
||||
enum class IoctlCommand : u32_le {
|
||||
IocSetNVMAPfdCommand = 0x40044801,
|
||||
};
|
||||
|
||||
struct IoctlSetNvmapFD {
|
||||
u32_le nvmap_fd;
|
||||
};
|
||||
static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
|
||||
|
||||
u32_le nvmap_fd{};
|
||||
|
||||
u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
@@ -30,6 +30,8 @@ u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& o
|
||||
return IocFromId(input, output);
|
||||
case IoctlCommand::Param:
|
||||
return IocParam(input, output);
|
||||
case IoctlCommand::Free:
|
||||
return IocFree(input, output);
|
||||
}
|
||||
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl");
|
||||
@@ -45,6 +47,7 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
object->id = next_id++;
|
||||
object->size = params.size;
|
||||
object->status = Object::Status::Created;
|
||||
object->refcount = 1;
|
||||
|
||||
u32 handle = next_handle++;
|
||||
handles[handle] = std::move(object);
|
||||
@@ -101,6 +104,8 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
[&](const auto& entry) { return entry.second->id == params.id; });
|
||||
ASSERT(itr != handles.end());
|
||||
|
||||
itr->second->refcount++;
|
||||
|
||||
// Return the existing handle instead of creating a new one.
|
||||
params.handle = itr->first;
|
||||
|
||||
@@ -114,25 +119,25 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IocParamParams params;
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
|
||||
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.type);
|
||||
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param);
|
||||
|
||||
auto object = GetObject(params.handle);
|
||||
ASSERT(object);
|
||||
ASSERT(object->status == Object::Status::Allocated);
|
||||
|
||||
switch (static_cast<ParamTypes>(params.type)) {
|
||||
switch (static_cast<ParamTypes>(params.param)) {
|
||||
case ParamTypes::Size:
|
||||
params.value = object->size;
|
||||
params.result = object->size;
|
||||
break;
|
||||
case ParamTypes::Alignment:
|
||||
params.value = object->align;
|
||||
params.result = object->align;
|
||||
break;
|
||||
case ParamTypes::Heap:
|
||||
// TODO(Subv): Seems to be a hardcoded value?
|
||||
params.value = 0x40000000;
|
||||
params.result = 0x40000000;
|
||||
break;
|
||||
case ParamTypes::Kind:
|
||||
params.value = object->kind;
|
||||
params.result = object->kind;
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
@@ -142,4 +147,34 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
enum FreeFlags {
|
||||
Freed = 0,
|
||||
NotFreedYet = 1,
|
||||
};
|
||||
|
||||
IocFreeParams params;
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
|
||||
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called");
|
||||
|
||||
auto itr = handles.find(params.handle);
|
||||
ASSERT(itr != handles.end());
|
||||
|
||||
itr->second->refcount--;
|
||||
|
||||
params.refcount = itr->second->refcount;
|
||||
params.size = itr->second->size;
|
||||
|
||||
if (itr->second->refcount == 0)
|
||||
params.flags = Freed;
|
||||
else
|
||||
params.flags = NotFreedYet;
|
||||
|
||||
handles.erase(params.handle);
|
||||
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -34,6 +34,7 @@ public:
|
||||
u8 kind;
|
||||
VAddr addr;
|
||||
Status status;
|
||||
u32 refcount;
|
||||
};
|
||||
|
||||
std::shared_ptr<Object> GetObject(u32 handle) const {
|
||||
@@ -58,16 +59,25 @@ private:
|
||||
Create = 0xC0080101,
|
||||
FromId = 0xC0080103,
|
||||
Alloc = 0xC0200104,
|
||||
Free = 0xC0180105,
|
||||
Param = 0xC00C0109,
|
||||
GetId = 0xC008010E
|
||||
GetId = 0xC008010E,
|
||||
};
|
||||
|
||||
struct IocCreateParams {
|
||||
// Input
|
||||
u32_le size;
|
||||
// Output
|
||||
u32_le handle;
|
||||
};
|
||||
static_assert(sizeof(IocCreateParams) == 8, "IocCreateParams has wrong size");
|
||||
|
||||
struct IocFromIdParams {
|
||||
// Input
|
||||
u32_le id;
|
||||
// Output
|
||||
u32_le handle;
|
||||
};
|
||||
static_assert(sizeof(IocFromIdParams) == 8, "IocFromIdParams has wrong size");
|
||||
|
||||
struct IocAllocParams {
|
||||
// Input
|
||||
@@ -79,6 +89,25 @@ private:
|
||||
INSERT_PADDING_BYTES(7);
|
||||
u64_le addr;
|
||||
};
|
||||
static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size");
|
||||
|
||||
struct IocFreeParams {
|
||||
u32_le handle;
|
||||
INSERT_PADDING_BYTES(4);
|
||||
u64_le refcount;
|
||||
u32_le size;
|
||||
u32_le flags;
|
||||
};
|
||||
static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size");
|
||||
|
||||
struct IocParamParams {
|
||||
// Input
|
||||
u32_le handle;
|
||||
u32_le param;
|
||||
// Output
|
||||
u32_le result;
|
||||
};
|
||||
static_assert(sizeof(IocParamParams) == 12, "IocParamParams has wrong size");
|
||||
|
||||
struct IocGetIdParams {
|
||||
// Output
|
||||
@@ -86,27 +115,14 @@ private:
|
||||
// Input
|
||||
u32_le handle;
|
||||
};
|
||||
|
||||
struct IocFromIdParams {
|
||||
// Input
|
||||
u32_le id;
|
||||
// Output
|
||||
u32_le handle;
|
||||
};
|
||||
|
||||
struct IocParamParams {
|
||||
// Input
|
||||
u32_le handle;
|
||||
u32_le type;
|
||||
// Output
|
||||
u32_le value;
|
||||
};
|
||||
static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
|
||||
|
||||
u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 IocFromId(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 IocParam(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 IocFree(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvmap.h"
|
||||
#include "core/hle/service/nvdrv/interface.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
@@ -36,6 +37,7 @@ Module::Module() {
|
||||
devices["/dev/nvmap"] = nvmap_dev;
|
||||
devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(nvmap_dev);
|
||||
devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>();
|
||||
devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>();
|
||||
}
|
||||
|
||||
u32 Module::Open(std::string device_name) {
|
||||
|
||||
@@ -20,11 +20,13 @@
|
||||
#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/bcat/bcat.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"
|
||||
#include "core/hle/service/lm/lm.h"
|
||||
#include "core/hle/service/mm/mm_u.h"
|
||||
#include "core/hle/service/nfp/nfp.h"
|
||||
#include "core/hle/service/nifm/nifm.h"
|
||||
#include "core/hle/service/ns/ns.h"
|
||||
@@ -144,10 +146,12 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
return ResultCode(ErrorModule::HIPC, ErrorDescription::RemoteProcessDead);
|
||||
}
|
||||
case IPC::CommandType::ControlWithContext:
|
||||
case IPC::CommandType::Control: {
|
||||
Core::System::GetInstance().ServiceManager().InvokeControlRequest(context);
|
||||
break;
|
||||
}
|
||||
case IPC::CommandType::RequestWithContext:
|
||||
case IPC::CommandType::Request: {
|
||||
InvokeRequest(context);
|
||||
break;
|
||||
@@ -181,12 +185,14 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
|
||||
AM::InstallInterfaces(*sm, nv_flinger);
|
||||
AOC::InstallInterfaces(*sm);
|
||||
APM::InstallInterfaces(*sm);
|
||||
BCAT::InstallInterfaces(*sm);
|
||||
Audio::InstallInterfaces(*sm);
|
||||
Fatal::InstallInterfaces(*sm);
|
||||
FileSystem::InstallInterfaces(*sm);
|
||||
Friend::InstallInterfaces(*sm);
|
||||
HID::InstallInterfaces(*sm);
|
||||
LM::InstallInterfaces(*sm);
|
||||
MM::InstallInterfaces(*sm);
|
||||
NFP::InstallInterfaces(*sm);
|
||||
NIFM::InstallInterfaces(*sm);
|
||||
NS::InstallInterfaces(*sm);
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
namespace Service::Set {
|
||||
|
||||
void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 id = rp.Pop<u32>();
|
||||
|
||||
static constexpr std::array<LanguageCode, 17> available_language_codes = {{
|
||||
LanguageCode::JA,
|
||||
LanguageCode::EN_US,
|
||||
@@ -50,7 +47,7 @@ SET::SET() : ServiceFramework("set") {
|
||||
{2, nullptr, "MakeLanguageCode"},
|
||||
{3, nullptr, "GetAvailableLanguageCodeCount"},
|
||||
{4, nullptr, "GetRegionCode"},
|
||||
{5, nullptr, "GetAvailableLanguageCodes2"},
|
||||
{5, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes2"},
|
||||
{6, nullptr, "GetAvailableLanguageCodeCount2"},
|
||||
{7, nullptr, "GetKeyCodeMap"},
|
||||
{8, nullptr, "GetQuestFlag"},
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
@@ -77,7 +78,7 @@ public:
|
||||
{3, nullptr, "LoadLocationNameList"},
|
||||
{4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"},
|
||||
{5, nullptr, "GetTimeZoneRuleVersion"},
|
||||
{100, nullptr, "ToCalendarTime"},
|
||||
{100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
|
||||
{101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
|
||||
{200, nullptr, "ToPosixTime"},
|
||||
{201, nullptr, "ToPosixTimeWithMyRule"},
|
||||
@@ -86,9 +87,11 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
LocationName location_name{"UTC"};
|
||||
TimeZoneRule my_time_zone_rule{};
|
||||
|
||||
void GetDeviceLocationName(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_WARNING(Service_Time, "(STUBBED) called");
|
||||
LocationName location_name{};
|
||||
NGLOG_DEBUG(Service_Time, "called");
|
||||
IPC::ResponseBuilder rb{ctx, (sizeof(LocationName) / 4) + 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(location_name);
|
||||
@@ -103,23 +106,70 @@ private:
|
||||
|
||||
void LoadTimeZoneRule(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_WARNING(Service_Time, "(STUBBED) called");
|
||||
|
||||
ctx.WriteBuffer(&my_time_zone_rule, sizeof(TimeZoneRule));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ToCalendarTime(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 posix_time = rp.Pop<u64>();
|
||||
|
||||
NGLOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time);
|
||||
|
||||
TimeZoneRule time_zone_rule{};
|
||||
auto buffer = ctx.ReadBuffer();
|
||||
std::memcpy(&time_zone_rule, buffer.data(), buffer.size());
|
||||
|
||||
CalendarTime calendar_time{2018, 1, 1, 0, 0, 0};
|
||||
CalendarAdditionalInfo additional_info{};
|
||||
|
||||
PosixToCalendar(posix_time, calendar_time, additional_info, time_zone_rule);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 10};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(calendar_time);
|
||||
rb.PushRaw(additional_info);
|
||||
}
|
||||
|
||||
void ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u64 posix_time = rp.Pop<u64>();
|
||||
const u64 posix_time = rp.Pop<u64>();
|
||||
|
||||
NGLOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time);
|
||||
|
||||
CalendarTime calendar_time{2018, 1, 1, 0, 0, 0};
|
||||
CalendarAdditionalInfo additional_info{};
|
||||
|
||||
PosixToCalendar(posix_time, calendar_time, additional_info, my_time_zone_rule);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 10};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(calendar_time);
|
||||
rb.PushRaw(additional_info);
|
||||
}
|
||||
|
||||
void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time,
|
||||
CalendarAdditionalInfo& additional_info, const TimeZoneRule& /*rule*/) {
|
||||
std::time_t t(posix_time);
|
||||
std::tm* tm = std::localtime(&t);
|
||||
if (!tm) {
|
||||
return;
|
||||
}
|
||||
calendar_time.year = tm->tm_year + 1900;
|
||||
calendar_time.month = tm->tm_mon + 1;
|
||||
calendar_time.day = tm->tm_mday;
|
||||
calendar_time.hour = tm->tm_hour;
|
||||
calendar_time.minute = tm->tm_min;
|
||||
calendar_time.second = tm->tm_sec;
|
||||
|
||||
additional_info.day_of_week = tm->tm_wday;
|
||||
additional_info.day_of_year = tm->tm_yday;
|
||||
std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC"));
|
||||
additional_info.utc_offset = 0;
|
||||
}
|
||||
};
|
||||
|
||||
void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::Time {
|
||||
|
||||
// TODO(Rozelette) RE this structure
|
||||
struct LocationName {
|
||||
INSERT_PADDING_BYTES(0x24);
|
||||
std::array<u8, 0x24> name;
|
||||
};
|
||||
static_assert(sizeof(LocationName) == 0x24, "LocationName is incorrect size");
|
||||
|
||||
@@ -25,26 +25,34 @@ struct CalendarTime {
|
||||
};
|
||||
static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime structure has incorrect size");
|
||||
|
||||
// TODO(Rozelette) RE this structure
|
||||
struct CalendarAdditionalInfo {
|
||||
INSERT_PADDING_BYTES(0x18);
|
||||
u32_le day_of_week;
|
||||
u32_le day_of_year;
|
||||
std::array<u8, 8> name;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
s32_le utc_offset;
|
||||
};
|
||||
static_assert(sizeof(CalendarAdditionalInfo) == 0x18,
|
||||
"CalendarAdditionalInfo structure has incorrect size");
|
||||
|
||||
// TODO(bunnei) RE this structure
|
||||
struct SystemClockContext {
|
||||
INSERT_PADDING_BYTES(0x20);
|
||||
// TODO(mailwl) RE this structure
|
||||
struct TimeZoneRule {
|
||||
INSERT_PADDING_BYTES(0x4000);
|
||||
};
|
||||
static_assert(sizeof(SystemClockContext) == 0x20,
|
||||
"SystemClockContext structure has incorrect size");
|
||||
|
||||
struct SteadyClockTimePoint {
|
||||
u64 value;
|
||||
u64_le value;
|
||||
INSERT_PADDING_WORDS(4);
|
||||
};
|
||||
static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");
|
||||
|
||||
struct SystemClockContext {
|
||||
u64_le offset;
|
||||
SteadyClockTimePoint time_point;
|
||||
};
|
||||
static_assert(sizeof(SystemClockContext) == 0x20,
|
||||
"SystemClockContext structure has incorrect size");
|
||||
|
||||
class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
|
||||
@@ -28,8 +28,13 @@ static PageTable* current_page_table = nullptr;
|
||||
|
||||
void SetCurrentPageTable(PageTable* page_table) {
|
||||
current_page_table = page_table;
|
||||
if (Core::System::GetInstance().IsPoweredOn()) {
|
||||
Core::CPU().PageTableChanged();
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
if (system.IsPoweredOn()) {
|
||||
system.ArmInterface(0).PageTableChanged();
|
||||
system.ArmInterface(1).PageTableChanged();
|
||||
system.ArmInterface(2).PageTableChanged();
|
||||
system.ArmInterface(3).PageTableChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -121,6 +121,7 @@ struct Values {
|
||||
|
||||
// Core
|
||||
bool use_cpu_jit;
|
||||
bool use_multi_core;
|
||||
|
||||
// Data Storage
|
||||
bool use_virtual_sd;
|
||||
|
||||
@@ -155,6 +155,8 @@ TelemetrySession::TelemetrySession() {
|
||||
|
||||
// Log user configuration information
|
||||
AddField(Telemetry::FieldType::UserConfig, "Core_UseCpuJit", Settings::values.use_cpu_jit);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Core_UseMultiCore",
|
||||
Settings::values.use_multi_core);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Renderer_ResolutionFactor",
|
||||
Settings::values.resolution_factor);
|
||||
AddField(Telemetry::FieldType::UserConfig, "Renderer_ToggleFramelimit",
|
||||
|
||||
@@ -9,6 +9,8 @@ add_library(video_core STATIC
|
||||
engines/maxwell_3d.h
|
||||
engines/maxwell_compute.cpp
|
||||
engines/maxwell_compute.h
|
||||
engines/maxwell_dma.cpp
|
||||
engines/maxwell_dma.h
|
||||
engines/shader_bytecode.h
|
||||
gpu.cpp
|
||||
gpu.h
|
||||
@@ -39,6 +41,8 @@ add_library(video_core STATIC
|
||||
renderer_opengl/maxwell_to_gl.h
|
||||
renderer_opengl/renderer_opengl.cpp
|
||||
renderer_opengl/renderer_opengl.h
|
||||
textures/astc.cpp
|
||||
textures/astc.h
|
||||
textures/decoders.cpp
|
||||
textures/decoders.h
|
||||
textures/texture.h
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/maxwell_compute.h"
|
||||
#include "video_core/engines/maxwell_dma.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
@@ -36,7 +37,6 @@ void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params)
|
||||
if (method == static_cast<u32>(BufferMethods::BindObject)) {
|
||||
// Bind the current subchannel to the desired engine id.
|
||||
NGLOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", subchannel, value);
|
||||
ASSERT(bound_engines.find(subchannel) == bound_engines.end());
|
||||
bound_engines[subchannel] = static_cast<EngineID>(value);
|
||||
return;
|
||||
}
|
||||
@@ -61,8 +61,11 @@ void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params)
|
||||
case EngineID::MAXWELL_COMPUTE_B:
|
||||
maxwell_compute->WriteReg(method, value);
|
||||
break;
|
||||
case EngineID::MAXWELL_DMA_COPY_A:
|
||||
maxwell_dma->WriteReg(method, value);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
UNIMPLEMENTED_MSG("Unimplemented engine");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ void Fermi2D::HandleSurfaceCopy() {
|
||||
|
||||
if (regs.src.linear == regs.dst.linear) {
|
||||
// If the input layout and the output layout are the same, just perform a raw copy.
|
||||
ASSERT(regs.src.BlockHeight() == regs.dst.BlockHeight());
|
||||
Memory::CopyBlock(dest_cpu, source_cpu,
|
||||
src_bytes_per_pixel * regs.dst.width * regs.dst.height);
|
||||
return;
|
||||
|
||||
@@ -156,16 +156,15 @@ void Maxwell3D::ProcessQueryGet() {
|
||||
// TODO(Subv): Support the other query units.
|
||||
ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop,
|
||||
"Units other than CROP are unimplemented");
|
||||
ASSERT_MSG(regs.query.query_get.short_query,
|
||||
"Writing the entire query result structure is unimplemented");
|
||||
|
||||
u32 value = Memory::Read32(*address);
|
||||
u32 result = 0;
|
||||
u64 result = 0;
|
||||
|
||||
// TODO(Subv): Support the other query variables
|
||||
switch (regs.query.query_get.select) {
|
||||
case Regs::QuerySelect::Zero:
|
||||
result = 0;
|
||||
// This seems to actually write the query sequence to the query address.
|
||||
result = regs.query.query_sequence;
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented query select type {}",
|
||||
@@ -174,15 +173,31 @@ void Maxwell3D::ProcessQueryGet() {
|
||||
|
||||
// TODO(Subv): Research and implement how query sync conditions work.
|
||||
|
||||
struct LongQueryResult {
|
||||
u64_le value;
|
||||
u64_le timestamp;
|
||||
};
|
||||
static_assert(sizeof(LongQueryResult) == 16, "LongQueryResult has wrong size");
|
||||
|
||||
switch (regs.query.query_get.mode) {
|
||||
case Regs::QueryMode::Write:
|
||||
case Regs::QueryMode::Write2: {
|
||||
// Write the current query sequence to the sequence address.
|
||||
u32 sequence = regs.query.query_sequence;
|
||||
Memory::Write32(*address, sequence);
|
||||
|
||||
// TODO(Subv): Write the proper query response structure to the address when not using short
|
||||
// mode.
|
||||
if (regs.query.query_get.short_query) {
|
||||
// Write the current query sequence to the sequence address.
|
||||
// TODO(Subv): Find out what happens if you use a long query type but mark it as a short
|
||||
// query.
|
||||
Memory::Write32(*address, sequence);
|
||||
} else {
|
||||
// Write the 128-bit result structure in long mode. Note: We emulate an infinitely fast
|
||||
// GPU, this command may actually take a while to complete in real hardware due to GPU
|
||||
// wait queues.
|
||||
LongQueryResult query_result{};
|
||||
query_result.value = result;
|
||||
// TODO(Subv): Generate a real GPU timestamp and write it here instead of 0
|
||||
query_result.timestamp = 0;
|
||||
Memory::WriteBlock(*address, &query_result, sizeof(query_result));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -313,8 +328,9 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt
|
||||
|
||||
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);
|
||||
tex_info.index =
|
||||
static_cast<u32>(current_texture - tex_info_buffer.address - TextureInfoOffset) /
|
||||
sizeof(Texture::TextureHandle);
|
||||
|
||||
// Load the TIC data.
|
||||
if (tex_handle.tic_id != 0) {
|
||||
@@ -339,6 +355,40 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt
|
||||
return textures;
|
||||
}
|
||||
|
||||
Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage, size_t offset) const {
|
||||
auto& shader = state.shader_stages[static_cast<size_t>(stage)];
|
||||
auto& tex_info_buffer = shader.const_buffers[regs.tex_cb_index];
|
||||
ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0);
|
||||
|
||||
GPUVAddr tex_info_address = tex_info_buffer.address + offset * sizeof(Texture::TextureHandle);
|
||||
|
||||
ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size);
|
||||
|
||||
boost::optional<VAddr> tex_address_cpu = memory_manager.GpuToCpuAddress(tex_info_address);
|
||||
Texture::TextureHandle tex_handle{Memory::Read32(*tex_address_cpu)};
|
||||
|
||||
Texture::FullTextureInfo tex_info{};
|
||||
tex_info.index = static_cast<u32>(offset);
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
return tex_info;
|
||||
}
|
||||
|
||||
u32 Maxwell3D::GetRegisterValue(u32 method) const {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS, "Invalid Maxwell3D register");
|
||||
return regs.reg_array[method];
|
||||
|
||||
@@ -318,6 +318,7 @@ public:
|
||||
Equation equation_a;
|
||||
Factor factor_source_a;
|
||||
Factor factor_dest_a;
|
||||
INSERT_PADDING_WORDS(1);
|
||||
};
|
||||
|
||||
union {
|
||||
@@ -354,10 +355,35 @@ public:
|
||||
f32 scale_x;
|
||||
f32 scale_y;
|
||||
f32 scale_z;
|
||||
u32 translate_x;
|
||||
u32 translate_y;
|
||||
u32 translate_z;
|
||||
f32 translate_x;
|
||||
f32 translate_y;
|
||||
f32 translate_z;
|
||||
INSERT_PADDING_WORDS(2);
|
||||
|
||||
MathUtil::Rectangle<s32> GetRect() const {
|
||||
return {
|
||||
GetX(), // left
|
||||
GetY() + GetHeight(), // top
|
||||
GetX() + GetWidth(), // right
|
||||
GetY() // bottom
|
||||
};
|
||||
};
|
||||
|
||||
s32 GetX() const {
|
||||
return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
|
||||
}
|
||||
|
||||
s32 GetY() const {
|
||||
return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
|
||||
}
|
||||
|
||||
s32 GetWidth() const {
|
||||
return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
|
||||
}
|
||||
|
||||
s32 GetHeight() const {
|
||||
return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
|
||||
}
|
||||
} viewport_transform[NumViewports];
|
||||
|
||||
struct {
|
||||
@@ -371,15 +397,6 @@ public:
|
||||
};
|
||||
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);
|
||||
@@ -416,7 +433,27 @@ public:
|
||||
};
|
||||
} rt_control;
|
||||
|
||||
INSERT_PADDING_WORDS(0xCF);
|
||||
INSERT_PADDING_WORDS(0x31);
|
||||
|
||||
u32 independent_blend_enable;
|
||||
|
||||
INSERT_PADDING_WORDS(0x15);
|
||||
|
||||
struct {
|
||||
u32 separate_alpha;
|
||||
Blend::Equation equation_rgb;
|
||||
Blend::Factor factor_source_rgb;
|
||||
Blend::Factor factor_dest_rgb;
|
||||
Blend::Equation equation_a;
|
||||
Blend::Factor factor_source_a;
|
||||
INSERT_PADDING_WORDS(1);
|
||||
Blend::Factor factor_dest_a;
|
||||
|
||||
u32 enable_common;
|
||||
u32 enable[NumRenderTargets];
|
||||
} blend;
|
||||
|
||||
INSERT_PADDING_WORDS(0x77);
|
||||
|
||||
struct {
|
||||
u32 tsc_address_high;
|
||||
@@ -541,9 +578,7 @@ public:
|
||||
|
||||
} vertex_array[NumVertexArrays];
|
||||
|
||||
Blend blend;
|
||||
|
||||
INSERT_PADDING_WORDS(0x39);
|
||||
Blend independent_blend[NumRenderTargets];
|
||||
|
||||
struct {
|
||||
u32 limit_high;
|
||||
@@ -648,6 +683,9 @@ public:
|
||||
/// Returns a list of enabled textures for the specified shader stage.
|
||||
std::vector<Texture::FullTextureInfo> GetStageTextures(Regs::ShaderStage stage) const;
|
||||
|
||||
/// Returns the texture information for a specific texture in a specific shader stage.
|
||||
Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, size_t offset) const;
|
||||
|
||||
/// Returns whether the specified shader stage is enabled or not.
|
||||
bool IsShaderStageEnabled(Regs::ShaderStage stage) const;
|
||||
|
||||
@@ -703,6 +741,8 @@ 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(independent_blend_enable, 0x4B9);
|
||||
ASSERT_REG_POSITION(blend, 0x4CF);
|
||||
ASSERT_REG_POSITION(tsc, 0x557);
|
||||
ASSERT_REG_POSITION(tic, 0x55D);
|
||||
ASSERT_REG_POSITION(code_address, 0x582);
|
||||
@@ -710,7 +750,7 @@ ASSERT_REG_POSITION(draw, 0x585);
|
||||
ASSERT_REG_POSITION(index_array, 0x5F2);
|
||||
ASSERT_REG_POSITION(query, 0x6C0);
|
||||
ASSERT_REG_POSITION(vertex_array[0], 0x700);
|
||||
ASSERT_REG_POSITION(blend, 0x780);
|
||||
ASSERT_REG_POSITION(independent_blend, 0x780);
|
||||
ASSERT_REG_POSITION(vertex_array_limit[0], 0x7C0);
|
||||
ASSERT_REG_POSITION(shader_config[0], 0x800);
|
||||
ASSERT_REG_POSITION(const_buffer, 0x8E0);
|
||||
|
||||
69
src/video_core/engines/maxwell_dma.cpp
Normal file
69
src/video_core/engines/maxwell_dma.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/memory.h"
|
||||
#include "video_core/engines/maxwell_dma.h"
|
||||
#include "video_core/textures/decoders.h"
|
||||
|
||||
namespace Tegra {
|
||||
namespace Engines {
|
||||
|
||||
MaxwellDMA::MaxwellDMA(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
|
||||
|
||||
void MaxwellDMA::WriteReg(u32 method, u32 value) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
"Invalid MaxwellDMA register, increase the size of the Regs structure");
|
||||
|
||||
regs.reg_array[method] = value;
|
||||
|
||||
#define MAXWELLDMA_REG_INDEX(field_name) \
|
||||
(offsetof(Tegra::Engines::MaxwellDMA::Regs, field_name) / sizeof(u32))
|
||||
|
||||
switch (method) {
|
||||
case MAXWELLDMA_REG_INDEX(exec): {
|
||||
HandleCopy();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#undef MAXWELLDMA_REG_INDEX
|
||||
}
|
||||
|
||||
void MaxwellDMA::HandleCopy() {
|
||||
NGLOG_WARNING(HW_GPU, "Requested a DMA copy");
|
||||
|
||||
const GPUVAddr source = regs.src_address.Address();
|
||||
const GPUVAddr dest = regs.dst_address.Address();
|
||||
|
||||
const VAddr source_cpu = *memory_manager.GpuToCpuAddress(source);
|
||||
const VAddr dest_cpu = *memory_manager.GpuToCpuAddress(dest);
|
||||
|
||||
// TODO(Subv): Perform more research and implement all features of this engine.
|
||||
ASSERT(regs.exec.enable_swizzle == 0);
|
||||
ASSERT(regs.exec.enable_2d == 1);
|
||||
ASSERT(regs.exec.query_mode == Regs::QueryMode::None);
|
||||
ASSERT(regs.exec.query_intr == Regs::QueryIntr::None);
|
||||
ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2);
|
||||
ASSERT(regs.src_params.pos_x == 0);
|
||||
ASSERT(regs.src_params.pos_y == 0);
|
||||
ASSERT(regs.dst_params.pos_x == 0);
|
||||
ASSERT(regs.dst_params.pos_y == 0);
|
||||
ASSERT(regs.exec.is_dst_linear != regs.exec.is_src_linear);
|
||||
|
||||
u8* src_buffer = Memory::GetPointer(source_cpu);
|
||||
u8* dst_buffer = Memory::GetPointer(dest_cpu);
|
||||
|
||||
if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
|
||||
// If the input is tiled and the output is linear, deswizzle the input and copy it over.
|
||||
Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y, 1, 1, src_buffer,
|
||||
dst_buffer, true, regs.src_params.BlockHeight());
|
||||
} else {
|
||||
// If the input is linear and the output is tiled, swizzle the input and copy it over.
|
||||
Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y, 1, 1, dst_buffer,
|
||||
src_buffer, false, regs.dst_params.BlockHeight());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Engines
|
||||
} // namespace Tegra
|
||||
155
src/video_core/engines/maxwell_dma.h
Normal file
155
src/video_core/engines/maxwell_dma.h
Normal file
@@ -0,0 +1,155 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
|
||||
namespace Tegra {
|
||||
namespace Engines {
|
||||
|
||||
class MaxwellDMA final {
|
||||
public:
|
||||
explicit MaxwellDMA(MemoryManager& memory_manager);
|
||||
~MaxwellDMA() = default;
|
||||
|
||||
/// Write the value to the register identified by method.
|
||||
void WriteReg(u32 method, u32 value);
|
||||
|
||||
struct Regs {
|
||||
static constexpr size_t NUM_REGS = 0x1D6;
|
||||
|
||||
struct Parameters {
|
||||
union {
|
||||
BitField<0, 4, u32> block_depth;
|
||||
BitField<4, 4, u32> block_height;
|
||||
BitField<8, 4, u32> block_width;
|
||||
};
|
||||
u32 size_x;
|
||||
u32 size_y;
|
||||
u32 size_z;
|
||||
u32 pos_z;
|
||||
union {
|
||||
BitField<0, 16, u32> pos_x;
|
||||
BitField<16, 16, u32> pos_y;
|
||||
};
|
||||
|
||||
u32 BlockHeight() const {
|
||||
return 1 << block_height;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(Parameters) == 24, "Parameters has wrong size");
|
||||
|
||||
enum class CopyMode : u32 {
|
||||
None = 0,
|
||||
Unk1 = 1,
|
||||
Unk2 = 2,
|
||||
};
|
||||
|
||||
enum class QueryMode : u32 {
|
||||
None = 0,
|
||||
Short = 1,
|
||||
Long = 2,
|
||||
};
|
||||
|
||||
enum class QueryIntr : u32 {
|
||||
None = 0,
|
||||
Block = 1,
|
||||
NonBlock = 2,
|
||||
};
|
||||
|
||||
union {
|
||||
struct {
|
||||
INSERT_PADDING_WORDS(0xC0);
|
||||
|
||||
struct {
|
||||
union {
|
||||
BitField<0, 2, CopyMode> copy_mode;
|
||||
BitField<2, 1, u32> flush;
|
||||
|
||||
BitField<3, 2, QueryMode> query_mode;
|
||||
BitField<5, 2, QueryIntr> query_intr;
|
||||
|
||||
BitField<7, 1, u32> is_src_linear;
|
||||
BitField<8, 1, u32> is_dst_linear;
|
||||
|
||||
BitField<9, 1, u32> enable_2d;
|
||||
BitField<10, 1, u32> enable_swizzle;
|
||||
};
|
||||
} exec;
|
||||
|
||||
INSERT_PADDING_WORDS(0x3F);
|
||||
|
||||
struct {
|
||||
u32 address_high;
|
||||
u32 address_low;
|
||||
|
||||
GPUVAddr Address() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
|
||||
address_low);
|
||||
}
|
||||
} src_address;
|
||||
|
||||
struct {
|
||||
u32 address_high;
|
||||
u32 address_low;
|
||||
|
||||
GPUVAddr Address() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
|
||||
address_low);
|
||||
}
|
||||
} dst_address;
|
||||
|
||||
u32 src_pitch;
|
||||
u32 dst_pitch;
|
||||
u32 x_count;
|
||||
u32 y_count;
|
||||
|
||||
INSERT_PADDING_WORDS(0xBB);
|
||||
|
||||
Parameters dst_params;
|
||||
|
||||
INSERT_PADDING_WORDS(1);
|
||||
|
||||
Parameters src_params;
|
||||
|
||||
INSERT_PADDING_WORDS(0x13);
|
||||
};
|
||||
std::array<u32, NUM_REGS> reg_array;
|
||||
};
|
||||
} regs{};
|
||||
|
||||
MemoryManager& memory_manager;
|
||||
|
||||
private:
|
||||
/// Performs the copy from the source buffer to the destination buffer as configured in the
|
||||
/// registers.
|
||||
void HandleCopy();
|
||||
};
|
||||
|
||||
#define ASSERT_REG_POSITION(field_name, position) \
|
||||
static_assert(offsetof(MaxwellDMA::Regs, field_name) == position * 4, \
|
||||
"Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_REG_POSITION(exec, 0xC0);
|
||||
ASSERT_REG_POSITION(src_address, 0x100);
|
||||
ASSERT_REG_POSITION(dst_address, 0x102);
|
||||
ASSERT_REG_POSITION(src_pitch, 0x104);
|
||||
ASSERT_REG_POSITION(dst_pitch, 0x105);
|
||||
ASSERT_REG_POSITION(x_count, 0x106);
|
||||
ASSERT_REG_POSITION(y_count, 0x107);
|
||||
ASSERT_REG_POSITION(dst_params, 0x1C3);
|
||||
ASSERT_REG_POSITION(src_params, 0x1CA);
|
||||
|
||||
#undef ASSERT_REG_POSITION
|
||||
|
||||
} // namespace Engines
|
||||
} // namespace Tegra
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -75,6 +75,10 @@ union Attribute {
|
||||
enum class Index : u64 {
|
||||
Position = 7,
|
||||
Attribute_0 = 8,
|
||||
// This attribute contains a tuple of (~, ~, InstanceId, VertexId) when inside a vertex
|
||||
// shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval
|
||||
// shader.
|
||||
TessCoordInstanceIDVertexID = 47,
|
||||
};
|
||||
|
||||
union {
|
||||
@@ -105,11 +109,6 @@ union Sampler {
|
||||
u64 value{};
|
||||
};
|
||||
|
||||
union Uniform {
|
||||
BitField<20, 14, u64> offset;
|
||||
BitField<34, 5, u64> index;
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -152,6 +151,13 @@ enum class PredOperation : u64 {
|
||||
Xor = 2,
|
||||
};
|
||||
|
||||
enum class LogicOperation : u64 {
|
||||
And = 0,
|
||||
Or = 1,
|
||||
Xor = 2,
|
||||
PassB = 3,
|
||||
};
|
||||
|
||||
enum class SubOp : u64 {
|
||||
Cos = 0x0,
|
||||
Sin = 0x1,
|
||||
@@ -162,6 +168,31 @@ enum class SubOp : u64 {
|
||||
Min = 0x8,
|
||||
};
|
||||
|
||||
enum class F2iRoundingOp : u64 {
|
||||
None = 0,
|
||||
Floor = 1,
|
||||
Ceil = 2,
|
||||
Trunc = 3,
|
||||
};
|
||||
|
||||
enum class F2fRoundingOp : u64 {
|
||||
None = 0,
|
||||
Pass = 3,
|
||||
Round = 8,
|
||||
Floor = 9,
|
||||
Ceil = 10,
|
||||
Trunc = 11,
|
||||
};
|
||||
|
||||
enum class UniformType : u64 {
|
||||
UnsignedByte = 0,
|
||||
SignedByte = 1,
|
||||
UnsignedShort = 2,
|
||||
SignedShort = 3,
|
||||
Single = 4,
|
||||
Double = 5,
|
||||
};
|
||||
|
||||
union Instruction {
|
||||
Instruction& operator=(const Instruction& instr) {
|
||||
value = instr.value;
|
||||
@@ -185,14 +216,33 @@ union Instruction {
|
||||
|
||||
union {
|
||||
BitField<20, 19, u64> imm20_19;
|
||||
BitField<20, 32, u64> imm20_32;
|
||||
BitField<20, 32, s64> imm20_32;
|
||||
BitField<45, 1, u64> negate_b;
|
||||
BitField<46, 1, u64> abs_a;
|
||||
BitField<48, 1, u64> negate_a;
|
||||
BitField<49, 1, u64> abs_b;
|
||||
BitField<50, 1, u64> abs_d;
|
||||
BitField<50, 1, u64> saturate_d;
|
||||
BitField<56, 1, u64> negate_imm;
|
||||
|
||||
union {
|
||||
BitField<39, 3, u64> pred;
|
||||
BitField<42, 1, u64> negate_pred;
|
||||
} fmnmx;
|
||||
|
||||
union {
|
||||
BitField<39, 1, u64> invert_a;
|
||||
BitField<40, 1, u64> invert_b;
|
||||
BitField<41, 2, LogicOperation> operation;
|
||||
BitField<44, 2, u64> unk44;
|
||||
BitField<48, 3, Pred> pred48;
|
||||
} lop;
|
||||
|
||||
union {
|
||||
BitField<53, 2, LogicOperation> operation;
|
||||
BitField<55, 1, u64> invert_a;
|
||||
BitField<56, 1, u64> invert_b;
|
||||
} lop32i;
|
||||
|
||||
float GetImm20_19() const {
|
||||
float result{};
|
||||
u32 imm{static_cast<u32>(imm20_19)};
|
||||
@@ -204,17 +254,55 @@ union Instruction {
|
||||
|
||||
float GetImm20_32() const {
|
||||
float result{};
|
||||
u32 imm{static_cast<u32>(imm20_32)};
|
||||
s32 imm{static_cast<s32>(imm20_32)};
|
||||
std::memcpy(&result, &imm, sizeof(imm));
|
||||
return result;
|
||||
}
|
||||
|
||||
s32 GetSignedImm20_20() const {
|
||||
u32 immediate = static_cast<u32>(imm20_19 | (negate_imm << 19));
|
||||
// Sign extend the 20-bit value.
|
||||
u32 mask = 1U << (20 - 1);
|
||||
return static_cast<s32>((immediate ^ mask) - mask);
|
||||
}
|
||||
} alu;
|
||||
|
||||
union {
|
||||
BitField<48, 1, u64> is_signed;
|
||||
} shift;
|
||||
|
||||
union {
|
||||
BitField<39, 5, u64> shift_amount;
|
||||
BitField<48, 1, u64> negate_b;
|
||||
BitField<49, 1, u64> negate_a;
|
||||
} alu_integer;
|
||||
|
||||
union {
|
||||
BitField<54, 1, u64> saturate;
|
||||
BitField<56, 1, u64> negate_a;
|
||||
} iadd32i;
|
||||
|
||||
union {
|
||||
BitField<20, 8, u64> shift_position;
|
||||
BitField<28, 8, u64> shift_length;
|
||||
BitField<48, 1, u64> negate_b;
|
||||
BitField<49, 1, u64> negate_a;
|
||||
|
||||
u64 GetLeftShiftValue() const {
|
||||
return 32 - (shift_position + shift_length);
|
||||
}
|
||||
} bfe;
|
||||
|
||||
union {
|
||||
BitField<48, 1, u64> negate_b;
|
||||
BitField<49, 1, u64> negate_c;
|
||||
} ffma;
|
||||
|
||||
union {
|
||||
BitField<48, 3, UniformType> type;
|
||||
BitField<44, 2, u64> unknown;
|
||||
} ld_c;
|
||||
|
||||
union {
|
||||
BitField<0, 3, u64> pred0;
|
||||
BitField<3, 3, u64> pred3;
|
||||
@@ -229,6 +317,16 @@ union Instruction {
|
||||
BitField<56, 1, u64> neg_b;
|
||||
} fsetp;
|
||||
|
||||
union {
|
||||
BitField<0, 3, u64> pred0;
|
||||
BitField<3, 3, u64> pred3;
|
||||
BitField<39, 3, u64> pred39;
|
||||
BitField<42, 1, u64> neg_pred;
|
||||
BitField<45, 2, PredOperation> op;
|
||||
BitField<48, 1, u64> is_signed;
|
||||
BitField<49, 3, PredCondition> cond;
|
||||
} isetp;
|
||||
|
||||
union {
|
||||
BitField<39, 3, u64> pred39;
|
||||
BitField<42, 1, u64> neg_pred;
|
||||
@@ -236,28 +334,96 @@ union Instruction {
|
||||
BitField<44, 1, u64> abs_b;
|
||||
BitField<45, 2, PredOperation> op;
|
||||
BitField<48, 4, PredCondition> cond;
|
||||
BitField<52, 1, u64> bf;
|
||||
BitField<53, 1, u64> neg_b;
|
||||
BitField<54, 1, u64> abs_a;
|
||||
BitField<52, 1, u64> bf;
|
||||
BitField<55, 1, u64> ftz;
|
||||
BitField<56, 1, u64> neg_imm;
|
||||
} fset;
|
||||
|
||||
union {
|
||||
BitField<10, 2, Register::Size> size;
|
||||
BitField<13, 1, u64> is_signed;
|
||||
BitField<39, 3, u64> pred39;
|
||||
BitField<42, 1, u64> neg_pred;
|
||||
BitField<44, 1, u64> bf;
|
||||
BitField<45, 2, PredOperation> op;
|
||||
BitField<48, 1, u64> is_signed;
|
||||
BitField<49, 3, PredCondition> cond;
|
||||
} iset;
|
||||
|
||||
union {
|
||||
BitField<8, 2, Register::Size> dest_size;
|
||||
BitField<10, 2, Register::Size> src_size;
|
||||
BitField<12, 1, u64> is_output_signed;
|
||||
BitField<13, 1, u64> is_input_signed;
|
||||
BitField<41, 2, u64> selector;
|
||||
BitField<45, 1, u64> negate_a;
|
||||
BitField<49, 1, u64> abs_a;
|
||||
BitField<50, 1, u64> saturate_a;
|
||||
|
||||
union {
|
||||
BitField<39, 2, F2iRoundingOp> rounding;
|
||||
} f2i;
|
||||
|
||||
union {
|
||||
BitField<39, 4, F2fRoundingOp> rounding;
|
||||
} f2f;
|
||||
} conversion;
|
||||
|
||||
union {
|
||||
BitField<31, 4, u64> component_mask;
|
||||
|
||||
bool IsComponentEnabled(size_t component) const {
|
||||
return ((1ull << component) & component_mask) != 0;
|
||||
}
|
||||
} tex;
|
||||
|
||||
union {
|
||||
BitField<50, 3, u64> component_mask_selector;
|
||||
BitField<28, 8, Register> gpr28;
|
||||
|
||||
bool HasTwoDestinations() const {
|
||||
return gpr28.Value() != Register::ZeroIndex;
|
||||
}
|
||||
|
||||
bool IsComponentEnabled(size_t component) const {
|
||||
static constexpr std::array<size_t, 5> one_dest_mask{0x1, 0x2, 0x4, 0x8, 0x3};
|
||||
static constexpr std::array<size_t, 5> two_dest_mask{0x7, 0xb, 0xd, 0xe, 0xf};
|
||||
const auto& mask{HasTwoDestinations() ? two_dest_mask : one_dest_mask};
|
||||
|
||||
ASSERT(component_mask_selector < mask.size());
|
||||
|
||||
return ((1ull << component) & mask[component_mask_selector]) != 0;
|
||||
}
|
||||
} texs;
|
||||
|
||||
union {
|
||||
BitField<20, 24, u64> target;
|
||||
BitField<5, 1, u64> constant_buffer;
|
||||
|
||||
s32 GetBranchTarget() const {
|
||||
// Sign extend the branch target offset
|
||||
u32 mask = 1U << (24 - 1);
|
||||
u32 value = static_cast<u32>(target);
|
||||
// The branch offset is relative to the next instruction and is stored in bytes, so
|
||||
// divide it by the size of an instruction and add 1 to it.
|
||||
return static_cast<s32>((value ^ mask) - mask) / sizeof(Instruction) + 1;
|
||||
}
|
||||
} bra;
|
||||
|
||||
union {
|
||||
BitField<20, 14, u64> offset;
|
||||
BitField<34, 5, u64> index;
|
||||
} cbuf34;
|
||||
|
||||
union {
|
||||
BitField<20, 16, s64> offset;
|
||||
BitField<36, 5, u64> index;
|
||||
} cbuf36;
|
||||
|
||||
BitField<61, 1, u64> is_b_imm;
|
||||
BitField<60, 1, u64> is_b_gpr;
|
||||
BitField<59, 1, u64> is_c_gpr;
|
||||
|
||||
Attribute attribute;
|
||||
Uniform uniform;
|
||||
Sampler sampler;
|
||||
|
||||
u64 value;
|
||||
@@ -270,8 +436,15 @@ class OpCode {
|
||||
public:
|
||||
enum class Id {
|
||||
KIL,
|
||||
SSY,
|
||||
BFE_C,
|
||||
BFE_R,
|
||||
BFE_IMM,
|
||||
BRA,
|
||||
LD_A,
|
||||
LD_C,
|
||||
ST_A,
|
||||
TEX,
|
||||
TEXQ, // Texture Query
|
||||
TEXS, // Texture Fetch with scalar/non-vec4 source/destinations
|
||||
TLDS, // Texture Load with scalar/non-vec4 source/destinations
|
||||
@@ -288,8 +461,17 @@ public:
|
||||
FMUL_R,
|
||||
FMUL_IMM,
|
||||
FMUL32_IMM,
|
||||
MUFU, // Multi-Function Operator
|
||||
RRO, // Range Reduction Operator
|
||||
IADD_C,
|
||||
IADD_R,
|
||||
IADD_IMM,
|
||||
IADD32I,
|
||||
ISCADD_C, // Scale and Add
|
||||
ISCADD_R,
|
||||
ISCADD_IMM,
|
||||
MUFU, // Multi-Function Operator
|
||||
RRO_C, // Range Reduction Operator
|
||||
RRO_R,
|
||||
RRO_IMM,
|
||||
F2F_C,
|
||||
F2F_R,
|
||||
F2F_IMM,
|
||||
@@ -302,15 +484,26 @@ public:
|
||||
I2I_C,
|
||||
I2I_R,
|
||||
I2I_IMM,
|
||||
LOP_C,
|
||||
LOP_R,
|
||||
LOP_IMM,
|
||||
LOP32I,
|
||||
MOV_C,
|
||||
MOV_R,
|
||||
MOV_IMM,
|
||||
MOV32_IMM,
|
||||
SHL_C,
|
||||
SHL_R,
|
||||
SHL_IMM,
|
||||
SHR_C,
|
||||
SHR_R,
|
||||
SHR_IMM,
|
||||
FMNMX,
|
||||
FMNMX_C,
|
||||
FMNMX_R,
|
||||
FMNMX_IMM,
|
||||
IMNMX_C,
|
||||
IMNMX_R,
|
||||
IMNMX_IMM,
|
||||
FSETP_C, // Set Predicate
|
||||
FSETP_R,
|
||||
FSETP_IMM,
|
||||
@@ -320,17 +513,32 @@ public:
|
||||
ISETP_C,
|
||||
ISETP_IMM,
|
||||
ISETP_R,
|
||||
ISET_R,
|
||||
ISET_C,
|
||||
ISET_IMM,
|
||||
PSETP,
|
||||
XMAD_IMM,
|
||||
XMAD_CR,
|
||||
XMAD_RC,
|
||||
XMAD_RR,
|
||||
};
|
||||
|
||||
enum class Type {
|
||||
Trivial,
|
||||
Arithmetic,
|
||||
ArithmeticImmediate,
|
||||
ArithmeticInteger,
|
||||
ArithmeticIntegerImmediate,
|
||||
Bfe,
|
||||
Shift,
|
||||
Ffma,
|
||||
Flow,
|
||||
Memory,
|
||||
FloatSet,
|
||||
FloatSetPredicate,
|
||||
IntegerSet,
|
||||
IntegerSetPredicate,
|
||||
PredicateSetPredicate,
|
||||
Conversion,
|
||||
Unknown,
|
||||
};
|
||||
@@ -427,8 +635,12 @@ private:
|
||||
std::vector<Matcher> table = {
|
||||
#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name)
|
||||
INST("111000110011----", Id::KIL, Type::Flow, "KIL"),
|
||||
INST("111000101001----", Id::SSY, Type::Flow, "SSY"),
|
||||
INST("111000100100----", Id::BRA, Type::Flow, "BRA"),
|
||||
INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
|
||||
INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"),
|
||||
INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"),
|
||||
INST("1100000000111---", Id::TEX, Type::Memory, "TEX"),
|
||||
INST("1101111101001---", Id::TEXQ, Type::Memory, "TEXQ"),
|
||||
INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"),
|
||||
INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"),
|
||||
@@ -444,24 +656,47 @@ private:
|
||||
INST("0100110001101---", Id::FMUL_C, Type::Arithmetic, "FMUL_C"),
|
||||
INST("0101110001101---", Id::FMUL_R, Type::Arithmetic, "FMUL_R"),
|
||||
INST("0011100-01101---", Id::FMUL_IMM, Type::Arithmetic, "FMUL_IMM"),
|
||||
INST("00011110--------", Id::FMUL32_IMM, Type::Arithmetic, "FMUL32_IMM"),
|
||||
INST("00011110--------", Id::FMUL32_IMM, Type::ArithmeticImmediate, "FMUL32_IMM"),
|
||||
INST("0100110000010---", Id::IADD_C, Type::ArithmeticInteger, "IADD_C"),
|
||||
INST("0101110000010---", Id::IADD_R, Type::ArithmeticInteger, "IADD_R"),
|
||||
INST("0011100-00010---", Id::IADD_IMM, Type::ArithmeticInteger, "IADD_IMM"),
|
||||
INST("0001110---------", Id::IADD32I, Type::ArithmeticIntegerImmediate, "IADD32I"),
|
||||
INST("0100110000011---", Id::ISCADD_C, Type::ArithmeticInteger, "ISCADD_C"),
|
||||
INST("0101110000011---", Id::ISCADD_R, Type::ArithmeticInteger, "ISCADD_R"),
|
||||
INST("0011100-00011---", Id::ISCADD_IMM, Type::ArithmeticInteger, "ISCADD_IMM"),
|
||||
INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"),
|
||||
INST("0101110010010---", Id::RRO, Type::Arithmetic, "RRO"),
|
||||
INST("0100110010101---", Id::F2F_C, Type::Arithmetic, "F2F_C"),
|
||||
INST("0101110010101---", Id::F2F_R, Type::Arithmetic, "F2F_R"),
|
||||
INST("0011100-10101---", Id::F2F_IMM, Type::Arithmetic, "F2F_IMM"),
|
||||
INST("0100110010110---", Id::F2I_C, Type::Arithmetic, "F2I_C"),
|
||||
INST("0101110010110---", Id::F2I_R, Type::Arithmetic, "F2I_R"),
|
||||
INST("0011100-10110---", Id::F2I_IMM, Type::Arithmetic, "F2I_IMM"),
|
||||
INST("000001----------", Id::LOP32I, Type::Arithmetic, "LOP32I"),
|
||||
INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"),
|
||||
INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"),
|
||||
INST("0011100-10010---", Id::RRO_IMM, Type::Arithmetic, "RRO_IMM"),
|
||||
INST("0100110010101---", Id::F2F_C, Type::Conversion, "F2F_C"),
|
||||
INST("0101110010101---", Id::F2F_R, Type::Conversion, "F2F_R"),
|
||||
INST("0011100-10101---", Id::F2F_IMM, Type::Conversion, "F2F_IMM"),
|
||||
INST("0100110010110---", Id::F2I_C, Type::Conversion, "F2I_C"),
|
||||
INST("0101110010110---", Id::F2I_R, Type::Conversion, "F2I_R"),
|
||||
INST("0011100-10110---", Id::F2I_IMM, Type::Conversion, "F2I_IMM"),
|
||||
INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"),
|
||||
INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"),
|
||||
INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"),
|
||||
INST("000000010000----", Id::MOV32_IMM, Type::Arithmetic, "MOV32_IMM"),
|
||||
INST("0100110000101---", Id::SHR_C, Type::Arithmetic, "SHR_C"),
|
||||
INST("0101110000101---", Id::SHR_R, Type::Arithmetic, "SHR_R"),
|
||||
INST("0011100-00101---", Id::SHR_IMM, Type::Arithmetic, "SHR_IMM"),
|
||||
INST("0101110001100---", Id::FMNMX, Type::Arithmetic, "FMNMX"),
|
||||
INST("000000010000----", Id::MOV32_IMM, Type::ArithmeticImmediate, "MOV32_IMM"),
|
||||
INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"),
|
||||
INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"),
|
||||
INST("0011100-01100---", Id::FMNMX_IMM, Type::Arithmetic, "FMNMX_IMM"),
|
||||
INST("0100110000100---", Id::IMNMX_C, Type::Arithmetic, "FMNMX_IMM"),
|
||||
INST("0101110000100---", Id::IMNMX_R, Type::Arithmetic, "FMNMX_IMM"),
|
||||
INST("0011100-00100---", Id::IMNMX_IMM, Type::Arithmetic, "FMNMX_IMM"),
|
||||
INST("0100110000000---", Id::BFE_C, Type::Bfe, "BFE_C"),
|
||||
INST("0101110000000---", Id::BFE_R, Type::Bfe, "BFE_R"),
|
||||
INST("0011100-00000---", Id::BFE_IMM, Type::Bfe, "BFE_IMM"),
|
||||
INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"),
|
||||
INST("0101110001000---", Id::LOP_R, Type::ArithmeticInteger, "LOP_R"),
|
||||
INST("0011100001000---", Id::LOP_IMM, Type::ArithmeticInteger, "LOP_IMM"),
|
||||
INST("000001----------", Id::LOP32I, Type::ArithmeticIntegerImmediate, "LOP32I"),
|
||||
INST("0100110001001---", Id::SHL_C, Type::Shift, "SHL_C"),
|
||||
INST("0101110001001---", Id::SHL_R, Type::Shift, "SHL_R"),
|
||||
INST("0011100-01001---", Id::SHL_IMM, Type::Shift, "SHL_IMM"),
|
||||
INST("0100110000101---", Id::SHR_C, Type::Shift, "SHR_C"),
|
||||
INST("0101110000101---", Id::SHR_R, Type::Shift, "SHR_R"),
|
||||
INST("0011100-00101---", Id::SHR_IMM, Type::Shift, "SHR_IMM"),
|
||||
INST("0100110011100---", Id::I2I_C, Type::Conversion, "I2I_C"),
|
||||
INST("0101110011100---", Id::I2I_R, Type::Conversion, "I2I_R"),
|
||||
INST("01110001-1000---", Id::I2I_IMM, Type::Conversion, "I2I_IMM"),
|
||||
@@ -477,6 +712,14 @@ private:
|
||||
INST("010010110110----", Id::ISETP_C, Type::IntegerSetPredicate, "ISETP_C"),
|
||||
INST("010110110110----", Id::ISETP_R, Type::IntegerSetPredicate, "ISETP_R"),
|
||||
INST("0011011-0110----", Id::ISETP_IMM, Type::IntegerSetPredicate, "ISETP_IMM"),
|
||||
INST("010110110101----", Id::ISET_R, Type::IntegerSet, "ISET_R"),
|
||||
INST("010010110101----", Id::ISET_C, Type::IntegerSet, "ISET_C"),
|
||||
INST("0011011-0101----", Id::ISET_IMM, Type::IntegerSet, "ISET_IMM"),
|
||||
INST("0101000010010---", Id::PSETP, Type::PredicateSetPredicate, "PSETP"),
|
||||
INST("0011011-00------", Id::XMAD_IMM, Type::Arithmetic, "XMAD_IMM"),
|
||||
INST("0100111---------", Id::XMAD_CR, Type::Arithmetic, "XMAD_CR"),
|
||||
INST("010100010-------", Id::XMAD_RC, Type::Arithmetic, "XMAD_RC"),
|
||||
INST("0101101100------", Id::XMAD_RR, Type::Arithmetic, "XMAD_RR"),
|
||||
};
|
||||
#undef INST
|
||||
std::stable_sort(table.begin(), table.end(), [](const auto& a, const auto& b) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/maxwell_compute.h"
|
||||
#include "video_core/engines/maxwell_dma.h"
|
||||
#include "video_core/gpu.h"
|
||||
|
||||
namespace Tegra {
|
||||
@@ -14,6 +15,7 @@ GPU::GPU() {
|
||||
maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager);
|
||||
fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager);
|
||||
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
|
||||
maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager);
|
||||
}
|
||||
|
||||
GPU::~GPU() = default;
|
||||
@@ -26,6 +28,10 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
|
||||
ASSERT(format != RenderTargetFormat::NONE);
|
||||
|
||||
switch (format) {
|
||||
case RenderTargetFormat::RGBA32_FLOAT:
|
||||
return 16;
|
||||
case RenderTargetFormat::RGBA16_FLOAT:
|
||||
return 8;
|
||||
case RenderTargetFormat::RGBA8_UNORM:
|
||||
case RenderTargetFormat::RGB10_A2_UNORM:
|
||||
return 4;
|
||||
|
||||
@@ -15,10 +15,12 @@ namespace Tegra {
|
||||
|
||||
enum class RenderTargetFormat : u32 {
|
||||
NONE = 0x0,
|
||||
RGBA32_FLOAT = 0xC0,
|
||||
RGBA16_FLOAT = 0xCA,
|
||||
RGB10_A2_UNORM = 0xD1,
|
||||
RGBA8_UNORM = 0xD5,
|
||||
RGBA8_SRGB = 0xD6,
|
||||
R11G11B10_FLOAT = 0xE0,
|
||||
};
|
||||
|
||||
/// Returns the number of bytes per pixel of each rendertarget format.
|
||||
@@ -61,6 +63,7 @@ namespace Engines {
|
||||
class Fermi2D;
|
||||
class Maxwell3D;
|
||||
class MaxwellCompute;
|
||||
class MaxwellDMA;
|
||||
} // namespace Engines
|
||||
|
||||
enum class EngineID {
|
||||
@@ -101,6 +104,8 @@ private:
|
||||
std::unique_ptr<Engines::Fermi2D> fermi_2d;
|
||||
/// Compute engine
|
||||
std::unique_ptr<Engines::MaxwellCompute> maxwell_compute;
|
||||
/// DMA engine
|
||||
std::unique_ptr<Engines::MaxwellDMA> maxwell_dma;
|
||||
};
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -58,6 +58,25 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size)
|
||||
return gpu_addr;
|
||||
}
|
||||
|
||||
GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
|
||||
ASSERT((gpu_addr & PAGE_MASK) == 0);
|
||||
|
||||
for (u64 offset = 0; offset < size; offset += PAGE_SIZE) {
|
||||
ASSERT(PageSlot(gpu_addr + offset) != static_cast<u64>(PageStatus::Allocated) &&
|
||||
PageSlot(gpu_addr + offset) != static_cast<u64>(PageStatus::Unmapped));
|
||||
PageSlot(gpu_addr + offset) = static_cast<u64>(PageStatus::Unmapped);
|
||||
}
|
||||
|
||||
// Delete the region mappings that are contained within the unmapped region
|
||||
mapped_regions.erase(std::remove_if(mapped_regions.begin(), mapped_regions.end(),
|
||||
[&](const MappedRegion& region) {
|
||||
return region.gpu_addr <= gpu_addr &&
|
||||
region.gpu_addr + region.size < gpu_addr + size;
|
||||
}),
|
||||
mapped_regions.end());
|
||||
return gpu_addr;
|
||||
}
|
||||
|
||||
boost::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) {
|
||||
GPUVAddr gpu_addr = 0;
|
||||
u64 free_space = 0;
|
||||
|
||||
@@ -25,6 +25,7 @@ public:
|
||||
GPUVAddr AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align);
|
||||
GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size);
|
||||
GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size);
|
||||
GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size);
|
||||
boost::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr);
|
||||
std::vector<GPUVAddr> CpuToGpuAddress(VAddr cpu_addr) const;
|
||||
|
||||
|
||||
@@ -75,14 +75,11 @@ RasterizerOpenGL::RasterizerOpenGL() {
|
||||
// Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
|
||||
state.clip_distance[0] = true;
|
||||
|
||||
// Generate VBO, VAO and UBO
|
||||
vertex_buffer = OGLStreamBuffer::MakeBuffer(GLAD_GL_ARB_buffer_storage, GL_ARRAY_BUFFER);
|
||||
vertex_buffer->Create(VERTEX_BUFFER_SIZE, VERTEX_BUFFER_SIZE / 2);
|
||||
// Generate VAO and UBO
|
||||
sw_vao.Create();
|
||||
uniform_buffer.Create();
|
||||
|
||||
state.draw.vertex_array = sw_vao.handle;
|
||||
state.draw.vertex_buffer = vertex_buffer->GetHandle();
|
||||
state.draw.uniform_buffer = uniform_buffer.handle;
|
||||
state.Apply();
|
||||
|
||||
@@ -90,7 +87,6 @@ RasterizerOpenGL::RasterizerOpenGL() {
|
||||
framebuffer.Create();
|
||||
|
||||
hw_vao.Create();
|
||||
hw_vao_enabled_attributes.fill(false);
|
||||
|
||||
stream_buffer = OGLStreamBuffer::MakeBuffer(has_ARB_buffer_storage, GL_ARRAY_BUFFER);
|
||||
stream_buffer->Create(STREAM_BUFFER_SIZE, STREAM_BUFFER_SIZE / 2);
|
||||
@@ -181,8 +177,6 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
|
||||
glVertexAttribFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
|
||||
attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
|
||||
glVertexAttribBinding(index, attrib.buffer);
|
||||
|
||||
hw_vao_enabled_attributes[index] = true;
|
||||
}
|
||||
|
||||
return {array_ptr, buffer_offset};
|
||||
@@ -202,8 +196,10 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) {
|
||||
auto& gpu = Core::System().GetInstance().GPU().Maxwell3D();
|
||||
ASSERT_MSG(!gpu.regs.shader_config[0].enable, "VertexA is unsupported!");
|
||||
|
||||
// Next available bindpoint to use when uploading the const buffers to the GLSL shaders.
|
||||
// Next available bindpoints to use when uploading the const buffers and textures to the GLSL
|
||||
// shaders.
|
||||
u32 current_constbuffer_bindpoint = 0;
|
||||
u32 current_texture_bindpoint = 0;
|
||||
|
||||
for (unsigned index = 1; index < Maxwell::MaxShaderProgram; ++index) {
|
||||
auto& shader_config = gpu.regs.shader_config[index];
|
||||
@@ -218,13 +214,17 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) {
|
||||
continue;
|
||||
}
|
||||
|
||||
GLShader::MaxwellUniformData ubo{};
|
||||
ubo.SetFromRegs(gpu.state.shader_stages[stage]);
|
||||
std::memcpy(buffer_ptr, &ubo, sizeof(ubo));
|
||||
|
||||
// Flush the buffer so that the GPU can see the data we just wrote.
|
||||
glFlushMappedBufferRange(GL_ARRAY_BUFFER, buffer_offset, sizeof(ubo));
|
||||
|
||||
// Upload uniform data as one UBO per stage
|
||||
const GLintptr ubo_offset = buffer_offset;
|
||||
copy_buffer(uniform_buffers[stage].handle, ubo_offset,
|
||||
sizeof(GLShader::MaxwellUniformData));
|
||||
GLShader::MaxwellUniformData* ub_ptr =
|
||||
reinterpret_cast<GLShader::MaxwellUniformData*>(buffer_ptr);
|
||||
ub_ptr->SetFromRegs(gpu.state.shader_stages[stage]);
|
||||
|
||||
buffer_ptr += sizeof(GLShader::MaxwellUniformData);
|
||||
buffer_offset += sizeof(GLShader::MaxwellUniformData);
|
||||
@@ -264,6 +264,11 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) {
|
||||
current_constbuffer_bindpoint =
|
||||
SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), gl_stage_program,
|
||||
current_constbuffer_bindpoint, shader_resources.const_buffer_entries);
|
||||
|
||||
// Configure the textures for this shader stage.
|
||||
current_texture_bindpoint =
|
||||
SetupTextures(static_cast<Maxwell::ShaderStage>(stage), gl_stage_program,
|
||||
current_texture_bindpoint, shader_resources.texture_samplers);
|
||||
}
|
||||
|
||||
shader_program_manager->UseTrivialGeometryShader();
|
||||
@@ -304,7 +309,7 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
const bool has_stencil = false;
|
||||
const bool using_color_fb = true;
|
||||
const bool using_depth_fb = false;
|
||||
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport[0].GetRect()};
|
||||
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()};
|
||||
|
||||
const bool write_color_fb =
|
||||
state.color_mask.red_enabled == GL_TRUE || state.color_mask.green_enabled == GL_TRUE ||
|
||||
@@ -344,12 +349,12 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
// Sync the viewport
|
||||
SyncViewport(surfaces_rect, res_scale);
|
||||
|
||||
// Sync the blend state registers
|
||||
SyncBlendState();
|
||||
|
||||
// TODO(bunnei): Sync framebuffer_scale uniform here
|
||||
// TODO(bunnei): Sync scissorbox uniform(s) here
|
||||
|
||||
// Sync and bind the texture surfaces
|
||||
BindTextures();
|
||||
|
||||
// Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect. Enable
|
||||
// scissor test to prevent drawing outside of the framebuffer region
|
||||
state.scissor.enabled = true;
|
||||
@@ -453,65 +458,7 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::BindTextures() {
|
||||
using Regs = Tegra::Engines::Maxwell3D::Regs;
|
||||
auto& maxwell3d = Core::System::GetInstance().GPU().Get3DEngine();
|
||||
|
||||
// Each Maxwell shader stage can have an arbitrary number of textures, but we're limited to a
|
||||
// certain number in OpenGL. We try to only use the minimum amount of host textures by not
|
||||
// keeping a 1:1 relation between guest texture ids and host texture ids, ie, guest texture id 8
|
||||
// can be host texture id 0 if it's the only texture used in the guest shader program.
|
||||
u32 host_texture_index = 0;
|
||||
for (u32 stage = 0; stage < Regs::MaxShaderStage; ++stage) {
|
||||
ASSERT(host_texture_index < texture_samplers.size());
|
||||
const auto textures = maxwell3d.GetStageTextures(static_cast<Regs::ShaderStage>(stage));
|
||||
for (unsigned texture_index = 0; texture_index < textures.size(); ++texture_index) {
|
||||
const auto& texture = textures[texture_index];
|
||||
|
||||
if (texture.enabled) {
|
||||
texture_samplers[host_texture_index].SyncWithConfig(texture.tsc);
|
||||
Surface surface = res_cache.GetTextureSurface(texture);
|
||||
if (surface != nullptr) {
|
||||
state.texture_units[host_texture_index].texture_2d = surface->texture.handle;
|
||||
} else {
|
||||
// Can occur when texture addr is null or its memory is unmapped/invalid
|
||||
state.texture_units[texture_index].texture_2d = 0;
|
||||
}
|
||||
|
||||
++host_texture_index;
|
||||
} else {
|
||||
state.texture_units[texture_index].texture_2d = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 method) {
|
||||
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
|
||||
switch (method) {
|
||||
case MAXWELL3D_REG_INDEX(blend.separate_alpha):
|
||||
ASSERT_MSG(false, "unimplemented");
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(blend.equation_rgb):
|
||||
state.blend.rgb_equation = MaxwellToGL::BlendEquation(regs.blend.equation_rgb);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(blend.factor_source_rgb):
|
||||
state.blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_rgb);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(blend.factor_dest_rgb):
|
||||
state.blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_rgb);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(blend.equation_a):
|
||||
state.blend.a_equation = MaxwellToGL::BlendEquation(regs.blend.equation_a);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(blend.factor_source_a):
|
||||
state.blend.src_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_a);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(blend.factor_dest_a):
|
||||
state.blend.dst_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 method) {}
|
||||
|
||||
void RasterizerOpenGL::FlushAll() {
|
||||
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
||||
@@ -660,7 +607,16 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr
|
||||
buffer_draw_state.bindpoint = current_bindpoint + bindpoint;
|
||||
|
||||
boost::optional<VAddr> addr = gpu.memory_manager->GpuToCpuAddress(buffer.address);
|
||||
std::vector<u8> data(used_buffer.GetSize() * sizeof(float));
|
||||
|
||||
std::vector<u8> data;
|
||||
if (used_buffer.IsIndirect()) {
|
||||
// Buffer is accessed indirectly, so upload the entire thing
|
||||
data.resize(buffer.size * sizeof(float));
|
||||
} else {
|
||||
// Buffer is accessed directly, upload just what we use
|
||||
data.resize(used_buffer.GetSize() * sizeof(float));
|
||||
}
|
||||
|
||||
Memory::ReadBlock(*addr, data.data(), data.size());
|
||||
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer_draw_state.ssbo);
|
||||
@@ -677,7 +633,53 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr
|
||||
|
||||
state.Apply();
|
||||
|
||||
return current_bindpoint + entries.size();
|
||||
return current_bindpoint + static_cast<u32>(entries.size());
|
||||
}
|
||||
|
||||
u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program, u32 current_unit,
|
||||
const std::vector<GLShader::SamplerEntry>& entries) {
|
||||
auto& gpu = Core::System::GetInstance().GPU();
|
||||
auto& maxwell3d = gpu.Get3DEngine();
|
||||
|
||||
ASSERT_MSG(maxwell3d.IsShaderStageEnabled(stage),
|
||||
"Attempted to upload textures of disabled shader stage");
|
||||
|
||||
ASSERT_MSG(current_unit + entries.size() <= std::size(state.texture_units),
|
||||
"Exceeded the number of active textures.");
|
||||
|
||||
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
|
||||
const auto& entry = entries[bindpoint];
|
||||
u32 current_bindpoint = current_unit + bindpoint;
|
||||
|
||||
// Bind the uniform to the sampler.
|
||||
GLint uniform = glGetUniformLocation(program, entry.GetName().c_str());
|
||||
ASSERT(uniform != -1);
|
||||
glProgramUniform1i(program, uniform, current_bindpoint);
|
||||
|
||||
const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset());
|
||||
ASSERT(texture.enabled);
|
||||
|
||||
texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
|
||||
Surface surface = res_cache.GetTextureSurface(texture);
|
||||
if (surface != nullptr) {
|
||||
state.texture_units[current_bindpoint].texture_2d = surface->texture.handle;
|
||||
state.texture_units[current_bindpoint].swizzle.r =
|
||||
MaxwellToGL::SwizzleSource(texture.tic.x_source);
|
||||
state.texture_units[current_bindpoint].swizzle.g =
|
||||
MaxwellToGL::SwizzleSource(texture.tic.y_source);
|
||||
state.texture_units[current_bindpoint].swizzle.b =
|
||||
MaxwellToGL::SwizzleSource(texture.tic.z_source);
|
||||
state.texture_units[current_bindpoint].swizzle.a =
|
||||
MaxwellToGL::SwizzleSource(texture.tic.w_source);
|
||||
} else {
|
||||
// Can occur when texture addr is null or its memory is unmapped/invalid
|
||||
state.texture_units[current_bindpoint].texture_2d = 0;
|
||||
}
|
||||
}
|
||||
|
||||
state.Apply();
|
||||
|
||||
return current_unit + static_cast<u32>(entries.size());
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface,
|
||||
@@ -708,7 +710,7 @@ void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface,
|
||||
|
||||
void RasterizerOpenGL::SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect, u16 res_scale) {
|
||||
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
|
||||
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport[0].GetRect()};
|
||||
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()};
|
||||
|
||||
state.viewport.x = static_cast<GLint>(surfaces_rect.left) + viewport_rect.left * res_scale;
|
||||
state.viewport.y = static_cast<GLint>(surfaces_rect.bottom) + viewport_rect.bottom * res_scale;
|
||||
@@ -736,14 +738,21 @@ void RasterizerOpenGL::SyncDepthOffset() {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncBlendEnabled() {
|
||||
UNREACHABLE();
|
||||
}
|
||||
void RasterizerOpenGL::SyncBlendState() {
|
||||
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
|
||||
|
||||
void RasterizerOpenGL::SyncBlendFuncs() {
|
||||
UNREACHABLE();
|
||||
}
|
||||
// TODO(Subv): Support more than just render target 0.
|
||||
state.blend.enabled = regs.blend.enable[0] != 0;
|
||||
|
||||
void RasterizerOpenGL::SyncBlendColor() {
|
||||
UNREACHABLE();
|
||||
if (!state.blend.enabled)
|
||||
return;
|
||||
|
||||
ASSERT_MSG(regs.independent_blend_enable == 1, "Only independent blending is implemented");
|
||||
ASSERT_MSG(!regs.independent_blend[0].separate_alpha, "Unimplemented");
|
||||
state.blend.rgb_equation = MaxwellToGL::BlendEquation(regs.independent_blend[0].equation_rgb);
|
||||
state.blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_source_rgb);
|
||||
state.blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_dest_rgb);
|
||||
state.blend.a_equation = MaxwellToGL::BlendEquation(regs.independent_blend[0].equation_a);
|
||||
state.blend.src_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_source_a);
|
||||
state.blend.dst_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_dest_a);
|
||||
}
|
||||
|
||||
@@ -80,9 +80,6 @@ private:
|
||||
void BindFramebufferSurfaces(const Surface& color_surface, const Surface& depth_surface,
|
||||
bool has_stencil);
|
||||
|
||||
/// Binds the required textures to OpenGL before drawing a batch.
|
||||
void BindTextures();
|
||||
|
||||
/*
|
||||
* Configures the current constbuffers to use for the draw command.
|
||||
* @param stage The shader stage to configure buffers for.
|
||||
@@ -95,6 +92,17 @@ private:
|
||||
u32 current_bindpoint,
|
||||
const std::vector<GLShader::ConstBufferEntry>& entries);
|
||||
|
||||
/*
|
||||
* Configures the current textures to use for the draw command.
|
||||
* @param stage The shader stage to configure textures for.
|
||||
* @param program The OpenGL program object that contains the specified stage.
|
||||
* @param current_unit The offset at which to start counting unused texture units.
|
||||
* @param entries Vector describing the textures that are actually used in the guest shader.
|
||||
* @returns The next available bindpoint for use in the next shader stage.
|
||||
*/
|
||||
u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, GLuint program,
|
||||
u32 current_unit, const std::vector<GLShader::SamplerEntry>& entries);
|
||||
|
||||
/// Syncs the viewport to match the guest state
|
||||
void SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect, u16 res_scale);
|
||||
|
||||
@@ -113,14 +121,8 @@ private:
|
||||
/// Syncs the depth offset to match the guest state
|
||||
void SyncDepthOffset();
|
||||
|
||||
/// Syncs the blend enabled status to match the guest state
|
||||
void SyncBlendEnabled();
|
||||
|
||||
/// Syncs the blend functions to match the guest state
|
||||
void SyncBlendFuncs();
|
||||
|
||||
/// Syncs the blend color to match the guest state
|
||||
void SyncBlendColor();
|
||||
/// Syncs the blend state to match the guest state
|
||||
void SyncBlendState();
|
||||
|
||||
bool has_ARB_buffer_storage;
|
||||
bool has_ARB_direct_state_access;
|
||||
@@ -134,21 +136,17 @@ private:
|
||||
std::unique_ptr<GLShader::ProgramManager> shader_program_manager;
|
||||
OGLVertexArray sw_vao;
|
||||
OGLVertexArray hw_vao;
|
||||
std::array<bool, 16> hw_vao_enabled_attributes;
|
||||
|
||||
std::array<SamplerInfo, GLShader::NumTextureSamplers> texture_samplers;
|
||||
std::array<std::array<OGLBuffer, Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers>,
|
||||
Tegra::Engines::Maxwell3D::Regs::MaxShaderStage>
|
||||
ssbos;
|
||||
|
||||
static constexpr size_t VERTEX_BUFFER_SIZE = 128 * 1024 * 1024;
|
||||
std::unique_ptr<OGLStreamBuffer> vertex_buffer;
|
||||
static constexpr size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
|
||||
std::unique_ptr<OGLStreamBuffer> stream_buffer;
|
||||
OGLBuffer uniform_buffer;
|
||||
OGLFramebuffer framebuffer;
|
||||
|
||||
static constexpr size_t STREAM_BUFFER_SIZE = 4 * 1024 * 1024;
|
||||
std::unique_ptr<OGLStreamBuffer> stream_buffer;
|
||||
|
||||
size_t CalculateVertexArraysSize() const;
|
||||
|
||||
std::pair<u8*, GLintptr> SetupVertexArrays(u8* array_ptr, GLintptr buffer_offset);
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/textures/astc.h"
|
||||
#include "video_core/textures/decoders.h"
|
||||
#include "video_core/utils.h"
|
||||
#include "video_core/video_core.h"
|
||||
@@ -45,19 +46,27 @@ struct FormatTuple {
|
||||
|
||||
static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, false}, // ABGR8
|
||||
{GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, false}, // B5G6R5
|
||||
{GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, false}, // B5G6R5
|
||||
{GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, false}, // A2B10G10R10
|
||||
{GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, false}, // A1B5G5R5
|
||||
{GL_R8, GL_RED, GL_UNSIGNED_BYTE, false}, // R8
|
||||
{GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false}, // RGBA16F
|
||||
{GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, false}, // R11FG11FB10F
|
||||
{GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT1
|
||||
{GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT23
|
||||
{GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT45
|
||||
{GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, true}, // DXN1
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_4X4
|
||||
}};
|
||||
|
||||
static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) {
|
||||
const SurfaceType type = SurfaceParams::GetFormatType(pixel_format);
|
||||
if (type == SurfaceType::ColorTexture) {
|
||||
ASSERT(static_cast<size_t>(pixel_format) < tex_format_tuples.size());
|
||||
// For now only UNORM components are supported
|
||||
ASSERT(component_type == ComponentType::UNorm);
|
||||
// For now only UNORM components are supported, or either R11FG11FB10F or RGBA16F which are
|
||||
// type FLOAT
|
||||
ASSERT(component_type == ComponentType::UNorm || pixel_format == PixelFormat::RGBA16F ||
|
||||
pixel_format == PixelFormat::R11FG11FB10F);
|
||||
return tex_format_tuples[static_cast<unsigned int>(pixel_format)];
|
||||
} else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) {
|
||||
// TODO(Subv): Implement depth formats
|
||||
@@ -79,6 +88,23 @@ static u16 GetResolutionScaleFactor() {
|
||||
: Settings::values.resolution_factor);
|
||||
}
|
||||
|
||||
static void ConvertASTCToRGBA8(std::vector<u8>& data, PixelFormat format, u32 width, u32 height) {
|
||||
u32 block_width{};
|
||||
u32 block_height{};
|
||||
|
||||
switch (format) {
|
||||
case PixelFormat::ASTC_2D_4X4:
|
||||
block_width = 4;
|
||||
block_height = 4;
|
||||
break;
|
||||
default:
|
||||
NGLOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
data = Tegra::Texture::ASTC::Decompress(data, width, height, block_width, block_height);
|
||||
}
|
||||
|
||||
template <bool morton_to_gl, PixelFormat format>
|
||||
void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, Tegra::GPUVAddr base,
|
||||
Tegra::GPUVAddr start, Tegra::GPUVAddr end) {
|
||||
@@ -90,6 +116,12 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, Tegra::
|
||||
auto data = Tegra::Texture::UnswizzleTexture(
|
||||
*gpu.memory_manager->GpuToCpuAddress(base),
|
||||
SurfaceParams::TextureFormatFromPixelFormat(format), stride, height, block_height);
|
||||
|
||||
if (SurfaceParams::IsFormatASTC(format)) {
|
||||
// ASTC formats are converted to RGBA8 in software, as most PC GPUs do not support this
|
||||
ConvertASTCToRGBA8(data, format, stride, height);
|
||||
}
|
||||
|
||||
std::memcpy(gl_buffer, data.data(), data.size());
|
||||
} else {
|
||||
// TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should check
|
||||
@@ -106,9 +138,12 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr, Tegra:
|
||||
Tegra::GPUVAddr),
|
||||
SurfaceParams::MaxPixelFormat>
|
||||
morton_to_gl_fns = {
|
||||
MortonCopy<true, PixelFormat::ABGR8>, MortonCopy<true, PixelFormat::B5G6R5>,
|
||||
MortonCopy<true, PixelFormat::A2B10G10R10>, MortonCopy<true, PixelFormat::DXT1>,
|
||||
MortonCopy<true, PixelFormat::DXT23>, MortonCopy<true, PixelFormat::DXT45>,
|
||||
MortonCopy<true, PixelFormat::ABGR8>, MortonCopy<true, PixelFormat::B5G6R5>,
|
||||
MortonCopy<true, PixelFormat::A2B10G10R10>, MortonCopy<true, PixelFormat::A1B5G5R5>,
|
||||
MortonCopy<true, PixelFormat::R8>, MortonCopy<true, PixelFormat::RGBA16F>,
|
||||
MortonCopy<true, PixelFormat::R11FG11FB10F>, MortonCopy<true, PixelFormat::DXT1>,
|
||||
MortonCopy<true, PixelFormat::DXT23>, MortonCopy<true, PixelFormat::DXT45>,
|
||||
MortonCopy<true, PixelFormat::DXN1>, MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
|
||||
};
|
||||
|
||||
static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr, Tegra::GPUVAddr,
|
||||
@@ -118,10 +153,16 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr, Tegra:
|
||||
MortonCopy<false, PixelFormat::ABGR8>,
|
||||
MortonCopy<false, PixelFormat::B5G6R5>,
|
||||
MortonCopy<false, PixelFormat::A2B10G10R10>,
|
||||
// TODO(Subv): Swizzling the DXT1/DXT23/DXT45 formats is not yet supported
|
||||
MortonCopy<false, PixelFormat::A1B5G5R5>,
|
||||
MortonCopy<false, PixelFormat::R8>,
|
||||
MortonCopy<false, PixelFormat::RGBA16F>,
|
||||
MortonCopy<false, PixelFormat::R11FG11FB10F>,
|
||||
// TODO(Subv): Swizzling the DXT1/DXT23/DXT45/DXN1 formats is not yet supported
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
MortonCopy<false, PixelFormat::ABGR8>,
|
||||
};
|
||||
|
||||
// Allocate an uninitialized texture of appropriate size and format for the surface
|
||||
@@ -154,60 +195,10 @@ static void AllocateSurfaceTexture(GLuint texture, const FormatTuple& format_tup
|
||||
static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect, GLuint dst_tex,
|
||||
const MathUtil::Rectangle<u32>& dst_rect, SurfaceType type,
|
||||
GLuint read_fb_handle, GLuint draw_fb_handle) {
|
||||
OpenGLState state = OpenGLState::GetCurState();
|
||||
|
||||
OpenGLState prev_state = state;
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
// Make sure textures aren't bound to texture units, since going to bind them to framebuffer
|
||||
// components
|
||||
state.ResetTexture(src_tex);
|
||||
state.ResetTexture(dst_tex);
|
||||
|
||||
state.draw.read_framebuffer = read_fb_handle;
|
||||
state.draw.draw_framebuffer = draw_fb_handle;
|
||||
state.Apply();
|
||||
|
||||
u32 buffers = 0;
|
||||
|
||||
if (type == SurfaceType::ColorTexture) {
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, src_tex,
|
||||
0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
0);
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex,
|
||||
0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
0);
|
||||
|
||||
buffers = GL_COLOR_BUFFER_BIT;
|
||||
} else if (type == SurfaceType::Depth) {
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, src_tex, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, dst_tex, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
|
||||
buffers = GL_DEPTH_BUFFER_BIT;
|
||||
} else if (type == SurfaceType::DepthStencil) {
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
src_tex, 0);
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
dst_tex, 0);
|
||||
|
||||
buffers = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
|
||||
}
|
||||
|
||||
glBlitFramebuffer(src_rect.left, src_rect.bottom, src_rect.right, src_rect.top, dst_rect.left,
|
||||
dst_rect.bottom, dst_rect.right, dst_rect.top, buffers,
|
||||
buffers == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST);
|
||||
|
||||
glCopyImageSubData(src_tex, GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom, 0, dst_tex,
|
||||
GL_TEXTURE_2D, 0, dst_rect.left, dst_rect.bottom, 0, src_rect.GetWidth(),
|
||||
src_rect.GetHeight(), 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -584,7 +575,7 @@ void CachedSurface::UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint
|
||||
glCompressedTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format,
|
||||
static_cast<GLsizei>(rect.GetWidth() * GetCompresssionFactor()),
|
||||
static_cast<GLsizei>(rect.GetHeight() * GetCompresssionFactor()), 0,
|
||||
size, &gl_buffer[buffer_offset]);
|
||||
static_cast<GLsizei>(size), &gl_buffer[buffer_offset]);
|
||||
} else {
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
|
||||
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
|
||||
@@ -923,8 +914,6 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatc
|
||||
// Use GetSurfaceSubRect instead
|
||||
ASSERT(params.width == params.stride);
|
||||
|
||||
ASSERT(!params.is_tiled || (params.width % 8 == 0 && params.height % 8 == 0));
|
||||
|
||||
// Check for an exact match in existing surfaces
|
||||
Surface surface =
|
||||
FindMatch<MatchFlags::Exact | MatchFlags::Invalid>(surface_cache, params, match_res_scale);
|
||||
@@ -1067,8 +1056,11 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu
|
||||
params.addr = config.tic.Address();
|
||||
params.is_tiled = config.tic.IsTiled();
|
||||
params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(config.tic.format);
|
||||
params.width = config.tic.Width() / params.GetCompresssionFactor();
|
||||
params.height = config.tic.Height() / params.GetCompresssionFactor();
|
||||
|
||||
params.width = Common::AlignUp(config.tic.Width(), params.GetCompresssionFactor()) /
|
||||
params.GetCompresssionFactor();
|
||||
params.height = Common::AlignUp(config.tic.Height(), params.GetCompresssionFactor()) /
|
||||
params.GetCompresssionFactor();
|
||||
|
||||
// TODO(Subv): Different types per component are not supported.
|
||||
ASSERT(config.tic.r_type.Value() == config.tic.g_type.Value() &&
|
||||
@@ -1079,6 +1071,13 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu
|
||||
|
||||
if (config.tic.IsTiled()) {
|
||||
params.block_height = config.tic.BlockHeight();
|
||||
|
||||
// TODO(bunnei): The below align up is a hack. This is here because some compressed textures
|
||||
// are not a multiple of their own compression factor, and so this accounts for that. This
|
||||
// could potentially result in an extra row of 4px being decoded if a texture is not a
|
||||
// multiple of 4.
|
||||
params.width = Common::AlignUp(params.width, 4);
|
||||
params.height = Common::AlignUp(params.height, 4);
|
||||
} else {
|
||||
// Use the texture-provided stride value if the texture isn't tiled.
|
||||
params.stride = static_cast<u32>(params.PixelsInBytes(config.tic.Pitch()));
|
||||
@@ -1086,23 +1085,6 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu
|
||||
|
||||
params.UpdateParams();
|
||||
|
||||
if (config.tic.Width() % 8 != 0 || config.tic.Height() % 8 != 0 ||
|
||||
params.stride != params.width) {
|
||||
Surface src_surface;
|
||||
MathUtil::Rectangle<u32> rect;
|
||||
std::tie(src_surface, rect) = GetSurfaceSubRect(params, ScaleMatch::Ignore, true);
|
||||
|
||||
params.res_scale = src_surface->res_scale;
|
||||
Surface tmp_surface = CreateSurface(params);
|
||||
BlitTextures(src_surface->texture.handle, rect, tmp_surface->texture.handle,
|
||||
tmp_surface->GetScaledRect(),
|
||||
SurfaceParams::GetFormatType(params.pixel_format), read_framebuffer.handle,
|
||||
draw_framebuffer.handle);
|
||||
|
||||
remove_surfaces.emplace(tmp_surface);
|
||||
return tmp_surface;
|
||||
}
|
||||
|
||||
return GetSurface(params, ScaleMatch::Ignore, true);
|
||||
}
|
||||
|
||||
@@ -1277,7 +1259,7 @@ void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, Tegra::GPUVA
|
||||
|
||||
const auto interval = *it & validate_interval;
|
||||
// Look for a valid surface to copy from
|
||||
SurfaceParams params = surface->FromInterval(interval);
|
||||
SurfaceParams params = *surface;
|
||||
|
||||
Surface copy_surface =
|
||||
FindMatch<MatchFlags::Copy>(surface_cache, params, ScaleMatch::Ignore, interval);
|
||||
|
||||
@@ -57,9 +57,15 @@ struct SurfaceParams {
|
||||
ABGR8 = 0,
|
||||
B5G6R5 = 1,
|
||||
A2B10G10R10 = 2,
|
||||
DXT1 = 3,
|
||||
DXT23 = 4,
|
||||
DXT45 = 5,
|
||||
A1B5G5R5 = 3,
|
||||
R8 = 4,
|
||||
RGBA16F = 5,
|
||||
R11FG11FB10F = 6,
|
||||
DXT1 = 7,
|
||||
DXT23 = 8,
|
||||
DXT45 = 9,
|
||||
DXN1 = 10, // This is also known as BC4
|
||||
ASTC_2D_4X4 = 11,
|
||||
|
||||
Max,
|
||||
Invalid = 255,
|
||||
@@ -98,9 +104,15 @@ struct SurfaceParams {
|
||||
1, // ABGR8
|
||||
1, // B5G6R5
|
||||
1, // A2B10G10R10
|
||||
1, // A1B5G5R5
|
||||
1, // R8
|
||||
1, // RGBA16F
|
||||
1, // R11FG11FB10F
|
||||
4, // DXT1
|
||||
4, // DXT23
|
||||
4, // DXT45
|
||||
4, // DXN1
|
||||
1, // ASTC_2D_4X4
|
||||
}};
|
||||
|
||||
ASSERT(static_cast<size_t>(format) < compression_factor_table.size());
|
||||
@@ -118,9 +130,15 @@ struct SurfaceParams {
|
||||
32, // ABGR8
|
||||
16, // B5G6R5
|
||||
32, // A2B10G10R10
|
||||
16, // A1B5G5R5
|
||||
8, // R8
|
||||
64, // RGBA16F
|
||||
32, // R11FG11FB10F
|
||||
64, // DXT1
|
||||
128, // DXT23
|
||||
128, // DXT45
|
||||
64, // DXN1
|
||||
32, // ASTC_2D_4X4
|
||||
}};
|
||||
|
||||
ASSERT(static_cast<size_t>(format) < bpp_table.size());
|
||||
@@ -133,15 +151,29 @@ struct SurfaceParams {
|
||||
static PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) {
|
||||
switch (format) {
|
||||
case Tegra::RenderTargetFormat::RGBA8_UNORM:
|
||||
case Tegra::RenderTargetFormat::RGBA8_SRGB:
|
||||
return PixelFormat::ABGR8;
|
||||
case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
|
||||
return PixelFormat::A2B10G10R10;
|
||||
case Tegra::RenderTargetFormat::RGBA16_FLOAT:
|
||||
return PixelFormat::RGBA16F;
|
||||
case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
|
||||
return PixelFormat::R11FG11FB10F;
|
||||
default:
|
||||
NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsFormatASTC(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::ASTC_2D_4X4:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) {
|
||||
switch (format) {
|
||||
case Tegra::FramebufferConfig::PixelFormat::ABGR8:
|
||||
@@ -161,12 +193,24 @@ struct SurfaceParams {
|
||||
return PixelFormat::B5G6R5;
|
||||
case Tegra::Texture::TextureFormat::A2B10G10R10:
|
||||
return PixelFormat::A2B10G10R10;
|
||||
case Tegra::Texture::TextureFormat::A1B5G5R5:
|
||||
return PixelFormat::A1B5G5R5;
|
||||
case Tegra::Texture::TextureFormat::R8:
|
||||
return PixelFormat::R8;
|
||||
case Tegra::Texture::TextureFormat::R16_G16_B16_A16:
|
||||
return PixelFormat::RGBA16F;
|
||||
case Tegra::Texture::TextureFormat::BF10GF11RF11:
|
||||
return PixelFormat::R11FG11FB10F;
|
||||
case Tegra::Texture::TextureFormat::DXT1:
|
||||
return PixelFormat::DXT1;
|
||||
case Tegra::Texture::TextureFormat::DXT23:
|
||||
return PixelFormat::DXT23;
|
||||
case Tegra::Texture::TextureFormat::DXT45:
|
||||
return PixelFormat::DXT45;
|
||||
case Tegra::Texture::TextureFormat::DXN1:
|
||||
return PixelFormat::DXN1;
|
||||
case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
|
||||
return PixelFormat::ASTC_2D_4X4;
|
||||
default:
|
||||
NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
@@ -182,12 +226,24 @@ struct SurfaceParams {
|
||||
return Tegra::Texture::TextureFormat::B5G6R5;
|
||||
case PixelFormat::A2B10G10R10:
|
||||
return Tegra::Texture::TextureFormat::A2B10G10R10;
|
||||
case PixelFormat::A1B5G5R5:
|
||||
return Tegra::Texture::TextureFormat::A1B5G5R5;
|
||||
case PixelFormat::R8:
|
||||
return Tegra::Texture::TextureFormat::R8;
|
||||
case PixelFormat::RGBA16F:
|
||||
return Tegra::Texture::TextureFormat::R16_G16_B16_A16;
|
||||
case PixelFormat::R11FG11FB10F:
|
||||
return Tegra::Texture::TextureFormat::BF10GF11RF11;
|
||||
case PixelFormat::DXT1:
|
||||
return Tegra::Texture::TextureFormat::DXT1;
|
||||
case PixelFormat::DXT23:
|
||||
return Tegra::Texture::TextureFormat::DXT23;
|
||||
case PixelFormat::DXT45:
|
||||
return Tegra::Texture::TextureFormat::DXT45;
|
||||
case PixelFormat::DXN1:
|
||||
return Tegra::Texture::TextureFormat::DXN1;
|
||||
case PixelFormat::ASTC_2D_4X4:
|
||||
return Tegra::Texture::TextureFormat::ASTC_2D_4X4;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -208,8 +264,12 @@ struct SurfaceParams {
|
||||
// TODO(Subv): Implement more render targets
|
||||
switch (format) {
|
||||
case Tegra::RenderTargetFormat::RGBA8_UNORM:
|
||||
case Tegra::RenderTargetFormat::RGBA8_SRGB:
|
||||
case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
|
||||
return ComponentType::UNorm;
|
||||
case Tegra::RenderTargetFormat::RGBA16_FLOAT:
|
||||
case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
|
||||
return ComponentType::Float;
|
||||
default:
|
||||
NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -39,6 +39,10 @@ void main() {
|
||||
// Viewport can be flipped, which is unsupported by glViewport
|
||||
position.xy *= viewport_flip.xy;
|
||||
gl_Position = position;
|
||||
|
||||
// TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0
|
||||
// For now, this is here to bring order in lieu of proper emulation
|
||||
position.w = 1.0;
|
||||
}
|
||||
)";
|
||||
out += program.first;
|
||||
@@ -62,8 +66,6 @@ layout (std140) uniform fs_config {
|
||||
vec4 viewport_flip;
|
||||
};
|
||||
|
||||
uniform sampler2D tex[32];
|
||||
|
||||
void main() {
|
||||
exec_shader();
|
||||
}
|
||||
|
||||
@@ -22,17 +22,28 @@ class ConstBufferEntry {
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
public:
|
||||
void MarkAsUsed(unsigned index, unsigned offset, Maxwell::ShaderStage stage) {
|
||||
void MarkAsUsed(u64 index, u64 offset, Maxwell::ShaderStage stage) {
|
||||
is_used = true;
|
||||
this->index = index;
|
||||
this->index = static_cast<unsigned>(index);
|
||||
this->stage = stage;
|
||||
max_offset = std::max(max_offset, static_cast<unsigned>(offset));
|
||||
}
|
||||
|
||||
void MarkAsUsedIndirect(u64 index, Maxwell::ShaderStage stage) {
|
||||
is_used = true;
|
||||
is_indirect = true;
|
||||
this->index = static_cast<unsigned>(index);
|
||||
this->stage = stage;
|
||||
max_offset = std::max(max_offset, offset);
|
||||
}
|
||||
|
||||
bool IsUsed() const {
|
||||
return is_used;
|
||||
}
|
||||
|
||||
bool IsIndirect() const {
|
||||
return is_indirect;
|
||||
}
|
||||
|
||||
unsigned GetIndex() const {
|
||||
return index;
|
||||
}
|
||||
@@ -51,13 +62,54 @@ private:
|
||||
};
|
||||
|
||||
bool is_used{};
|
||||
bool is_indirect{};
|
||||
unsigned index{};
|
||||
unsigned max_offset{};
|
||||
Maxwell::ShaderStage stage;
|
||||
};
|
||||
|
||||
class SamplerEntry {
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
public:
|
||||
SamplerEntry(Maxwell::ShaderStage stage, size_t offset, size_t index)
|
||||
: offset(offset), stage(stage), sampler_index(index) {}
|
||||
|
||||
size_t GetOffset() const {
|
||||
return offset;
|
||||
}
|
||||
|
||||
size_t GetIndex() const {
|
||||
return sampler_index;
|
||||
}
|
||||
|
||||
Maxwell::ShaderStage GetStage() const {
|
||||
return stage;
|
||||
}
|
||||
|
||||
std::string GetName() const {
|
||||
return std::string(TextureSamplerNames[static_cast<size_t>(stage)]) + '[' +
|
||||
std::to_string(sampler_index) + ']';
|
||||
}
|
||||
|
||||
static std::string GetArrayName(Maxwell::ShaderStage stage) {
|
||||
return TextureSamplerNames[static_cast<size_t>(stage)];
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr std::array<const char*, Maxwell::MaxShaderStage> TextureSamplerNames = {
|
||||
"tex_vs", "tex_tessc", "tex_tesse", "tex_gs", "tex_fs",
|
||||
};
|
||||
/// Offset in TSC memory from which to read the sampler object, as specified by the sampling
|
||||
/// instruction.
|
||||
size_t offset;
|
||||
Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used.
|
||||
size_t sampler_index; ///< Value used to index into the generated GLSL sampler array.
|
||||
};
|
||||
|
||||
struct ShaderEntries {
|
||||
std::vector<ConstBufferEntry> const_buffer_entries;
|
||||
std::vector<SamplerEntry> texture_samplers;
|
||||
};
|
||||
|
||||
using ProgramResult = std::pair<std::string, ShaderEntries>;
|
||||
|
||||
@@ -32,33 +32,14 @@ void SetShaderUniformBlockBindings(GLuint shader) {
|
||||
sizeof(MaxwellUniformData));
|
||||
}
|
||||
|
||||
void SetShaderSamplerBindings(GLuint shader) {
|
||||
OpenGLState cur_state = OpenGLState::GetCurState();
|
||||
GLuint old_program = std::exchange(cur_state.draw.shader_program, shader);
|
||||
cur_state.Apply();
|
||||
|
||||
// Set the texture samplers to correspond to different texture units
|
||||
for (u32 texture = 0; texture < NumTextureSamplers; ++texture) {
|
||||
// Set the texture samplers to correspond to different texture units
|
||||
std::string uniform_name = "tex[" + std::to_string(texture) + "]";
|
||||
GLint uniform_tex = glGetUniformLocation(shader, uniform_name.c_str());
|
||||
if (uniform_tex != -1) {
|
||||
glUniform1i(uniform_tex, TextureUnits::MaxwellTexture(texture).id);
|
||||
}
|
||||
}
|
||||
|
||||
cur_state.draw.shader_program = old_program;
|
||||
cur_state.Apply();
|
||||
}
|
||||
|
||||
} // namespace Impl
|
||||
|
||||
void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage) {
|
||||
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
|
||||
|
||||
// TODO(bunnei): Support more than one viewport
|
||||
viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0 : 1.0;
|
||||
viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0 : 1.0;
|
||||
viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f;
|
||||
viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f;
|
||||
}
|
||||
|
||||
} // namespace GLShader
|
||||
|
||||
@@ -45,7 +45,6 @@ public:
|
||||
shader.Create(program_result.first.c_str(), type);
|
||||
program.Create(true, shader.handle);
|
||||
Impl::SetShaderUniformBlockBindings(program.handle);
|
||||
Impl::SetShaderSamplerBindings(program.handle);
|
||||
entries = program_result.second;
|
||||
}
|
||||
GLuint GetHandle() const {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <glad/glad.h>
|
||||
#include "common/assert.h"
|
||||
@@ -11,6 +12,27 @@
|
||||
|
||||
namespace GLShader {
|
||||
|
||||
/**
|
||||
* Utility function to log the source code of a list of shaders.
|
||||
* @param shaders The OpenGL shaders whose source we will print.
|
||||
*/
|
||||
template <typename... T>
|
||||
void LogShaderSource(T... shaders) {
|
||||
auto shader_list = {shaders...};
|
||||
|
||||
for (const auto& shader : shader_list) {
|
||||
if (shader == 0)
|
||||
continue;
|
||||
|
||||
GLint source_length;
|
||||
glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &source_length);
|
||||
|
||||
std::string source(source_length, ' ');
|
||||
glGetShaderSource(shader, source_length, nullptr, &source[0]);
|
||||
NGLOG_INFO(Render_OpenGL, "Shader source {}", source);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to create and compile an OpenGL GLSL shader
|
||||
* @param source String of the GLSL shader program
|
||||
@@ -55,6 +77,11 @@ GLuint LoadProgram(bool separable_program, T... shaders) {
|
||||
}
|
||||
}
|
||||
|
||||
if (result == GL_FALSE) {
|
||||
// There was a problem linking the shader, print the source for debugging purposes.
|
||||
LogShaderSource(shaders...);
|
||||
}
|
||||
|
||||
ASSERT_MSG(result == GL_TRUE, "Shader not linked");
|
||||
|
||||
((shaders == 0 ? (void)0 : glDetachShader(program_id, shaders)), ...);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user