Compare commits
171 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2f2c56780 | ||
|
|
666b37ad56 | ||
|
|
e2730372b8 | ||
|
|
450cbcfee6 | ||
|
|
bf9c010be5 | ||
|
|
ab65de2f96 | ||
|
|
393cdb15f5 | ||
|
|
edb291b3be | ||
|
|
6989fd65f3 | ||
|
|
e02687ff47 | ||
|
|
edb2caaae5 | ||
|
|
b284c43385 | ||
|
|
883fab2fff | ||
|
|
f1d8c83e1c | ||
|
|
07632ad825 | ||
|
|
b2305dcee0 | ||
|
|
b971b82275 | ||
|
|
84b5804834 | ||
|
|
539675b21a | ||
|
|
8df93132cd | ||
|
|
a1dddca4ab | ||
|
|
839c91cd14 | ||
|
|
a8ba6dc3c9 | ||
|
|
32b6fc4062 | ||
|
|
9ce6ea648f | ||
|
|
a67d00ef31 | ||
|
|
739d90ee66 | ||
|
|
ed89bcc767 | ||
|
|
f1aabc21ee | ||
|
|
80a0f2a118 | ||
|
|
6001538139 | ||
|
|
63cc4e417f | ||
|
|
e60733aad3 | ||
|
|
5fb27f83cf | ||
|
|
f16a94fb39 | ||
|
|
e5abf11186 | ||
|
|
1074c87f18 | ||
|
|
7a051c4973 | ||
|
|
a4306b9e56 | ||
|
|
da11a27f42 | ||
|
|
505aa3a4c1 | ||
|
|
6744e7ea4a | ||
|
|
0193202964 | ||
|
|
fb0fefc75c | ||
|
|
a0ee597b19 | ||
|
|
a45a57641f | ||
|
|
fca26980a2 | ||
|
|
995067538d | ||
|
|
01a1adfb0c | ||
|
|
798c1b457d | ||
|
|
c574ab5aa1 | ||
|
|
d7a2dc4cea | ||
|
|
d39b457566 | ||
|
|
755506d404 | ||
|
|
7351ca8c75 | ||
|
|
75a01475d1 | ||
|
|
6d8d7ebc66 | ||
|
|
7f4d96d873 | ||
|
|
4c269e5ced | ||
|
|
b24b463c87 | ||
|
|
17242a8865 | ||
|
|
0373ead96e | ||
|
|
47e26d7bc7 | ||
|
|
0974533c96 | ||
|
|
df27813e76 | ||
|
|
bd2feb6873 | ||
|
|
48aa076049 | ||
|
|
e73c53fad1 | ||
|
|
63d23835ef | ||
|
|
0f60ccdde2 | ||
|
|
f9e748b721 | ||
|
|
1c7d106aac | ||
|
|
83b1b259bd | ||
|
|
95cd2e17ab | ||
|
|
48ff15602e | ||
|
|
77bdd09aa2 | ||
|
|
5311b562aa | ||
|
|
9c2773409a | ||
|
|
713e204a8a | ||
|
|
3c7a115afe | ||
|
|
ec13746e4d | ||
|
|
cf76769026 | ||
|
|
4e2464a713 | ||
|
|
2a87ddcce2 | ||
|
|
2a9d17b7e7 | ||
|
|
dff8f1f9f8 | ||
|
|
e7c77b90c4 | ||
|
|
6ae0b83442 | ||
|
|
35f7740b6c | ||
|
|
2af7e40ce6 | ||
|
|
1db10b8f4c | ||
|
|
155c8ba04c | ||
|
|
40cf9288e9 | ||
|
|
7bd3558c64 | ||
|
|
b700d657d7 | ||
|
|
fa4d6df4c5 | ||
|
|
4f678284e2 | ||
|
|
be06b21f7b | ||
|
|
76bd617b2d | ||
|
|
c0dc8f9d25 | ||
|
|
52882a93a5 | ||
|
|
9f8e17cb18 | ||
|
|
f829932ed1 | ||
|
|
d00972fce1 | ||
|
|
3096adb347 | ||
|
|
e69d715e3d | ||
|
|
41a333321a | ||
|
|
8a1cfcc8b7 | ||
|
|
639cf169b2 | ||
|
|
98fcd3ba5d | ||
|
|
064b7bfa5f | ||
|
|
b13fd9e65a | ||
|
|
af787e451b | ||
|
|
6e1639c7b0 | ||
|
|
6b7c8e469b | ||
|
|
c58e21cd76 | ||
|
|
3f910efb40 | ||
|
|
38b585a309 | ||
|
|
b1b1ed7597 | ||
|
|
f6cb128eac | ||
|
|
34a590e509 | ||
|
|
28aa697528 | ||
|
|
24c2930012 | ||
|
|
15a04fb704 | ||
|
|
fb13f053bb | ||
|
|
c91710a82f | ||
|
|
00a1d106bd | ||
|
|
a76e11e7f0 | ||
|
|
dfdf87d844 | ||
|
|
1b31755ba6 | ||
|
|
765e37aa35 | ||
|
|
86946ea13c | ||
|
|
f3630a0713 | ||
|
|
a0c499aef7 | ||
|
|
f2eead3b5b | ||
|
|
6a0010d0c6 | ||
|
|
8562b516c0 | ||
|
|
9ff3401133 | ||
|
|
dcf345febe | ||
|
|
f0baf2abf2 | ||
|
|
bd697bef03 | ||
|
|
9d55e5586f | ||
|
|
8584a77eb2 | ||
|
|
1a84209418 | ||
|
|
c94b398f14 | ||
|
|
a6db8e5f4d | ||
|
|
c387a72c76 | ||
|
|
7527402a46 | ||
|
|
6481d91e4a | ||
|
|
ecbc813711 | ||
|
|
3f739514e3 | ||
|
|
c18dc9c707 | ||
|
|
380fbd8cb7 | ||
|
|
e3d2b646e0 | ||
|
|
5226610a15 | ||
|
|
743e1f02a0 | ||
|
|
d4e07fd95e | ||
|
|
901bc09dd7 | ||
|
|
fcc23139f6 | ||
|
|
8ccc8cb252 | ||
|
|
f5d2a1e8bd | ||
|
|
28046ae3a9 | ||
|
|
0f729ef078 | ||
|
|
46b4461fbb | ||
|
|
968d631aa5 | ||
|
|
5f0fa4cb82 | ||
|
|
121af3646d | ||
|
|
c94583d867 | ||
|
|
0076a08d04 | ||
|
|
18a42d3815 | ||
|
|
0248614add |
@@ -1,4 +1,13 @@
|
||||
<!--
|
||||
---
|
||||
name: Bug Report / Feature Request
|
||||
about: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu or you are requesting a feature you believe would make yuzu better.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!---
|
||||
Please keep in mind yuzu is EXPERIMENTAL SOFTWARE.
|
||||
|
||||
Please read the FAQ:
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: yuzu Discord
|
||||
url: https://discord.com/invite/u77vRWY
|
||||
about: If you are experiencing an issue with yuzu, and you need tech support, or if you have a general question, try asking in the official yuzu Discord linked here. Piracy is not allowed.
|
||||
- name: Community forums
|
||||
url: https://community.citra-emu.org
|
||||
about: This is an alternative place for tech support, however helpers there are not as active.
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -34,6 +34,9 @@
|
||||
[submodule "xbyak"]
|
||||
path = externals/xbyak
|
||||
url = https://github.com/herumi/xbyak.git
|
||||
[submodule "externals/libusb"]
|
||||
path = externals/libusb
|
||||
url = https://github.com/ameerj/libusb
|
||||
[submodule "opus"]
|
||||
path = externals/opus/opus
|
||||
url = https://github.com/xiph/opus.git
|
||||
|
||||
@@ -118,8 +118,17 @@ message(STATUS "Target architecture: ${ARCHITECTURE}")
|
||||
# Configure C++ standard
|
||||
# ===========================
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
# boost asio's concept usage doesn't play nicely with some compilers yet.
|
||||
add_definitions(-DBOOST_ASIO_DISABLE_CONCEPTS)
|
||||
if (MSVC)
|
||||
add_compile_options(/std:c++latest)
|
||||
|
||||
# cubeb and boost still make use of deprecated result_of.
|
||||
add_definitions(-D_HAS_DEPRECATED_RESULT_OF)
|
||||
else()
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
endif()
|
||||
|
||||
# Output binaries to bin/
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
@@ -151,7 +160,7 @@ macro(yuzu_find_packages)
|
||||
# Cmake Pkg Prefix Version Conan Pkg
|
||||
"Boost 1.71 boost/1.72.0"
|
||||
"Catch2 2.11 catch2/2.11.0"
|
||||
"fmt 6.2 fmt/6.2.0"
|
||||
"fmt 7.0 fmt/7.0.1"
|
||||
# can't use until https://github.com/bincrafters/community/issues/1173
|
||||
#"libzip 1.5 libzip/1.5.2@bincrafters/stable"
|
||||
"lz4 1.8 lz4/1.9.2"
|
||||
@@ -211,7 +220,7 @@ if(ENABLE_QT)
|
||||
|
||||
set(QT_PREFIX_HINT HINTS "${QT_PREFIX}")
|
||||
endif()
|
||||
find_package(Qt5 5.9 COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT})
|
||||
find_package(Qt5 5.9 COMPONENTS Widgets ${QT_PREFIX_HINT})
|
||||
if (YUZU_USE_QT_WEB_ENGINE)
|
||||
find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets)
|
||||
endif()
|
||||
@@ -287,7 +296,7 @@ if (CONAN_REQUIRED_LIBS)
|
||||
if(ENABLE_QT)
|
||||
list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}")
|
||||
list(APPEND CMAKE_PREFIX_PATH "${CONAN_QT_ROOT_RELEASE}")
|
||||
find_package(Qt5 5.9 REQUIRED COMPONENTS Widgets OpenGL)
|
||||
find_package(Qt5 5.9 REQUIRED COMPONENTS Widgets)
|
||||
if (YUZU_USE_QT_WEB_ENGINE)
|
||||
find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets)
|
||||
endif()
|
||||
@@ -329,6 +338,17 @@ elseif(SDL2_FOUND)
|
||||
target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARIES}")
|
||||
endif()
|
||||
|
||||
# Ensure libusb is properly configured (based on dolphin libusb include)
|
||||
if(NOT APPLE)
|
||||
include(FindPkgConfig)
|
||||
find_package(LibUSB)
|
||||
endif()
|
||||
if (NOT LIBUSB_FOUND)
|
||||
add_subdirectory(externals/libusb)
|
||||
set(LIBUSB_INCLUDE_DIR "")
|
||||
set(LIBUSB_LIBRARIES usb)
|
||||
endif()
|
||||
|
||||
# Prefer the -pthread flag on Linux.
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
@@ -15,7 +15,6 @@ function(copy_yuzu_Qt5_deps target_dir)
|
||||
icuuc*.dll
|
||||
Qt5Core$<$<CONFIG:Debug>:d>.*
|
||||
Qt5Gui$<$<CONFIG:Debug>:d>.*
|
||||
Qt5OpenGL$<$<CONFIG:Debug>:d>.*
|
||||
Qt5Widgets$<$<CONFIG:Debug>:d>.*
|
||||
)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ yuzu emulator
|
||||
=============
|
||||
[](https://travis-ci.com/yuzu-emu/yuzu)
|
||||
[](https://dev.azure.com/yuzu-emu/yuzu/)
|
||||
[](https://discord.gg/XQV6dn9)
|
||||
[](https://discord.com/invite/u77vRWY)
|
||||
|
||||
yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/).
|
||||
|
||||
@@ -16,7 +16,7 @@ yuzu is licensed under the GPLv2 (or any later version). Refer to the license.tx
|
||||
|
||||
Check out our [website](https://yuzu-emu.org/)!
|
||||
|
||||
For development discussion, please join us on [Discord](https://discord.gg/XQV6dn9).
|
||||
For development discussion, please join us on [Discord](https://discord.com/invite/u77vRWY).
|
||||
|
||||
### Development
|
||||
|
||||
|
||||
2
externals/Vulkan-Headers
vendored
2
externals/Vulkan-Headers
vendored
Submodule externals/Vulkan-Headers updated: 9250d5ae8f...8188e3fbbc
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 4f967387c0...82417da780
43
externals/find-modules/FindLibUSB.cmake
vendored
Normal file
43
externals/find-modules/FindLibUSB.cmake
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# - Find libusb-1.0 library
|
||||
# This module defines
|
||||
# LIBUSB_INCLUDE_DIR, where to find bluetooth.h
|
||||
# LIBUSB_LIBRARIES, the libraries needed to use libusb-1.0.
|
||||
# LIBUSB_FOUND, If false, do not try to use libusb-1.0.
|
||||
#
|
||||
# Copyright (c) 2009, Michal Cihar, <michal@cihar.com>
|
||||
#
|
||||
# vim: expandtab sw=4 ts=4 sts=4:
|
||||
|
||||
if(ANDROID)
|
||||
set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found")
|
||||
message(STATUS "libusb-1.0 not found.")
|
||||
elseif (NOT LIBUSB_FOUND)
|
||||
pkg_check_modules (LIBUSB_PKG libusb-1.0)
|
||||
|
||||
find_path(LIBUSB_INCLUDE_DIR NAMES libusb.h
|
||||
PATHS
|
||||
${LIBUSB_PKG_INCLUDE_DIRS}
|
||||
/usr/include/libusb-1.0
|
||||
/usr/include
|
||||
/usr/local/include/libusb-1.0
|
||||
/usr/local/include
|
||||
)
|
||||
|
||||
find_library(LIBUSB_LIBRARIES NAMES usb-1.0 usb
|
||||
PATHS
|
||||
${LIBUSB_PKG_LIBRARY_DIRS}
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
)
|
||||
|
||||
if(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
|
||||
set(LIBUSB_FOUND TRUE CACHE INTERNAL "libusb-1.0 found")
|
||||
message(STATUS "Found libusb-1.0: ${LIBUSB_INCLUDE_DIR}, ${LIBUSB_LIBRARIES}")
|
||||
else(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
|
||||
set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found")
|
||||
message(STATUS "libusb-1.0 not found.")
|
||||
endif(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
|
||||
|
||||
mark_as_advanced(LIBUSB_INCLUDE_DIR LIBUSB_LIBRARIES)
|
||||
endif ()
|
||||
|
||||
1
externals/libusb
vendored
Submodule
1
externals/libusb
vendored
Submodule
Submodule externals/libusb added at 3406d72cda
@@ -193,7 +193,7 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const
|
||||
const std::size_t samples_to_write = num_channels * num_frames;
|
||||
std::size_t samples_written;
|
||||
|
||||
if (Settings::values.enable_audio_stretching) {
|
||||
if (Settings::values.enable_audio_stretching.GetValue()) {
|
||||
const std::vector<s16> in{impl->queue.Pop()};
|
||||
const std::size_t num_in{in.size() / num_channels};
|
||||
s16* const out{reinterpret_cast<s16*>(buffer)};
|
||||
|
||||
@@ -38,7 +38,7 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format fo
|
||||
sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} {
|
||||
|
||||
release_event = Core::Timing::CreateEvent(
|
||||
name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(); });
|
||||
name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(cycles_late); });
|
||||
}
|
||||
|
||||
void Stream::Play() {
|
||||
@@ -66,15 +66,6 @@ s64 Stream::GetBufferReleaseNS(const Buffer& buffer) const {
|
||||
return ns.count();
|
||||
}
|
||||
|
||||
s64 Stream::GetBufferReleaseNSHostTiming(const Buffer& buffer) const {
|
||||
const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
|
||||
/// DSP signals before playing the last sample, in HLE we emulate this in this way
|
||||
s64 base_samples = std::max<s64>(static_cast<s64>(num_samples) - 1, 0);
|
||||
const auto ns =
|
||||
std::chrono::nanoseconds((static_cast<u64>(base_samples) * 1000000000ULL) / sample_rate);
|
||||
return ns.count();
|
||||
}
|
||||
|
||||
static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
|
||||
const float volume{std::clamp(Settings::Volume() - (1.0f - game_volume), 0.0f, 1.0f)};
|
||||
|
||||
@@ -89,7 +80,7 @@ static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
|
||||
}
|
||||
}
|
||||
|
||||
void Stream::PlayNextBuffer() {
|
||||
void Stream::PlayNextBuffer(s64 cycles_late) {
|
||||
if (!IsPlaying()) {
|
||||
// Ensure we are in playing state before playing the next buffer
|
||||
sink_stream.Flush();
|
||||
@@ -114,18 +105,17 @@ void Stream::PlayNextBuffer() {
|
||||
|
||||
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
|
||||
|
||||
if (core_timing.IsHostTiming()) {
|
||||
core_timing.ScheduleEvent(GetBufferReleaseNSHostTiming(*active_buffer), release_event, {});
|
||||
} else {
|
||||
core_timing.ScheduleEvent(GetBufferReleaseNS(*active_buffer), release_event, {});
|
||||
}
|
||||
core_timing.ScheduleEvent(
|
||||
GetBufferReleaseNS(*active_buffer) -
|
||||
(Settings::values.enable_audio_stretching.GetValue() ? 0 : cycles_late),
|
||||
release_event, {});
|
||||
}
|
||||
|
||||
void Stream::ReleaseActiveBuffer() {
|
||||
void Stream::ReleaseActiveBuffer(s64 cycles_late) {
|
||||
ASSERT(active_buffer);
|
||||
released_buffers.push(std::move(active_buffer));
|
||||
release_callback();
|
||||
PlayNextBuffer();
|
||||
PlayNextBuffer(cycles_late);
|
||||
}
|
||||
|
||||
bool Stream::QueueBuffer(BufferPtr&& buffer) {
|
||||
|
||||
@@ -90,10 +90,10 @@ public:
|
||||
|
||||
private:
|
||||
/// Plays the next queued buffer in the audio stream, starting playback if necessary
|
||||
void PlayNextBuffer();
|
||||
void PlayNextBuffer(s64 cycles_late = 0);
|
||||
|
||||
/// Releases the actively playing buffer, signalling that it has been completed
|
||||
void ReleaseActiveBuffer();
|
||||
void ReleaseActiveBuffer(s64 cycles_late = 0);
|
||||
|
||||
/// Gets the number of core cycles when the specified buffer will be released
|
||||
s64 GetBufferReleaseNS(const Buffer& buffer) const;
|
||||
|
||||
@@ -11,7 +11,9 @@ namespace Common {
|
||||
template <typename T>
|
||||
constexpr T AlignUp(T value, std::size_t size) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return static_cast<T>(value + (size - value % size) % size);
|
||||
auto mod{static_cast<T>(value % size)};
|
||||
value -= mod;
|
||||
return static_cast<T>(mod == T{0} ? value : value + size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/thread.h"
|
||||
#include "core/arm/cpu_interrupt_handler.h"
|
||||
|
||||
|
||||
@@ -142,10 +142,32 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
|
||||
// Timing
|
||||
config.wall_clock_cntpct = uses_wall_clock;
|
||||
|
||||
// Optimizations
|
||||
if (Settings::values.disable_cpu_opt) {
|
||||
config.enable_optimizations = false;
|
||||
config.enable_fast_dispatch = false;
|
||||
// Safe optimizations
|
||||
if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) {
|
||||
if (!Settings::values.cpuopt_page_tables) {
|
||||
config.page_table = nullptr;
|
||||
}
|
||||
if (!Settings::values.cpuopt_block_linking) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking;
|
||||
}
|
||||
if (!Settings::values.cpuopt_return_stack_buffer) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::ReturnStackBuffer;
|
||||
}
|
||||
if (!Settings::values.cpuopt_fast_dispatcher) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::FastDispatch;
|
||||
}
|
||||
if (!Settings::values.cpuopt_context_elimination) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::GetSetElimination;
|
||||
}
|
||||
if (!Settings::values.cpuopt_const_prop) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::ConstProp;
|
||||
}
|
||||
if (!Settings::values.cpuopt_misc_ir) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::MiscIROpt;
|
||||
}
|
||||
if (!Settings::values.cpuopt_reduce_misalign_checks) {
|
||||
config.only_detect_misalignment_via_page_table_on_page_boundary = false;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_unique<Dynarmic::A32::Jit>(config);
|
||||
|
||||
@@ -191,15 +191,37 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
|
||||
// Unpredictable instructions
|
||||
config.define_unpredictable_behaviour = true;
|
||||
|
||||
// Optimizations
|
||||
if (Settings::values.disable_cpu_opt) {
|
||||
config.enable_optimizations = false;
|
||||
config.enable_fast_dispatch = false;
|
||||
}
|
||||
|
||||
// Timing
|
||||
config.wall_clock_cntpct = uses_wall_clock;
|
||||
|
||||
// Safe optimizations
|
||||
if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) {
|
||||
if (!Settings::values.cpuopt_page_tables) {
|
||||
config.page_table = nullptr;
|
||||
}
|
||||
if (!Settings::values.cpuopt_block_linking) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking;
|
||||
}
|
||||
if (!Settings::values.cpuopt_return_stack_buffer) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::ReturnStackBuffer;
|
||||
}
|
||||
if (!Settings::values.cpuopt_fast_dispatcher) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::FastDispatch;
|
||||
}
|
||||
if (!Settings::values.cpuopt_context_elimination) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::GetSetElimination;
|
||||
}
|
||||
if (!Settings::values.cpuopt_const_prop) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::ConstProp;
|
||||
}
|
||||
if (!Settings::values.cpuopt_misc_ir) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::MiscIROpt;
|
||||
}
|
||||
if (!Settings::values.cpuopt_reduce_misalign_checks) {
|
||||
config.only_detect_misalignment_via_page_table_on_page_boundary = false;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_shared<Dynarmic::A64::Jit>(config);
|
||||
}
|
||||
|
||||
|
||||
@@ -147,8 +147,8 @@ struct System::Impl {
|
||||
|
||||
device_memory = std::make_unique<Core::DeviceMemory>(system);
|
||||
|
||||
is_multicore = Settings::values.use_multi_core;
|
||||
is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation;
|
||||
is_multicore = Settings::values.use_multi_core.GetValue();
|
||||
is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
||||
|
||||
kernel.SetMulticore(is_multicore);
|
||||
cpu_manager.SetMulticore(is_multicore);
|
||||
@@ -162,7 +162,7 @@ struct System::Impl {
|
||||
const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch());
|
||||
Settings::values.custom_rtc_differential =
|
||||
Settings::values.custom_rtc.value_or(current_time) - current_time;
|
||||
Settings::values.custom_rtc.GetValue().value_or(current_time) - current_time;
|
||||
|
||||
// Create a default fs if one doesn't already exist.
|
||||
if (virtual_filesystem == nullptr)
|
||||
|
||||
@@ -172,7 +172,7 @@ void CoreTiming::ClearPendingEvents() {
|
||||
}
|
||||
|
||||
void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
|
||||
basic_lock.lock();
|
||||
std::scoped_lock lock{basic_lock};
|
||||
|
||||
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
|
||||
return e.type.lock().get() == event_type.get();
|
||||
@@ -183,12 +183,10 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
|
||||
event_queue.erase(itr, event_queue.end());
|
||||
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
}
|
||||
basic_lock.unlock();
|
||||
}
|
||||
|
||||
std::optional<s64> CoreTiming::Advance() {
|
||||
std::scoped_lock advance_scope{advance_lock};
|
||||
std::scoped_lock basic_scope{basic_lock};
|
||||
std::scoped_lock lock{advance_lock, basic_lock};
|
||||
global_timer = GetGlobalTimeNs().count();
|
||||
|
||||
while (!event_queue.empty() && event_queue.front().time <= global_timer) {
|
||||
|
||||
@@ -695,8 +695,9 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
|
||||
}
|
||||
|
||||
void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
|
||||
if (s128_keys.find({id, field1, field2}) != s128_keys.end())
|
||||
if (s128_keys.find({id, field1, field2}) != s128_keys.end() || key == Key128{}) {
|
||||
return;
|
||||
}
|
||||
if (id == S128KeyType::Titlekey) {
|
||||
Key128 rights_id;
|
||||
std::memcpy(rights_id.data(), &field2, sizeof(u64));
|
||||
@@ -716,8 +717,9 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
|
||||
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
|
||||
std::tie(id, field1, field2);
|
||||
});
|
||||
if (iter2 != s128_file_id.end())
|
||||
if (iter2 != s128_file_id.end()) {
|
||||
WriteKeyToFile(category, iter2->first, key);
|
||||
}
|
||||
|
||||
// Variable cases
|
||||
if (id == S128KeyType::KeyArea) {
|
||||
@@ -745,16 +747,18 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
|
||||
}
|
||||
|
||||
void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
|
||||
if (s256_keys.find({id, field1, field2}) != s256_keys.end())
|
||||
if (s256_keys.find({id, field1, field2}) != s256_keys.end() || key == Key256{}) {
|
||||
return;
|
||||
}
|
||||
const auto iter = std::find_if(
|
||||
s256_file_id.begin(), s256_file_id.end(),
|
||||
[&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {
|
||||
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
|
||||
std::tie(id, field1, field2);
|
||||
});
|
||||
if (iter != s256_file_id.end())
|
||||
if (iter != s256_file_id.end()) {
|
||||
WriteKeyToFile(KeyCategory::Standard, iter->first, key);
|
||||
}
|
||||
s256_keys[{id, field1, field2}] = key;
|
||||
}
|
||||
|
||||
|
||||
@@ -223,13 +223,16 @@ bool operator<(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
|
||||
|
||||
class KeyManager {
|
||||
public:
|
||||
static KeyManager& instance() {
|
||||
static KeyManager& Instance() {
|
||||
static KeyManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
KeyManager(KeyManager const&) = delete;
|
||||
void operator=(KeyManager const&) = delete;
|
||||
KeyManager(const KeyManager&) = delete;
|
||||
KeyManager& operator=(const KeyManager&) = delete;
|
||||
|
||||
KeyManager(KeyManager&&) = delete;
|
||||
KeyManager& operator=(KeyManager&&) = delete;
|
||||
|
||||
bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
|
||||
bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
constexpr u64 NAND_USER_SIZE = 0x680000000; // 26624 MiB
|
||||
constexpr u64 NAND_SYSTEM_SIZE = 0xA0000000; // 2560 MiB
|
||||
constexpr u64 NAND_TOTAL_SIZE = 0x747C00000; // 29820 MiB
|
||||
|
||||
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir dump_root_)
|
||||
: nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
|
||||
dump_root(std::move(dump_root_)),
|
||||
@@ -79,7 +83,7 @@ VirtualDir BISFactory::OpenPartition(BisPartitionId id) const {
|
||||
}
|
||||
|
||||
VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const {
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance();
|
||||
auto& keys = Core::Crypto::KeyManager::Instance();
|
||||
Core::Crypto::PartitionDataManager pdm{
|
||||
Core::System::GetInstance().GetFilesystem()->OpenDirectory(
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)};
|
||||
@@ -110,30 +114,29 @@ VirtualDir BISFactory::GetImageDirectory() const {
|
||||
|
||||
u64 BISFactory::GetSystemNANDFreeSpace() const {
|
||||
const auto sys_dir = GetOrCreateDirectoryRelative(nand_root, "/system");
|
||||
if (sys_dir == nullptr)
|
||||
return 0;
|
||||
if (sys_dir == nullptr) {
|
||||
return GetSystemNANDTotalSpace();
|
||||
}
|
||||
|
||||
return GetSystemNANDTotalSpace() - sys_dir->GetSize();
|
||||
}
|
||||
|
||||
u64 BISFactory::GetSystemNANDTotalSpace() const {
|
||||
return static_cast<u64>(Settings::values.nand_system_size);
|
||||
return NAND_SYSTEM_SIZE;
|
||||
}
|
||||
|
||||
u64 BISFactory::GetUserNANDFreeSpace() const {
|
||||
const auto usr_dir = GetOrCreateDirectoryRelative(nand_root, "/user");
|
||||
if (usr_dir == nullptr)
|
||||
return 0;
|
||||
|
||||
return GetUserNANDTotalSpace() - usr_dir->GetSize();
|
||||
// For some reason games such as BioShock 1 checks whether this is exactly 0x680000000 bytes.
|
||||
// Set the free space to be 1 MiB less than the total as a workaround to this issue.
|
||||
return GetUserNANDTotalSpace() - 0x100000;
|
||||
}
|
||||
|
||||
u64 BISFactory::GetUserNANDTotalSpace() const {
|
||||
return static_cast<u64>(Settings::values.nand_user_size);
|
||||
return NAND_USER_SIZE;
|
||||
}
|
||||
|
||||
u64 BISFactory::GetFullNANDTotalSpace() const {
|
||||
return static_cast<u64>(Settings::values.nand_total_size);
|
||||
return NAND_TOTAL_SIZE;
|
||||
}
|
||||
|
||||
VirtualDir BISFactory::GetBCATDirectory(u64 title_id) const {
|
||||
|
||||
@@ -140,6 +140,6 @@ private:
|
||||
|
||||
u64 update_normal_partition_end;
|
||||
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance();
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||
};
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -158,7 +158,7 @@ private:
|
||||
bool encrypted = false;
|
||||
bool is_update = false;
|
||||
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance();
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -80,16 +80,6 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
if (exefs == nullptr)
|
||||
return exefs;
|
||||
|
||||
if (Settings::values.dump_exefs) {
|
||||
LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
|
||||
const auto dump_dir =
|
||||
Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
|
||||
if (dump_dir != nullptr) {
|
||||
const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
|
||||
VfsRawCopyD(exefs, exefs_dir);
|
||||
}
|
||||
}
|
||||
|
||||
const auto& installed = Core::System::GetInstance().GetContentProvider();
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
@@ -135,6 +125,16 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings::values.dump_exefs) {
|
||||
LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
|
||||
const auto dump_dir =
|
||||
Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
|
||||
if (dump_dir != nullptr) {
|
||||
const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
|
||||
VfsRawCopyD(exefs, exefs_dir);
|
||||
}
|
||||
}
|
||||
|
||||
return exefs;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
|
||||
protected:
|
||||
// A single instance of KeyManager to be used by GetEntry()
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance();
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||
};
|
||||
|
||||
class PlaceholderCache {
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
constexpr u64 SDMC_TOTAL_SIZE = 0x10000000000; // 1 TiB
|
||||
|
||||
SDMCFactory::SDMCFactory(VirtualDir dir_)
|
||||
: dir(std::move(dir_)), contents(std::make_unique<RegisteredCache>(
|
||||
GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
|
||||
@@ -46,7 +48,7 @@ u64 SDMCFactory::GetSDMCFreeSpace() const {
|
||||
}
|
||||
|
||||
u64 SDMCFactory::GetSDMCTotalSpace() const {
|
||||
return static_cast<u64>(Settings::values.sdmc_size);
|
||||
return SDMC_TOTAL_SIZE;
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
namespace FileSys {
|
||||
namespace {
|
||||
void SetTicketKeys(const std::vector<VirtualFile>& files) {
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance();
|
||||
auto& keys = Core::Crypto::KeyManager::Instance();
|
||||
|
||||
for (const auto& ticket_file : files) {
|
||||
if (ticket_file == nullptr) {
|
||||
|
||||
@@ -73,7 +73,7 @@ private:
|
||||
std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas;
|
||||
std::vector<VirtualFile> ticket_files;
|
||||
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance();
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||
|
||||
VirtualFile romfs;
|
||||
VirtualDir exefs;
|
||||
|
||||
@@ -112,19 +112,26 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
|
||||
const auto new_path =
|
||||
FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
|
||||
|
||||
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
|
||||
FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
|
||||
return nullptr;
|
||||
|
||||
if (cache.find(old_path) != cache.end()) {
|
||||
auto cached = cache[old_path];
|
||||
if (!cached.expired()) {
|
||||
auto file = cached.lock();
|
||||
file->Open(new_path, "r+b");
|
||||
cache.erase(old_path);
|
||||
cache[new_path] = file;
|
||||
auto file = cache[old_path].lock();
|
||||
|
||||
if (!cache[old_path].expired()) {
|
||||
file->Close();
|
||||
}
|
||||
|
||||
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
|
||||
FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cache.erase(old_path);
|
||||
file->Open(new_path, "r+b");
|
||||
cache[new_path] = file;
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return OpenFile(new_path, Mode::ReadWrite);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,6 @@ private:
|
||||
|
||||
VirtualFile dec_file;
|
||||
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance();
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||
};
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -29,7 +29,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
|
||||
|
||||
const float window_aspect_ratio = static_cast<float>(height) / width;
|
||||
const float emulation_aspect_ratio = EmulationAspectRatio(
|
||||
static_cast<AspectRatio>(Settings::values.aspect_ratio), window_aspect_ratio);
|
||||
static_cast<AspectRatio>(Settings::values.aspect_ratio.GetValue()), window_aspect_ratio);
|
||||
|
||||
const Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
||||
Common::Rectangle<u32> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio);
|
||||
|
||||
@@ -123,7 +123,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
|
||||
: kernel.CreateNewUserProcessID();
|
||||
process->capabilities.InitializeForMetadatalessProcess();
|
||||
|
||||
std::mt19937 rng(Settings::values.rng_seed.value_or(0));
|
||||
std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(0));
|
||||
std::uniform_int_distribution<u64> distribution;
|
||||
std::generate(process->random_entropy.begin(), process->random_entropy.end(),
|
||||
[&] { return distribution(rng); });
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
// licensed under GPLv2 or later under exception provided by the author.
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
@@ -31,22 +32,20 @@ GlobalScheduler::GlobalScheduler(KernelCore& kernel) : kernel{kernel} {}
|
||||
GlobalScheduler::~GlobalScheduler() = default;
|
||||
|
||||
void GlobalScheduler::AddThread(std::shared_ptr<Thread> thread) {
|
||||
global_list_guard.lock();
|
||||
std::scoped_lock lock{global_list_guard};
|
||||
thread_list.push_back(std::move(thread));
|
||||
global_list_guard.unlock();
|
||||
}
|
||||
|
||||
void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) {
|
||||
global_list_guard.lock();
|
||||
std::scoped_lock lock{global_list_guard};
|
||||
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
|
||||
thread_list.end());
|
||||
global_list_guard.unlock();
|
||||
}
|
||||
|
||||
u32 GlobalScheduler::SelectThreads() {
|
||||
ASSERT(is_locked);
|
||||
const auto update_thread = [](Thread* thread, Scheduler& sched) {
|
||||
sched.guard.lock();
|
||||
std::scoped_lock lock{sched.guard};
|
||||
if (thread != sched.selected_thread_set.get()) {
|
||||
if (thread == nullptr) {
|
||||
++sched.idle_selection_count;
|
||||
@@ -57,7 +56,6 @@ u32 GlobalScheduler::SelectThreads() {
|
||||
sched.is_context_switch_pending || (sched.selected_thread_set != sched.current_thread);
|
||||
sched.is_context_switch_pending = reschedule_pending;
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
sched.guard.unlock();
|
||||
return reschedule_pending;
|
||||
};
|
||||
if (!is_reselection_pending.load()) {
|
||||
@@ -757,11 +755,12 @@ void Scheduler::OnSwitch(void* this_scheduler) {
|
||||
|
||||
void Scheduler::SwitchToCurrent() {
|
||||
while (true) {
|
||||
guard.lock();
|
||||
selected_thread = selected_thread_set;
|
||||
current_thread = selected_thread;
|
||||
is_context_switch_pending = false;
|
||||
guard.unlock();
|
||||
{
|
||||
std::scoped_lock lock{guard};
|
||||
selected_thread = selected_thread_set;
|
||||
current_thread = selected_thread;
|
||||
is_context_switch_pending = false;
|
||||
}
|
||||
while (!is_context_switch_pending) {
|
||||
if (current_thread != nullptr && !current_thread->IsHLEThread()) {
|
||||
current_thread->context_guard.lock();
|
||||
|
||||
@@ -776,6 +776,15 @@ void Module::Interface::ListQualifiedUsers(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
|
||||
// TODO(ogniK): Handle open contexts
|
||||
ctx.WriteBuffer(profile_manager->GetOpenUsers());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
// A u8 is passed into this function which we can safely ignore. It's to determine if we have
|
||||
|
||||
@@ -34,6 +34,7 @@ public:
|
||||
void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx);
|
||||
void GetProfileEditor(Kernel::HLERequestContext& ctx);
|
||||
void ListQualifiedUsers(Kernel::HLERequestContext& ctx);
|
||||
void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx);
|
||||
|
||||
private:
|
||||
ResultCode InitializeApplicationInfoBase();
|
||||
|
||||
@@ -20,7 +20,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
|
||||
{6, nullptr, "GetProfileDigest"}, // 3.0.0+
|
||||
{50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
|
||||
{51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
|
||||
{60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0
|
||||
{60, &ACC_SU::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0
|
||||
{99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+
|
||||
{100, nullptr, "GetUserRegistrationNotifier"},
|
||||
{101, nullptr, "GetUserStateChangeNotifier"},
|
||||
|
||||
@@ -20,7 +20,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
|
||||
{6, nullptr, "GetProfileDigest"}, // 3.0.0+
|
||||
{50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
|
||||
{51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
|
||||
{60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0
|
||||
{60, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0
|
||||
{99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+
|
||||
{100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
|
||||
{101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},
|
||||
@@ -30,7 +30,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
|
||||
{111, nullptr, "ClearSaveDataThumbnail"},
|
||||
{120, nullptr, "CreateGuestLoginRequest"},
|
||||
{130, nullptr, "LoadOpenContext"}, // 5.0.0+
|
||||
{131, nullptr, "ListOpenContextStoredUsers"}, // 6.0.0+
|
||||
{131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+
|
||||
{140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+
|
||||
{141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+
|
||||
{150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"}, // 6.0.0+
|
||||
|
||||
@@ -20,7 +20,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
|
||||
{6, nullptr, "GetProfileDigest"}, // 3.0.0+
|
||||
{50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
|
||||
{51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
|
||||
{60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0
|
||||
{60, &ACC_U1::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0
|
||||
{99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+
|
||||
{100, nullptr, "GetUserRegistrationNotifier"},
|
||||
{101, nullptr, "GetUserStateChangeNotifier"},
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
@@ -271,7 +272,7 @@ ISelfController::ISelfController(Core::System& system,
|
||||
{41, nullptr, "IsSystemBufferSharingEnabled"},
|
||||
{42, nullptr, "GetSystemSharedLayerHandle"},
|
||||
{43, nullptr, "GetSystemSharedBufferHandle"},
|
||||
{44, nullptr, "CreateManagedDisplaySeparableLayer"},
|
||||
{44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"},
|
||||
{45, nullptr, "SetManagedDisplayLayerSeparationMode"},
|
||||
{50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
|
||||
{51, nullptr, "ApproveToDisplay"},
|
||||
@@ -461,6 +462,24 @@ void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx)
|
||||
rb.Push(*layer_id);
|
||||
}
|
||||
|
||||
void ISelfController::CreateManagedDisplaySeparableLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
// TODO(Subv): Find out how AM determines the display to use, for now just
|
||||
// create the layer in the Default display.
|
||||
// This calls nn::vi::CreateRecordingLayer() which creates another layer.
|
||||
// Currently we do not support more than 1 layer per display, output 1 layer id for now.
|
||||
// Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
|
||||
// side effects.
|
||||
// TODO: Support multiple layers
|
||||
const auto display_id = nvflinger->OpenDisplay("Default");
|
||||
const auto layer_id = nvflinger->CreateLayer(*display_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(*layer_id);
|
||||
}
|
||||
|
||||
void ISelfController::SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
@@ -730,14 +749,14 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
|
||||
|
||||
if (Settings::values.use_docked_mode) {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
} else {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -841,7 +860,7 @@ public:
|
||||
{110, nullptr, "NeedsToExitProcess"},
|
||||
{120, nullptr, "GetLibraryAppletInfo"},
|
||||
{150, nullptr, "RequestForAppletToGetForeground"},
|
||||
{160, nullptr, "GetIndirectLayerConsumerHandle"},
|
||||
{160, &ILibraryAppletAccessor::GetIndirectLayerConsumerHandle, "GetIndirectLayerConsumerHandle"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -960,6 +979,18 @@ private:
|
||||
rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent());
|
||||
}
|
||||
|
||||
void GetIndirectLayerConsumerHandle(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
// We require a non-zero handle to be valid. Using 0xdeadbeef allows us to trace if this is
|
||||
// actually used anywhere
|
||||
constexpr u64 handle = 0xdeadbeef;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(handle);
|
||||
}
|
||||
|
||||
std::shared_ptr<Applets::Applet> applet;
|
||||
};
|
||||
|
||||
@@ -1341,14 +1372,25 @@ void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
std::array<u8, 0x10> version_string{};
|
||||
|
||||
FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
|
||||
const auto res = pm.GetControlMetadata();
|
||||
const auto res = [this] {
|
||||
const auto title_id = system.CurrentProcess()->GetTitleID();
|
||||
|
||||
FileSys::PatchManager pm{title_id};
|
||||
auto res = pm.GetControlMetadata();
|
||||
if (res.first != nullptr) {
|
||||
return res;
|
||||
}
|
||||
|
||||
FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)};
|
||||
return pm_update.GetControlMetadata();
|
||||
}();
|
||||
|
||||
if (res.first != nullptr) {
|
||||
const auto& version = res.first->GetVersionString();
|
||||
std::copy(version.begin(), version.end(), version_string.begin());
|
||||
} else {
|
||||
constexpr u128 default_version = {1, 0};
|
||||
std::memcpy(version_string.data(), default_version.data(), sizeof(u128));
|
||||
constexpr char default_version[]{"1.0.0"};
|
||||
std::memcpy(version_string.data(), default_version, sizeof(default_version));
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
@@ -1365,7 +1407,19 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
|
||||
u32 supported_languages = 0;
|
||||
FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
|
||||
|
||||
const auto res = pm.GetControlMetadata();
|
||||
const auto res = [this] {
|
||||
const auto title_id = system.CurrentProcess()->GetTitleID();
|
||||
|
||||
FileSys::PatchManager pm{title_id};
|
||||
auto res = pm.GetControlMetadata();
|
||||
if (res.first != nullptr) {
|
||||
return res;
|
||||
}
|
||||
|
||||
FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)};
|
||||
return pm_update.GetControlMetadata();
|
||||
}();
|
||||
|
||||
if (res.first != nullptr) {
|
||||
supported_languages = res.first->GetSupportedLanguages();
|
||||
}
|
||||
|
||||
@@ -140,6 +140,7 @@ private:
|
||||
void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx);
|
||||
void SetAlbumImageOrientation(Kernel::HLERequestContext& ctx);
|
||||
void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx);
|
||||
void CreateManagedDisplaySeparableLayer(Kernel::HLERequestContext& ctx);
|
||||
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
|
||||
void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
|
||||
void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -302,7 +302,7 @@ private:
|
||||
rb.Push<u64>(write_size);
|
||||
}
|
||||
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance();
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
|
||||
@@ -121,11 +121,83 @@ public:
|
||||
{39, nullptr, "PrepareShutdown"},
|
||||
{40, nullptr, "ListApplyDeltaTask"},
|
||||
{41, nullptr, "ClearNotEnoughSpaceStateOfApplyDeltaTask"},
|
||||
{42, nullptr, "Unknown1"},
|
||||
{43, nullptr, "Unknown2"},
|
||||
{44, nullptr, "Unknown3"},
|
||||
{45, nullptr, "Unknown4"},
|
||||
{46, nullptr, "Unknown5"},
|
||||
{42, nullptr, "Unknown42"},
|
||||
{43, nullptr, "Unknown43"},
|
||||
{44, nullptr, "Unknown44"},
|
||||
{45, nullptr, "Unknown45"},
|
||||
{46, nullptr, "Unknown46"},
|
||||
{47, nullptr, "Unknown47"},
|
||||
{48, nullptr, "Unknown48"},
|
||||
{49, nullptr, "Unknown49"},
|
||||
{50, nullptr, "Unknown50"},
|
||||
{51, nullptr, "Unknown51"},
|
||||
{52, nullptr, "Unknown52"},
|
||||
{53, nullptr, "Unknown53"},
|
||||
{54, nullptr, "Unknown54"},
|
||||
{55, nullptr, "Unknown55"},
|
||||
{56, nullptr, "Unknown56"},
|
||||
{57, nullptr, "Unknown57"},
|
||||
{58, nullptr, "Unknown58"},
|
||||
{59, nullptr, "Unknown59"},
|
||||
{60, nullptr, "Unknown60"},
|
||||
{61, nullptr, "Unknown61"},
|
||||
{62, nullptr, "Unknown62"},
|
||||
{63, nullptr, "Unknown63"},
|
||||
{64, nullptr, "Unknown64"},
|
||||
{65, nullptr, "Unknown65"},
|
||||
{66, nullptr, "Unknown66"},
|
||||
{67, nullptr, "Unknown67"},
|
||||
{68, nullptr, "Unknown68"},
|
||||
{69, nullptr, "Unknown69"},
|
||||
{70, nullptr, "Unknown70"},
|
||||
{71, nullptr, "Unknown71"},
|
||||
{72, nullptr, "Unknown72"},
|
||||
{73, nullptr, "Unknown73"},
|
||||
{74, nullptr, "Unknown74"},
|
||||
{75, nullptr, "Unknown75"},
|
||||
{76, nullptr, "Unknown76"},
|
||||
{77, nullptr, "Unknown77"},
|
||||
{78, nullptr, "Unknown78"},
|
||||
{79, nullptr, "Unknown79"},
|
||||
{80, nullptr, "Unknown80"},
|
||||
{81, nullptr, "Unknown81"},
|
||||
{82, nullptr, "Unknown82"},
|
||||
{83, nullptr, "Unknown83"},
|
||||
{84, nullptr, "Unknown84"},
|
||||
{85, nullptr, "Unknown85"},
|
||||
{86, nullptr, "Unknown86"},
|
||||
{87, nullptr, "Unknown87"},
|
||||
{88, nullptr, "Unknown88"},
|
||||
{89, nullptr, "Unknown89"},
|
||||
{90, nullptr, "Unknown90"},
|
||||
{91, nullptr, "Unknown91"},
|
||||
{92, nullptr, "Unknown92"},
|
||||
{93, nullptr, "Unknown93"},
|
||||
{94, nullptr, "Unknown94"},
|
||||
{95, nullptr, "Unknown95"},
|
||||
{96, nullptr, "Unknown96"},
|
||||
{97, nullptr, "Unknown97"},
|
||||
{98, nullptr, "Unknown98"},
|
||||
{99, nullptr, "Unknown99"},
|
||||
{100, nullptr, "Unknown100"},
|
||||
{101, nullptr, "Unknown101"},
|
||||
{102, nullptr, "Unknown102"},
|
||||
{103, nullptr, "Unknown103"},
|
||||
{104, nullptr, "Unknown104"},
|
||||
{105, nullptr, "Unknown105"},
|
||||
{106, nullptr, "Unknown106"},
|
||||
{107, nullptr, "Unknown107"},
|
||||
{108, nullptr, "Unknown108"},
|
||||
{109, nullptr, "Unknown109"},
|
||||
{110, nullptr, "Unknown110"},
|
||||
{111, nullptr, "Unknown111"},
|
||||
{112, nullptr, "Unknown112"},
|
||||
{113, nullptr, "Unknown113"},
|
||||
{114, nullptr, "Unknown114"},
|
||||
{115, nullptr, "Unknown115"},
|
||||
{116, nullptr, "Unknown116"},
|
||||
{117, nullptr, "Unknown117"},
|
||||
{118, nullptr, "Unknown118"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -142,6 +214,7 @@ public:
|
||||
{1, nullptr, "RefreshDebugAvailability"},
|
||||
{2, nullptr, "ClearDebugResponse"},
|
||||
{3, nullptr, "RegisterDebugResponse"},
|
||||
{4, nullptr, "IsLargeResourceAvailable"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -164,6 +237,8 @@ public:
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "RequestDeviceAuthenticationToken"},
|
||||
{1, nullptr, "RequestCachedDeviceAuthenticationToken"},
|
||||
{2, nullptr, "RequestEdgeToken"},
|
||||
{3, nullptr, "RequestCachedEdgeToken"},
|
||||
{100, nullptr, "RequestRegisterDeviceAccount"},
|
||||
{101, nullptr, "RequestUnregisterDeviceAccount"},
|
||||
{102, nullptr, "RequestDeviceAccountStatus"},
|
||||
@@ -181,7 +256,8 @@ public:
|
||||
{305, nullptr, "RequestCreateVirtualAccount"},
|
||||
{306, nullptr, "RequestDeviceLinkStatus"},
|
||||
{400, nullptr, "GetAccountByVirtualAccount"},
|
||||
{500, nullptr, "RequestSyncTicket"},
|
||||
{401, nullptr, "GetVirtualAccount"},
|
||||
{500, nullptr, "RequestSyncTicketLegacy"},
|
||||
{501, nullptr, "RequestDownloadTicket"},
|
||||
{502, nullptr, "RequestDownloadTicketForPrepurchasedContents"},
|
||||
{503, nullptr, "RequestSyncTicket"},
|
||||
|
||||
@@ -30,6 +30,7 @@ public:
|
||||
{23, nullptr, "DestroyToken"},
|
||||
{24, nullptr, "DestroyTokenWithApplicationId"},
|
||||
{25, nullptr, "QueryIsTokenValid"},
|
||||
{26, nullptr, "ListenToMyApplicationId"},
|
||||
{31, nullptr, "UploadTokenToBaaS"},
|
||||
{32, nullptr, "DestroyTokenForBaaS"},
|
||||
{33, nullptr, "CreateTokenForBaaS"},
|
||||
|
||||
@@ -104,7 +104,7 @@ IApplicationManagerInterface::IApplicationManagerInterface()
|
||||
{94, nullptr, "LaunchApplication"},
|
||||
{95, nullptr, "GetApplicationLaunchInfo"},
|
||||
{96, nullptr, "AcquireApplicationLaunchInfo"},
|
||||
{97, nullptr, "GetMainApplicationProgramIndex2"},
|
||||
{97, nullptr, "GetMainApplicationProgramIndexByApplicationLaunchInfo"},
|
||||
{98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
|
||||
{99, nullptr, "LaunchDevMenu"},
|
||||
{100, nullptr, "ResetToFactorySettings"},
|
||||
@@ -254,7 +254,7 @@ IApplicationManagerInterface::IApplicationManagerInterface()
|
||||
{2170, nullptr, "GetRightsEnvironmentStatus"},
|
||||
{2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
|
||||
{2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
|
||||
{2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"},
|
||||
{2181, nullptr, "GetResultOfExtendRightsInRightsEnvironment"},
|
||||
{2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
|
||||
{2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
|
||||
{2199, nullptr, "GetRightsEnvironmentCountForDebug"},
|
||||
@@ -366,7 +366,8 @@ ResultVal<u8> IApplicationManagerInterface::GetApplicationDesiredLanguage(
|
||||
LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages);
|
||||
|
||||
// Get language code from settings
|
||||
const auto language_code = Set::GetLanguageCodeFromIndex(Settings::values.language_index);
|
||||
const auto language_code =
|
||||
Set::GetLanguageCodeFromIndex(Settings::values.language_index.GetValue());
|
||||
|
||||
// Convert to application language, get priority list
|
||||
const auto application_language = ConvertToApplicationLanguage(language_code);
|
||||
@@ -445,8 +446,8 @@ IApplicationVersionInterface::IApplicationVersionInterface()
|
||||
|
||||
IApplicationVersionInterface::~IApplicationVersionInterface() = default;
|
||||
|
||||
IContentManagerInterface::IContentManagerInterface()
|
||||
: ServiceFramework{"IContentManagerInterface"} {
|
||||
IContentManagementInterface::IContentManagementInterface()
|
||||
: ServiceFramework{"IContentManagementInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{11, nullptr, "CalculateApplicationOccupiedSize"},
|
||||
@@ -463,7 +464,7 @@ IContentManagerInterface::IContentManagerInterface()
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IContentManagerInterface::~IContentManagerInterface() = default;
|
||||
IContentManagementInterface::~IContentManagementInterface() = default;
|
||||
|
||||
IDocumentInterface::IDocumentInterface() : ServiceFramework{"IDocumentInterface"} {
|
||||
// clang-format off
|
||||
@@ -545,7 +546,7 @@ NS::NS(const char* name) : ServiceFramework{name} {
|
||||
{7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
|
||||
{7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"},
|
||||
{7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
|
||||
{7998, &NS::PushInterface<IContentManagerInterface>, "GetContentManagementInterface"},
|
||||
{7998, &NS::PushInterface<IContentManagementInterface>, "GetContentManagementInterface"},
|
||||
{7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -572,9 +573,9 @@ public:
|
||||
{6, nullptr, "TerminateApplication"},
|
||||
{7, nullptr, "PrepareLaunchProgramFromHost"},
|
||||
{8, nullptr, "LaunchApplication"},
|
||||
{9, nullptr, "LaunchApplicationWithStorageId"},
|
||||
{10, nullptr, "TerminateApplication2"},
|
||||
{11, nullptr, "GetRunningApplicationProcessId"},
|
||||
{9, nullptr, "LaunchApplicationWithStorageIdForDevelop"},
|
||||
{10, nullptr, "IsSystemMemoryResourceLimitBoosted"},
|
||||
{11, nullptr, "GetRunningApplicationProcessIdForDevelop"},
|
||||
{12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActive"},
|
||||
{13, nullptr, "CreateApplicationResourceForDevelop"},
|
||||
{14, nullptr, "IsPreomiaForDevelop"},
|
||||
@@ -636,6 +637,10 @@ public:
|
||||
{9, nullptr, "GetSystemUpdateNotificationEventForContentDelivery"},
|
||||
{10, nullptr, "NotifySystemUpdateForContentDelivery"},
|
||||
{11, nullptr, "PrepareShutdown"},
|
||||
{12, nullptr, "Unknown12"},
|
||||
{13, nullptr, "Unknown13"},
|
||||
{14, nullptr, "Unknown14"},
|
||||
{15, nullptr, "Unknown15"},
|
||||
{16, nullptr, "DestroySystemUpdateTask"},
|
||||
{17, nullptr, "RequestSendSystemUpdate"},
|
||||
{18, nullptr, "GetSendSystemUpdateProgress"},
|
||||
|
||||
@@ -40,10 +40,10 @@ public:
|
||||
~IApplicationVersionInterface() override;
|
||||
};
|
||||
|
||||
class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> {
|
||||
class IContentManagementInterface final : public ServiceFramework<IContentManagementInterface> {
|
||||
public:
|
||||
explicit IContentManagerInterface();
|
||||
~IContentManagerInterface() override;
|
||||
explicit IContentManagementInterface();
|
||||
~IContentManagementInterface() override;
|
||||
};
|
||||
|
||||
class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
|
||||
|
||||
@@ -163,7 +163,7 @@ PL_U::PL_U(Core::System& system)
|
||||
{5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"},
|
||||
{6, nullptr, "GetSharedFontInOrderOfPriorityForSystem"},
|
||||
{100, nullptr, "RequestApplicationFunctionAuthorization"},
|
||||
{101, nullptr, "RequestApplicationFunctionAuthorizationForSystem"},
|
||||
{101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"},
|
||||
{102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"},
|
||||
{1000, nullptr, "LoadNgWordDataForPlatformRegionChina"},
|
||||
{1001, nullptr, "GetNgWordDataSizeForPlatformRegionChina"},
|
||||
|
||||
@@ -144,7 +144,7 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) {
|
||||
void NVDRV::SetAruid(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
pid = rp.Pop<u64>();
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid=0x{:X}", pid);
|
||||
@@ -154,7 +154,7 @@ void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u32>(0);
|
||||
}
|
||||
|
||||
void NVDRV::FinishInitialize(Kernel::HLERequestContext& ctx) {
|
||||
void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -187,13 +187,14 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
|
||||
{4, &NVDRV::QueryEvent, "QueryEvent"},
|
||||
{5, nullptr, "MapSharedMem"},
|
||||
{6, &NVDRV::GetStatus, "GetStatus"},
|
||||
{7, nullptr, "ForceSetClientPID"},
|
||||
{8, &NVDRV::SetClientPID, "SetClientPID"},
|
||||
{7, nullptr, "SetAruidForTest"},
|
||||
{8, &NVDRV::SetAruid, "SetAruid"},
|
||||
{9, &NVDRV::DumpGraphicsMemoryInfo, "DumpGraphicsMemoryInfo"},
|
||||
{10, nullptr, "InitializeDevtools"},
|
||||
{11, &NVDRV::Ioctl2, "Ioctl2"},
|
||||
{12, &NVDRV::Ioctl3, "Ioctl3"},
|
||||
{13, &NVDRV::FinishInitialize, "FinishInitialize"},
|
||||
{13, &NVDRV::SetGraphicsFirmwareMemoryMarginEnabled,
|
||||
"SetGraphicsFirmwareMemoryMarginEnabled"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ private:
|
||||
void Close(Kernel::HLERequestContext& ctx);
|
||||
void Initialize(Kernel::HLERequestContext& ctx);
|
||||
void QueryEvent(Kernel::HLERequestContext& ctx);
|
||||
void SetClientPID(Kernel::HLERequestContext& ctx);
|
||||
void FinishInitialize(Kernel::HLERequestContext& ctx);
|
||||
void SetAruid(Kernel::HLERequestContext& ctx);
|
||||
void SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx);
|
||||
void GetStatus(Kernel::HLERequestContext& ctx);
|
||||
void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx);
|
||||
void IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version);
|
||||
|
||||
@@ -10,19 +10,19 @@ namespace Service::Nvidia {
|
||||
|
||||
NVMEMP::NVMEMP() : ServiceFramework("nvmemp") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &NVMEMP::Cmd0, "Cmd0"},
|
||||
{1, &NVMEMP::Cmd1, "Cmd1"},
|
||||
{0, &NVMEMP::Open, "Open"},
|
||||
{1, &NVMEMP::GetAruid, "GetAruid"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
NVMEMP::~NVMEMP() = default;
|
||||
|
||||
void NVMEMP::Cmd0(Kernel::HLERequestContext& ctx) {
|
||||
void NVMEMP::Open(Kernel::HLERequestContext& ctx) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void NVMEMP::Cmd1(Kernel::HLERequestContext& ctx) {
|
||||
void NVMEMP::GetAruid(Kernel::HLERequestContext& ctx) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ public:
|
||||
~NVMEMP() override;
|
||||
|
||||
private:
|
||||
void Cmd0(Kernel::HLERequestContext& ctx);
|
||||
void Cmd1(Kernel::HLERequestContext& ctx);
|
||||
void Open(Kernel::HLERequestContext& ctx);
|
||||
void GetAruid(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia
|
||||
|
||||
@@ -36,6 +36,9 @@ public:
|
||||
{18, nullptr, "ReleaseIrq"},
|
||||
{19, nullptr, "SetIrqEnable"},
|
||||
{20, nullptr, "SetAspmEnable"},
|
||||
{21, nullptr, "SetResetUponResumeEnable"},
|
||||
{22, nullptr, "Unknown22"},
|
||||
{23, nullptr, "Unknown23"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -42,6 +42,9 @@ public:
|
||||
{24, nullptr, "GetModuleStateTable"},
|
||||
{25, nullptr, "GetPowerDomainStateTable"},
|
||||
{26, nullptr, "GetFuseInfo"},
|
||||
{27, nullptr, "GetDramId"},
|
||||
{28, nullptr, "IsPoweredOn"},
|
||||
{29, nullptr, "GetVoltage"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -78,13 +78,13 @@ public:
|
||||
: ServiceFramework{"pm:dmnt"}, kernel(kernel) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetDebugProcesses"},
|
||||
{1, nullptr, "StartDebugProcess"},
|
||||
{2, &DebugMonitor::GetTitlePid, "GetTitlePid"},
|
||||
{3, nullptr, "EnableDebugForTitleId"},
|
||||
{4, &DebugMonitor::GetApplicationPid, "GetApplicationPid"},
|
||||
{5, nullptr, "EnableDebugForApplication"},
|
||||
{6, nullptr, "DisableDebug"},
|
||||
{0, nullptr, "GetJitDebugProcessIdList"},
|
||||
{1, nullptr, "StartProcess"},
|
||||
{2, &DebugMonitor::GetProcessId, "GetProcessId"},
|
||||
{3, nullptr, "HookToCreateProcess"},
|
||||
{4, &DebugMonitor::GetApplicationProcessId, "GetApplicationProcessId"},
|
||||
{5, nullptr, "HookToCreateApplicationProgress"},
|
||||
{6, nullptr, "ClearHook"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -92,7 +92,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void GetTitlePid(Kernel::HLERequestContext& ctx) {
|
||||
void GetProcessId(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
@@ -114,7 +114,7 @@ private:
|
||||
rb.Push((*process)->GetProcessID());
|
||||
}
|
||||
|
||||
void GetApplicationPid(Kernel::HLERequestContext& ctx) {
|
||||
void GetApplicationProcessId(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_PM, "called");
|
||||
GetApplicationPidGeneric(ctx, kernel.GetProcessList());
|
||||
}
|
||||
@@ -163,15 +163,15 @@ public:
|
||||
: ServiceFramework{"pm:shell"}, kernel(kernel) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "LaunchProcess"},
|
||||
{1, nullptr, "TerminateProcessByPid"},
|
||||
{2, nullptr, "TerminateProcessByTitleId"},
|
||||
{3, nullptr, "GetProcessEventWaiter"},
|
||||
{4, nullptr, "GetProcessEventType"},
|
||||
{0, nullptr, "LaunchProgram"},
|
||||
{1, nullptr, "TerminateProcess"},
|
||||
{2, nullptr, "TerminateProgram"},
|
||||
{3, nullptr, "GetProcessEventHandle"},
|
||||
{4, nullptr, "GetProcessEventInfo"},
|
||||
{5, nullptr, "NotifyBootFinished"},
|
||||
{6, &Shell::GetApplicationPid, "GetApplicationPid"},
|
||||
{6, &Shell::GetApplicationProcessIdForShell, "GetApplicationProcessIdForShell"},
|
||||
{7, nullptr, "BoostSystemMemoryResourceLimit"},
|
||||
{8, nullptr, "EnableAdditionalSystemThreads"},
|
||||
{8, nullptr, "BoostApplicationThreadResourceLimit"},
|
||||
{9, nullptr, "GetBootFinishedEventHandle"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -180,7 +180,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void GetApplicationPid(Kernel::HLERequestContext& ctx) {
|
||||
void GetApplicationProcessIdForShell(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_PM, "called");
|
||||
GetApplicationPidGeneric(ctx, kernel.GetProcessList());
|
||||
}
|
||||
|
||||
@@ -42,6 +42,11 @@ public:
|
||||
{40101, nullptr, "SetUserAgreementCheckEnabled"},
|
||||
{50100, nullptr, "ReadAllApplicationReportFiles"},
|
||||
{90100, nullptr, "ReadAllReportFiles"},
|
||||
{90101, nullptr, "Unknown90101"},
|
||||
{90102, nullptr, "Unknown90102"},
|
||||
{90200, nullptr, "GetStatistics"},
|
||||
{90201, nullptr, "GetThroughputHistory"},
|
||||
{90300, nullptr, "GetLastUploadError"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ public:
|
||||
{4, nullptr, "Cancel"},
|
||||
{5, nullptr, "PrintModuleInformation"},
|
||||
{6, nullptr, "GetModuleInformation"},
|
||||
{10, nullptr, "Unknown10"},
|
||||
{11, nullptr, "Unknown11"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ public:
|
||||
{15, nullptr, "GetBatteryAgePercentage"},
|
||||
{16, nullptr, "GetBatteryChargeInfoEvent"},
|
||||
{17, nullptr, "GetBatteryChargeInfoFields"},
|
||||
{18, nullptr, "GetBatteryChargeCalibratedEvent"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
@@ -31,6 +32,44 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{
|
||||
LanguageCode::ZH_HANT,
|
||||
}};
|
||||
|
||||
enum class KeyboardLayout : u64 {
|
||||
Japanese = 0,
|
||||
EnglishUs = 1,
|
||||
EnglishUsInternational = 2,
|
||||
EnglishUk = 3,
|
||||
French = 4,
|
||||
FrenchCa = 5,
|
||||
Spanish = 6,
|
||||
SpanishLatin = 7,
|
||||
German = 8,
|
||||
Italian = 9,
|
||||
Portuguese = 10,
|
||||
Russian = 11,
|
||||
Korean = 12,
|
||||
ChineseSimplified = 13,
|
||||
ChineseTraditional = 14,
|
||||
};
|
||||
|
||||
constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 17> language_to_layout{{
|
||||
{LanguageCode::JA, KeyboardLayout::Japanese},
|
||||
{LanguageCode::EN_US, KeyboardLayout::EnglishUs},
|
||||
{LanguageCode::FR, KeyboardLayout::French},
|
||||
{LanguageCode::DE, KeyboardLayout::German},
|
||||
{LanguageCode::IT, KeyboardLayout::Italian},
|
||||
{LanguageCode::ES, KeyboardLayout::Spanish},
|
||||
{LanguageCode::ZH_CN, KeyboardLayout::ChineseSimplified},
|
||||
{LanguageCode::KO, KeyboardLayout::Korean},
|
||||
{LanguageCode::NL, KeyboardLayout::EnglishUsInternational},
|
||||
{LanguageCode::PT, KeyboardLayout::Portuguese},
|
||||
{LanguageCode::RU, KeyboardLayout::Russian},
|
||||
{LanguageCode::ZH_TW, KeyboardLayout::ChineseTraditional},
|
||||
{LanguageCode::EN_GB, KeyboardLayout::EnglishUk},
|
||||
{LanguageCode::FR_CA, KeyboardLayout::FrenchCa},
|
||||
{LanguageCode::ES_419, KeyboardLayout::SpanishLatin},
|
||||
{LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified},
|
||||
{LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional},
|
||||
}};
|
||||
|
||||
constexpr std::size_t pre4_0_0_max_entries = 15;
|
||||
constexpr std::size_t post4_0_0_max_entries = 17;
|
||||
|
||||
@@ -50,6 +89,25 @@ void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t m
|
||||
ctx.WriteBuffer(available_language_codes.data(), copy_size);
|
||||
PushResponseLanguageCode(ctx, copy_amount);
|
||||
}
|
||||
|
||||
void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) {
|
||||
const auto language_code = available_language_codes[Settings::values.language_index.GetValue()];
|
||||
const auto key_code =
|
||||
std::find_if(language_to_layout.cbegin(), language_to_layout.cend(),
|
||||
[=](const auto& element) { return element.first == language_code; });
|
||||
KeyboardLayout layout = KeyboardLayout::EnglishUs;
|
||||
if (key_code == language_to_layout.cend()) {
|
||||
LOG_ERROR(Service_SET,
|
||||
"Could not find keyboard layout for language index {}, defaulting to English us",
|
||||
Settings::values.language_index.GetValue());
|
||||
} else {
|
||||
layout = key_code->second;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
ctx.WriteBuffer(&layout, sizeof(KeyboardLayout));
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
|
||||
@@ -105,11 +163,11 @@ void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index);
|
||||
LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index.GetValue());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushEnum(available_language_codes[Settings::values.language_index]);
|
||||
rb.PushEnum(available_language_codes[Settings::values.language_index.GetValue()]);
|
||||
}
|
||||
|
||||
void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
|
||||
@@ -117,7 +175,17 @@ void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(Settings::values.region_index);
|
||||
rb.Push(Settings::values.region_index.GetValue());
|
||||
}
|
||||
|
||||
void SET::GetKeyCodeMap(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "Called {}", ctx.Description());
|
||||
GetKeyCodeMapImpl(ctx);
|
||||
}
|
||||
|
||||
void SET::GetKeyCodeMap2(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "Called {}", ctx.Description());
|
||||
GetKeyCodeMapImpl(ctx);
|
||||
}
|
||||
|
||||
SET::SET() : ServiceFramework("set") {
|
||||
@@ -130,9 +198,9 @@ SET::SET() : ServiceFramework("set") {
|
||||
{4, &SET::GetRegionCode, "GetRegionCode"},
|
||||
{5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"},
|
||||
{6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"},
|
||||
{7, nullptr, "GetKeyCodeMap"},
|
||||
{7, &SET::GetKeyCodeMap, "GetKeyCodeMap"},
|
||||
{8, &SET::GetQuestFlag, "GetQuestFlag"},
|
||||
{9, nullptr, "GetKeyCodeMap2"},
|
||||
{9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"},
|
||||
{10, nullptr, "GetFirmwareVersionForDebug"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -44,6 +44,8 @@ private:
|
||||
void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx);
|
||||
void GetQuestFlag(Kernel::HLERequestContext& ctx);
|
||||
void GetRegionCode(Kernel::HLERequestContext& ctx);
|
||||
void GetKeyCodeMap(Kernel::HLERequestContext& ctx);
|
||||
void GetKeyCodeMap2(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
} // namespace Service::Set
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
namespace Service::SM {
|
||||
|
||||
void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) {
|
||||
void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) {
|
||||
ASSERT_MSG(ctx.Session()->IsSession(), "Session is already a domain");
|
||||
LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetObjectId());
|
||||
ctx.Session()->ConvertToDomain();
|
||||
@@ -22,7 +22,7 @@ void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u32>(1); // Converted sessions start with 1 request handler
|
||||
}
|
||||
|
||||
void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
|
||||
void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) {
|
||||
// TODO(bunnei): This is just creating a new handle to the same Session. I assume this is wrong
|
||||
// and that we probably want to actually make an entirely new Session, but we still need to
|
||||
// verify this on hardware.
|
||||
@@ -33,10 +33,10 @@ void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
|
||||
rb.PushMoveObjects(ctx.Session()->GetParent()->Client());
|
||||
}
|
||||
|
||||
void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service, "(STUBBED) called, using DuplicateSession");
|
||||
void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service, "(STUBBED) called, using CloneCurrentObject");
|
||||
|
||||
DuplicateSession(ctx);
|
||||
CloneCurrentObject(ctx);
|
||||
}
|
||||
|
||||
void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
@@ -47,13 +47,14 @@ void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u16>(0x1000);
|
||||
}
|
||||
|
||||
// https://switchbrew.org/wiki/IPC_Marshalling
|
||||
Controller::Controller() : ServiceFramework("IpcController") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x00000000, &Controller::ConvertSessionToDomain, "ConvertSessionToDomain"},
|
||||
{0x00000001, nullptr, "ConvertDomainToSession"},
|
||||
{0x00000002, &Controller::DuplicateSession, "DuplicateSession"},
|
||||
{0x00000003, &Controller::QueryPointerBufferSize, "QueryPointerBufferSize"},
|
||||
{0x00000004, &Controller::DuplicateSessionEx, "DuplicateSessionEx"},
|
||||
{0, &Controller::ConvertCurrentObjectToDomain, "ConvertCurrentObjectToDomain"},
|
||||
{1, nullptr, "CopyFromCurrentDomain"},
|
||||
{2, &Controller::CloneCurrentObject, "CloneCurrentObject"},
|
||||
{3, &Controller::QueryPointerBufferSize, "QueryPointerBufferSize"},
|
||||
{4, &Controller::CloneCurrentObjectEx, "CloneCurrentObjectEx"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ public:
|
||||
~Controller() override;
|
||||
|
||||
private:
|
||||
void ConvertSessionToDomain(Kernel::HLERequestContext& ctx);
|
||||
void DuplicateSession(Kernel::HLERequestContext& ctx);
|
||||
void DuplicateSessionEx(Kernel::HLERequestContext& ctx);
|
||||
void ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx);
|
||||
void CloneCurrentObject(Kernel::HLERequestContext& ctx);
|
||||
void CloneCurrentObjectEx(Kernel::HLERequestContext& ctx);
|
||||
void QueryPointerBufferSize(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ NSD::NSD(const char* name) : ServiceFramework(name) {
|
||||
{12, nullptr, "GetDeviceId"},
|
||||
{13, nullptr, "DeleteSettings"},
|
||||
{14, nullptr, "ImportSettings"},
|
||||
{15, nullptr, "SetChangeEnvironmentIdentifierDisabled"},
|
||||
{20, nullptr, "Resolve"},
|
||||
{21, nullptr, "ResolveEx"},
|
||||
{30, nullptr, "GetNasServiceSetting"},
|
||||
@@ -28,6 +29,11 @@ NSD::NSD(const char* name) : ServiceFramework(name) {
|
||||
{60, nullptr, "ReadSaveDataFromFsForTest"},
|
||||
{61, nullptr, "WriteSaveDataToFsForTest"},
|
||||
{62, nullptr, "DeleteSaveDataOfFsForTest"},
|
||||
{63, nullptr, "IsChangeEnvironmentIdentifierDisabled"},
|
||||
{64, nullptr, "SetWithoutDomainExchangeFqdns"},
|
||||
{100, nullptr, "GetApplicationServerEnvironmentType"},
|
||||
{101, nullptr, "SetApplicationServerEnvironmentType"},
|
||||
{102, nullptr, "DeleteApplicationServerEnvironmentType"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
namespace Service::Sockets {
|
||||
|
||||
void SFDNSRES::GetAddrInfo(Kernel::HLERequestContext& ctx) {
|
||||
void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
|
||||
struct Parameters {
|
||||
u8 use_nsd_resolve;
|
||||
u32 unknown;
|
||||
@@ -29,15 +29,20 @@ SFDNSRES::SFDNSRES() : ServiceFramework("sfdnsres") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "SetDnsAddressesPrivate"},
|
||||
{1, nullptr, "GetDnsAddressPrivate"},
|
||||
{2, nullptr, "GetHostByName"},
|
||||
{3, nullptr, "GetHostByAddr"},
|
||||
{4, nullptr, "GetHostStringError"},
|
||||
{5, nullptr, "GetGaiStringError"},
|
||||
{6, &SFDNSRES::GetAddrInfo, "GetAddrInfo"},
|
||||
{7, nullptr, "GetNameInfo"},
|
||||
{8, nullptr, "RequestCancelHandle"},
|
||||
{9, nullptr, "CancelSocketCall"},
|
||||
{11, nullptr, "ClearDnsIpServerAddressArray"},
|
||||
{2, nullptr, "GetHostByNameRequest"},
|
||||
{3, nullptr, "GetHostByAddrRequest"},
|
||||
{4, nullptr, "GetHostStringErrorRequest"},
|
||||
{5, nullptr, "GetGaiStringErrorRequest"},
|
||||
{6, &SFDNSRES::GetAddrInfoRequest, "GetAddrInfoRequest"},
|
||||
{7, nullptr, "GetNameInfoRequest"},
|
||||
{8, nullptr, "RequestCancelHandleRequest"},
|
||||
{9, nullptr, "CancelRequest"},
|
||||
{10, nullptr, "GetHostByNameRequestWithOptions"},
|
||||
{11, nullptr, "GetHostByAddrRequestWithOptions"},
|
||||
{12, nullptr, "GetAddrInfoRequestWithOptions"},
|
||||
{13, nullptr, "GetNameInfoRequestWithOptions"},
|
||||
{14, nullptr, "ResolverSetOptionRequest"},
|
||||
{15, nullptr, "ResolverGetOptionRequest"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ public:
|
||||
~SFDNSRES() override;
|
||||
|
||||
private:
|
||||
void GetAddrInfo(Kernel::HLERequestContext& ctx);
|
||||
void GetAddrInfoRequest(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
} // namespace Service::Sockets
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Service::SPL {
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)),
|
||||
rng(Settings::values.rng_seed.value_or(std::time(nullptr))) {}
|
||||
rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr))) {}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
|
||||
@@ -9,35 +9,36 @@ namespace Service::SPL {
|
||||
SPL::SPL(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "spl:") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetConfig"},
|
||||
{1, nullptr, "UserExpMod"},
|
||||
{1, nullptr, "ModularExponentiate"},
|
||||
{2, nullptr, "GenerateAesKek"},
|
||||
{3, nullptr, "LoadAesKey"},
|
||||
{4, nullptr, "GenerateAesKey"},
|
||||
{5, nullptr, "SetConfig"},
|
||||
{7, &SPL::GetRandomBytes, "GetRandomBytes"},
|
||||
{9, nullptr, "LoadSecureExpModKey"},
|
||||
{10, nullptr, "SecureExpMod"},
|
||||
{9, nullptr, "ImportLotusKey"},
|
||||
{10, nullptr, "DecryptLotusMessage"},
|
||||
{11, nullptr, "IsDevelopment"},
|
||||
{12, nullptr, "GenerateSpecificAesKey"},
|
||||
{13, nullptr, "DecryptPrivk"},
|
||||
{13, nullptr, "DecryptDeviceUniqueData"},
|
||||
{14, nullptr, "DecryptAesKey"},
|
||||
{15, nullptr, "DecryptAesCtr"},
|
||||
{15, nullptr, "CryptAesCtr"},
|
||||
{16, nullptr, "ComputeCmac"},
|
||||
{17, nullptr, "LoadRsaOaepKey"},
|
||||
{18, nullptr, "UnwrapRsaOaepWrappedTitleKey"},
|
||||
{17, nullptr, "ImportEsKey"},
|
||||
{18, nullptr, "UnwrapTitleKey"},
|
||||
{19, nullptr, "LoadTitleKey"},
|
||||
{20, nullptr, "UnwrapAesWrappedTitleKey"},
|
||||
{21, nullptr, "LockAesEngine"},
|
||||
{22, nullptr, "UnlockAesEngine"},
|
||||
{23, nullptr, "GetSplWaitEvent"},
|
||||
{24, nullptr, "SetSharedData"},
|
||||
{25, nullptr, "GetSharedData"},
|
||||
{26, nullptr, "ImportSslRsaKey"},
|
||||
{27, nullptr, "SecureExpModWithSslKey"},
|
||||
{28, nullptr, "ImportEsRsaKey"},
|
||||
{29, nullptr, "SecureExpModWithEsKey"},
|
||||
{30, nullptr, "EncryptManuRsaKeyForImport"},
|
||||
{31, nullptr, "GetPackage2Hash"},
|
||||
{20, nullptr, "PrepareEsCommonKey"},
|
||||
{21, nullptr, "AllocateAesKeyslot"},
|
||||
{22, nullptr, "DeallocateAesKeySlot"},
|
||||
{23, nullptr, "GetAesKeyslotAvailableEvent"},
|
||||
{24, nullptr, "SetBootReason"},
|
||||
{25, nullptr, "GetBootReason"},
|
||||
{26, nullptr, "DecryptAndStoreSslClientCertKey"},
|
||||
{27, nullptr, "ModularExponentiateWithSslClientCertKey"},
|
||||
{28, nullptr, "DecryptAndStoreDrmDeviceCertKey"},
|
||||
{29, nullptr, "ModularExponentiateWithDrmDeviceCertKey"},
|
||||
{30, nullptr, "ReencryptDeviceUniqueData "},
|
||||
{31, nullptr, "PrepareEsArchiveKey"}, // This is also GetPackage2Hash?
|
||||
{32, nullptr, "LoadPreparedAesKey"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -90,6 +90,13 @@ public:
|
||||
: ServiceFramework("ISteadyClock"), clock_core{clock_core}, system{system} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"},
|
||||
{2, nullptr, "GetTestOffset"},
|
||||
{3, nullptr, "SetTestOffset"},
|
||||
{100, nullptr, "GetRtcValue"},
|
||||
{101, nullptr, "IsRtcResetDetected"},
|
||||
{102, nullptr, "GetSetupResultValue"},
|
||||
{200, nullptr, "GetInternalOffset"},
|
||||
{201, nullptr, "SetInternalOffset"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetDsEndpoint"},
|
||||
{1, nullptr, "GetSetupEvent"},
|
||||
{2, nullptr, "Unknown"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "EnableInterface"},
|
||||
{4, nullptr, "DisableInterface"},
|
||||
{5, nullptr, "CtrlInPostBufferAsync"},
|
||||
@@ -55,6 +55,7 @@ public:
|
||||
{9, nullptr, "SetBinaryObjectStore"},
|
||||
{10, nullptr, "Enable"},
|
||||
{11, nullptr, "Disable"},
|
||||
{12, nullptr, "Unknown12"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -69,13 +70,13 @@ public:
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Open"},
|
||||
{1, nullptr, "Close"},
|
||||
{2, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Populate"},
|
||||
{4, nullptr, "PostBufferAsync"},
|
||||
{5, nullptr, "GetXferReport"},
|
||||
{6, nullptr, "PostBufferMultiAsync"},
|
||||
{7, nullptr, "Unknown3"},
|
||||
{8, nullptr, "Unknown4"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -88,13 +89,13 @@ public:
|
||||
explicit IClientIfSession() : ServiceFramework{"IClientIfSession"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{0, nullptr, "Unknown0"},
|
||||
{1, nullptr, "SetInterface"},
|
||||
{2, nullptr, "GetInterface"},
|
||||
{3, nullptr, "GetAlternateInterface"},
|
||||
{4, nullptr, "GetCurrentFrame"},
|
||||
{5, nullptr, "CtrlXferAsync"},
|
||||
{6, nullptr, "Unknown2"},
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "GetCtrlXferReport"},
|
||||
{8, nullptr, "ResetDevice"},
|
||||
{9, nullptr, "OpenUsbEp"},
|
||||
@@ -118,7 +119,7 @@ public:
|
||||
{5, nullptr, "DestroyInterfaceAvailableEvent"},
|
||||
{6, nullptr, "GetInterfaceStateChangeEvent"},
|
||||
{7, nullptr, "AcquireUsbIf"},
|
||||
{8, nullptr, "Unknown1"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -179,8 +180,8 @@ public:
|
||||
{4, nullptr, "GetFwRevision"},
|
||||
{5, nullptr, "GetManufacturerId"},
|
||||
{6, nullptr, "GetDeviceId"},
|
||||
{7, nullptr, "Unknown1"},
|
||||
{8, nullptr, "Unknown2"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -215,12 +216,12 @@ public:
|
||||
explicit USB_PM() : ServiceFramework{"usb:pm"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{1, nullptr, "Unknown2"},
|
||||
{2, nullptr, "Unknown3"},
|
||||
{3, nullptr, "Unknown4"},
|
||||
{4, nullptr, "Unknown5"},
|
||||
{5, nullptr, "Unknown6"},
|
||||
{0, nullptr, "Unknown0"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -519,9 +519,9 @@ private:
|
||||
IGBPConnectRequestParcel request{ctx.ReadBuffer()};
|
||||
IGBPConnectResponseParcel response{
|
||||
static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedWidth) *
|
||||
Settings::values.resolution_factor),
|
||||
Settings::values.resolution_factor.GetValue()),
|
||||
static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *
|
||||
Settings::values.resolution_factor)};
|
||||
Settings::values.resolution_factor.GetValue())};
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
break;
|
||||
}
|
||||
@@ -700,6 +700,7 @@ public:
|
||||
{3215, nullptr, "SetDisplayGamma"},
|
||||
{3216, nullptr, "GetDisplayCmuLuma"},
|
||||
{3217, nullptr, "SetDisplayCmuLuma"},
|
||||
{6013, nullptr, "GetLayerPresentationSubmissionTimestamps"},
|
||||
{8225, nullptr, "GetSharedBufferMemoryHandleId"},
|
||||
{8250, nullptr, "OpenSharedLayer"},
|
||||
{8251, nullptr, "CloseSharedLayer"},
|
||||
@@ -748,14 +749,14 @@ private:
|
||||
|
||||
if (Settings::values.use_docked_mode) {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
} else {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
}
|
||||
|
||||
rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games.
|
||||
@@ -785,6 +786,7 @@ public:
|
||||
{2300, nullptr, "AcquireLayerTexturePresentingEvent"},
|
||||
{2301, nullptr, "ReleaseLayerTexturePresentingEvent"},
|
||||
{2302, nullptr, "GetDisplayHotplugEvent"},
|
||||
{2303, nullptr, "GetDisplayModeChangedEvent"},
|
||||
{2402, nullptr, "GetDisplayHotplugState"},
|
||||
{2501, nullptr, "GetCompositorErrorInfo"},
|
||||
{2601, nullptr, "GetDisplayErrorEvent"},
|
||||
@@ -1029,9 +1031,9 @@ private:
|
||||
// between docked and undocked dimensions. We take the liberty of applying
|
||||
// the resolution scaling factor here.
|
||||
rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
}
|
||||
|
||||
void SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
|
||||
@@ -1064,8 +1066,8 @@ private:
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
DisplayInfo display_info;
|
||||
display_info.width *= static_cast<u64>(Settings::values.resolution_factor);
|
||||
display_info.height *= static_cast<u64>(Settings::values.resolution_factor);
|
||||
display_info.width *= static_cast<u64>(Settings::values.resolution_factor.GetValue());
|
||||
display_info.height *= static_cast<u64>(Settings::values.resolution_factor.GetValue());
|
||||
ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
@@ -12,6 +12,7 @@ VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework{"vi:u"}, nv_flinger{std::move(nv_flinger)} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &VI_U::GetDisplayService, "GetDisplayService"},
|
||||
{1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -15,34 +15,37 @@ public:
|
||||
explicit WLANInfra() : ServiceFramework{"wlan:inf"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{1, nullptr, "Unknown2"},
|
||||
{0, nullptr, "OpenMode"},
|
||||
{1, nullptr, "CloseMode"},
|
||||
{2, nullptr, "GetMacAddress"},
|
||||
{3, nullptr, "StartScan"},
|
||||
{4, nullptr, "StopScan"},
|
||||
{5, nullptr, "Connect"},
|
||||
{6, nullptr, "CancelConnect"},
|
||||
{7, nullptr, "Disconnect"},
|
||||
{8, nullptr, "Unknown3"},
|
||||
{9, nullptr, "Unknown4"},
|
||||
{8, nullptr, "GetConnectionEvent"},
|
||||
{9, nullptr, "GetConnectionStatus"},
|
||||
{10, nullptr, "GetState"},
|
||||
{11, nullptr, "GetScanResult"},
|
||||
{12, nullptr, "GetRssi"},
|
||||
{13, nullptr, "ChangeRxAntenna"},
|
||||
{14, nullptr, "Unknown5"},
|
||||
{15, nullptr, "Unknown6"},
|
||||
{14, nullptr, "GetFwVersion"},
|
||||
{15, nullptr, "RequestSleep"},
|
||||
{16, nullptr, "RequestWakeUp"},
|
||||
{17, nullptr, "RequestIfUpDown"},
|
||||
{18, nullptr, "Unknown7"},
|
||||
{19, nullptr, "Unknown8"},
|
||||
{20, nullptr, "Unknown9"},
|
||||
{21, nullptr, "Unknown10"},
|
||||
{22, nullptr, "Unknown11"},
|
||||
{23, nullptr, "Unknown12"},
|
||||
{24, nullptr, "Unknown13"},
|
||||
{25, nullptr, "Unknown14"},
|
||||
{26, nullptr, "Unknown15"},
|
||||
{27, nullptr, "Unknown16"},
|
||||
{18, nullptr, "Unknown18"},
|
||||
{19, nullptr, "Unknown19"},
|
||||
{20, nullptr, "Unknown20"},
|
||||
{21, nullptr, "Unknown21"},
|
||||
{22, nullptr, "Unknown22"},
|
||||
{23, nullptr, "Unknown23"},
|
||||
{24, nullptr, "Unknown24"},
|
||||
{25, nullptr, "Unknown25"},
|
||||
{26, nullptr, "Unknown26"},
|
||||
{27, nullptr, "Unknown27"},
|
||||
{28, nullptr, "Unknown28"},
|
||||
{29, nullptr, "Unknown29"},
|
||||
{30, nullptr, "Unknown30"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -55,12 +58,12 @@ public:
|
||||
explicit WLANLocal() : ServiceFramework{"wlan:lcl"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{1, nullptr, "Unknown2"},
|
||||
{2, nullptr, "Unknown3"},
|
||||
{3, nullptr, "Unknown4"},
|
||||
{4, nullptr, "Unknown5"},
|
||||
{5, nullptr, "Unknown6"},
|
||||
{0, nullptr, "Unknown0"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "GetMacAddress"},
|
||||
{7, nullptr, "CreateBss"},
|
||||
{8, nullptr, "DestroyBss"},
|
||||
@@ -72,38 +75,42 @@ public:
|
||||
{14, nullptr, "CancelJoin"},
|
||||
{15, nullptr, "Disconnect"},
|
||||
{16, nullptr, "SetBeaconLostCount"},
|
||||
{17, nullptr, "Unknown7"},
|
||||
{18, nullptr, "Unknown8"},
|
||||
{19, nullptr, "Unknown9"},
|
||||
{17, nullptr, "Unknown17"},
|
||||
{18, nullptr, "Unknown18"},
|
||||
{19, nullptr, "Unknown19"},
|
||||
{20, nullptr, "GetBssIndicationEvent"},
|
||||
{21, nullptr, "GetBssIndicationInfo"},
|
||||
{22, nullptr, "GetState"},
|
||||
{23, nullptr, "GetAllowedChannels"},
|
||||
{24, nullptr, "AddIe"},
|
||||
{25, nullptr, "DeleteIe"},
|
||||
{26, nullptr, "Unknown10"},
|
||||
{27, nullptr, "Unknown11"},
|
||||
{26, nullptr, "Unknown26"},
|
||||
{27, nullptr, "Unknown27"},
|
||||
{28, nullptr, "CreateRxEntry"},
|
||||
{29, nullptr, "DeleteRxEntry"},
|
||||
{30, nullptr, "Unknown12"},
|
||||
{31, nullptr, "Unknown13"},
|
||||
{30, nullptr, "Unknown30"},
|
||||
{31, nullptr, "Unknown31"},
|
||||
{32, nullptr, "AddMatchingDataToRxEntry"},
|
||||
{33, nullptr, "RemoveMatchingDataFromRxEntry"},
|
||||
{34, nullptr, "GetScanResult"},
|
||||
{35, nullptr, "Unknown14"},
|
||||
{35, nullptr, "Unknown35"},
|
||||
{36, nullptr, "SetActionFrameWithBeacon"},
|
||||
{37, nullptr, "CancelActionFrameWithBeacon"},
|
||||
{38, nullptr, "CreateRxEntryForActionFrame"},
|
||||
{39, nullptr, "DeleteRxEntryForActionFrame"},
|
||||
{40, nullptr, "Unknown15"},
|
||||
{41, nullptr, "Unknown16"},
|
||||
{40, nullptr, "Unknown40"},
|
||||
{41, nullptr, "Unknown41"},
|
||||
{42, nullptr, "CancelGetActionFrame"},
|
||||
{43, nullptr, "GetRssi"},
|
||||
{44, nullptr, "Unknown17"},
|
||||
{45, nullptr, "Unknown18"},
|
||||
{46, nullptr, "Unknown19"},
|
||||
{47, nullptr, "Unknown20"},
|
||||
{48, nullptr, "Unknown21"},
|
||||
{44, nullptr, "Unknown44"},
|
||||
{45, nullptr, "Unknown45"},
|
||||
{46, nullptr, "Unknown46"},
|
||||
{47, nullptr, "Unknown47"},
|
||||
{48, nullptr, "Unknown48"},
|
||||
{49, nullptr, "Unknown49"},
|
||||
{50, nullptr, "Unknown50"},
|
||||
{51, nullptr, "Unknown51"},
|
||||
{52, nullptr, "Unknown52"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -142,18 +149,19 @@ public:
|
||||
explicit WLANSocketManager() : ServiceFramework{"wlan:soc"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{1, nullptr, "Unknown2"},
|
||||
{2, nullptr, "Unknown3"},
|
||||
{3, nullptr, "Unknown4"},
|
||||
{4, nullptr, "Unknown5"},
|
||||
{5, nullptr, "Unknown6"},
|
||||
{0, nullptr, "Unknown0"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "GetMacAddress"},
|
||||
{7, nullptr, "SwitchTsfTimerFunction"},
|
||||
{8, nullptr, "Unknown7"},
|
||||
{9, nullptr, "Unknown8"},
|
||||
{10, nullptr, "Unknown9"},
|
||||
{11, nullptr, "Unknown10"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
{9, nullptr, "Unknown9"},
|
||||
{10, nullptr, "Unknown10"},
|
||||
{11, nullptr, "Unknown11"},
|
||||
{12, nullptr, "Unknown12"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -548,9 +548,9 @@ struct Memory::Impl {
|
||||
// longer exist, and we should just leave the pagetable entry blank.
|
||||
page_type = Common::PageType::Unmapped;
|
||||
} else {
|
||||
page_type = Common::PageType::Memory;
|
||||
current_page_table->pointers[vaddr >> PAGE_BITS] =
|
||||
pointer - (vaddr & ~PAGE_MASK);
|
||||
page_type = Common::PageType::Memory;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -591,9 +591,12 @@ struct Memory::Impl {
|
||||
base + page_table.pointers.size());
|
||||
|
||||
if (!target) {
|
||||
ASSERT_MSG(type != Common::PageType::Memory,
|
||||
"Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE);
|
||||
|
||||
while (base != end) {
|
||||
page_table.pointers[base] = nullptr;
|
||||
page_table.attributes[base] = type;
|
||||
page_table.pointers[base] = nullptr;
|
||||
page_table.backing_addr[base] = 0;
|
||||
|
||||
base += 1;
|
||||
|
||||
@@ -119,13 +119,14 @@ double PerfStats::GetLastFrameTimeScale() {
|
||||
}
|
||||
|
||||
void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
|
||||
if (!Settings::values.use_frame_limit || Settings::values.use_multi_core) {
|
||||
if (!Settings::values.use_frame_limit.GetValue() ||
|
||||
Settings::values.use_multi_core.GetValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto now = Clock::now();
|
||||
|
||||
const double sleep_scale = Settings::values.frame_limit / 100.0;
|
||||
const double sleep_scale = Settings::values.frame_limit.GetValue() / 100.0;
|
||||
|
||||
// Max lag caused by slow frames. Shouldn't be more than the length of a frame at the current
|
||||
// speed percent or it will clamp too much and prevent this from properly limiting to that
|
||||
|
||||
@@ -62,6 +62,7 @@ const std::array<const char*, NumMouseButtons> mapping = {{
|
||||
}
|
||||
|
||||
Values values = {};
|
||||
bool configuring_global = true;
|
||||
|
||||
std::string GetTimeZoneString() {
|
||||
static constexpr std::array<const char*, 46> timezones{{
|
||||
@@ -73,9 +74,9 @@ std::string GetTimeZoneString() {
|
||||
"UCT", "Universal", "UTC", "W-SU", "WET", "Zulu",
|
||||
}};
|
||||
|
||||
ASSERT(Settings::values.time_zone_index < timezones.size());
|
||||
ASSERT(Settings::values.time_zone_index.GetValue() < timezones.size());
|
||||
|
||||
return timezones[Settings::values.time_zone_index];
|
||||
return timezones[Settings::values.time_zone_index.GetValue()];
|
||||
}
|
||||
|
||||
void Apply() {
|
||||
@@ -97,25 +98,25 @@ void LogSetting(const std::string& name, const T& value) {
|
||||
|
||||
void LogSettings() {
|
||||
LOG_INFO(Config, "yuzu Configuration:");
|
||||
LogSetting("System_UseDockedMode", Settings::values.use_docked_mode);
|
||||
LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0));
|
||||
LogSetting("Controls_UseDockedMode", Settings::values.use_docked_mode);
|
||||
LogSetting("System_RngSeed", Settings::values.rng_seed.GetValue().value_or(0));
|
||||
LogSetting("System_CurrentUser", Settings::values.current_user);
|
||||
LogSetting("System_LanguageIndex", Settings::values.language_index);
|
||||
LogSetting("System_RegionIndex", Settings::values.region_index);
|
||||
LogSetting("System_TimeZoneIndex", Settings::values.time_zone_index);
|
||||
LogSetting("Core_UseMultiCore", Settings::values.use_multi_core);
|
||||
LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
|
||||
LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
|
||||
LogSetting("Renderer_FrameLimit", Settings::values.frame_limit);
|
||||
LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
|
||||
LogSetting("Renderer_GPUAccuracyLevel", Settings::values.gpu_accuracy);
|
||||
LogSetting("System_LanguageIndex", Settings::values.language_index.GetValue());
|
||||
LogSetting("System_RegionIndex", Settings::values.region_index.GetValue());
|
||||
LogSetting("System_TimeZoneIndex", Settings::values.time_zone_index.GetValue());
|
||||
LogSetting("Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
|
||||
LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor.GetValue());
|
||||
LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit.GetValue());
|
||||
LogSetting("Renderer_FrameLimit", Settings::values.frame_limit.GetValue());
|
||||
LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache.GetValue());
|
||||
LogSetting("Renderer_GPUAccuracyLevel", Settings::values.gpu_accuracy.GetValue());
|
||||
LogSetting("Renderer_UseAsynchronousGpuEmulation",
|
||||
Settings::values.use_asynchronous_gpu_emulation);
|
||||
LogSetting("Renderer_UseVsync", Settings::values.use_vsync);
|
||||
LogSetting("Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders);
|
||||
LogSetting("Renderer_AnisotropicFilteringLevel", Settings::values.max_anisotropy);
|
||||
Settings::values.use_asynchronous_gpu_emulation.GetValue());
|
||||
LogSetting("Renderer_UseVsync", Settings::values.use_vsync.GetValue());
|
||||
LogSetting("Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders.GetValue());
|
||||
LogSetting("Renderer_AnisotropicFilteringLevel", Settings::values.max_anisotropy.GetValue());
|
||||
LogSetting("Audio_OutputEngine", Settings::values.sink_id);
|
||||
LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
|
||||
LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching.GetValue());
|
||||
LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
|
||||
LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd);
|
||||
LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
|
||||
@@ -131,15 +132,56 @@ float Volume() {
|
||||
if (values.audio_muted) {
|
||||
return 0.0f;
|
||||
}
|
||||
return values.volume;
|
||||
return values.volume.GetValue();
|
||||
}
|
||||
|
||||
bool IsGPULevelExtreme() {
|
||||
return values.gpu_accuracy == GPUAccuracy::Extreme;
|
||||
return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme;
|
||||
}
|
||||
|
||||
bool IsGPULevelHigh() {
|
||||
return values.gpu_accuracy == GPUAccuracy::Extreme || values.gpu_accuracy == GPUAccuracy::High;
|
||||
return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme ||
|
||||
values.gpu_accuracy.GetValue() == GPUAccuracy::High;
|
||||
}
|
||||
|
||||
void RestoreGlobalState() {
|
||||
// If a game is running, DO NOT restore the global settings state
|
||||
if (Core::System::GetInstance().IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Audio
|
||||
values.enable_audio_stretching.SetGlobal(true);
|
||||
values.volume.SetGlobal(true);
|
||||
|
||||
// Core
|
||||
values.use_multi_core.SetGlobal(true);
|
||||
|
||||
// Renderer
|
||||
values.renderer_backend.SetGlobal(true);
|
||||
values.vulkan_device.SetGlobal(true);
|
||||
values.aspect_ratio.SetGlobal(true);
|
||||
values.max_anisotropy.SetGlobal(true);
|
||||
values.use_frame_limit.SetGlobal(true);
|
||||
values.frame_limit.SetGlobal(true);
|
||||
values.use_disk_shader_cache.SetGlobal(true);
|
||||
values.gpu_accuracy.SetGlobal(true);
|
||||
values.use_asynchronous_gpu_emulation.SetGlobal(true);
|
||||
values.use_vsync.SetGlobal(true);
|
||||
values.use_assembly_shaders.SetGlobal(true);
|
||||
values.use_fast_gpu_time.SetGlobal(true);
|
||||
values.force_30fps_mode.SetGlobal(true);
|
||||
values.bg_red.SetGlobal(true);
|
||||
values.bg_green.SetGlobal(true);
|
||||
values.bg_blue.SetGlobal(true);
|
||||
|
||||
// System
|
||||
values.language_index.SetGlobal(true);
|
||||
values.region_index.SetGlobal(true);
|
||||
values.time_zone_index.SetGlobal(true);
|
||||
values.rng_seed.SetGlobal(true);
|
||||
values.custom_rtc.SetGlobal(true);
|
||||
values.sound_index.SetGlobal(true);
|
||||
}
|
||||
|
||||
} // namespace Settings
|
||||
|
||||
@@ -346,31 +346,6 @@ struct TouchscreenInput {
|
||||
u32 rotation_angle;
|
||||
};
|
||||
|
||||
enum class NANDTotalSize : u64 {
|
||||
S29_1GB = 0x747C00000ULL,
|
||||
};
|
||||
|
||||
enum class NANDUserSize : u64 {
|
||||
S26GB = 0x680000000ULL,
|
||||
};
|
||||
|
||||
enum class NANDSystemSize : u64 {
|
||||
S2_5GB = 0xA0000000,
|
||||
};
|
||||
|
||||
enum class SDMCSize : u64 {
|
||||
S1GB = 0x40000000,
|
||||
S2GB = 0x80000000,
|
||||
S4GB = 0x100000000ULL,
|
||||
S8GB = 0x200000000ULL,
|
||||
S16GB = 0x400000000ULL,
|
||||
S32GB = 0x800000000ULL,
|
||||
S64GB = 0x1000000000ULL,
|
||||
S128GB = 0x2000000000ULL,
|
||||
S256GB = 0x4000000000ULL,
|
||||
S1TB = 0x10000000000ULL,
|
||||
};
|
||||
|
||||
enum class RendererBackend {
|
||||
OpenGL = 0,
|
||||
Vulkan = 1,
|
||||
@@ -382,20 +357,102 @@ enum class GPUAccuracy : u32 {
|
||||
Extreme = 2,
|
||||
};
|
||||
|
||||
enum class CPUAccuracy {
|
||||
Accurate = 0,
|
||||
DebugMode = 1,
|
||||
};
|
||||
|
||||
extern bool configuring_global;
|
||||
|
||||
template <typename Type>
|
||||
class Setting final {
|
||||
public:
|
||||
Setting() = default;
|
||||
explicit Setting(Type val) : global{val} {}
|
||||
~Setting() = default;
|
||||
void SetGlobal(bool to_global) {
|
||||
use_global = to_global;
|
||||
}
|
||||
bool UsingGlobal() const {
|
||||
return use_global;
|
||||
}
|
||||
Type GetValue(bool need_global = false) const {
|
||||
if (use_global || need_global) {
|
||||
return global;
|
||||
}
|
||||
return local;
|
||||
}
|
||||
void SetValue(const Type& value) {
|
||||
if (use_global) {
|
||||
global = value;
|
||||
} else {
|
||||
local = value;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool use_global = true;
|
||||
Type global{};
|
||||
Type local{};
|
||||
};
|
||||
|
||||
struct Values {
|
||||
// Audio
|
||||
std::string audio_device_id;
|
||||
std::string sink_id;
|
||||
bool audio_muted;
|
||||
Setting<bool> enable_audio_stretching;
|
||||
Setting<float> volume;
|
||||
|
||||
// Core
|
||||
Setting<bool> use_multi_core;
|
||||
|
||||
// Cpu
|
||||
CPUAccuracy cpu_accuracy;
|
||||
|
||||
bool cpuopt_page_tables;
|
||||
bool cpuopt_block_linking;
|
||||
bool cpuopt_return_stack_buffer;
|
||||
bool cpuopt_fast_dispatcher;
|
||||
bool cpuopt_context_elimination;
|
||||
bool cpuopt_const_prop;
|
||||
bool cpuopt_misc_ir;
|
||||
bool cpuopt_reduce_misalign_checks;
|
||||
|
||||
// Renderer
|
||||
Setting<RendererBackend> renderer_backend;
|
||||
bool renderer_debug;
|
||||
Setting<int> vulkan_device;
|
||||
|
||||
Setting<u16> resolution_factor = Setting(static_cast<u16>(1));
|
||||
Setting<int> aspect_ratio;
|
||||
Setting<int> max_anisotropy;
|
||||
Setting<bool> use_frame_limit;
|
||||
Setting<u16> frame_limit;
|
||||
Setting<bool> use_disk_shader_cache;
|
||||
Setting<GPUAccuracy> gpu_accuracy;
|
||||
Setting<bool> use_asynchronous_gpu_emulation;
|
||||
Setting<bool> use_vsync;
|
||||
Setting<bool> use_assembly_shaders;
|
||||
Setting<bool> force_30fps_mode;
|
||||
Setting<bool> use_fast_gpu_time;
|
||||
|
||||
Setting<float> bg_red;
|
||||
Setting<float> bg_green;
|
||||
Setting<float> bg_blue;
|
||||
|
||||
// System
|
||||
bool use_docked_mode;
|
||||
std::optional<u32> rng_seed;
|
||||
Setting<std::optional<u32>> rng_seed;
|
||||
// Measured in seconds since epoch
|
||||
std::optional<std::chrono::seconds> custom_rtc;
|
||||
Setting<std::optional<std::chrono::seconds>> custom_rtc;
|
||||
// Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
|
||||
std::chrono::seconds custom_rtc_differential;
|
||||
|
||||
s32 current_user;
|
||||
s32 language_index;
|
||||
s32 region_index;
|
||||
s32 time_zone_index;
|
||||
s32 sound_index;
|
||||
Setting<s32> language_index;
|
||||
Setting<s32> region_index;
|
||||
Setting<s32> time_zone_index;
|
||||
Setting<s32> sound_index;
|
||||
|
||||
// Controls
|
||||
std::array<PlayerInput, 10> players;
|
||||
@@ -419,51 +476,13 @@ struct Values {
|
||||
u16 udp_input_port;
|
||||
u8 udp_pad_index;
|
||||
|
||||
// Core
|
||||
bool use_multi_core;
|
||||
bool use_docked_mode;
|
||||
|
||||
// Data Storage
|
||||
bool use_virtual_sd;
|
||||
bool gamecard_inserted;
|
||||
bool gamecard_current_game;
|
||||
std::string gamecard_path;
|
||||
NANDTotalSize nand_total_size;
|
||||
NANDSystemSize nand_system_size;
|
||||
NANDUserSize nand_user_size;
|
||||
SDMCSize sdmc_size;
|
||||
|
||||
// Renderer
|
||||
RendererBackend renderer_backend;
|
||||
bool renderer_debug;
|
||||
int vulkan_device;
|
||||
|
||||
u16 resolution_factor{1};
|
||||
int aspect_ratio;
|
||||
int max_anisotropy;
|
||||
bool use_frame_limit;
|
||||
u16 frame_limit;
|
||||
bool use_disk_shader_cache;
|
||||
GPUAccuracy gpu_accuracy;
|
||||
bool use_asynchronous_gpu_emulation;
|
||||
bool use_vsync;
|
||||
bool use_assembly_shaders;
|
||||
bool force_30fps_mode;
|
||||
bool use_fast_gpu_time;
|
||||
|
||||
float bg_red;
|
||||
float bg_green;
|
||||
float bg_blue;
|
||||
|
||||
std::string log_filter;
|
||||
|
||||
bool use_dev_keys;
|
||||
|
||||
// Audio
|
||||
bool audio_muted;
|
||||
std::string sink_id;
|
||||
bool enable_audio_stretching;
|
||||
std::string audio_device_id;
|
||||
float volume;
|
||||
|
||||
// Debugging
|
||||
bool record_frame_times;
|
||||
@@ -474,10 +493,13 @@ struct Values {
|
||||
bool dump_nso;
|
||||
bool reporting_services;
|
||||
bool quest_flag;
|
||||
bool disable_cpu_opt;
|
||||
bool disable_macro_jit;
|
||||
|
||||
// BCAT
|
||||
// Misceallaneous
|
||||
std::string log_filter;
|
||||
bool use_dev_keys;
|
||||
|
||||
// Services
|
||||
std::string bcat_backend;
|
||||
bool bcat_boxcat_local;
|
||||
|
||||
@@ -501,4 +523,7 @@ std::string GetTimeZoneString();
|
||||
void Apply();
|
||||
void LogSettings();
|
||||
|
||||
// Restore the global state of all applicable settings in the Values struct
|
||||
void RestoreGlobalState();
|
||||
|
||||
} // namespace Settings
|
||||
|
||||
@@ -189,19 +189,24 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
|
||||
// Log user configuration information
|
||||
constexpr auto field_type = Telemetry::FieldType::UserConfig;
|
||||
AddField(field_type, "Audio_SinkId", Settings::values.sink_id);
|
||||
AddField(field_type, "Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
|
||||
AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core);
|
||||
AddField(field_type, "Renderer_Backend", TranslateRenderer(Settings::values.renderer_backend));
|
||||
AddField(field_type, "Renderer_ResolutionFactor", Settings::values.resolution_factor);
|
||||
AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit);
|
||||
AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit);
|
||||
AddField(field_type, "Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
|
||||
AddField(field_type, "Audio_EnableAudioStretching",
|
||||
Settings::values.enable_audio_stretching.GetValue());
|
||||
AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
|
||||
AddField(field_type, "Renderer_Backend",
|
||||
TranslateRenderer(Settings::values.renderer_backend.GetValue()));
|
||||
AddField(field_type, "Renderer_ResolutionFactor",
|
||||
Settings::values.resolution_factor.GetValue());
|
||||
AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit.GetValue());
|
||||
AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit.GetValue());
|
||||
AddField(field_type, "Renderer_UseDiskShaderCache",
|
||||
Settings::values.use_disk_shader_cache.GetValue());
|
||||
AddField(field_type, "Renderer_GPUAccuracyLevel",
|
||||
TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy));
|
||||
TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy.GetValue()));
|
||||
AddField(field_type, "Renderer_UseAsynchronousGpuEmulation",
|
||||
Settings::values.use_asynchronous_gpu_emulation);
|
||||
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync);
|
||||
AddField(field_type, "Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders);
|
||||
Settings::values.use_asynchronous_gpu_emulation.GetValue());
|
||||
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
|
||||
AddField(field_type, "Renderer_UseAssemblyShaders",
|
||||
Settings::values.use_assembly_shaders.GetValue());
|
||||
AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,10 @@ add_library(input_common STATIC
|
||||
main.h
|
||||
motion_emu.cpp
|
||||
motion_emu.h
|
||||
gcadapter/gc_adapter.cpp
|
||||
gcadapter/gc_adapter.h
|
||||
gcadapter/gc_poller.cpp
|
||||
gcadapter/gc_poller.h
|
||||
sdl/sdl.cpp
|
||||
sdl/sdl.h
|
||||
udp/client.cpp
|
||||
@@ -26,5 +30,8 @@ if(SDL2_FOUND)
|
||||
target_compile_definitions(input_common PRIVATE HAVE_SDL2)
|
||||
endif()
|
||||
|
||||
target_include_directories(input_common SYSTEM PRIVATE ${LIBUSB_INCLUDE_DIR})
|
||||
target_link_libraries(input_common PRIVATE ${LIBUSB_LIBRARIES})
|
||||
|
||||
create_target_directory_groups(input_common)
|
||||
target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost)
|
||||
|
||||
399
src/input_common/gcadapter/gc_adapter.cpp
Normal file
399
src/input_common/gcadapter/gc_adapter.cpp
Normal file
@@ -0,0 +1,399 @@
|
||||
// Copyright 2014 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <libusb.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "input_common/gcadapter/gc_adapter.h"
|
||||
|
||||
namespace GCAdapter {
|
||||
|
||||
/// Used to loop through and assign button in poller
|
||||
constexpr std::array<PadButton, 12> PadButtonArray{
|
||||
PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, PadButton::PAD_BUTTON_DOWN,
|
||||
PadButton::PAD_BUTTON_UP, PadButton::PAD_TRIGGER_Z, PadButton::PAD_TRIGGER_R,
|
||||
PadButton::PAD_TRIGGER_L, PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B,
|
||||
PadButton::PAD_BUTTON_X, PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_START,
|
||||
};
|
||||
|
||||
Adapter::Adapter() {
|
||||
if (usb_adapter_handle != nullptr) {
|
||||
return;
|
||||
}
|
||||
LOG_INFO(Input, "GC Adapter Initialization started");
|
||||
|
||||
current_status = NO_ADAPTER_DETECTED;
|
||||
|
||||
const int init_res = libusb_init(&libusb_ctx);
|
||||
if (init_res == LIBUSB_SUCCESS) {
|
||||
StartScanThread();
|
||||
} else {
|
||||
LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
|
||||
}
|
||||
}
|
||||
|
||||
GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload) {
|
||||
GCPadStatus pad = {};
|
||||
bool get_origin = false;
|
||||
|
||||
ControllerTypes type = ControllerTypes(adapter_payload[1 + (9 * port)] >> 4);
|
||||
if (type != ControllerTypes::None) {
|
||||
get_origin = true;
|
||||
}
|
||||
|
||||
adapter_controllers_status[port] = type;
|
||||
|
||||
static constexpr std::array<PadButton, 8> b1_buttons{
|
||||
PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B, PadButton::PAD_BUTTON_X,
|
||||
PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT,
|
||||
PadButton::PAD_BUTTON_DOWN, PadButton::PAD_BUTTON_UP,
|
||||
};
|
||||
|
||||
static constexpr std::array<PadButton, 4> b2_buttons{
|
||||
PadButton::PAD_BUTTON_START,
|
||||
PadButton::PAD_TRIGGER_Z,
|
||||
PadButton::PAD_TRIGGER_R,
|
||||
PadButton::PAD_TRIGGER_L,
|
||||
};
|
||||
|
||||
if (adapter_controllers_status[port] != ControllerTypes::None) {
|
||||
const u8 b1 = adapter_payload[1 + (9 * port) + 1];
|
||||
const u8 b2 = adapter_payload[1 + (9 * port) + 2];
|
||||
|
||||
for (std::size_t i = 0; i < b1_buttons.size(); ++i) {
|
||||
if ((b1 & (1U << i)) != 0) {
|
||||
pad.button |= static_cast<u16>(b1_buttons[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t j = 0; j < b2_buttons.size(); ++j) {
|
||||
if ((b2 & (1U << j)) != 0) {
|
||||
pad.button |= static_cast<u16>(b2_buttons[j]);
|
||||
}
|
||||
}
|
||||
|
||||
if (get_origin) {
|
||||
pad.button |= PAD_GET_ORIGIN;
|
||||
}
|
||||
|
||||
pad.stick_x = adapter_payload[1 + (9 * port) + 3];
|
||||
pad.stick_y = adapter_payload[1 + (9 * port) + 4];
|
||||
pad.substick_x = adapter_payload[1 + (9 * port) + 5];
|
||||
pad.substick_y = adapter_payload[1 + (9 * port) + 6];
|
||||
pad.trigger_left = adapter_payload[1 + (9 * port) + 7];
|
||||
pad.trigger_right = adapter_payload[1 + (9 * port) + 8];
|
||||
}
|
||||
return pad;
|
||||
}
|
||||
|
||||
void Adapter::PadToState(const GCPadStatus& pad, GCState& state) {
|
||||
for (const auto& button : PadButtonArray) {
|
||||
const u16 button_value = static_cast<u16>(button);
|
||||
state.buttons.insert_or_assign(button_value, pad.button & button_value);
|
||||
}
|
||||
|
||||
state.axes.insert_or_assign(static_cast<u8>(PadAxes::StickX), pad.stick_x);
|
||||
state.axes.insert_or_assign(static_cast<u8>(PadAxes::StickY), pad.stick_y);
|
||||
state.axes.insert_or_assign(static_cast<u8>(PadAxes::SubstickX), pad.substick_x);
|
||||
state.axes.insert_or_assign(static_cast<u8>(PadAxes::SubstickY), pad.substick_y);
|
||||
state.axes.insert_or_assign(static_cast<u8>(PadAxes::TriggerLeft), pad.trigger_left);
|
||||
state.axes.insert_or_assign(static_cast<u8>(PadAxes::TriggerRight), pad.trigger_right);
|
||||
}
|
||||
|
||||
void Adapter::Read() {
|
||||
LOG_DEBUG(Input, "GC Adapter Read() thread started");
|
||||
|
||||
int payload_size_in, payload_size_copy;
|
||||
std::array<u8, 37> adapter_payload;
|
||||
std::array<u8, 37> adapter_payload_copy;
|
||||
std::array<GCPadStatus, 4> pads;
|
||||
|
||||
while (adapter_thread_running) {
|
||||
libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(),
|
||||
sizeof(adapter_payload), &payload_size_in, 16);
|
||||
payload_size_copy = 0;
|
||||
// this mutex might be redundant?
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(s_mutex);
|
||||
std::copy(std::begin(adapter_payload), std::end(adapter_payload),
|
||||
std::begin(adapter_payload_copy));
|
||||
payload_size_copy = payload_size_in;
|
||||
}
|
||||
|
||||
if (payload_size_copy != sizeof(adapter_payload_copy) ||
|
||||
adapter_payload_copy[0] != LIBUSB_DT_HID) {
|
||||
LOG_ERROR(Input, "error reading payload (size: {}, type: {:02x})", payload_size_copy,
|
||||
adapter_payload_copy[0]);
|
||||
adapter_thread_running = false; // error reading from adapter, stop reading.
|
||||
break;
|
||||
}
|
||||
for (std::size_t port = 0; port < pads.size(); ++port) {
|
||||
pads[port] = GetPadStatus(port, adapter_payload_copy);
|
||||
if (DeviceConnected(port) && configuring) {
|
||||
if (pads[port].button != PAD_GET_ORIGIN) {
|
||||
pad_queue[port].Push(pads[port]);
|
||||
}
|
||||
|
||||
// Accounting for a threshold here because of some controller variance
|
||||
if (pads[port].stick_x > pads[port].MAIN_STICK_CENTER_X + pads[port].THRESHOLD ||
|
||||
pads[port].stick_x < pads[port].MAIN_STICK_CENTER_X - pads[port].THRESHOLD) {
|
||||
pads[port].axis = GCAdapter::PadAxes::StickX;
|
||||
pads[port].axis_value = pads[port].stick_x;
|
||||
pad_queue[port].Push(pads[port]);
|
||||
}
|
||||
if (pads[port].stick_y > pads[port].MAIN_STICK_CENTER_Y + pads[port].THRESHOLD ||
|
||||
pads[port].stick_y < pads[port].MAIN_STICK_CENTER_Y - pads[port].THRESHOLD) {
|
||||
pads[port].axis = GCAdapter::PadAxes::StickY;
|
||||
pads[port].axis_value = pads[port].stick_y;
|
||||
pad_queue[port].Push(pads[port]);
|
||||
}
|
||||
if (pads[port].substick_x > pads[port].C_STICK_CENTER_X + pads[port].THRESHOLD ||
|
||||
pads[port].substick_x < pads[port].C_STICK_CENTER_X - pads[port].THRESHOLD) {
|
||||
pads[port].axis = GCAdapter::PadAxes::SubstickX;
|
||||
pads[port].axis_value = pads[port].substick_x;
|
||||
pad_queue[port].Push(pads[port]);
|
||||
}
|
||||
if (pads[port].substick_y > pads[port].C_STICK_CENTER_Y + pads[port].THRESHOLD ||
|
||||
pads[port].substick_y < pads[port].C_STICK_CENTER_Y - pads[port].THRESHOLD) {
|
||||
pads[port].axis = GCAdapter::PadAxes::SubstickY;
|
||||
pads[port].axis_value = pads[port].substick_y;
|
||||
pad_queue[port].Push(pads[port]);
|
||||
}
|
||||
if (pads[port].trigger_left > pads[port].TRIGGER_THRESHOLD) {
|
||||
pads[port].axis = GCAdapter::PadAxes::TriggerLeft;
|
||||
pads[port].axis_value = pads[port].trigger_left;
|
||||
pad_queue[port].Push(pads[port]);
|
||||
}
|
||||
if (pads[port].trigger_right > pads[port].TRIGGER_THRESHOLD) {
|
||||
pads[port].axis = GCAdapter::PadAxes::TriggerRight;
|
||||
pads[port].axis_value = pads[port].trigger_right;
|
||||
pad_queue[port].Push(pads[port]);
|
||||
}
|
||||
}
|
||||
PadToState(pads[port], state[port]);
|
||||
}
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
void Adapter::ScanThreadFunc() {
|
||||
LOG_INFO(Input, "GC Adapter scanning thread started");
|
||||
|
||||
while (detect_thread_running) {
|
||||
if (usb_adapter_handle == nullptr) {
|
||||
std::lock_guard<std::mutex> lk(initialization_mutex);
|
||||
Setup();
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
}
|
||||
}
|
||||
|
||||
void Adapter::StartScanThread() {
|
||||
if (detect_thread_running) {
|
||||
return;
|
||||
}
|
||||
if (!libusb_ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
detect_thread_running = true;
|
||||
detect_thread = std::thread(&Adapter::ScanThreadFunc, this);
|
||||
}
|
||||
|
||||
void Adapter::StopScanThread() {
|
||||
detect_thread_running = false;
|
||||
detect_thread.join();
|
||||
}
|
||||
|
||||
void Adapter::Setup() {
|
||||
// Reset the error status in case the adapter gets unplugged
|
||||
if (current_status < 0) {
|
||||
current_status = NO_ADAPTER_DETECTED;
|
||||
}
|
||||
|
||||
adapter_controllers_status.fill(ControllerTypes::None);
|
||||
|
||||
// pointer to list of connected usb devices
|
||||
libusb_device** devices{};
|
||||
|
||||
// populate the list of devices, get the count
|
||||
const ssize_t device_count = libusb_get_device_list(libusb_ctx, &devices);
|
||||
if (device_count < 0) {
|
||||
LOG_ERROR(Input, "libusb_get_device_list failed with error: {}", device_count);
|
||||
detect_thread_running = false; // Stop the loop constantly checking for gc adapter
|
||||
// TODO: For hotplug+gc adapter checkbox implementation, revert this.
|
||||
return;
|
||||
}
|
||||
|
||||
if (devices != nullptr) {
|
||||
for (std::size_t index = 0; index < static_cast<std::size_t>(device_count); ++index) {
|
||||
if (CheckDeviceAccess(devices[index])) {
|
||||
// GC Adapter found and accessible, registering it
|
||||
GetGCEndpoint(devices[index]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
libusb_free_device_list(devices, 1);
|
||||
}
|
||||
}
|
||||
|
||||
bool Adapter::CheckDeviceAccess(libusb_device* device) {
|
||||
libusb_device_descriptor desc;
|
||||
const int get_descriptor_error = libusb_get_device_descriptor(device, &desc);
|
||||
if (get_descriptor_error) {
|
||||
// could not acquire the descriptor, no point in trying to use it.
|
||||
LOG_ERROR(Input, "libusb_get_device_descriptor failed with error: {}",
|
||||
get_descriptor_error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (desc.idVendor != 0x057e || desc.idProduct != 0x0337) {
|
||||
// This isn't the device we are looking for.
|
||||
return false;
|
||||
}
|
||||
const int open_error = libusb_open(device, &usb_adapter_handle);
|
||||
|
||||
if (open_error == LIBUSB_ERROR_ACCESS) {
|
||||
LOG_ERROR(Input, "Yuzu can not gain access to this device: ID {:04X}:{:04X}.",
|
||||
desc.idVendor, desc.idProduct);
|
||||
return false;
|
||||
}
|
||||
if (open_error) {
|
||||
LOG_ERROR(Input, "libusb_open failed to open device with error = {}", open_error);
|
||||
return false;
|
||||
}
|
||||
|
||||
int kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0);
|
||||
if (kernel_driver_error == 1) {
|
||||
kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0);
|
||||
if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
|
||||
LOG_ERROR(Input, "libusb_detach_kernel_driver failed with error = {}",
|
||||
kernel_driver_error);
|
||||
}
|
||||
}
|
||||
|
||||
if (kernel_driver_error && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
|
||||
libusb_close(usb_adapter_handle);
|
||||
usb_adapter_handle = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
const int interface_claim_error = libusb_claim_interface(usb_adapter_handle, 0);
|
||||
if (interface_claim_error) {
|
||||
LOG_ERROR(Input, "libusb_claim_interface failed with error = {}", interface_claim_error);
|
||||
libusb_close(usb_adapter_handle);
|
||||
usb_adapter_handle = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Adapter::GetGCEndpoint(libusb_device* device) {
|
||||
libusb_config_descriptor* config = nullptr;
|
||||
const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config);
|
||||
if (config_descriptor_return != LIBUSB_SUCCESS) {
|
||||
LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}",
|
||||
config_descriptor_return);
|
||||
return;
|
||||
}
|
||||
|
||||
for (u8 ic = 0; ic < config->bNumInterfaces; ic++) {
|
||||
const libusb_interface* interfaceContainer = &config->interface[ic];
|
||||
for (int i = 0; i < interfaceContainer->num_altsetting; i++) {
|
||||
const libusb_interface_descriptor* interface = &interfaceContainer->altsetting[i];
|
||||
for (u8 e = 0; e < interface->bNumEndpoints; e++) {
|
||||
const libusb_endpoint_descriptor* endpoint = &interface->endpoint[e];
|
||||
if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) {
|
||||
input_endpoint = endpoint->bEndpointAddress;
|
||||
} else {
|
||||
output_endpoint = endpoint->bEndpointAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// This transfer seems to be responsible for clearing the state of the adapter
|
||||
// Used to clear the "busy" state of when the device is unexpectedly unplugged
|
||||
unsigned char clear_payload = 0x13;
|
||||
libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload,
|
||||
sizeof(clear_payload), nullptr, 16);
|
||||
|
||||
adapter_thread_running = true;
|
||||
current_status = ADAPTER_DETECTED;
|
||||
adapter_input_thread = std::thread([=] { Read(); }); // Read input
|
||||
}
|
||||
|
||||
Adapter::~Adapter() {
|
||||
StopScanThread();
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Adapter::Reset() {
|
||||
std::unique_lock<std::mutex> lock(initialization_mutex, std::defer_lock);
|
||||
if (!lock.try_lock()) {
|
||||
return;
|
||||
}
|
||||
if (current_status != ADAPTER_DETECTED) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (adapter_thread_running) {
|
||||
adapter_thread_running = false;
|
||||
}
|
||||
adapter_input_thread.join();
|
||||
|
||||
adapter_controllers_status.fill(ControllerTypes::None);
|
||||
current_status = NO_ADAPTER_DETECTED;
|
||||
|
||||
if (usb_adapter_handle) {
|
||||
libusb_release_interface(usb_adapter_handle, 1);
|
||||
libusb_close(usb_adapter_handle);
|
||||
usb_adapter_handle = nullptr;
|
||||
}
|
||||
|
||||
if (libusb_ctx) {
|
||||
libusb_exit(libusb_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
bool Adapter::DeviceConnected(std::size_t port) {
|
||||
return adapter_controllers_status[port] != ControllerTypes::None;
|
||||
}
|
||||
|
||||
void Adapter::ResetDeviceType(std::size_t port) {
|
||||
adapter_controllers_status[port] = ControllerTypes::None;
|
||||
}
|
||||
|
||||
void Adapter::BeginConfiguration() {
|
||||
for (auto& pq : pad_queue) {
|
||||
pq.Clear();
|
||||
}
|
||||
configuring = true;
|
||||
}
|
||||
|
||||
void Adapter::EndConfiguration() {
|
||||
for (auto& pq : pad_queue) {
|
||||
pq.Clear();
|
||||
}
|
||||
configuring = false;
|
||||
}
|
||||
|
||||
std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() {
|
||||
return pad_queue;
|
||||
}
|
||||
|
||||
const std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() const {
|
||||
return pad_queue;
|
||||
}
|
||||
|
||||
std::array<GCState, 4>& Adapter::GetPadState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
const std::array<GCState, 4>& Adapter::GetPadState() const {
|
||||
return state;
|
||||
}
|
||||
|
||||
} // namespace GCAdapter
|
||||
164
src/input_common/gcadapter/gc_adapter.h
Normal file
164
src/input_common/gcadapter/gc_adapter.h
Normal file
@@ -0,0 +1,164 @@
|
||||
// Copyright 2014 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include "common/common_types.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
|
||||
struct libusb_context;
|
||||
struct libusb_device;
|
||||
struct libusb_device_handle;
|
||||
|
||||
namespace GCAdapter {
|
||||
|
||||
enum {
|
||||
PAD_USE_ORIGIN = 0x0080,
|
||||
PAD_GET_ORIGIN = 0x2000,
|
||||
PAD_ERR_STATUS = 0x8000,
|
||||
};
|
||||
|
||||
enum class PadButton {
|
||||
PAD_BUTTON_LEFT = 0x0001,
|
||||
PAD_BUTTON_RIGHT = 0x0002,
|
||||
PAD_BUTTON_DOWN = 0x0004,
|
||||
PAD_BUTTON_UP = 0x0008,
|
||||
PAD_TRIGGER_Z = 0x0010,
|
||||
PAD_TRIGGER_R = 0x0020,
|
||||
PAD_TRIGGER_L = 0x0040,
|
||||
PAD_BUTTON_A = 0x0100,
|
||||
PAD_BUTTON_B = 0x0200,
|
||||
PAD_BUTTON_X = 0x0400,
|
||||
PAD_BUTTON_Y = 0x0800,
|
||||
PAD_BUTTON_START = 0x1000,
|
||||
// Below is for compatibility with "AxisButton" type
|
||||
PAD_STICK = 0x2000,
|
||||
};
|
||||
|
||||
extern const std::array<PadButton, 12> PadButtonArray;
|
||||
|
||||
enum class PadAxes : u8 {
|
||||
StickX,
|
||||
StickY,
|
||||
SubstickX,
|
||||
SubstickY,
|
||||
TriggerLeft,
|
||||
TriggerRight,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
struct GCPadStatus {
|
||||
u16 button{}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits
|
||||
u8 stick_x{}; // 0 <= stick_x <= 255
|
||||
u8 stick_y{}; // 0 <= stick_y <= 255
|
||||
u8 substick_x{}; // 0 <= substick_x <= 255
|
||||
u8 substick_y{}; // 0 <= substick_y <= 255
|
||||
u8 trigger_left{}; // 0 <= trigger_left <= 255
|
||||
u8 trigger_right{}; // 0 <= trigger_right <= 255
|
||||
|
||||
static constexpr u8 MAIN_STICK_CENTER_X = 0x80;
|
||||
static constexpr u8 MAIN_STICK_CENTER_Y = 0x80;
|
||||
static constexpr u8 MAIN_STICK_RADIUS = 0x7f;
|
||||
static constexpr u8 C_STICK_CENTER_X = 0x80;
|
||||
static constexpr u8 C_STICK_CENTER_Y = 0x80;
|
||||
static constexpr u8 C_STICK_RADIUS = 0x7f;
|
||||
static constexpr u8 THRESHOLD = 10;
|
||||
|
||||
// 256/4, at least a quarter press to count as a press. For polling mostly
|
||||
static constexpr u8 TRIGGER_THRESHOLD = 64;
|
||||
|
||||
u8 port{};
|
||||
PadAxes axis{PadAxes::Undefined};
|
||||
u8 axis_value{255};
|
||||
};
|
||||
|
||||
struct GCState {
|
||||
std::unordered_map<int, bool> buttons;
|
||||
std::unordered_map<int, u16> axes;
|
||||
};
|
||||
|
||||
enum class ControllerTypes { None, Wired, Wireless };
|
||||
|
||||
enum {
|
||||
NO_ADAPTER_DETECTED = 0,
|
||||
ADAPTER_DETECTED = 1,
|
||||
};
|
||||
|
||||
class Adapter {
|
||||
public:
|
||||
/// Initialize the GC Adapter capture and read sequence
|
||||
Adapter();
|
||||
|
||||
/// Close the adapter read thread and release the adapter
|
||||
~Adapter();
|
||||
/// Used for polling
|
||||
void BeginConfiguration();
|
||||
void EndConfiguration();
|
||||
|
||||
/// Returns true if there is a device connected to port
|
||||
bool DeviceConnected(std::size_t port);
|
||||
|
||||
std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue();
|
||||
const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const;
|
||||
|
||||
std::array<GCState, 4>& GetPadState();
|
||||
const std::array<GCState, 4>& GetPadState() const;
|
||||
|
||||
private:
|
||||
GCPadStatus GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload);
|
||||
|
||||
void PadToState(const GCPadStatus& pad, GCState& state);
|
||||
|
||||
void Read();
|
||||
void ScanThreadFunc();
|
||||
/// Begin scanning for the GC Adapter.
|
||||
void StartScanThread();
|
||||
|
||||
/// Stop scanning for the adapter
|
||||
void StopScanThread();
|
||||
|
||||
/// Resets status of device connected to port
|
||||
void ResetDeviceType(std::size_t port);
|
||||
|
||||
/// Returns true if we successfully gain access to GC Adapter
|
||||
bool CheckDeviceAccess(libusb_device* device);
|
||||
|
||||
/// Captures GC Adapter endpoint address,
|
||||
void GetGCEndpoint(libusb_device* device);
|
||||
|
||||
/// For shutting down, clear all data, join all threads, release usb
|
||||
void Reset();
|
||||
|
||||
/// For use in initialization, querying devices to find the adapter
|
||||
void Setup();
|
||||
|
||||
int current_status = NO_ADAPTER_DETECTED;
|
||||
libusb_device_handle* usb_adapter_handle = nullptr;
|
||||
std::array<ControllerTypes, 4> adapter_controllers_status{};
|
||||
|
||||
std::mutex s_mutex;
|
||||
|
||||
std::thread adapter_input_thread;
|
||||
bool adapter_thread_running;
|
||||
|
||||
std::mutex initialization_mutex;
|
||||
std::thread detect_thread;
|
||||
bool detect_thread_running = false;
|
||||
|
||||
libusb_context* libusb_ctx;
|
||||
|
||||
u8 input_endpoint = 0;
|
||||
u8 output_endpoint = 0;
|
||||
|
||||
bool configuring = false;
|
||||
|
||||
std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue;
|
||||
std::array<GCState, 4> state;
|
||||
};
|
||||
|
||||
} // namespace GCAdapter
|
||||
286
src/input_common/gcadapter/gc_poller.cpp
Normal file
286
src/input_common/gcadapter/gc_poller.cpp
Normal file
@@ -0,0 +1,286 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <atomic>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include "common/assert.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "input_common/gcadapter/gc_adapter.h"
|
||||
#include "input_common/gcadapter/gc_poller.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
class GCButton final : public Input::ButtonDevice {
|
||||
public:
|
||||
explicit GCButton(int port_, int button_, GCAdapter::Adapter* adapter)
|
||||
: port(port_), button(button_), gcadapter(adapter) {}
|
||||
|
||||
~GCButton() override;
|
||||
|
||||
bool GetStatus() const override {
|
||||
if (gcadapter->DeviceConnected(port)) {
|
||||
return gcadapter->GetPadState()[port].buttons.at(button);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const int port;
|
||||
const int button;
|
||||
GCAdapter::Adapter* gcadapter;
|
||||
};
|
||||
|
||||
class GCAxisButton final : public Input::ButtonDevice {
|
||||
public:
|
||||
explicit GCAxisButton(int port_, int axis_, float threshold_, bool trigger_if_greater_,
|
||||
GCAdapter::Adapter* adapter)
|
||||
: port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_),
|
||||
gcadapter(adapter) {
|
||||
// L/R triggers range is only in positive direction beginning near 0
|
||||
// 0.0 threshold equates to near half trigger press, but threshold accounts for variability.
|
||||
if (axis > 3) {
|
||||
threshold *= -0.5;
|
||||
}
|
||||
}
|
||||
|
||||
bool GetStatus() const override {
|
||||
if (gcadapter->DeviceConnected(port)) {
|
||||
const float axis_value =
|
||||
(gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 128.0f;
|
||||
if (trigger_if_greater) {
|
||||
// TODO: Might be worthwile to set a slider for the trigger threshold. It is
|
||||
// currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
|
||||
return axis_value > threshold;
|
||||
}
|
||||
return axis_value < -threshold;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const int port;
|
||||
const int axis;
|
||||
float threshold;
|
||||
bool trigger_if_greater;
|
||||
GCAdapter::Adapter* gcadapter;
|
||||
};
|
||||
|
||||
GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
|
||||
: adapter(std::move(adapter_)) {}
|
||||
|
||||
GCButton::~GCButton() = default;
|
||||
|
||||
std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::ParamPackage& params) {
|
||||
const int button_id = params.Get("button", 0);
|
||||
const int port = params.Get("port", 0);
|
||||
|
||||
constexpr int PAD_STICK_ID = static_cast<u16>(GCAdapter::PadButton::PAD_STICK);
|
||||
|
||||
// button is not an axis/stick button
|
||||
if (button_id != PAD_STICK_ID) {
|
||||
auto button = std::make_unique<GCButton>(port, button_id, adapter.get());
|
||||
return std::move(button);
|
||||
}
|
||||
|
||||
// For Axis buttons, used by the binary sticks.
|
||||
if (button_id == PAD_STICK_ID) {
|
||||
const int axis = params.Get("axis", 0);
|
||||
const float threshold = params.Get("threshold", 0.25f);
|
||||
const std::string direction_name = params.Get("direction", "");
|
||||
bool trigger_if_greater;
|
||||
if (direction_name == "+") {
|
||||
trigger_if_greater = true;
|
||||
} else if (direction_name == "-") {
|
||||
trigger_if_greater = false;
|
||||
} else {
|
||||
trigger_if_greater = true;
|
||||
LOG_ERROR(Input, "Unknown direction {}", direction_name);
|
||||
}
|
||||
return std::make_unique<GCAxisButton>(port, axis, threshold, trigger_if_greater,
|
||||
adapter.get());
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::ParamPackage GCButtonFactory::GetNextInput() const {
|
||||
Common::ParamPackage params;
|
||||
GCAdapter::GCPadStatus pad;
|
||||
auto& queue = adapter->GetPadQueue();
|
||||
for (std::size_t port = 0; port < queue.size(); ++port) {
|
||||
while (queue[port].Pop(pad)) {
|
||||
// This while loop will break on the earliest detected button
|
||||
params.Set("engine", "gcpad");
|
||||
params.Set("port", static_cast<int>(port));
|
||||
for (const auto& button : GCAdapter::PadButtonArray) {
|
||||
const u16 button_value = static_cast<u16>(button);
|
||||
if (pad.button & button_value) {
|
||||
params.Set("button", button_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// For Axis button implementation
|
||||
if (pad.axis != GCAdapter::PadAxes::Undefined) {
|
||||
params.Set("axis", static_cast<u8>(pad.axis));
|
||||
params.Set("button", static_cast<u16>(GCAdapter::PadButton::PAD_STICK));
|
||||
if (pad.axis_value > 128) {
|
||||
params.Set("direction", "+");
|
||||
params.Set("threshold", "0.25");
|
||||
} else {
|
||||
params.Set("direction", "-");
|
||||
params.Set("threshold", "-0.25");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
void GCButtonFactory::BeginConfiguration() {
|
||||
polling = true;
|
||||
adapter->BeginConfiguration();
|
||||
}
|
||||
|
||||
void GCButtonFactory::EndConfiguration() {
|
||||
polling = false;
|
||||
adapter->EndConfiguration();
|
||||
}
|
||||
|
||||
class GCAnalog final : public Input::AnalogDevice {
|
||||
public:
|
||||
GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter)
|
||||
: port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter) {}
|
||||
|
||||
float GetAxis(int axis) const {
|
||||
if (gcadapter->DeviceConnected(port)) {
|
||||
std::lock_guard lock{mutex};
|
||||
// division is not by a perfect 128 to account for some variance in center location
|
||||
// e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range
|
||||
// [20-230]
|
||||
return (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 95.0f;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
std::pair<float, float> GetAnalog(int axis_x, int axis_y) const {
|
||||
float x = GetAxis(axis_x);
|
||||
float y = GetAxis(axis_y);
|
||||
|
||||
// Make sure the coordinates are in the unit circle,
|
||||
// otherwise normalize it.
|
||||
float r = x * x + y * y;
|
||||
if (r > 1.0f) {
|
||||
r = std::sqrt(r);
|
||||
x /= r;
|
||||
y /= r;
|
||||
}
|
||||
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
std::tuple<float, float> GetStatus() const override {
|
||||
const auto [x, y] = GetAnalog(axis_x, axis_y);
|
||||
const float r = std::sqrt((x * x) + (y * y));
|
||||
if (r > deadzone) {
|
||||
return {x / r * (r - deadzone) / (1 - deadzone),
|
||||
y / r * (r - deadzone) / (1 - deadzone)};
|
||||
}
|
||||
return {0.0f, 0.0f};
|
||||
}
|
||||
|
||||
bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
|
||||
const auto [x, y] = GetStatus();
|
||||
const float directional_deadzone = 0.4f;
|
||||
switch (direction) {
|
||||
case Input::AnalogDirection::RIGHT:
|
||||
return x > directional_deadzone;
|
||||
case Input::AnalogDirection::LEFT:
|
||||
return x < -directional_deadzone;
|
||||
case Input::AnalogDirection::UP:
|
||||
return y > directional_deadzone;
|
||||
case Input::AnalogDirection::DOWN:
|
||||
return y < -directional_deadzone;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const int port;
|
||||
const int axis_x;
|
||||
const int axis_y;
|
||||
const float deadzone;
|
||||
mutable std::mutex mutex;
|
||||
GCAdapter::Adapter* gcadapter;
|
||||
};
|
||||
|
||||
/// An analog device factory that creates analog devices from GC Adapter
|
||||
GCAnalogFactory::GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
|
||||
: adapter(std::move(adapter_)) {}
|
||||
|
||||
/**
|
||||
* Creates analog device from joystick axes
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "port": the nth gcpad on the adapter
|
||||
* - "axis_x": the index of the axis to be bind as x-axis
|
||||
* - "axis_y": the index of the axis to be bind as y-axis
|
||||
*/
|
||||
std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::ParamPackage& params) {
|
||||
const int port = params.Get("port", 0);
|
||||
const int axis_x = params.Get("axis_x", 0);
|
||||
const int axis_y = params.Get("axis_y", 1);
|
||||
const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
|
||||
|
||||
return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get());
|
||||
}
|
||||
|
||||
void GCAnalogFactory::BeginConfiguration() {
|
||||
polling = true;
|
||||
adapter->BeginConfiguration();
|
||||
}
|
||||
|
||||
void GCAnalogFactory::EndConfiguration() {
|
||||
polling = false;
|
||||
adapter->EndConfiguration();
|
||||
}
|
||||
|
||||
Common::ParamPackage GCAnalogFactory::GetNextInput() {
|
||||
GCAdapter::GCPadStatus pad;
|
||||
auto& queue = adapter->GetPadQueue();
|
||||
for (std::size_t port = 0; port < queue.size(); ++port) {
|
||||
while (queue[port].Pop(pad)) {
|
||||
if (pad.axis == GCAdapter::PadAxes::Undefined ||
|
||||
std::abs((pad.axis_value - 128.0f) / 128.0f) < 0.1) {
|
||||
continue;
|
||||
}
|
||||
// An analog device needs two axes, so we need to store the axis for later and wait for
|
||||
// a second input event. The axes also must be from the same joystick.
|
||||
const u8 axis = static_cast<u8>(pad.axis);
|
||||
if (analog_x_axis == -1) {
|
||||
analog_x_axis = axis;
|
||||
controller_number = static_cast<int>(port);
|
||||
} else if (analog_y_axis == -1 && analog_x_axis != axis && controller_number == port) {
|
||||
analog_y_axis = axis;
|
||||
}
|
||||
}
|
||||
}
|
||||
Common::ParamPackage params;
|
||||
if (analog_x_axis != -1 && analog_y_axis != -1) {
|
||||
params.Set("engine", "gcpad");
|
||||
params.Set("port", controller_number);
|
||||
params.Set("axis_x", analog_x_axis);
|
||||
params.Set("axis_y", analog_y_axis);
|
||||
analog_x_axis = -1;
|
||||
analog_y_axis = -1;
|
||||
controller_number = -1;
|
||||
return params;
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
67
src/input_common/gcadapter/gc_poller.h
Normal file
67
src/input_common/gcadapter/gc_poller.h
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "core/frontend/input.h"
|
||||
#include "input_common/gcadapter/gc_adapter.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
/**
|
||||
* A button device factory representing a gcpad. It receives gcpad events and forward them
|
||||
* to all button devices it created.
|
||||
*/
|
||||
class GCButtonFactory final : public Input::Factory<Input::ButtonDevice> {
|
||||
public:
|
||||
explicit GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_);
|
||||
|
||||
/**
|
||||
* Creates a button device from a button press
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "code": the code of the key to bind with the button
|
||||
*/
|
||||
std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
|
||||
|
||||
Common::ParamPackage GetNextInput() const;
|
||||
|
||||
/// For device input configuration/polling
|
||||
void BeginConfiguration();
|
||||
void EndConfiguration();
|
||||
|
||||
bool IsPolling() const {
|
||||
return polling;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<GCAdapter::Adapter> adapter;
|
||||
bool polling = false;
|
||||
};
|
||||
|
||||
/// An analog device factory that creates analog devices from GC Adapter
|
||||
class GCAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
|
||||
public:
|
||||
explicit GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_);
|
||||
|
||||
std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override;
|
||||
Common::ParamPackage GetNextInput();
|
||||
|
||||
/// For device input configuration/polling
|
||||
void BeginConfiguration();
|
||||
void EndConfiguration();
|
||||
|
||||
bool IsPolling() const {
|
||||
return polling;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<GCAdapter::Adapter> adapter;
|
||||
int analog_x_axis = -1;
|
||||
int analog_y_axis = -1;
|
||||
int controller_number = -1;
|
||||
bool polling = false;
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <thread>
|
||||
#include "common/param_package.h"
|
||||
#include "input_common/analog_from_button.h"
|
||||
#include "input_common/gcadapter/gc_adapter.h"
|
||||
#include "input_common/gcadapter/gc_poller.h"
|
||||
#include "input_common/keyboard.h"
|
||||
#include "input_common/main.h"
|
||||
#include "input_common/motion_emu.h"
|
||||
@@ -22,8 +24,16 @@ static std::shared_ptr<MotionEmu> motion_emu;
|
||||
static std::unique_ptr<SDL::State> sdl;
|
||||
#endif
|
||||
static std::unique_ptr<CemuhookUDP::State> udp;
|
||||
static std::shared_ptr<GCButtonFactory> gcbuttons;
|
||||
static std::shared_ptr<GCAnalogFactory> gcanalog;
|
||||
|
||||
void Init() {
|
||||
auto gcadapter = std::make_shared<GCAdapter::Adapter>();
|
||||
gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
|
||||
Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
|
||||
gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
|
||||
Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
|
||||
|
||||
keyboard = std::make_shared<Keyboard>();
|
||||
Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
|
||||
Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
|
||||
@@ -48,6 +58,11 @@ void Shutdown() {
|
||||
sdl.reset();
|
||||
#endif
|
||||
udp.reset();
|
||||
Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
|
||||
Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
|
||||
|
||||
gcbuttons.reset();
|
||||
gcanalog.reset();
|
||||
}
|
||||
|
||||
Keyboard* GetKeyboard() {
|
||||
@@ -58,6 +73,14 @@ MotionEmu* GetMotionEmu() {
|
||||
return motion_emu.get();
|
||||
}
|
||||
|
||||
GCButtonFactory* GetGCButtons() {
|
||||
return gcbuttons.get();
|
||||
}
|
||||
|
||||
GCAnalogFactory* GetGCAnalogs() {
|
||||
return gcanalog.get();
|
||||
}
|
||||
|
||||
std::string GenerateKeyboardParam(int key_code) {
|
||||
Common::ParamPackage param{
|
||||
{"engine", "keyboard"},
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "input_common/gcadapter/gc_poller.h"
|
||||
|
||||
namespace Common {
|
||||
class ParamPackage;
|
||||
@@ -30,6 +31,10 @@ class MotionEmu;
|
||||
/// Gets the motion emulation factory.
|
||||
MotionEmu* GetMotionEmu();
|
||||
|
||||
GCButtonFactory* GetGCButtons();
|
||||
|
||||
GCAnalogFactory* GetGCAnalogs();
|
||||
|
||||
/// Generates a serialized param package for creating a keyboard button device
|
||||
std::string GenerateKeyboardParam(int key_code);
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
std::function<void(Status)> status_callback,
|
||||
std::function<void(u16, u16, u16, u16)> data_callback) {
|
||||
|
||||
std::thread([=] {
|
||||
std::thread([=, this] {
|
||||
constexpr u16 CALIBRATION_THRESHOLD = 100;
|
||||
|
||||
u16 min_x{UINT16_MAX};
|
||||
|
||||
@@ -96,7 +96,8 @@ public:
|
||||
}
|
||||
if (is_written) {
|
||||
map->MarkAsModified(true, GetModifiedTicks());
|
||||
if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) {
|
||||
if (Settings::IsGPULevelHigh() &&
|
||||
Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
|
||||
MarkForAsyncFlush(map);
|
||||
}
|
||||
if (!map->is_written) {
|
||||
@@ -322,8 +323,7 @@ protected:
|
||||
}
|
||||
|
||||
private:
|
||||
MapInterval* MapAddress(const Buffer* block, GPUVAddr gpu_addr, VAddr cpu_addr,
|
||||
std::size_t size) {
|
||||
MapInterval* MapAddress(Buffer* block, GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size) {
|
||||
const VectorMapInterval overlaps = GetMapsInRange(cpu_addr, size);
|
||||
if (overlaps.empty()) {
|
||||
auto& memory_manager = system.GPU().MemoryManager();
|
||||
@@ -370,15 +370,15 @@ private:
|
||||
}
|
||||
if (modified_inheritance) {
|
||||
map->MarkAsModified(true, GetModifiedTicks());
|
||||
if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) {
|
||||
if (Settings::IsGPULevelHigh() &&
|
||||
Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
|
||||
MarkForAsyncFlush(map);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
void UpdateBlock(const Buffer* block, VAddr start, VAddr end,
|
||||
const VectorMapInterval& overlaps) {
|
||||
void UpdateBlock(Buffer* block, VAddr start, VAddr end, const VectorMapInterval& overlaps) {
|
||||
const IntervalType base_interval{start, end};
|
||||
IntervalSet interval_set{};
|
||||
interval_set.add(base_interval);
|
||||
|
||||
@@ -14,50 +14,45 @@
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
using namespace Texture;
|
||||
|
||||
MaxwellDMA::MaxwellDMA(Core::System& system, MemoryManager& memory_manager)
|
||||
: system{system}, memory_manager{memory_manager} {}
|
||||
|
||||
void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
"Invalid MaxwellDMA register, increase the size of the Regs structure");
|
||||
ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register");
|
||||
|
||||
regs.reg_array[method] = method_argument;
|
||||
|
||||
#define MAXWELLDMA_REG_INDEX(field_name) \
|
||||
(offsetof(Tegra::Engines::MaxwellDMA::Regs, field_name) / sizeof(u32))
|
||||
|
||||
switch (method) {
|
||||
case MAXWELLDMA_REG_INDEX(exec): {
|
||||
HandleCopy();
|
||||
break;
|
||||
if (method == offsetof(Regs, launch_dma) / sizeof(u32)) {
|
||||
Launch();
|
||||
}
|
||||
}
|
||||
|
||||
#undef MAXWELLDMA_REG_INDEX
|
||||
}
|
||||
|
||||
void MaxwellDMA::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) {
|
||||
for (std::size_t i = 0; i < amount; i++) {
|
||||
for (size_t i = 0; i < amount; ++i) {
|
||||
CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1);
|
||||
}
|
||||
}
|
||||
|
||||
void MaxwellDMA::HandleCopy() {
|
||||
LOG_TRACE(HW_GPU, "Requested a DMA copy");
|
||||
|
||||
const GPUVAddr source = regs.src_address.Address();
|
||||
const GPUVAddr dest = regs.dst_address.Address();
|
||||
void MaxwellDMA::Launch() {
|
||||
LOG_TRACE(Render_OpenGL, "DMA copy 0x{:x} -> 0x{:x}", static_cast<GPUVAddr>(regs.offset_in),
|
||||
static_cast<GPUVAddr>(regs.offset_out));
|
||||
|
||||
// TODO(Subv): Perform more research and implement all features of this engine.
|
||||
ASSERT(regs.exec.enable_swizzle == 0);
|
||||
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.dst_params.pos_x == 0);
|
||||
ASSERT(regs.dst_params.pos_y == 0);
|
||||
const LaunchDMA& launch = regs.launch_dma;
|
||||
ASSERT(launch.remap_enable == 0);
|
||||
ASSERT(launch.semaphore_type == LaunchDMA::SemaphoreType::NONE);
|
||||
ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE);
|
||||
ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED);
|
||||
ASSERT(regs.dst_params.origin.x == 0);
|
||||
ASSERT(regs.dst_params.origin.y == 0);
|
||||
|
||||
if (!regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
|
||||
const bool is_src_pitch = launch.src_memory_layout == LaunchDMA::MemoryLayout::PITCH;
|
||||
const bool is_dst_pitch = launch.dst_memory_layout == LaunchDMA::MemoryLayout::PITCH;
|
||||
|
||||
if (!is_src_pitch && !is_dst_pitch) {
|
||||
// If both the source and the destination are in block layout, assert.
|
||||
UNREACHABLE_MSG("Tiled->Tiled DMA transfers are not yet implemented");
|
||||
return;
|
||||
@@ -66,144 +61,161 @@ void MaxwellDMA::HandleCopy() {
|
||||
// All copies here update the main memory, so mark all rasterizer states as invalid.
|
||||
system.GPU().Maxwell3D().OnMemoryWrite();
|
||||
|
||||
if (regs.exec.is_dst_linear && regs.exec.is_src_linear) {
|
||||
// When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
|
||||
// buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count,
|
||||
// y_count).
|
||||
if (!regs.exec.enable_2d) {
|
||||
memory_manager.CopyBlock(dest, source, regs.x_count);
|
||||
return;
|
||||
}
|
||||
|
||||
// If both the source and the destination are in linear layout, perform a line-by-line
|
||||
// copy. We're going to take a subrect of size (x_count, y_count) from the source
|
||||
// rectangle. There is no need to manually flush/invalidate the regions because
|
||||
// CopyBlock does that for us.
|
||||
for (u32 line = 0; line < regs.y_count; ++line) {
|
||||
const GPUVAddr source_line = source + line * regs.src_pitch;
|
||||
const GPUVAddr dest_line = dest + line * regs.dst_pitch;
|
||||
memory_manager.CopyBlock(dest_line, source_line, regs.x_count);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(regs.exec.enable_2d == 1);
|
||||
|
||||
if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
|
||||
|
||||
ASSERT(regs.src_params.BlockDepth() == 0);
|
||||
// Optimized path for micro copies.
|
||||
if (regs.dst_pitch * regs.y_count < Texture::GetGOBSize() && regs.dst_pitch <= 64) {
|
||||
const u32 bytes_per_pixel = regs.dst_pitch / regs.x_count;
|
||||
const std::size_t src_size = Texture::GetGOBSize();
|
||||
const std::size_t dst_size = regs.dst_pitch * regs.y_count;
|
||||
u32 pos_x = regs.src_params.pos_x;
|
||||
u32 pos_y = regs.src_params.pos_y;
|
||||
const u64 offset =
|
||||
Texture::GetGOBOffset(regs.src_params.size_x, regs.src_params.size_y, pos_x, pos_y,
|
||||
regs.src_params.BlockDepth(), bytes_per_pixel);
|
||||
const u32 x_in_gob = 64 / bytes_per_pixel;
|
||||
pos_x = pos_x % x_in_gob;
|
||||
pos_y = pos_y % 8;
|
||||
|
||||
if (read_buffer.size() < src_size) {
|
||||
read_buffer.resize(src_size);
|
||||
}
|
||||
|
||||
if (write_buffer.size() < dst_size) {
|
||||
write_buffer.resize(dst_size);
|
||||
}
|
||||
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
memory_manager.ReadBlock(source + offset, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(source + offset, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch,
|
||||
regs.src_params.size_x, bytes_per_pixel, read_buffer.data(),
|
||||
write_buffer.data(), regs.src_params.BlockHeight(), pos_x,
|
||||
pos_y);
|
||||
|
||||
memory_manager.WriteBlock(dest, write_buffer.data(), dst_size);
|
||||
|
||||
return;
|
||||
}
|
||||
// If the input is tiled and the output is linear, deswizzle the input and copy it over.
|
||||
const u32 bytes_per_pixel = regs.dst_pitch / regs.x_count;
|
||||
const std::size_t src_size = Texture::CalculateSize(
|
||||
true, bytes_per_pixel, regs.src_params.size_x, regs.src_params.size_y,
|
||||
regs.src_params.size_z, regs.src_params.BlockHeight(), regs.src_params.BlockDepth());
|
||||
|
||||
const std::size_t src_layer_size = Texture::CalculateSize(
|
||||
true, bytes_per_pixel, regs.src_params.size_x, regs.src_params.size_y, 1,
|
||||
regs.src_params.BlockHeight(), regs.src_params.BlockDepth());
|
||||
|
||||
const std::size_t dst_size = regs.dst_pitch * regs.y_count;
|
||||
|
||||
if (read_buffer.size() < src_size) {
|
||||
read_buffer.resize(src_size);
|
||||
}
|
||||
|
||||
if (write_buffer.size() < dst_size) {
|
||||
write_buffer.resize(dst_size);
|
||||
}
|
||||
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
memory_manager.ReadBlock(source, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(source, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
Texture::UnswizzleSubrect(
|
||||
regs.x_count, regs.y_count, regs.dst_pitch, regs.src_params.size_x, bytes_per_pixel,
|
||||
read_buffer.data() + src_layer_size * regs.src_params.pos_z, write_buffer.data(),
|
||||
regs.src_params.BlockHeight(), regs.src_params.pos_x, regs.src_params.pos_y);
|
||||
|
||||
memory_manager.WriteBlock(dest, write_buffer.data(), dst_size);
|
||||
if (is_src_pitch && is_dst_pitch) {
|
||||
CopyPitchToPitch();
|
||||
} else {
|
||||
ASSERT(regs.dst_params.BlockDepth() == 0);
|
||||
ASSERT(launch.multi_line_enable == 1);
|
||||
|
||||
const u32 bytes_per_pixel = regs.src_pitch / regs.x_count;
|
||||
|
||||
const std::size_t dst_size = Texture::CalculateSize(
|
||||
true, bytes_per_pixel, regs.dst_params.size_x, regs.dst_params.size_y,
|
||||
regs.dst_params.size_z, regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());
|
||||
|
||||
const std::size_t dst_layer_size = Texture::CalculateSize(
|
||||
true, bytes_per_pixel, regs.dst_params.size_x, regs.dst_params.size_y, 1,
|
||||
regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());
|
||||
|
||||
const std::size_t src_size = regs.src_pitch * regs.y_count;
|
||||
|
||||
if (read_buffer.size() < src_size) {
|
||||
read_buffer.resize(src_size);
|
||||
}
|
||||
|
||||
if (write_buffer.size() < dst_size) {
|
||||
write_buffer.resize(dst_size);
|
||||
}
|
||||
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
memory_manager.ReadBlock(source, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
|
||||
if (!is_src_pitch && is_dst_pitch) {
|
||||
CopyBlockLinearToPitch();
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(source, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size);
|
||||
CopyPitchToBlockLinear();
|
||||
}
|
||||
|
||||
// If the input is linear and the output is tiled, swizzle the input and copy it over.
|
||||
Texture::SwizzleSubrect(
|
||||
regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x, bytes_per_pixel,
|
||||
write_buffer.data() + dst_layer_size * regs.dst_params.pos_z, read_buffer.data(),
|
||||
regs.dst_params.BlockHeight(), regs.dst_params.pos_x, regs.dst_params.pos_y);
|
||||
|
||||
memory_manager.WriteBlock(dest, write_buffer.data(), dst_size);
|
||||
}
|
||||
}
|
||||
|
||||
void MaxwellDMA::CopyPitchToPitch() {
|
||||
// When `multi_line_enable` bit is disabled the copy is performed as if we were copying a 1D
|
||||
// buffer of length `line_length_in`.
|
||||
// Otherwise we copy a 2D image of dimensions (line_length_in, line_count).
|
||||
if (!regs.launch_dma.multi_line_enable) {
|
||||
memory_manager.CopyBlock(regs.offset_out, regs.offset_in, regs.line_length_in);
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform a line-by-line copy.
|
||||
// We're going to take a subrect of size (line_length_in, line_count) from the source rectangle.
|
||||
// There is no need to manually flush/invalidate the regions because CopyBlock does that for us.
|
||||
for (u32 line = 0; line < regs.line_count; ++line) {
|
||||
const GPUVAddr source_line = regs.offset_in + static_cast<size_t>(line) * regs.pitch_in;
|
||||
const GPUVAddr dest_line = regs.offset_out + static_cast<size_t>(line) * regs.pitch_out;
|
||||
memory_manager.CopyBlock(dest_line, source_line, regs.line_length_in);
|
||||
}
|
||||
}
|
||||
|
||||
void MaxwellDMA::CopyBlockLinearToPitch() {
|
||||
ASSERT(regs.src_params.block_size.depth == 0);
|
||||
|
||||
// Optimized path for micro copies.
|
||||
const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count;
|
||||
if (dst_size < GOB_SIZE && regs.pitch_out <= GOB_SIZE_X) {
|
||||
FastCopyBlockLinearToPitch();
|
||||
return;
|
||||
}
|
||||
|
||||
// Deswizzle the input and copy it over.
|
||||
const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in;
|
||||
const Parameters& src_params = regs.src_params;
|
||||
const u32 width = src_params.width;
|
||||
const u32 height = src_params.height;
|
||||
const u32 depth = src_params.depth;
|
||||
const u32 block_height = src_params.block_size.height;
|
||||
const u32 block_depth = src_params.block_size.depth;
|
||||
const size_t src_size =
|
||||
CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
|
||||
const size_t src_layer_size =
|
||||
CalculateSize(true, bytes_per_pixel, width, height, 1, block_height, block_depth);
|
||||
|
||||
if (read_buffer.size() < src_size) {
|
||||
read_buffer.resize(src_size);
|
||||
}
|
||||
if (write_buffer.size() < dst_size) {
|
||||
write_buffer.resize(dst_size);
|
||||
}
|
||||
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, width, bytes_per_pixel,
|
||||
read_buffer.data() + src_layer_size * src_params.layer, write_buffer.data(),
|
||||
block_height, src_params.origin.x, src_params.origin.y);
|
||||
|
||||
memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
void MaxwellDMA::CopyPitchToBlockLinear() {
|
||||
const auto& dst_params = regs.dst_params;
|
||||
const u32 bytes_per_pixel = regs.pitch_in / regs.line_length_in;
|
||||
const u32 width = dst_params.width;
|
||||
const u32 height = dst_params.height;
|
||||
const u32 depth = dst_params.depth;
|
||||
const u32 block_height = dst_params.block_size.height;
|
||||
const u32 block_depth = dst_params.block_size.depth;
|
||||
const size_t dst_size =
|
||||
CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
|
||||
const size_t dst_layer_size =
|
||||
CalculateSize(true, bytes_per_pixel, width, height, 1, block_height, block_depth);
|
||||
|
||||
const size_t src_size = static_cast<size_t>(regs.pitch_in) * regs.line_count;
|
||||
|
||||
if (read_buffer.size() < src_size) {
|
||||
read_buffer.resize(src_size);
|
||||
}
|
||||
if (write_buffer.size() < dst_size) {
|
||||
write_buffer.resize(dst_size);
|
||||
}
|
||||
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
// If the input is linear and the output is tiled, swizzle the input and copy it over.
|
||||
if (regs.dst_params.block_size.depth > 0) {
|
||||
ASSERT(dst_params.layer == 0);
|
||||
SwizzleSliceToVoxel(regs.line_length_in, regs.line_count, regs.pitch_in, width, height,
|
||||
bytes_per_pixel, block_height, block_depth, dst_params.origin.x,
|
||||
dst_params.origin.y, write_buffer.data(), read_buffer.data());
|
||||
} else {
|
||||
SwizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_in, width, bytes_per_pixel,
|
||||
write_buffer.data() + dst_layer_size * dst_params.layer, read_buffer.data(),
|
||||
block_height, dst_params.origin.x, dst_params.origin.y);
|
||||
}
|
||||
|
||||
memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
void MaxwellDMA::FastCopyBlockLinearToPitch() {
|
||||
const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in;
|
||||
const size_t src_size = GOB_SIZE;
|
||||
const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count;
|
||||
u32 pos_x = regs.src_params.origin.x;
|
||||
u32 pos_y = regs.src_params.origin.y;
|
||||
const u64 offset = GetGOBOffset(regs.src_params.width, regs.src_params.height, pos_x, pos_y,
|
||||
regs.src_params.block_size.height, bytes_per_pixel);
|
||||
const u32 x_in_gob = 64 / bytes_per_pixel;
|
||||
pos_x = pos_x % x_in_gob;
|
||||
pos_y = pos_y % 8;
|
||||
|
||||
if (read_buffer.size() < src_size) {
|
||||
read_buffer.resize(src_size);
|
||||
}
|
||||
|
||||
if (write_buffer.size() < dst_size) {
|
||||
write_buffer.resize(dst_size);
|
||||
}
|
||||
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
memory_manager.ReadBlock(regs.offset_in + offset, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_in + offset, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, regs.src_params.width,
|
||||
bytes_per_pixel, read_buffer.data(), write_buffer.data(),
|
||||
regs.src_params.block_size.height, pos_x, pos_y);
|
||||
|
||||
memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -24,12 +24,167 @@ class MemoryManager;
|
||||
namespace Tegra::Engines {
|
||||
|
||||
/**
|
||||
* This Engine is known as GK104_Copy. Documentation can be found in:
|
||||
* This engine is known as gk104_copy. Documentation can be found in:
|
||||
* https://github.com/NVIDIA/open-gpu-doc/blob/master/classes/dma-copy/clb0b5.h
|
||||
* https://github.com/envytools/envytools/blob/master/rnndb/fifo/gk104_copy.xml
|
||||
*/
|
||||
|
||||
class MaxwellDMA final : public EngineInterface {
|
||||
public:
|
||||
struct PackedGPUVAddr {
|
||||
u32 upper;
|
||||
u32 lower;
|
||||
|
||||
constexpr operator GPUVAddr() const noexcept {
|
||||
return (static_cast<GPUVAddr>(upper & 0xff) << 32) | lower;
|
||||
}
|
||||
};
|
||||
|
||||
union BlockSize {
|
||||
BitField<0, 4, u32> width;
|
||||
BitField<4, 4, u32> height;
|
||||
BitField<8, 4, u32> depth;
|
||||
BitField<12, 4, u32> gob_height;
|
||||
};
|
||||
static_assert(sizeof(BlockSize) == 4);
|
||||
|
||||
union Origin {
|
||||
BitField<0, 16, u32> x;
|
||||
BitField<16, 16, u32> y;
|
||||
};
|
||||
static_assert(sizeof(Origin) == 4);
|
||||
|
||||
struct Parameters {
|
||||
BlockSize block_size;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 depth;
|
||||
u32 layer;
|
||||
Origin origin;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 24);
|
||||
|
||||
struct Semaphore {
|
||||
PackedGPUVAddr address;
|
||||
u32 payload;
|
||||
};
|
||||
static_assert(sizeof(Semaphore) == 12);
|
||||
|
||||
struct RenderEnable {
|
||||
enum class Mode : u32 {
|
||||
FALSE = 0,
|
||||
TRUE = 1,
|
||||
CONDITIONAL = 2,
|
||||
RENDER_IF_EQUAL = 3,
|
||||
RENDER_IF_NOT_EQUAL = 4,
|
||||
};
|
||||
|
||||
PackedGPUVAddr address;
|
||||
BitField<0, 3, Mode> mode;
|
||||
};
|
||||
static_assert(sizeof(RenderEnable) == 12);
|
||||
|
||||
enum class PhysModeTarget : u32 {
|
||||
LOCAL_FB = 0,
|
||||
COHERENT_SYSMEM = 1,
|
||||
NONCOHERENT_SYSMEM = 2,
|
||||
};
|
||||
using PhysMode = BitField<0, 2, PhysModeTarget>;
|
||||
|
||||
union LaunchDMA {
|
||||
enum class DataTransferType : u32 {
|
||||
NONE = 0,
|
||||
PIPELINED = 1,
|
||||
NON_PIPELINED = 2,
|
||||
};
|
||||
|
||||
enum class SemaphoreType : u32 {
|
||||
NONE = 0,
|
||||
RELEASE_ONE_WORD_SEMAPHORE = 1,
|
||||
RELEASE_FOUR_WORD_SEMAPHORE = 2,
|
||||
};
|
||||
|
||||
enum class InterruptType : u32 {
|
||||
NONE = 0,
|
||||
BLOCKING = 1,
|
||||
NON_BLOCKING = 2,
|
||||
};
|
||||
|
||||
enum class MemoryLayout : u32 {
|
||||
BLOCKLINEAR = 0,
|
||||
PITCH = 1,
|
||||
};
|
||||
|
||||
enum class Type : u32 {
|
||||
VIRTUAL = 0,
|
||||
PHYSICAL = 1,
|
||||
};
|
||||
|
||||
enum class SemaphoreReduction : u32 {
|
||||
IMIN = 0,
|
||||
IMAX = 1,
|
||||
IXOR = 2,
|
||||
IAND = 3,
|
||||
IOR = 4,
|
||||
IADD = 5,
|
||||
INC = 6,
|
||||
DEC = 7,
|
||||
FADD = 0xA,
|
||||
};
|
||||
|
||||
enum class SemaphoreReductionSign : u32 {
|
||||
SIGNED = 0,
|
||||
UNSIGNED = 1,
|
||||
};
|
||||
|
||||
enum class BypassL2 : u32 {
|
||||
USE_PTE_SETTING = 0,
|
||||
FORCE_VOLATILE = 1,
|
||||
};
|
||||
|
||||
BitField<0, 2, DataTransferType> data_transfer_type;
|
||||
BitField<2, 1, u32> flush_enable;
|
||||
BitField<3, 2, SemaphoreType> semaphore_type;
|
||||
BitField<5, 2, InterruptType> interrupt_type;
|
||||
BitField<7, 1, MemoryLayout> src_memory_layout;
|
||||
BitField<8, 1, MemoryLayout> dst_memory_layout;
|
||||
BitField<9, 1, u32> multi_line_enable;
|
||||
BitField<10, 1, u32> remap_enable;
|
||||
BitField<11, 1, u32> rmwdisable;
|
||||
BitField<12, 1, Type> src_type;
|
||||
BitField<13, 1, Type> dst_type;
|
||||
BitField<14, 4, SemaphoreReduction> semaphore_reduction;
|
||||
BitField<18, 1, SemaphoreReductionSign> semaphore_reduction_sign;
|
||||
BitField<19, 1, u32> reduction_enable;
|
||||
BitField<20, 1, BypassL2> bypass_l2;
|
||||
};
|
||||
static_assert(sizeof(LaunchDMA) == 4);
|
||||
|
||||
struct RemapConst {
|
||||
enum Swizzle : u32 {
|
||||
SRC_X = 0,
|
||||
SRC_Y = 1,
|
||||
SRC_Z = 2,
|
||||
SRC_W = 3,
|
||||
CONST_A = 4,
|
||||
CONST_B = 5,
|
||||
NO_WRITE = 6,
|
||||
};
|
||||
|
||||
PackedGPUVAddr address;
|
||||
|
||||
union {
|
||||
BitField<0, 3, Swizzle> dst_x;
|
||||
BitField<4, 3, Swizzle> dst_y;
|
||||
BitField<8, 3, Swizzle> dst_z;
|
||||
BitField<12, 3, Swizzle> dst_w;
|
||||
BitField<16, 2, u32> component_size_minus_one;
|
||||
BitField<20, 2, u32> num_src_components_minus_one;
|
||||
BitField<24, 2, u32> num_dst_components_minus_one;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(RemapConst) == 12);
|
||||
|
||||
explicit MaxwellDMA(Core::System& system, MemoryManager& memory_manager);
|
||||
~MaxwellDMA() = default;
|
||||
|
||||
@@ -40,144 +195,19 @@ public:
|
||||
void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) override;
|
||||
|
||||
struct Regs {
|
||||
static constexpr std::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 block_height.Value();
|
||||
}
|
||||
|
||||
u32 BlockDepth() const {
|
||||
return block_depth.Value();
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(Parameters) == 24, "Parameters has wrong size");
|
||||
|
||||
enum class ComponentMode : u32 {
|
||||
Src0 = 0,
|
||||
Src1 = 1,
|
||||
Src2 = 2,
|
||||
Src3 = 3,
|
||||
Const0 = 4,
|
||||
Const1 = 5,
|
||||
Zero = 6,
|
||||
};
|
||||
|
||||
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_UNION_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_UNION_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_UNION_PADDING_WORDS(0xB8);
|
||||
|
||||
u32 const0;
|
||||
u32 const1;
|
||||
union {
|
||||
BitField<0, 4, ComponentMode> component0;
|
||||
BitField<4, 4, ComponentMode> component1;
|
||||
BitField<8, 4, ComponentMode> component2;
|
||||
BitField<12, 4, ComponentMode> component3;
|
||||
BitField<16, 2, u32> component_size;
|
||||
BitField<20, 3, u32> src_num_components;
|
||||
BitField<24, 3, u32> dst_num_components;
|
||||
|
||||
u32 SrcBytePerPixel() const {
|
||||
return src_num_components.Value() * component_size.Value();
|
||||
}
|
||||
u32 DstBytePerPixel() const {
|
||||
return dst_num_components.Value() * component_size.Value();
|
||||
}
|
||||
} swizzle_config;
|
||||
|
||||
Parameters dst_params;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(1);
|
||||
|
||||
Parameters src_params;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x13);
|
||||
};
|
||||
std::array<u32, NUM_REGS> reg_array;
|
||||
};
|
||||
} regs{};
|
||||
|
||||
private:
|
||||
/// Performs the copy from the source buffer to the destination buffer as configured in the
|
||||
/// registers.
|
||||
void Launch();
|
||||
|
||||
void CopyPitchToPitch();
|
||||
|
||||
void CopyBlockLinearToPitch();
|
||||
|
||||
void CopyPitchToBlockLinear();
|
||||
|
||||
void FastCopyBlockLinearToPitch();
|
||||
|
||||
Core::System& system;
|
||||
|
||||
MemoryManager& memory_manager;
|
||||
@@ -185,28 +215,58 @@ private:
|
||||
std::vector<u8> read_buffer;
|
||||
std::vector<u8> write_buffer;
|
||||
|
||||
/// Performs the copy from the source buffer to the destination buffer as configured in the
|
||||
/// registers.
|
||||
void HandleCopy();
|
||||
};
|
||||
static constexpr std::size_t NUM_REGS = 0x800;
|
||||
struct Regs {
|
||||
union {
|
||||
struct {
|
||||
u32 reserved[0x40];
|
||||
u32 nop;
|
||||
u32 reserved01[0xf];
|
||||
u32 pm_trigger;
|
||||
u32 reserved02[0x3f];
|
||||
Semaphore semaphore;
|
||||
u32 reserved03[0x2];
|
||||
RenderEnable render_enable;
|
||||
PhysMode src_phys_mode;
|
||||
PhysMode dst_phys_mode;
|
||||
u32 reserved04[0x26];
|
||||
LaunchDMA launch_dma;
|
||||
u32 reserved05[0x3f];
|
||||
PackedGPUVAddr offset_in;
|
||||
PackedGPUVAddr offset_out;
|
||||
u32 pitch_in;
|
||||
u32 pitch_out;
|
||||
u32 line_length_in;
|
||||
u32 line_count;
|
||||
u32 reserved06[0xb8];
|
||||
RemapConst remap_const;
|
||||
Parameters dst_params;
|
||||
u32 reserved07[0x1];
|
||||
Parameters src_params;
|
||||
u32 reserved08[0x275];
|
||||
u32 pm_trigger_end;
|
||||
u32 reserved09[0x3ba];
|
||||
};
|
||||
std::array<u32, NUM_REGS> reg_array;
|
||||
};
|
||||
} regs{};
|
||||
|
||||
#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(const0, 0x1C0);
|
||||
ASSERT_REG_POSITION(const1, 0x1C1);
|
||||
ASSERT_REG_POSITION(swizzle_config, 0x1C2);
|
||||
ASSERT_REG_POSITION(dst_params, 0x1C3);
|
||||
ASSERT_REG_POSITION(src_params, 0x1CA);
|
||||
ASSERT_REG_POSITION(launch_dma, 0xC0);
|
||||
ASSERT_REG_POSITION(offset_in, 0x100);
|
||||
ASSERT_REG_POSITION(offset_out, 0x102);
|
||||
ASSERT_REG_POSITION(pitch_in, 0x104);
|
||||
ASSERT_REG_POSITION(pitch_out, 0x105);
|
||||
ASSERT_REG_POSITION(line_length_in, 0x106);
|
||||
ASSERT_REG_POSITION(line_count, 0x107);
|
||||
ASSERT_REG_POSITION(remap_const, 0x1C0);
|
||||
ASSERT_REG_POSITION(dst_params, 0x1C3);
|
||||
ASSERT_REG_POSITION(src_params, 0x1CA);
|
||||
|
||||
#undef ASSERT_REG_POSITION
|
||||
};
|
||||
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -157,7 +157,7 @@ u64 GPU::GetTicks() const {
|
||||
constexpr u64 gpu_ticks_den = 625;
|
||||
|
||||
u64 nanoseconds = system.CoreTiming().GetGlobalTimeNs().count();
|
||||
if (Settings::values.use_fast_gpu_time) {
|
||||
if (Settings::values.use_fast_gpu_time.GetValue()) {
|
||||
nanoseconds /= 256;
|
||||
}
|
||||
const u64 nanoseconds_num = nanoseconds / gpu_ticks_den;
|
||||
|
||||
@@ -132,7 +132,7 @@ public:
|
||||
}
|
||||
|
||||
query->BindCounter(Stream(type).Current(), timestamp);
|
||||
if (Settings::values.use_asynchronous_gpu_emulation) {
|
||||
if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
|
||||
AsyncFlushQuery(cpu_addr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ RendererBase::~RendererBase() = default;
|
||||
void RendererBase::RefreshBaseSettings() {
|
||||
UpdateCurrentFramebufferLayout();
|
||||
|
||||
renderer_settings.use_framelimiter = Settings::values.use_frame_limit;
|
||||
renderer_settings.use_framelimiter = Settings::values.use_frame_limit.GetValue();
|
||||
renderer_settings.set_background_color = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,20 +34,27 @@ Buffer::Buffer(const Device& device, VAddr cpu_addr, std::size_t size)
|
||||
|
||||
Buffer::~Buffer() = default;
|
||||
|
||||
void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) const {
|
||||
void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) {
|
||||
glNamedBufferSubData(Handle(), static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size),
|
||||
data);
|
||||
}
|
||||
|
||||
void Buffer::Download(std::size_t offset, std::size_t size, u8* data) const {
|
||||
void Buffer::Download(std::size_t offset, std::size_t size, u8* data) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Buffer_Download);
|
||||
const GLsizeiptr gl_size = static_cast<GLsizeiptr>(size);
|
||||
const GLintptr gl_offset = static_cast<GLintptr>(offset);
|
||||
if (read_buffer.handle == 0) {
|
||||
read_buffer.Create();
|
||||
glNamedBufferData(read_buffer.handle, static_cast<GLsizeiptr>(Size()), nullptr,
|
||||
GL_STREAM_READ);
|
||||
}
|
||||
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
|
||||
glGetNamedBufferSubData(Handle(), static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size),
|
||||
data);
|
||||
glCopyNamedBufferSubData(gl_buffer.handle, read_buffer.handle, gl_offset, gl_offset, gl_size);
|
||||
glGetNamedBufferSubData(read_buffer.handle, gl_offset, gl_size, data);
|
||||
}
|
||||
|
||||
void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset,
|
||||
std::size_t size) const {
|
||||
std::size_t size) {
|
||||
glCopyNamedBufferSubData(src.Handle(), Handle(), static_cast<GLintptr>(src_offset),
|
||||
static_cast<GLintptr>(dst_offset), static_cast<GLsizeiptr>(size));
|
||||
}
|
||||
|
||||
@@ -28,12 +28,12 @@ public:
|
||||
explicit Buffer(const Device& device, VAddr cpu_addr, std::size_t size);
|
||||
~Buffer();
|
||||
|
||||
void Upload(std::size_t offset, std::size_t size, const u8* data) const;
|
||||
void Upload(std::size_t offset, std::size_t size, const u8* data);
|
||||
|
||||
void Download(std::size_t offset, std::size_t size, u8* data) const;
|
||||
void Download(std::size_t offset, std::size_t size, u8* data);
|
||||
|
||||
void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset,
|
||||
std::size_t size) const;
|
||||
std::size_t size);
|
||||
|
||||
GLuint Handle() const noexcept {
|
||||
return gl_buffer.handle;
|
||||
@@ -45,6 +45,7 @@ public:
|
||||
|
||||
private:
|
||||
OGLBuffer gl_buffer;
|
||||
OGLBuffer read_buffer;
|
||||
u64 gpu_address = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -229,15 +229,15 @@ Device::Device()
|
||||
// uniform buffers as "push constants"
|
||||
has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data;
|
||||
|
||||
use_assembly_shaders = Settings::values.use_assembly_shaders && GLAD_GL_NV_gpu_program5 &&
|
||||
GLAD_GL_NV_compute_program5 && GLAD_GL_NV_transform_feedback &&
|
||||
GLAD_GL_NV_transform_feedback2;
|
||||
use_assembly_shaders = Settings::values.use_assembly_shaders.GetValue() &&
|
||||
GLAD_GL_NV_gpu_program5 && GLAD_GL_NV_compute_program5 &&
|
||||
GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2;
|
||||
|
||||
LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi);
|
||||
LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug);
|
||||
LOG_INFO(Render_OpenGL, "Renderer_PreciseBug: {}", has_precise_bug);
|
||||
|
||||
if (Settings::values.use_assembly_shaders && !use_assembly_shaders) {
|
||||
if (Settings::values.use_assembly_shaders.GetValue() && !use_assembly_shaders) {
|
||||
LOG_ERROR(Render_OpenGL, "Assembly shaders enabled but not supported");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ ShaderDiskCacheOpenGL::~ShaderDiskCacheOpenGL() = default;
|
||||
std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTransferable() {
|
||||
// Skip games without title id
|
||||
const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0;
|
||||
if (!Settings::values.use_disk_shader_cache || !has_title_id) {
|
||||
if (!Settings::values.use_disk_shader_cache.GetValue() || !has_title_id) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -191,6 +191,12 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
|
||||
} else {
|
||||
return GL_MIRROR_CLAMP_TO_EDGE;
|
||||
}
|
||||
case Tegra::Texture::WrapMode::MirrorOnceClampOGL:
|
||||
if (GL_EXT_texture_mirror_clamp) {
|
||||
return GL_MIRROR_CLAMP_EXT;
|
||||
} else {
|
||||
return GL_MIRROR_CLAMP_TO_EDGE;
|
||||
}
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));
|
||||
return GL_REPEAT;
|
||||
|
||||
@@ -455,8 +455,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
|
||||
void RendererOpenGL::InitOpenGLObjects() {
|
||||
frame_mailbox = std::make_unique<FrameMailbox>();
|
||||
|
||||
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||
0.0f);
|
||||
glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
|
||||
Settings::values.bg_blue.GetValue(), 0.0f);
|
||||
|
||||
// Create shader programs
|
||||
OGLShader vertex_shader;
|
||||
@@ -561,8 +561,8 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
|
||||
void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
|
||||
if (renderer_settings.set_background_color) {
|
||||
// Update background color before drawing
|
||||
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||
0.0f);
|
||||
glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
|
||||
Settings::values.bg_blue.GetValue(), 0.0f);
|
||||
}
|
||||
|
||||
// Set projection matrix
|
||||
|
||||
@@ -39,52 +39,18 @@ constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
void FixedPipelineState::DepthStencil::Fill(const Maxwell& regs) noexcept {
|
||||
raw = 0;
|
||||
front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail));
|
||||
front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail));
|
||||
front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass));
|
||||
front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func));
|
||||
if (regs.stencil_two_side_enable) {
|
||||
back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail));
|
||||
back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail));
|
||||
back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass));
|
||||
back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func));
|
||||
} else {
|
||||
back.action_stencil_fail.Assign(front.action_stencil_fail);
|
||||
back.action_depth_fail.Assign(front.action_depth_fail);
|
||||
back.action_depth_pass.Assign(front.action_depth_pass);
|
||||
back.test_func.Assign(front.test_func);
|
||||
}
|
||||
depth_test_enable.Assign(regs.depth_test_enable);
|
||||
depth_write_enable.Assign(regs.depth_write_enabled);
|
||||
depth_bounds_enable.Assign(regs.depth_bounds_enable);
|
||||
stencil_enable.Assign(regs.stencil_enable);
|
||||
depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
|
||||
}
|
||||
|
||||
void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept {
|
||||
void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_state) {
|
||||
const auto& clip = regs.view_volume_clip_control;
|
||||
const std::array enabled_lut = {regs.polygon_offset_point_enable,
|
||||
regs.polygon_offset_line_enable,
|
||||
regs.polygon_offset_fill_enable};
|
||||
const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
|
||||
|
||||
u32 packed_front_face = PackFrontFace(regs.front_face);
|
||||
if (regs.screen_y_control.triangle_rast_flip != 0) {
|
||||
// Flip front face
|
||||
packed_front_face = 1 - packed_front_face;
|
||||
}
|
||||
|
||||
raw = 0;
|
||||
topology.Assign(topology_index);
|
||||
primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
|
||||
cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0);
|
||||
depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
|
||||
depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value());
|
||||
ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
|
||||
cull_face.Assign(PackCullFace(regs.cull_face));
|
||||
front_face.Assign(packed_front_face);
|
||||
polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front));
|
||||
patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
|
||||
tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value()));
|
||||
@@ -93,19 +59,37 @@ void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept {
|
||||
logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
|
||||
logic_op.Assign(PackLogicOp(regs.logic_op.operation));
|
||||
rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
|
||||
std::memcpy(&point_size, ®s.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast
|
||||
}
|
||||
|
||||
void FixedPipelineState::ColorBlending::Fill(const Maxwell& regs) noexcept {
|
||||
std::memcpy(&point_size, ®s.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast
|
||||
|
||||
for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
|
||||
binding_divisors[index] =
|
||||
regs.instanced_arrays.IsInstancingEnabled(index) ? regs.vertex_array[index].divisor : 0;
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
|
||||
const auto& input = regs.vertex_attrib_format[index];
|
||||
auto& attribute = attributes[index];
|
||||
attribute.raw = 0;
|
||||
attribute.enabled.Assign(input.IsConstant() ? 0 : 1);
|
||||
attribute.buffer.Assign(input.buffer);
|
||||
attribute.offset.Assign(input.offset);
|
||||
attribute.type.Assign(static_cast<u32>(input.type.Value()));
|
||||
attribute.size.Assign(static_cast<u32>(input.size.Value()));
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < std::size(attachments); ++index) {
|
||||
attachments[index].Fill(regs, index);
|
||||
}
|
||||
}
|
||||
|
||||
void FixedPipelineState::ViewportSwizzles::Fill(const Maxwell& regs) noexcept {
|
||||
const auto& transform = regs.viewport_transform;
|
||||
std::transform(transform.begin(), transform.end(), swizzles.begin(),
|
||||
std::transform(transform.begin(), transform.end(), viewport_swizzles.begin(),
|
||||
[](const auto& viewport) { return static_cast<u16>(viewport.swizzle.raw); });
|
||||
|
||||
if (!has_extended_dynamic_state) {
|
||||
no_extended_dynamic_state.Assign(1);
|
||||
dynamic_state.Fill(regs);
|
||||
}
|
||||
}
|
||||
|
||||
void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) {
|
||||
@@ -147,20 +131,57 @@ void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size
|
||||
enable.Assign(1);
|
||||
}
|
||||
|
||||
void FixedPipelineState::Fill(const Maxwell& regs) {
|
||||
rasterizer.Fill(regs);
|
||||
depth_stencil.Fill(regs);
|
||||
color_blending.Fill(regs);
|
||||
viewport_swizzles.Fill(regs);
|
||||
void FixedPipelineState::DynamicState::Fill(const Maxwell& regs) {
|
||||
const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
|
||||
u32 packed_front_face = PackFrontFace(regs.front_face);
|
||||
if (regs.screen_y_control.triangle_rast_flip != 0) {
|
||||
// Flip front face
|
||||
packed_front_face = 1 - packed_front_face;
|
||||
}
|
||||
|
||||
raw1 = 0;
|
||||
raw2 = 0;
|
||||
front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail));
|
||||
front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail));
|
||||
front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass));
|
||||
front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func));
|
||||
if (regs.stencil_two_side_enable) {
|
||||
back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail));
|
||||
back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail));
|
||||
back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass));
|
||||
back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func));
|
||||
} else {
|
||||
back.action_stencil_fail.Assign(front.action_stencil_fail);
|
||||
back.action_depth_fail.Assign(front.action_depth_fail);
|
||||
back.action_depth_pass.Assign(front.action_depth_pass);
|
||||
back.test_func.Assign(front.test_func);
|
||||
}
|
||||
stencil_enable.Assign(regs.stencil_enable);
|
||||
depth_write_enable.Assign(regs.depth_write_enabled);
|
||||
depth_bounds_enable.Assign(regs.depth_bounds_enable);
|
||||
depth_test_enable.Assign(regs.depth_test_enable);
|
||||
front_face.Assign(packed_front_face);
|
||||
depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
|
||||
topology.Assign(topology_index);
|
||||
cull_face.Assign(PackCullFace(regs.cull_face));
|
||||
cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0);
|
||||
|
||||
for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
|
||||
const auto& input = regs.vertex_array[index];
|
||||
VertexBinding& binding = vertex_bindings[index];
|
||||
binding.raw = 0;
|
||||
binding.enabled.Assign(input.IsEnabled() ? 1 : 0);
|
||||
binding.stride.Assign(static_cast<u16>(input.stride.Value()));
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t FixedPipelineState::Hash() const noexcept {
|
||||
const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this);
|
||||
const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size());
|
||||
return static_cast<std::size_t>(hash);
|
||||
}
|
||||
|
||||
bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept {
|
||||
return std::memcmp(this, &rhs, sizeof *this) == 0;
|
||||
return std::memcmp(this, &rhs, Size()) == 0;
|
||||
}
|
||||
|
||||
u32 FixedPipelineState::PackComparisonOp(Maxwell::ComparisonOp op) noexcept {
|
||||
|
||||
@@ -60,14 +60,6 @@ struct FixedPipelineState {
|
||||
|
||||
void Fill(const Maxwell& regs, std::size_t index);
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
bool operator==(const BlendingAttachment& rhs) const noexcept;
|
||||
|
||||
bool operator!=(const BlendingAttachment& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
constexpr std::array<bool, 4> Mask() const noexcept {
|
||||
return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0};
|
||||
}
|
||||
@@ -97,83 +89,77 @@ struct FixedPipelineState {
|
||||
}
|
||||
};
|
||||
|
||||
struct VertexInput {
|
||||
union Binding {
|
||||
u16 raw;
|
||||
BitField<0, 1, u16> enabled;
|
||||
BitField<1, 12, u16> stride;
|
||||
};
|
||||
union VertexAttribute {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> enabled;
|
||||
BitField<1, 5, u32> buffer;
|
||||
BitField<6, 14, u32> offset;
|
||||
BitField<20, 3, u32> type;
|
||||
BitField<23, 6, u32> size;
|
||||
|
||||
union Attribute {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> enabled;
|
||||
BitField<1, 5, u32> buffer;
|
||||
BitField<6, 14, u32> offset;
|
||||
BitField<20, 3, u32> type;
|
||||
BitField<23, 6, u32> size;
|
||||
|
||||
constexpr Maxwell::VertexAttribute::Type Type() const noexcept {
|
||||
return static_cast<Maxwell::VertexAttribute::Type>(type.Value());
|
||||
}
|
||||
|
||||
constexpr Maxwell::VertexAttribute::Size Size() const noexcept {
|
||||
return static_cast<Maxwell::VertexAttribute::Size>(size.Value());
|
||||
}
|
||||
};
|
||||
|
||||
std::array<Binding, Maxwell::NumVertexArrays> bindings;
|
||||
std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
|
||||
std::array<Attribute, Maxwell::NumVertexAttributes> attributes;
|
||||
|
||||
void SetBinding(std::size_t index, bool enabled, u32 stride, u32 divisor) noexcept {
|
||||
auto& binding = bindings[index];
|
||||
binding.raw = 0;
|
||||
binding.enabled.Assign(enabled ? 1 : 0);
|
||||
binding.stride.Assign(static_cast<u16>(stride));
|
||||
binding_divisors[index] = divisor;
|
||||
constexpr Maxwell::VertexAttribute::Type Type() const noexcept {
|
||||
return static_cast<Maxwell::VertexAttribute::Type>(type.Value());
|
||||
}
|
||||
|
||||
void SetAttribute(std::size_t index, bool enabled, u32 buffer, u32 offset,
|
||||
Maxwell::VertexAttribute::Type type,
|
||||
Maxwell::VertexAttribute::Size size) noexcept {
|
||||
auto& attribute = attributes[index];
|
||||
attribute.raw = 0;
|
||||
attribute.enabled.Assign(enabled ? 1 : 0);
|
||||
attribute.buffer.Assign(buffer);
|
||||
attribute.offset.Assign(offset);
|
||||
attribute.type.Assign(static_cast<u32>(type));
|
||||
attribute.size.Assign(static_cast<u32>(size));
|
||||
constexpr Maxwell::VertexAttribute::Size Size() const noexcept {
|
||||
return static_cast<Maxwell::VertexAttribute::Size>(size.Value());
|
||||
}
|
||||
};
|
||||
|
||||
struct Rasterizer {
|
||||
template <std::size_t Position>
|
||||
union StencilFace {
|
||||
BitField<Position + 0, 3, u32> action_stencil_fail;
|
||||
BitField<Position + 3, 3, u32> action_depth_fail;
|
||||
BitField<Position + 6, 3, u32> action_depth_pass;
|
||||
BitField<Position + 9, 3, u32> test_func;
|
||||
|
||||
Maxwell::StencilOp ActionStencilFail() const noexcept {
|
||||
return UnpackStencilOp(action_stencil_fail);
|
||||
}
|
||||
|
||||
Maxwell::StencilOp ActionDepthFail() const noexcept {
|
||||
return UnpackStencilOp(action_depth_fail);
|
||||
}
|
||||
|
||||
Maxwell::StencilOp ActionDepthPass() const noexcept {
|
||||
return UnpackStencilOp(action_depth_pass);
|
||||
}
|
||||
|
||||
Maxwell::ComparisonOp TestFunc() const noexcept {
|
||||
return UnpackComparisonOp(test_func);
|
||||
}
|
||||
};
|
||||
|
||||
union VertexBinding {
|
||||
u16 raw;
|
||||
BitField<0, 12, u16> stride;
|
||||
BitField<12, 1, u16> enabled;
|
||||
};
|
||||
|
||||
struct DynamicState {
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 4, u32> topology;
|
||||
BitField<4, 1, u32> primitive_restart_enable;
|
||||
BitField<5, 1, u32> cull_enable;
|
||||
BitField<6, 1, u32> depth_bias_enable;
|
||||
BitField<7, 1, u32> depth_clamp_disabled;
|
||||
BitField<8, 1, u32> ndc_minus_one_to_one;
|
||||
BitField<9, 2, u32> cull_face;
|
||||
BitField<11, 1, u32> front_face;
|
||||
BitField<12, 2, u32> polygon_mode;
|
||||
BitField<14, 5, u32> patch_control_points_minus_one;
|
||||
BitField<19, 2, u32> tessellation_primitive;
|
||||
BitField<21, 2, u32> tessellation_spacing;
|
||||
BitField<23, 1, u32> tessellation_clockwise;
|
||||
BitField<24, 1, u32> logic_op_enable;
|
||||
BitField<25, 4, u32> logic_op;
|
||||
BitField<29, 1, u32> rasterize_enable;
|
||||
u32 raw1;
|
||||
StencilFace<0> front;
|
||||
StencilFace<12> back;
|
||||
BitField<24, 1, u32> stencil_enable;
|
||||
BitField<25, 1, u32> depth_write_enable;
|
||||
BitField<26, 1, u32> depth_bounds_enable;
|
||||
BitField<27, 1, u32> depth_test_enable;
|
||||
BitField<28, 1, u32> front_face;
|
||||
BitField<29, 3, u32> depth_test_func;
|
||||
};
|
||||
union {
|
||||
u32 raw2;
|
||||
BitField<0, 4, u32> topology;
|
||||
BitField<4, 2, u32> cull_face;
|
||||
BitField<6, 1, u32> cull_enable;
|
||||
};
|
||||
std::array<VertexBinding, Maxwell::NumVertexArrays> vertex_bindings;
|
||||
|
||||
// TODO(Rodrigo): Move this to push constants
|
||||
u32 point_size;
|
||||
void Fill(const Maxwell& regs);
|
||||
|
||||
void Fill(const Maxwell& regs) noexcept;
|
||||
|
||||
constexpr Maxwell::PrimitiveTopology Topology() const noexcept {
|
||||
return static_cast<Maxwell::PrimitiveTopology>(topology.Value());
|
||||
Maxwell::ComparisonOp DepthTestFunc() const noexcept {
|
||||
return UnpackComparisonOp(depth_test_func);
|
||||
}
|
||||
|
||||
Maxwell::CullFace CullFace() const noexcept {
|
||||
@@ -183,70 +169,36 @@ struct FixedPipelineState {
|
||||
Maxwell::FrontFace FrontFace() const noexcept {
|
||||
return UnpackFrontFace(front_face.Value());
|
||||
}
|
||||
};
|
||||
|
||||
struct DepthStencil {
|
||||
template <std::size_t Position>
|
||||
union StencilFace {
|
||||
BitField<Position + 0, 3, u32> action_stencil_fail;
|
||||
BitField<Position + 3, 3, u32> action_depth_fail;
|
||||
BitField<Position + 6, 3, u32> action_depth_pass;
|
||||
BitField<Position + 9, 3, u32> test_func;
|
||||
|
||||
Maxwell::StencilOp ActionStencilFail() const noexcept {
|
||||
return UnpackStencilOp(action_stencil_fail);
|
||||
}
|
||||
|
||||
Maxwell::StencilOp ActionDepthFail() const noexcept {
|
||||
return UnpackStencilOp(action_depth_fail);
|
||||
}
|
||||
|
||||
Maxwell::StencilOp ActionDepthPass() const noexcept {
|
||||
return UnpackStencilOp(action_depth_pass);
|
||||
}
|
||||
|
||||
Maxwell::ComparisonOp TestFunc() const noexcept {
|
||||
return UnpackComparisonOp(test_func);
|
||||
}
|
||||
};
|
||||
|
||||
union {
|
||||
u32 raw;
|
||||
StencilFace<0> front;
|
||||
StencilFace<12> back;
|
||||
BitField<24, 1, u32> depth_test_enable;
|
||||
BitField<25, 1, u32> depth_write_enable;
|
||||
BitField<26, 1, u32> depth_bounds_enable;
|
||||
BitField<27, 1, u32> stencil_enable;
|
||||
BitField<28, 3, u32> depth_test_func;
|
||||
};
|
||||
|
||||
void Fill(const Maxwell& regs) noexcept;
|
||||
|
||||
Maxwell::ComparisonOp DepthTestFunc() const noexcept {
|
||||
return UnpackComparisonOp(depth_test_func);
|
||||
constexpr Maxwell::PrimitiveTopology Topology() const noexcept {
|
||||
return static_cast<Maxwell::PrimitiveTopology>(topology.Value());
|
||||
}
|
||||
};
|
||||
|
||||
struct ColorBlending {
|
||||
std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
|
||||
|
||||
void Fill(const Maxwell& regs) noexcept;
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> no_extended_dynamic_state;
|
||||
BitField<2, 1, u32> primitive_restart_enable;
|
||||
BitField<3, 1, u32> depth_bias_enable;
|
||||
BitField<4, 1, u32> depth_clamp_disabled;
|
||||
BitField<5, 1, u32> ndc_minus_one_to_one;
|
||||
BitField<6, 2, u32> polygon_mode;
|
||||
BitField<8, 5, u32> patch_control_points_minus_one;
|
||||
BitField<13, 2, u32> tessellation_primitive;
|
||||
BitField<15, 2, u32> tessellation_spacing;
|
||||
BitField<17, 1, u32> tessellation_clockwise;
|
||||
BitField<18, 1, u32> logic_op_enable;
|
||||
BitField<19, 4, u32> logic_op;
|
||||
BitField<23, 1, u32> rasterize_enable;
|
||||
};
|
||||
u32 point_size;
|
||||
std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
|
||||
std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes;
|
||||
std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
|
||||
std::array<u16, Maxwell::NumViewports> viewport_swizzles;
|
||||
DynamicState dynamic_state;
|
||||
|
||||
struct ViewportSwizzles {
|
||||
std::array<u16, Maxwell::NumViewports> swizzles;
|
||||
|
||||
void Fill(const Maxwell& regs) noexcept;
|
||||
};
|
||||
|
||||
VertexInput vertex_input;
|
||||
Rasterizer rasterizer;
|
||||
DepthStencil depth_stencil;
|
||||
ColorBlending color_blending;
|
||||
ViewportSwizzles viewport_swizzles;
|
||||
|
||||
void Fill(const Maxwell& regs);
|
||||
void Fill(const Maxwell& regs, bool has_extended_dynamic_state);
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
@@ -255,6 +207,11 @@ struct FixedPipelineState {
|
||||
bool operator!=(const FixedPipelineState& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
std::size_t Size() const noexcept {
|
||||
const std::size_t total_size = sizeof *this;
|
||||
return total_size - (no_extended_dynamic_state != 0 ? 0 : sizeof(DynamicState));
|
||||
}
|
||||
};
|
||||
static_assert(std::has_unique_object_representations_v<FixedPipelineState>);
|
||||
static_assert(std::is_trivially_copyable_v<FixedPipelineState>);
|
||||
|
||||
@@ -409,7 +409,7 @@ bool RendererVulkan::PickDevices() {
|
||||
return false;
|
||||
}
|
||||
|
||||
const s32 device_index = Settings::values.vulkan_device;
|
||||
const s32 device_index = Settings::values.vulkan_device.GetValue();
|
||||
if (device_index < 0 || device_index >= static_cast<s32>(devices->size())) {
|
||||
LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index);
|
||||
return false;
|
||||
|
||||
@@ -56,7 +56,7 @@ Buffer::Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VKSchedu
|
||||
|
||||
Buffer::~Buffer() = default;
|
||||
|
||||
void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) const {
|
||||
void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) {
|
||||
const auto& staging = staging_pool.GetUnusedBuffer(size, true);
|
||||
std::memcpy(staging.commit->Map(size), data, size);
|
||||
|
||||
@@ -81,7 +81,7 @@ void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) const
|
||||
});
|
||||
}
|
||||
|
||||
void Buffer::Download(std::size_t offset, std::size_t size, u8* data) const {
|
||||
void Buffer::Download(std::size_t offset, std::size_t size, u8* data) {
|
||||
const auto& staging = staging_pool.GetUnusedBuffer(size, true);
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
|
||||
@@ -110,7 +110,7 @@ void Buffer::Download(std::size_t offset, std::size_t size, u8* data) const {
|
||||
}
|
||||
|
||||
void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset,
|
||||
std::size_t size) const {
|
||||
std::size_t size) {
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
|
||||
const VkBuffer dst_buffer = Handle();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user