Compare commits
97 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82f354dff0 | ||
|
|
30c753de1e | ||
|
|
52882a93a5 | ||
|
|
9f8e17cb18 | ||
|
|
f829932ed1 | ||
|
|
d00972fce1 | ||
|
|
3096adb347 | ||
|
|
e69d715e3d | ||
|
|
41a333321a | ||
|
|
8a1cfcc8b7 | ||
|
|
639cf169b2 | ||
|
|
98fcd3ba5d | ||
|
|
064b7bfa5f | ||
|
|
b13fd9e65a | ||
|
|
af787e451b | ||
|
|
6e1639c7b0 | ||
|
|
6b7c8e469b | ||
|
|
c58e21cd76 | ||
|
|
f6cb128eac | ||
|
|
34a590e509 | ||
|
|
15a04fb704 | ||
|
|
d5dfe34c49 | ||
|
|
0b954a3305 | ||
|
|
fb13f053bb | ||
|
|
beb172e9fc | ||
|
|
c91710a82f | ||
|
|
00a1d106bd | ||
|
|
3bb63bc0b3 | ||
|
|
c6b0353c4d | ||
|
|
424540d9e8 | ||
|
|
f1b1238e2d | ||
|
|
3b1683a152 | ||
|
|
a76e11e7f0 | ||
|
|
a4f48efea4 | ||
|
|
977a3ab352 | ||
|
|
dfdf87d844 | ||
|
|
1b31755ba6 | ||
|
|
d217017c9e | ||
|
|
7c970132b5 | ||
|
|
fa8e35c49f | ||
|
|
dda90ce1c2 | ||
|
|
e6085ea35f | ||
|
|
10eca7f651 | ||
|
|
765e37aa35 | ||
|
|
f84cbf6429 | ||
|
|
4a35df337b | ||
|
|
dcf345febe | ||
|
|
78d80d99a0 | ||
|
|
f0baf2abf2 | ||
|
|
323eb86c9f | ||
|
|
bd697bef03 | ||
|
|
b1f4de7874 | ||
|
|
db96b5ee3b | ||
|
|
bb2cbdf704 | ||
|
|
1d6be9febf | ||
|
|
6481d91e4a | ||
|
|
9e1975a166 | ||
|
|
0b86c7eb6a | ||
|
|
f8247826fa | ||
|
|
ecbc813711 | ||
|
|
3f739514e3 | ||
|
|
7d08d548a9 | ||
|
|
b9f0b9dd06 | ||
|
|
6582857356 | ||
|
|
0f4512291a | ||
|
|
a46df40939 | ||
|
|
9e7ac6a009 | ||
|
|
5c6adea222 | ||
|
|
04497d9e4a | ||
|
|
5f6e44552a | ||
|
|
57b93395a8 | ||
|
|
bc8d3b8f82 | ||
|
|
c18dc9c707 | ||
|
|
2f0b322e72 | ||
|
|
380fbd8cb7 | ||
|
|
e3d2b646e0 | ||
|
|
5226610a15 | ||
|
|
743e1f02a0 | ||
|
|
d4e07fd95e | ||
|
|
901bc09dd7 | ||
|
|
fcc23139f6 | ||
|
|
8ccc8cb252 | ||
|
|
f5d2a1e8bd | ||
|
|
8cf6efe677 | ||
|
|
2f09c7ddd3 | ||
|
|
28046ae3a9 | ||
|
|
0f729ef078 | ||
|
|
46b4461fbb | ||
|
|
968d631aa5 | ||
|
|
5f0fa4cb82 | ||
|
|
121af3646d | ||
|
|
c94583d867 | ||
|
|
0076a08d04 | ||
|
|
18a42d3815 | ||
|
|
0248614add | ||
|
|
20a779299a | ||
|
|
9f82a9a244 |
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -34,3 +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
|
||||
|
||||
@@ -156,8 +156,6 @@ macro(yuzu_find_packages)
|
||||
#"libzip 1.5 libzip/1.5.2@bincrafters/stable"
|
||||
"lz4 1.8 lz4/1.9.2"
|
||||
"nlohmann_json 3.7 nlohmann_json/3.7.3"
|
||||
# we need to be careful as the version check might be broken https://github.com/xiph/opus/issues/110
|
||||
"opus 1.3 opus/1.3.1"
|
||||
"ZLIB 1.2 zlib/1.2.11"
|
||||
"zstd 1.4 zstd/1.4.4"
|
||||
)
|
||||
@@ -214,6 +212,9 @@ if(ENABLE_QT)
|
||||
set(QT_PREFIX_HINT HINTS "${QT_PREFIX}")
|
||||
endif()
|
||||
find_package(Qt5 5.9 COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT})
|
||||
if (YUZU_USE_QT_WEB_ENGINE)
|
||||
find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets)
|
||||
endif()
|
||||
if (NOT Qt5_FOUND)
|
||||
list(APPEND CONAN_REQUIRED_LIBS "qt/5.14.1@bincrafters/stable")
|
||||
endif()
|
||||
@@ -328,6 +329,12 @@ elseif(SDL2_FOUND)
|
||||
target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARIES}")
|
||||
endif()
|
||||
|
||||
# Ensure libusb is properly configured (based on dolphin libusb include)
|
||||
find_package(LibUSB)
|
||||
add_subdirectory(externals/libusb)
|
||||
set(LIBUSB_LIBRARIES usb)
|
||||
|
||||
|
||||
# Prefer the -pthread flag on Linux.
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
3
externals/CMakeLists.txt
vendored
3
externals/CMakeLists.txt
vendored
@@ -91,3 +91,6 @@ if (ENABLE_WEB_SERVICE)
|
||||
target_compile_definitions(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
|
||||
target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
|
||||
endif()
|
||||
|
||||
# Opus
|
||||
add_subdirectory(opus)
|
||||
|
||||
1
externals/libusb
vendored
Submodule
1
externals/libusb
vendored
Submodule
Submodule externals/libusb added at 3406d72cda
254
externals/opus/CMakeLists.txt
vendored
Normal file
254
externals/opus/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
|
||||
project(opus)
|
||||
|
||||
option(OPUS_STACK_PROTECTOR "Use stack protection" OFF)
|
||||
option(OPUS_USE_ALLOCA "Use alloca for stack arrays (on non-C99 compilers)" OFF)
|
||||
option(OPUS_CUSTOM_MODES "Enable non-Opus modes, e.g. 44.1 kHz & 2^n frames" OFF)
|
||||
option(OPUS_FIXED_POINT "Compile as fixed-point (for machines without a fast enough FPU)" OFF)
|
||||
option(OPUS_ENABLE_FLOAT_API "Compile with the floating point API (for machines with float library" ON)
|
||||
|
||||
include(opus/opus_functions.cmake)
|
||||
|
||||
if(OPUS_STACK_PROTECTOR)
|
||||
if(NOT MSVC) # GC on by default on MSVC
|
||||
check_and_set_flag(STACK_PROTECTION_STRONG -fstack-protector-strong)
|
||||
endif()
|
||||
else()
|
||||
if(MSVC)
|
||||
check_and_set_flag(BUFFER_SECURITY_CHECK /GS-)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(opus STATIC
|
||||
# CELT sources
|
||||
opus/celt/bands.c
|
||||
opus/celt/celt.c
|
||||
opus/celt/celt_decoder.c
|
||||
opus/celt/celt_encoder.c
|
||||
opus/celt/celt_lpc.c
|
||||
opus/celt/cwrs.c
|
||||
opus/celt/entcode.c
|
||||
opus/celt/entdec.c
|
||||
opus/celt/entenc.c
|
||||
opus/celt/kiss_fft.c
|
||||
opus/celt/laplace.c
|
||||
opus/celt/mathops.c
|
||||
opus/celt/mdct.c
|
||||
opus/celt/modes.c
|
||||
opus/celt/pitch.c
|
||||
opus/celt/quant_bands.c
|
||||
opus/celt/rate.c
|
||||
opus/celt/vq.c
|
||||
|
||||
# SILK sources
|
||||
opus/silk/A2NLSF.c
|
||||
opus/silk/CNG.c
|
||||
opus/silk/HP_variable_cutoff.c
|
||||
opus/silk/LPC_analysis_filter.c
|
||||
opus/silk/LPC_fit.c
|
||||
opus/silk/LPC_inv_pred_gain.c
|
||||
opus/silk/LP_variable_cutoff.c
|
||||
opus/silk/NLSF2A.c
|
||||
opus/silk/NLSF_VQ.c
|
||||
opus/silk/NLSF_VQ_weights_laroia.c
|
||||
opus/silk/NLSF_decode.c
|
||||
opus/silk/NLSF_del_dec_quant.c
|
||||
opus/silk/NLSF_encode.c
|
||||
opus/silk/NLSF_stabilize.c
|
||||
opus/silk/NLSF_unpack.c
|
||||
opus/silk/NSQ.c
|
||||
opus/silk/NSQ_del_dec.c
|
||||
opus/silk/PLC.c
|
||||
opus/silk/VAD.c
|
||||
opus/silk/VQ_WMat_EC.c
|
||||
opus/silk/ana_filt_bank_1.c
|
||||
opus/silk/biquad_alt.c
|
||||
opus/silk/bwexpander.c
|
||||
opus/silk/bwexpander_32.c
|
||||
opus/silk/check_control_input.c
|
||||
opus/silk/code_signs.c
|
||||
opus/silk/control_SNR.c
|
||||
opus/silk/control_audio_bandwidth.c
|
||||
opus/silk/control_codec.c
|
||||
opus/silk/dec_API.c
|
||||
opus/silk/decode_core.c
|
||||
opus/silk/decode_frame.c
|
||||
opus/silk/decode_indices.c
|
||||
opus/silk/decode_parameters.c
|
||||
opus/silk/decode_pitch.c
|
||||
opus/silk/decode_pulses.c
|
||||
opus/silk/decoder_set_fs.c
|
||||
opus/silk/enc_API.c
|
||||
opus/silk/encode_indices.c
|
||||
opus/silk/encode_pulses.c
|
||||
opus/silk/gain_quant.c
|
||||
opus/silk/init_decoder.c
|
||||
opus/silk/init_encoder.c
|
||||
opus/silk/inner_prod_aligned.c
|
||||
opus/silk/interpolate.c
|
||||
opus/silk/lin2log.c
|
||||
opus/silk/log2lin.c
|
||||
opus/silk/pitch_est_tables.c
|
||||
opus/silk/process_NLSFs.c
|
||||
opus/silk/quant_LTP_gains.c
|
||||
opus/silk/resampler.c
|
||||
opus/silk/resampler_down2.c
|
||||
opus/silk/resampler_down2_3.c
|
||||
opus/silk/resampler_private_AR2.c
|
||||
opus/silk/resampler_private_IIR_FIR.c
|
||||
opus/silk/resampler_private_down_FIR.c
|
||||
opus/silk/resampler_private_up2_HQ.c
|
||||
opus/silk/resampler_rom.c
|
||||
opus/silk/shell_coder.c
|
||||
opus/silk/sigm_Q15.c
|
||||
opus/silk/sort.c
|
||||
opus/silk/stereo_LR_to_MS.c
|
||||
opus/silk/stereo_MS_to_LR.c
|
||||
opus/silk/stereo_decode_pred.c
|
||||
opus/silk/stereo_encode_pred.c
|
||||
opus/silk/stereo_find_predictor.c
|
||||
opus/silk/stereo_quant_pred.c
|
||||
opus/silk/sum_sqr_shift.c
|
||||
opus/silk/table_LSF_cos.c
|
||||
opus/silk/tables_LTP.c
|
||||
opus/silk/tables_NLSF_CB_NB_MB.c
|
||||
opus/silk/tables_NLSF_CB_WB.c
|
||||
opus/silk/tables_gain.c
|
||||
opus/silk/tables_other.c
|
||||
opus/silk/tables_pitch_lag.c
|
||||
opus/silk/tables_pulses_per_block.c
|
||||
|
||||
# Opus sources
|
||||
opus/src/analysis.c
|
||||
opus/src/mapping_matrix.c
|
||||
opus/src/mlp.c
|
||||
opus/src/mlp_data.c
|
||||
opus/src/opus.c
|
||||
opus/src/opus_decoder.c
|
||||
opus/src/opus_encoder.c
|
||||
opus/src/opus_multistream.c
|
||||
opus/src/opus_multistream_decoder.c
|
||||
opus/src/opus_multistream_encoder.c
|
||||
opus/src/opus_projection_decoder.c
|
||||
opus/src/opus_projection_encoder.c
|
||||
opus/src/repacketizer.c
|
||||
)
|
||||
|
||||
if (DEBUG)
|
||||
target_sources(opus PRIVATE opus/silk/debug.c)
|
||||
endif()
|
||||
|
||||
if (OPUS_FIXED_POINT)
|
||||
target_sources(opus PRIVATE
|
||||
opus/silk/fixed/LTP_analysis_filter_FIX.c
|
||||
opus/silk/fixed/LTP_scale_ctrl_FIX.c
|
||||
opus/silk/fixed/apply_sine_window_FIX.c
|
||||
opus/silk/fixed/autocorr_FIX.c
|
||||
opus/silk/fixed/burg_modified_FIX.c
|
||||
opus/silk/fixed/corrMatrix_FIX.c
|
||||
opus/silk/fixed/encode_frame_FIX.c
|
||||
opus/silk/fixed/find_LPC_FIX.c
|
||||
opus/silk/fixed/find_LTP_FIX.c
|
||||
opus/silk/fixed/find_pitch_lags_FIX.c
|
||||
opus/silk/fixed/find_pred_coefs_FIX.c
|
||||
opus/silk/fixed/k2a_FIX.c
|
||||
opus/silk/fixed/k2a_Q16_FIX.c
|
||||
opus/silk/fixed/noise_shape_analysis_FIX.c
|
||||
opus/silk/fixed/pitch_analysis_core_FIX.c
|
||||
opus/silk/fixed/prefilter_FIX.c
|
||||
opus/silk/fixed/process_gains_FIX.c
|
||||
opus/silk/fixed/regularize_correlations_FIX.c
|
||||
opus/silk/fixed/residual_energy16_FIX.c
|
||||
opus/silk/fixed/residual_energy_FIX.c
|
||||
opus/silk/fixed/schur64_FIX.c
|
||||
opus/silk/fixed/schur_FIX.c
|
||||
opus/silk/fixed/solve_LS_FIX.c
|
||||
opus/silk/fixed/vector_ops_FIX.c
|
||||
opus/silk/fixed/warped_autocorrelation_FIX.c
|
||||
)
|
||||
else()
|
||||
target_sources(opus PRIVATE
|
||||
opus/silk/float/LPC_analysis_filter_FLP.c
|
||||
opus/silk/float/LPC_inv_pred_gain_FLP.c
|
||||
opus/silk/float/LTP_analysis_filter_FLP.c
|
||||
opus/silk/float/LTP_scale_ctrl_FLP.c
|
||||
opus/silk/float/apply_sine_window_FLP.c
|
||||
opus/silk/float/autocorrelation_FLP.c
|
||||
opus/silk/float/burg_modified_FLP.c
|
||||
opus/silk/float/bwexpander_FLP.c
|
||||
opus/silk/float/corrMatrix_FLP.c
|
||||
opus/silk/float/encode_frame_FLP.c
|
||||
opus/silk/float/energy_FLP.c
|
||||
opus/silk/float/find_LPC_FLP.c
|
||||
opus/silk/float/find_LTP_FLP.c
|
||||
opus/silk/float/find_pitch_lags_FLP.c
|
||||
opus/silk/float/find_pred_coefs_FLP.c
|
||||
opus/silk/float/inner_product_FLP.c
|
||||
opus/silk/float/k2a_FLP.c
|
||||
opus/silk/float/noise_shape_analysis_FLP.c
|
||||
opus/silk/float/pitch_analysis_core_FLP.c
|
||||
opus/silk/float/process_gains_FLP.c
|
||||
opus/silk/float/regularize_correlations_FLP.c
|
||||
opus/silk/float/residual_energy_FLP.c
|
||||
opus/silk/float/scale_copy_vector_FLP.c
|
||||
opus/silk/float/scale_vector_FLP.c
|
||||
opus/silk/float/schur_FLP.c
|
||||
opus/silk/float/sort_FLP.c
|
||||
opus/silk/float/warped_autocorrelation_FLP.c
|
||||
opus/silk/float/wrappers_FLP.c
|
||||
)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(opus PRIVATE OPUS_BUILD ENABLE_HARDENING)
|
||||
|
||||
if(NOT MSVC)
|
||||
if(MINGW)
|
||||
target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=0)
|
||||
else()
|
||||
target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=2)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# It is strongly recommended to uncomment one of these VAR_ARRAYS: Use C99
|
||||
# variable-length arrays for stack allocation USE_ALLOCA: Use alloca() for stack
|
||||
# allocation If none is defined, then the fallback is a non-threadsafe global
|
||||
# array
|
||||
if(OPUS_USE_ALLOCA OR MSVC)
|
||||
target_compile_definitions(opus PRIVATE USE_ALLOCA)
|
||||
else()
|
||||
target_compile_definitions(opus PRIVATE VAR_ARRAYS)
|
||||
endif()
|
||||
|
||||
if(OPUS_CUSTOM_MODES)
|
||||
target_compile_definitions(opus PRIVATE CUSTOM_MODES)
|
||||
endif()
|
||||
|
||||
if(NOT OPUS_ENABLE_FLOAT_API)
|
||||
target_compile_definitions(opus PRIVATE DISABLE_FLOAT_API)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(opus
|
||||
PUBLIC
|
||||
-DOPUS_VERSION="\\"1.3.1\\""
|
||||
|
||||
PRIVATE
|
||||
# Use C99 intrinsics to speed up float-to-int conversion
|
||||
HAVE_LRINTF
|
||||
)
|
||||
|
||||
if (FIXED_POINT)
|
||||
target_compile_definitions(opus PRIVATE -DFIXED_POINT=1 -DDISABLE_FLOAT_API)
|
||||
endif()
|
||||
|
||||
target_include_directories(opus
|
||||
PUBLIC
|
||||
opus/include
|
||||
|
||||
PRIVATE
|
||||
opus/celt
|
||||
opus/silk
|
||||
opus/silk/fixed
|
||||
opus/silk/float
|
||||
opus/src
|
||||
)
|
||||
1
externals/opus/opus
vendored
Submodule
1
externals/opus/opus
vendored
Submodule
Submodule externals/opus/opus added at ad8fe90db7
@@ -9,10 +9,12 @@
|
||||
// clang-format on
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#ifdef __APPLE__
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||
#include <sys/sysctl.h>
|
||||
#else
|
||||
#elif defined(__linux__)
|
||||
#include <sys/sysinfo.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -38,15 +40,26 @@ static MemoryInfo Detect() {
|
||||
// hw and vm are defined in sysctl.h
|
||||
// https://github.com/apple/darwin-xnu/blob/master/bsd/sys/sysctl.h#L471
|
||||
// sysctlbyname(const char *, void *, size_t *, void *, size_t);
|
||||
sysctlbyname("hw.memsize", &ramsize, &sizeof_ramsize, NULL, 0);
|
||||
sysctlbyname("vm.swapusage", &vmusage, &sizeof_vmusage, NULL, 0);
|
||||
sysctlbyname("hw.memsize", &ramsize, &sizeof_ramsize, nullptr, 0);
|
||||
sysctlbyname("vm.swapusage", &vmusage, &sizeof_vmusage, nullptr, 0);
|
||||
mem_info.TotalPhysicalMemory = ramsize;
|
||||
mem_info.TotalSwapMemory = vmusage.xsu_total;
|
||||
#else
|
||||
#elif defined(__FreeBSD__)
|
||||
u_long physmem, swap_total;
|
||||
std::size_t sizeof_u_long = sizeof(u_long);
|
||||
// sysctlbyname(const char *, void *, size_t *, const void *, size_t);
|
||||
sysctlbyname("hw.physmem", &physmem, &sizeof_u_long, nullptr, 0);
|
||||
sysctlbyname("vm.swap_total", &swap_total, &sizeof_u_long, nullptr, 0);
|
||||
mem_info.TotalPhysicalMemory = physmem;
|
||||
mem_info.TotalSwapMemory = swap_total;
|
||||
#elif defined(__linux__)
|
||||
struct sysinfo meminfo;
|
||||
sysinfo(&meminfo);
|
||||
mem_info.TotalPhysicalMemory = meminfo.totalram;
|
||||
mem_info.TotalSwapMemory = meminfo.totalswap;
|
||||
#else
|
||||
mem_info.TotalPhysicalMemory = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE);
|
||||
mem_info.TotalSwapMemory = 0;
|
||||
#endif
|
||||
|
||||
return mem_info;
|
||||
|
||||
@@ -614,7 +614,7 @@ endif()
|
||||
create_target_directory_groups(core)
|
||||
|
||||
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
|
||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus unicorn zip)
|
||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls opus unicorn zip)
|
||||
|
||||
if (YUZU_ENABLE_BOXCAT)
|
||||
target_compile_definitions(core PRIVATE -DYUZU_ENABLE_BOXCAT)
|
||||
|
||||
@@ -223,7 +223,16 @@ bool operator<(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
|
||||
|
||||
class KeyManager {
|
||||
public:
|
||||
KeyManager();
|
||||
static KeyManager& Instance() {
|
||||
static KeyManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -257,6 +266,8 @@ public:
|
||||
bool AddTicketPersonalized(Ticket raw);
|
||||
|
||||
private:
|
||||
KeyManager();
|
||||
|
||||
std::map<KeyIndex<S128KeyType>, Key128> s128_keys;
|
||||
std::map<KeyIndex<S256KeyType>, Key256> s256_keys;
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ VirtualDir BISFactory::OpenPartition(BisPartitionId id) const {
|
||||
}
|
||||
|
||||
VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const {
|
||||
Core::Crypto::KeyManager keys;
|
||||
auto& keys = Core::Crypto::KeyManager::Instance();
|
||||
Core::Crypto::PartitionDataManager pdm{
|
||||
Core::System::GetInstance().GetFilesystem()->OpenDirectory(
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)};
|
||||
|
||||
@@ -178,7 +178,7 @@ u32 XCI::GetSystemUpdateVersion() {
|
||||
return 0;
|
||||
|
||||
for (const auto& file : update->GetFiles()) {
|
||||
NCA nca{file, nullptr, 0, keys};
|
||||
NCA nca{file, nullptr, 0};
|
||||
|
||||
if (nca.GetStatus() != Loader::ResultStatus::Success)
|
||||
continue;
|
||||
@@ -286,7 +286,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto nca = std::make_shared<NCA>(file, nullptr, 0, keys);
|
||||
auto nca = std::make_shared<NCA>(file, nullptr, 0);
|
||||
if (nca->IsUpdate()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -140,6 +140,6 @@ private:
|
||||
|
||||
u64 update_normal_partition_end;
|
||||
|
||||
Core::Crypto::KeyManager keys;
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||
};
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -118,9 +118,8 @@ static bool IsValidNCA(const NCAHeader& header) {
|
||||
return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
|
||||
}
|
||||
|
||||
NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset,
|
||||
Core::Crypto::KeyManager keys_)
|
||||
: file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)), keys(std::move(keys_)) {
|
||||
NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
|
||||
: file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) {
|
||||
if (file == nullptr) {
|
||||
status = Loader::ResultStatus::ErrorNullFile;
|
||||
return;
|
||||
|
||||
@@ -99,8 +99,7 @@ inline bool IsDirectoryLogoPartition(const VirtualDir& pfs) {
|
||||
class NCA : public ReadOnlyVfsDirectory {
|
||||
public:
|
||||
explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr,
|
||||
u64 bktr_base_ivfc_offset = 0,
|
||||
Core::Crypto::KeyManager keys = Core::Crypto::KeyManager());
|
||||
u64 bktr_base_ivfc_offset = 0);
|
||||
~NCA() override;
|
||||
|
||||
Loader::ResultStatus GetStatus() const;
|
||||
@@ -159,7 +158,7 @@ private:
|
||||
bool encrypted = false;
|
||||
bool is_update = false;
|
||||
|
||||
Core::Crypto::KeyManager keys;
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -408,7 +408,7 @@ void RegisteredCache::ProcessFiles(const std::vector<NcaID>& ids) {
|
||||
|
||||
if (file == nullptr)
|
||||
continue;
|
||||
const auto nca = std::make_shared<NCA>(parser(file, id), nullptr, 0, keys);
|
||||
const auto nca = std::make_shared<NCA>(parser(file, id), nullptr, 0);
|
||||
if (nca->GetStatus() != Loader::ResultStatus::Success ||
|
||||
nca->GetType() != NCAContentType::Meta) {
|
||||
continue;
|
||||
@@ -486,7 +486,7 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t
|
||||
const auto raw = GetEntryRaw(title_id, type);
|
||||
if (raw == nullptr)
|
||||
return nullptr;
|
||||
return std::make_unique<NCA>(raw, nullptr, 0, keys);
|
||||
return std::make_unique<NCA>(raw, nullptr, 0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -865,7 +865,7 @@ std::unique_ptr<NCA> ManualContentProvider::GetEntry(u64 title_id, ContentRecord
|
||||
const auto res = GetEntryRaw(title_id, type);
|
||||
if (res == nullptr)
|
||||
return nullptr;
|
||||
return std::make_unique<NCA>(res, nullptr, 0, keys);
|
||||
return std::make_unique<NCA>(res, nullptr, 0);
|
||||
}
|
||||
|
||||
std::vector<ContentProviderEntry> ManualContentProvider::ListEntriesFilter(
|
||||
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
|
||||
protected:
|
||||
// A single instance of KeyManager to be used by GetEntry()
|
||||
Core::Crypto::KeyManager keys;
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||
};
|
||||
|
||||
class PlaceholderCache {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
namespace FileSys {
|
||||
namespace {
|
||||
void SetTicketKeys(const std::vector<VirtualFile>& files) {
|
||||
Core::Crypto::KeyManager keys;
|
||||
auto& keys = Core::Crypto::KeyManager::Instance();
|
||||
|
||||
for (const auto& ticket_file : files) {
|
||||
if (ticket_file == nullptr) {
|
||||
@@ -285,7 +285,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0, keys);
|
||||
auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0);
|
||||
if (next_nca->GetType() == NCAContentType::Program) {
|
||||
program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
|
||||
}
|
||||
|
||||
@@ -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& keys = Core::Crypto::KeyManager::Instance();
|
||||
|
||||
VirtualFile romfs;
|
||||
VirtualDir exefs;
|
||||
|
||||
@@ -62,6 +62,6 @@ private:
|
||||
|
||||
VirtualFile dec_file;
|
||||
|
||||
Core::Crypto::KeyManager keys;
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||
};
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -841,7 +841,7 @@ public:
|
||||
{110, nullptr, "NeedsToExitProcess"},
|
||||
{120, nullptr, "GetLibraryAppletInfo"},
|
||||
{150, nullptr, "RequestForAppletToGetForeground"},
|
||||
{160, nullptr, "GetIndirectLayerConsumerHandle"},
|
||||
{160, &ILibraryAppletAccessor::GetIndirectLayerConsumerHandle, "GetIndirectLayerConsumerHandle"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -960,6 +960,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;
|
||||
};
|
||||
|
||||
|
||||
@@ -302,7 +302,7 @@ private:
|
||||
rb.Push<u64>(write_size);
|
||||
}
|
||||
|
||||
Core::Crypto::KeyManager keys;
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
|
||||
@@ -23,8 +23,8 @@ public:
|
||||
{10101, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old>, "SaveReportWithUserOld"},
|
||||
{10102, &PlayReport::SaveReport<Core::Reporter::PlayReportType::Old2>, "SaveReportOld2"},
|
||||
{10103, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old2>, "SaveReportWithUserOld2"},
|
||||
{10104, nullptr, "SaveReport"},
|
||||
{10105, nullptr, "SaveReportWithUser"},
|
||||
{10104, &PlayReport::SaveReport<Core::Reporter::PlayReportType::New>, "SaveReport"},
|
||||
{10105, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::New>, "SaveReportWithUser"},
|
||||
{10200, nullptr, "RequestImmediateTransmission"},
|
||||
{10300, nullptr, "GetTransmissionStatus"},
|
||||
{10400, nullptr, "GetSystemSessionId"},
|
||||
@@ -55,7 +55,8 @@ private:
|
||||
const auto process_id = rp.PopRaw<u64>();
|
||||
|
||||
std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)};
|
||||
if constexpr (Type == Core::Reporter::PlayReportType::Old2) {
|
||||
if constexpr (Type == Core::Reporter::PlayReportType::Old2 ||
|
||||
Type == Core::Reporter::PlayReportType::New) {
|
||||
data.emplace_back(ctx.ReadBuffer(1));
|
||||
}
|
||||
|
||||
@@ -75,8 +76,14 @@ private:
|
||||
const auto user_id = rp.PopRaw<u128>();
|
||||
const auto process_id = rp.PopRaw<u64>();
|
||||
std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)};
|
||||
if constexpr (Type == Core::Reporter::PlayReportType::Old2) {
|
||||
data.emplace_back(ctx.ReadBuffer(1));
|
||||
|
||||
if constexpr (Type == Core::Reporter::PlayReportType::Old2 ||
|
||||
Type == Core::Reporter::PlayReportType::New) {
|
||||
const auto read_buffer_count =
|
||||
ctx.BufferDescriptorX().size() + ctx.BufferDescriptorA().size();
|
||||
if (read_buffer_count > 1) {
|
||||
data.emplace_back(ctx.ReadBuffer(1));
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG(
|
||||
|
||||
@@ -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];
|
||||
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);
|
||||
} 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) {
|
||||
@@ -120,6 +178,16 @@ void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(Settings::values.region_index);
|
||||
}
|
||||
|
||||
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") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
@@ -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
|
||||
|
||||
@@ -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,7 @@ if(SDL2_FOUND)
|
||||
target_compile_definitions(input_common PRIVATE HAVE_SDL2)
|
||||
endif()
|
||||
|
||||
target_link_libraries(input_common PUBLIC ${LIBUSB_LIBRARIES})
|
||||
|
||||
create_target_directory_groups(input_common)
|
||||
target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost)
|
||||
|
||||
379
src/input_common/gcadapter/gc_adapter.cpp
Normal file
379
src/input_common/gcadapter/gc_adapter.cpp
Normal file
@@ -0,0 +1,379 @@
|
||||
// Copyright 2014 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#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;
|
||||
libusb_init(&libusb_ctx);
|
||||
|
||||
StartScanThread();
|
||||
}
|
||||
|
||||
GCPadStatus Adapter::GetPadStatus(int 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([=] { ScanThreadFunc(); });
|
||||
}
|
||||
|
||||
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 std::size_t device_count = libusb_get_device_list(libusb_ctx, &devices);
|
||||
|
||||
for (std::size_t index = 0; index < device_count; ++index) {
|
||||
if (CheckDeviceAccess(devices[index])) {
|
||||
// GC Adapter found and accessible, registering it
|
||||
GetGCEndpoint(devices[index]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
libusb_get_config_descriptor(device, 0, &config);
|
||||
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(int port) {
|
||||
return adapter_controllers_status[port] != ControllerTypes::None;
|
||||
}
|
||||
|
||||
void Adapter::ResetDeviceType(int 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
|
||||
160
src/input_common/gcadapter/gc_adapter.h
Normal file
160
src/input_common/gcadapter/gc_adapter.h
Normal file
@@ -0,0 +1,160 @@
|
||||
// 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 <libusb.h>
|
||||
#include "common/common_types.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
|
||||
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();
|
||||
|
||||
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(int 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();
|
||||
|
||||
/// Returns true if there is a device connected to port
|
||||
bool DeviceConnected(int port);
|
||||
|
||||
/// Resets status of device connected to port
|
||||
void ResetDeviceType(int 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
|
||||
272
src/input_common/gcadapter/gc_poller.cpp
Normal file
272
src/input_common/gcadapter/gc_poller.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
// 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/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 {
|
||||
return gcadapter->GetPadState()[port].buttons.at(button);
|
||||
}
|
||||
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
Common::ParamPackage GCButtonFactory::GetNextInput() {
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
|
||||
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 = 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();
|
||||
|
||||
/// 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
|
||||
@@ -4,8 +4,11 @@
|
||||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <libusb.h>
|
||||
#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 +25,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 +59,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 +74,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);
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ add_library(video_core STATIC
|
||||
buffer_cache/buffer_cache.h
|
||||
buffer_cache/map_interval.cpp
|
||||
buffer_cache/map_interval.h
|
||||
compatible_formats.cpp
|
||||
compatible_formats.h
|
||||
dirty_flags.cpp
|
||||
dirty_flags.h
|
||||
dma_pusher.cpp
|
||||
|
||||
@@ -322,8 +322,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();
|
||||
@@ -377,8 +376,7 @@ private:
|
||||
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);
|
||||
|
||||
162
src/video_core/compatible_formats.cpp
Normal file
162
src/video_core/compatible_formats.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cstddef>
|
||||
|
||||
#include "video_core/compatible_formats.h"
|
||||
#include "video_core/surface.h"
|
||||
|
||||
namespace VideoCore::Surface {
|
||||
|
||||
namespace {
|
||||
|
||||
// Compatibility table taken from Table 3.X.2 in:
|
||||
// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_view.txt
|
||||
|
||||
constexpr std::array VIEW_CLASS_128_BITS = {
|
||||
PixelFormat::RGBA32F,
|
||||
PixelFormat::RGBA32UI,
|
||||
};
|
||||
// Missing formats:
|
||||
// PixelFormat::RGBA32I
|
||||
|
||||
constexpr std::array VIEW_CLASS_96_BITS = {
|
||||
PixelFormat::RGB32F,
|
||||
};
|
||||
// Missing formats:
|
||||
// PixelFormat::RGB32UI,
|
||||
// PixelFormat::RGB32I,
|
||||
|
||||
constexpr std::array VIEW_CLASS_64_BITS = {
|
||||
PixelFormat::RGBA16F, PixelFormat::RG32F, PixelFormat::RGBA16UI, PixelFormat::RG32UI,
|
||||
PixelFormat::RGBA16U, PixelFormat::RGBA16F, PixelFormat::RGBA16S,
|
||||
};
|
||||
// Missing formats:
|
||||
// PixelFormat::RGBA16I
|
||||
// PixelFormat::RG32I
|
||||
|
||||
// TODO: How should we handle 48 bits?
|
||||
|
||||
constexpr std::array VIEW_CLASS_32_BITS = {
|
||||
PixelFormat::RG16F, PixelFormat::R11FG11FB10F, PixelFormat::R32F,
|
||||
PixelFormat::A2B10G10R10U, PixelFormat::RG16UI, PixelFormat::R32UI,
|
||||
PixelFormat::RG16I, PixelFormat::R32I, PixelFormat::ABGR8U,
|
||||
PixelFormat::RG16, PixelFormat::ABGR8S, PixelFormat::RG16S,
|
||||
PixelFormat::RGBA8_SRGB, PixelFormat::E5B9G9R9F, PixelFormat::BGRA8,
|
||||
PixelFormat::BGRA8_SRGB,
|
||||
};
|
||||
// Missing formats:
|
||||
// PixelFormat::RGBA8UI
|
||||
// PixelFormat::RGBA8I
|
||||
// PixelFormat::RGB10_A2_UI
|
||||
|
||||
// TODO: How should we handle 24 bits?
|
||||
|
||||
constexpr std::array VIEW_CLASS_16_BITS = {
|
||||
PixelFormat::R16F, PixelFormat::RG8UI, PixelFormat::R16UI, PixelFormat::R16I,
|
||||
PixelFormat::RG8U, PixelFormat::R16U, PixelFormat::RG8S, PixelFormat::R16S,
|
||||
};
|
||||
// Missing formats:
|
||||
// PixelFormat::RG8I
|
||||
|
||||
constexpr std::array VIEW_CLASS_8_BITS = {
|
||||
PixelFormat::R8UI,
|
||||
PixelFormat::R8U,
|
||||
};
|
||||
// Missing formats:
|
||||
// PixelFormat::R8I
|
||||
// PixelFormat::R8S
|
||||
|
||||
constexpr std::array VIEW_CLASS_RGTC1_RED = {
|
||||
PixelFormat::DXN1,
|
||||
};
|
||||
// Missing formats:
|
||||
// COMPRESSED_SIGNED_RED_RGTC1
|
||||
|
||||
constexpr std::array VIEW_CLASS_RGTC2_RG = {
|
||||
PixelFormat::DXN2UNORM,
|
||||
PixelFormat::DXN2SNORM,
|
||||
};
|
||||
|
||||
constexpr std::array VIEW_CLASS_BPTC_UNORM = {
|
||||
PixelFormat::BC7U,
|
||||
PixelFormat::BC7U_SRGB,
|
||||
};
|
||||
|
||||
constexpr std::array VIEW_CLASS_BPTC_FLOAT = {
|
||||
PixelFormat::BC6H_SF16,
|
||||
PixelFormat::BC6H_UF16,
|
||||
};
|
||||
|
||||
// Compatibility table taken from Table 4.X.1 in:
|
||||
// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_copy_image.txt
|
||||
|
||||
constexpr std::array COPY_CLASS_128_BITS = {
|
||||
PixelFormat::RGBA32UI, PixelFormat::RGBA32F, PixelFormat::DXT23,
|
||||
PixelFormat::DXT23_SRGB, PixelFormat::DXT45, PixelFormat::DXT45_SRGB,
|
||||
PixelFormat::DXN2SNORM, PixelFormat::BC7U, PixelFormat::BC7U_SRGB,
|
||||
PixelFormat::BC6H_SF16, PixelFormat::BC6H_UF16,
|
||||
};
|
||||
// Missing formats:
|
||||
// PixelFormat::RGBA32I
|
||||
// COMPRESSED_RG_RGTC2
|
||||
|
||||
constexpr std::array COPY_CLASS_64_BITS = {
|
||||
PixelFormat::RGBA16F, PixelFormat::RG32F, PixelFormat::RGBA16UI, PixelFormat::RG32UI,
|
||||
PixelFormat::RGBA16U, PixelFormat::RGBA16S, PixelFormat::DXT1_SRGB, PixelFormat::DXT1,
|
||||
|
||||
};
|
||||
// Missing formats:
|
||||
// PixelFormat::RGBA16I
|
||||
// PixelFormat::RG32I,
|
||||
// COMPRESSED_RGB_S3TC_DXT1_EXT
|
||||
// COMPRESSED_SRGB_S3TC_DXT1_EXT
|
||||
// COMPRESSED_RGBA_S3TC_DXT1_EXT
|
||||
// COMPRESSED_SIGNED_RED_RGTC1
|
||||
|
||||
void Enable(FormatCompatibility::Table& compatiblity, size_t format_a, size_t format_b) {
|
||||
compatiblity[format_a][format_b] = true;
|
||||
compatiblity[format_b][format_a] = true;
|
||||
}
|
||||
|
||||
void Enable(FormatCompatibility::Table& compatibility, PixelFormat format_a, PixelFormat format_b) {
|
||||
Enable(compatibility, static_cast<size_t>(format_a), static_cast<size_t>(format_b));
|
||||
}
|
||||
|
||||
template <typename Range>
|
||||
void EnableRange(FormatCompatibility::Table& compatibility, const Range& range) {
|
||||
for (auto it_a = range.begin(); it_a != range.end(); ++it_a) {
|
||||
for (auto it_b = it_a; it_b != range.end(); ++it_b) {
|
||||
Enable(compatibility, *it_a, *it_b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
FormatCompatibility::FormatCompatibility() {
|
||||
for (size_t i = 0; i < MaxPixelFormat; ++i) {
|
||||
// Identity is allowed
|
||||
Enable(view, i, i);
|
||||
}
|
||||
|
||||
EnableRange(view, VIEW_CLASS_128_BITS);
|
||||
EnableRange(view, VIEW_CLASS_96_BITS);
|
||||
EnableRange(view, VIEW_CLASS_64_BITS);
|
||||
EnableRange(view, VIEW_CLASS_32_BITS);
|
||||
EnableRange(view, VIEW_CLASS_16_BITS);
|
||||
EnableRange(view, VIEW_CLASS_8_BITS);
|
||||
EnableRange(view, VIEW_CLASS_RGTC1_RED);
|
||||
EnableRange(view, VIEW_CLASS_RGTC2_RG);
|
||||
EnableRange(view, VIEW_CLASS_BPTC_UNORM);
|
||||
EnableRange(view, VIEW_CLASS_BPTC_FLOAT);
|
||||
|
||||
copy = view;
|
||||
EnableRange(copy, COPY_CLASS_128_BITS);
|
||||
EnableRange(copy, COPY_CLASS_64_BITS);
|
||||
}
|
||||
|
||||
} // namespace VideoCore::Surface
|
||||
32
src/video_core/compatible_formats.h
Normal file
32
src/video_core/compatible_formats.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cstddef>
|
||||
|
||||
#include "video_core/surface.h"
|
||||
|
||||
namespace VideoCore::Surface {
|
||||
|
||||
class FormatCompatibility {
|
||||
public:
|
||||
using Table = std::array<std::bitset<MaxPixelFormat>, MaxPixelFormat>;
|
||||
|
||||
explicit FormatCompatibility();
|
||||
|
||||
bool TestView(PixelFormat format_a, PixelFormat format_b) const noexcept {
|
||||
return view[static_cast<size_t>(format_a)][static_cast<size_t>(format_b)];
|
||||
}
|
||||
|
||||
bool TestCopy(PixelFormat format_a, PixelFormat format_b) const noexcept {
|
||||
return copy[static_cast<size_t>(format_a)][static_cast<size_t>(format_b)];
|
||||
}
|
||||
|
||||
private:
|
||||
Table view;
|
||||
Table copy;
|
||||
};
|
||||
|
||||
} // namespace VideoCore::Surface
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <optional>
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
@@ -35,22 +36,40 @@ void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method,
|
||||
}
|
||||
} else {
|
||||
// Macro not compiled, check if it's uploaded and if so, compile it
|
||||
auto macro_code = uploaded_macro_code.find(method);
|
||||
std::optional<u32> mid_method = std::nullopt;
|
||||
const auto macro_code = uploaded_macro_code.find(method);
|
||||
if (macro_code == uploaded_macro_code.end()) {
|
||||
UNREACHABLE_MSG("Macro 0x{0:x} was not uploaded", method);
|
||||
return;
|
||||
for (const auto& [method_base, code] : uploaded_macro_code) {
|
||||
if (method >= method_base && (method - method_base) < code.size()) {
|
||||
mid_method = method_base;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!mid_method.has_value()) {
|
||||
UNREACHABLE_MSG("Macro 0x{0:x} was not uploaded", method);
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto& cache_info = macro_cache[method];
|
||||
cache_info.hash = boost::hash_value(macro_code->second);
|
||||
cache_info.lle_program = Compile(macro_code->second);
|
||||
|
||||
if (!mid_method.has_value()) {
|
||||
cache_info.lle_program = Compile(macro_code->second);
|
||||
cache_info.hash = boost::hash_value(macro_code->second);
|
||||
} else {
|
||||
const auto& macro_cached = uploaded_macro_code[mid_method.value()];
|
||||
const auto rebased_method = method - mid_method.value();
|
||||
auto& code = uploaded_macro_code[method];
|
||||
code.resize(macro_cached.size() - rebased_method);
|
||||
std::memcpy(code.data(), macro_cached.data() + rebased_method,
|
||||
code.size() * sizeof(u32));
|
||||
cache_info.hash = boost::hash_value(code);
|
||||
cache_info.lle_program = Compile(code);
|
||||
}
|
||||
|
||||
auto hle_program = hle_macros->GetHLEProgram(cache_info.hash);
|
||||
if (hle_program.has_value()) {
|
||||
cache_info.has_hle_program = true;
|
||||
cache_info.hle_program = std::move(hle_program.value());
|
||||
}
|
||||
|
||||
if (cache_info.has_hle_program) {
|
||||
cache_info.hle_program->Execute(parameters, method);
|
||||
} else {
|
||||
cache_info.lle_program->Execute(parameters, method);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -188,20 +188,6 @@ bool IsASTCSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief Returns true when a GL_RENDERER is a Turing GPU
|
||||
/// @param renderer GL_RENDERER string
|
||||
bool IsTuring(std::string_view renderer) {
|
||||
static constexpr std::array<std::string_view, 12> TURING_GPUS = {
|
||||
"GTX 1650", "GTX 1660", "RTX 2060", "RTX 2070",
|
||||
"RTX 2080", "TITAN RTX", "Quadro RTX 3000", "Quadro RTX 4000",
|
||||
"Quadro RTX 5000", "Quadro RTX 6000", "Quadro RTX 8000", "Tesla T4",
|
||||
};
|
||||
return std::any_of(TURING_GPUS.begin(), TURING_GPUS.end(),
|
||||
[renderer](std::string_view candidate) {
|
||||
return renderer.find(candidate) != std::string_view::npos;
|
||||
});
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
Device::Device()
|
||||
@@ -213,7 +199,6 @@ Device::Device()
|
||||
|
||||
const bool is_nvidia = vendor == "NVIDIA Corporation";
|
||||
const bool is_amd = vendor == "ATI Technologies Inc.";
|
||||
const bool is_turing = is_nvidia && IsTuring(renderer);
|
||||
|
||||
bool disable_fast_buffer_sub_data = false;
|
||||
if (is_nvidia && version == "4.6.0 NVIDIA 443.24") {
|
||||
@@ -238,15 +223,12 @@ Device::Device()
|
||||
has_component_indexing_bug = is_amd;
|
||||
has_precise_bug = TestPreciseBug();
|
||||
has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2;
|
||||
has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory;
|
||||
|
||||
// At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive
|
||||
// uniform buffers as "push constants"
|
||||
has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data;
|
||||
|
||||
// Nvidia's driver on Turing GPUs randomly crashes when the buffer is made resident, or on
|
||||
// DeleteBuffers. Disable unified memory on these devices.
|
||||
has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory && !is_turing;
|
||||
|
||||
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;
|
||||
|
||||
@@ -213,9 +213,10 @@ void RasterizerOpenGL::SetupVertexFormat() {
|
||||
if (attrib.type == Maxwell::VertexAttribute::Type::SignedInt ||
|
||||
attrib.type == Maxwell::VertexAttribute::Type::UnsignedInt) {
|
||||
glVertexAttribIFormat(gl_index, attrib.ComponentCount(),
|
||||
MaxwellToGL::VertexType(attrib), attrib.offset);
|
||||
MaxwellToGL::VertexFormat(attrib), attrib.offset);
|
||||
} else {
|
||||
glVertexAttribFormat(gl_index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
|
||||
glVertexAttribFormat(gl_index, attrib.ComponentCount(),
|
||||
MaxwellToGL::VertexFormat(attrib),
|
||||
attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
|
||||
}
|
||||
glVertexAttribBinding(gl_index, attrib.buffer);
|
||||
|
||||
@@ -24,10 +24,11 @@ namespace MaxwellToGL {
|
||||
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
|
||||
inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) {
|
||||
switch (attrib.type) {
|
||||
case Maxwell::VertexAttribute::Type::UnsignedInt:
|
||||
case Maxwell::VertexAttribute::Type::UnsignedNorm:
|
||||
case Maxwell::VertexAttribute::Type::UnsignedScaled:
|
||||
case Maxwell::VertexAttribute::Type::UnsignedInt:
|
||||
switch (attrib.size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
@@ -48,8 +49,9 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
|
||||
return GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::SignedInt:
|
||||
case Maxwell::VertexAttribute::Type::SignedNorm:
|
||||
case Maxwell::VertexAttribute::Type::SignedScaled:
|
||||
case Maxwell::VertexAttribute::Type::SignedInt:
|
||||
switch (attrib.size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
@@ -84,36 +86,8 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
|
||||
return GL_FLOAT;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::UnsignedScaled:
|
||||
switch (attrib.size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
return GL_UNSIGNED_BYTE;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
return GL_UNSIGNED_SHORT;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::SignedScaled:
|
||||
switch (attrib.size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
return GL_BYTE;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
return GL_SHORT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented vertex type={} and size={}", attrib.TypeString(),
|
||||
UNIMPLEMENTED_MSG("Unimplemented vertex format of type={} and size={}", attrib.TypeString(),
|
||||
attrib.SizeString());
|
||||
return {};
|
||||
}
|
||||
@@ -217,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;
|
||||
|
||||
@@ -294,30 +294,6 @@ VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const VKDevice& device,
|
||||
|
||||
VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) {
|
||||
switch (type) {
|
||||
case Maxwell::VertexAttribute::Type::SignedNorm:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
return VK_FORMAT_R8_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
return VK_FORMAT_R8G8_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
return VK_FORMAT_R8G8B8_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
return VK_FORMAT_R8G8B8A8_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
return VK_FORMAT_R16_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
return VK_FORMAT_R16G16_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
return VK_FORMAT_R16G16B16_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
return VK_FORMAT_R16G16B16A16_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
|
||||
return VK_FORMAT_A2B10G10R10_SNORM_PACK32;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::UnsignedNorm:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
@@ -338,38 +314,72 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
|
||||
return VK_FORMAT_R16G16B16A16_UNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
|
||||
return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::SignedInt:
|
||||
case Maxwell::VertexAttribute::Type::SignedNorm:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
return VK_FORMAT_R8_SINT;
|
||||
return VK_FORMAT_R8_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
return VK_FORMAT_R8G8_SINT;
|
||||
return VK_FORMAT_R8G8_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
return VK_FORMAT_R8G8B8_SINT;
|
||||
return VK_FORMAT_R8G8B8_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
return VK_FORMAT_R8G8B8A8_SINT;
|
||||
return VK_FORMAT_R8G8B8A8_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
return VK_FORMAT_R16_SINT;
|
||||
return VK_FORMAT_R16_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
return VK_FORMAT_R16G16_SINT;
|
||||
return VK_FORMAT_R16G16_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
return VK_FORMAT_R16G16B16_SINT;
|
||||
return VK_FORMAT_R16G16B16_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
return VK_FORMAT_R16G16B16A16_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32:
|
||||
return VK_FORMAT_R32_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32:
|
||||
return VK_FORMAT_R32G32_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32:
|
||||
return VK_FORMAT_R32G32B32_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
|
||||
return VK_FORMAT_R32G32B32A32_SINT;
|
||||
default:
|
||||
break;
|
||||
return VK_FORMAT_R16G16B16A16_SNORM;
|
||||
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
|
||||
return VK_FORMAT_A2B10G10R10_SNORM_PACK32;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::UnsignedScaled:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
return VK_FORMAT_R8_USCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
return VK_FORMAT_R8G8_USCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
return VK_FORMAT_R8G8B8_USCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
return VK_FORMAT_R8G8B8A8_USCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
return VK_FORMAT_R16_USCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
return VK_FORMAT_R16G16_USCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
return VK_FORMAT_R16G16B16_USCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
return VK_FORMAT_R16G16B16A16_USCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
|
||||
return VK_FORMAT_A2B10G10R10_USCALED_PACK32;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::SignedScaled:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
return VK_FORMAT_R8_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
return VK_FORMAT_R8G8_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
return VK_FORMAT_R8G8B8_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
return VK_FORMAT_R8G8B8A8_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
return VK_FORMAT_R16_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
return VK_FORMAT_R16G16_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
return VK_FORMAT_R16G16B16_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
return VK_FORMAT_R16G16B16A16_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
|
||||
return VK_FORMAT_A2B10G10R10_SSCALED_PACK32;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::UnsignedInt:
|
||||
@@ -398,64 +408,42 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
|
||||
return VK_FORMAT_R32G32B32_UINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
|
||||
return VK_FORMAT_R32G32B32A32_UINT;
|
||||
default:
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
|
||||
return VK_FORMAT_A2B10G10R10_UINT_PACK32;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::UnsignedScaled:
|
||||
case Maxwell::VertexAttribute::Type::SignedInt:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
return VK_FORMAT_R8_USCALED;
|
||||
return VK_FORMAT_R8_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
return VK_FORMAT_R8G8_USCALED;
|
||||
return VK_FORMAT_R8G8_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
return VK_FORMAT_R8G8B8_USCALED;
|
||||
return VK_FORMAT_R8G8B8_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
return VK_FORMAT_R8G8B8A8_USCALED;
|
||||
return VK_FORMAT_R8G8B8A8_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
return VK_FORMAT_R16_USCALED;
|
||||
return VK_FORMAT_R16_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
return VK_FORMAT_R16G16_USCALED;
|
||||
return VK_FORMAT_R16G16_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
return VK_FORMAT_R16G16B16_USCALED;
|
||||
return VK_FORMAT_R16G16B16_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
return VK_FORMAT_R16G16B16A16_USCALED;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::SignedScaled:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
return VK_FORMAT_R8_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
return VK_FORMAT_R8G8_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
return VK_FORMAT_R8G8B8_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
return VK_FORMAT_R8G8B8A8_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
return VK_FORMAT_R16_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
return VK_FORMAT_R16G16_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
return VK_FORMAT_R16G16B16_SSCALED;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
return VK_FORMAT_R16G16B16A16_SSCALED;
|
||||
default:
|
||||
break;
|
||||
return VK_FORMAT_R16G16B16A16_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32:
|
||||
return VK_FORMAT_R32_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32:
|
||||
return VK_FORMAT_R32G32_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32:
|
||||
return VK_FORMAT_R32G32B32_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
|
||||
return VK_FORMAT_R32G32B32A32_SINT;
|
||||
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
|
||||
return VK_FORMAT_A2B10G10R10_SINT_PACK32;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::Float:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_32:
|
||||
return VK_FORMAT_R32_SFLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32:
|
||||
return VK_FORMAT_R32G32_SFLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32:
|
||||
return VK_FORMAT_R32G32B32_SFLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
|
||||
return VK_FORMAT_R32G32B32A32_SFLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
return VK_FORMAT_R16_SFLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
@@ -464,8 +452,14 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
|
||||
return VK_FORMAT_R16G16B16_SFLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
return VK_FORMAT_R16G16B16A16_SFLOAT;
|
||||
default:
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Size::Size_32:
|
||||
return VK_FORMAT_R32_SFLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32:
|
||||
return VK_FORMAT_R32G32_SFLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32:
|
||||
return VK_FORMAT_R32G32B32_SFLOAT;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
|
||||
return VK_FORMAT_R32G32B32A32_SFLOAT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -155,11 +155,31 @@ vk::Instance CreateInstance(Common::DynamicLibrary& library, vk::InstanceDispatc
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr std::array layers_data{"VK_LAYER_LUNARG_standard_validation"};
|
||||
vk::Span<const char*> layers = layers_data;
|
||||
if (!enable_layers) {
|
||||
layers = {};
|
||||
std::vector<const char*> layers;
|
||||
layers.reserve(1);
|
||||
if (enable_layers) {
|
||||
layers.push_back("VK_LAYER_KHRONOS_validation");
|
||||
}
|
||||
|
||||
const std::optional layer_properties = vk::EnumerateInstanceLayerProperties(dld);
|
||||
if (!layer_properties) {
|
||||
LOG_ERROR(Render_Vulkan, "Failed to query layer properties, disabling layers");
|
||||
layers.clear();
|
||||
}
|
||||
|
||||
for (auto layer_it = layers.begin(); layer_it != layers.end();) {
|
||||
const char* const layer = *layer_it;
|
||||
const auto it = std::find_if(
|
||||
layer_properties->begin(), layer_properties->end(),
|
||||
[layer](const VkLayerProperties& prop) { return !std::strcmp(layer, prop.layerName); });
|
||||
if (it == layer_properties->end()) {
|
||||
LOG_ERROR(Render_Vulkan, "Layer {} not available, removing it", layer);
|
||||
layer_it = layers.erase(layer_it);
|
||||
} else {
|
||||
++layer_it;
|
||||
}
|
||||
}
|
||||
|
||||
vk::Instance instance = vk::Instance::Create(layers, extensions, dld);
|
||||
if (!instance) {
|
||||
LOG_ERROR(Render_Vulkan, "Failed to create Vulkan instance");
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -29,12 +29,12 @@ public:
|
||||
VKStagingBufferPool& staging_pool, 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);
|
||||
|
||||
VkBuffer Handle() const {
|
||||
return *buffer.handle;
|
||||
|
||||
@@ -153,7 +153,8 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
||||
|
||||
bool Load(InstanceDispatch& dld) noexcept {
|
||||
#define X(name) Proc(dld.name, dld, #name)
|
||||
return X(vkCreateInstance) && X(vkEnumerateInstanceExtensionProperties);
|
||||
return X(vkCreateInstance) && X(vkEnumerateInstanceExtensionProperties) &&
|
||||
X(vkEnumerateInstanceLayerProperties);
|
||||
#undef X
|
||||
}
|
||||
|
||||
@@ -770,4 +771,17 @@ std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProp
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties(
|
||||
const InstanceDispatch& dld) {
|
||||
u32 num;
|
||||
if (dld.vkEnumerateInstanceLayerProperties(&num, nullptr) != VK_SUCCESS) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::vector<VkLayerProperties> properties(num);
|
||||
if (dld.vkEnumerateInstanceLayerProperties(&num, properties.data()) != VK_SUCCESS) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
} // namespace Vulkan::vk
|
||||
|
||||
@@ -141,6 +141,7 @@ struct InstanceDispatch {
|
||||
PFN_vkCreateInstance vkCreateInstance;
|
||||
PFN_vkDestroyInstance vkDestroyInstance;
|
||||
PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties;
|
||||
PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;
|
||||
|
||||
PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT;
|
||||
PFN_vkCreateDevice vkCreateDevice;
|
||||
@@ -996,4 +997,7 @@ private:
|
||||
std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProperties(
|
||||
const InstanceDispatch& dld);
|
||||
|
||||
std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties(
|
||||
const InstanceDispatch& dld);
|
||||
|
||||
} // namespace Vulkan::vk
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace VideoCommon {
|
||||
template <class T>
|
||||
class ShaderCache {
|
||||
static constexpr u64 PAGE_BITS = 14;
|
||||
static constexpr u64 PAGE_SIZE = u64(1) << PAGE_BITS;
|
||||
|
||||
struct Entry {
|
||||
VAddr addr_start;
|
||||
@@ -87,8 +88,8 @@ protected:
|
||||
const VAddr addr_end = addr + size;
|
||||
Entry* const entry = NewEntry(addr, addr_end, data.get());
|
||||
|
||||
const u64 page_end = addr_end >> PAGE_BITS;
|
||||
for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) {
|
||||
const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS;
|
||||
for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) {
|
||||
invalidation_cache[page].push_back(entry);
|
||||
}
|
||||
|
||||
@@ -108,20 +109,13 @@ private:
|
||||
/// @pre invalidation_mutex is locked
|
||||
void InvalidatePagesInRegion(VAddr addr, std::size_t size) {
|
||||
const VAddr addr_end = addr + size;
|
||||
const u64 page_end = addr_end >> PAGE_BITS;
|
||||
for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) {
|
||||
const auto it = invalidation_cache.find(page);
|
||||
const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS;
|
||||
for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) {
|
||||
auto it = invalidation_cache.find(page);
|
||||
if (it == invalidation_cache.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<Entry*>& entries = it->second;
|
||||
InvalidatePageEntries(entries, addr, addr_end);
|
||||
|
||||
// If there's nothing else in this page, remove it to avoid overpopulating the hash map.
|
||||
if (entries.empty()) {
|
||||
invalidation_cache.erase(it);
|
||||
}
|
||||
InvalidatePageEntries(it->second, addr, addr_end);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,15 +125,22 @@ private:
|
||||
if (marked_for_removal.empty()) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{lookup_mutex};
|
||||
// Remove duplicates
|
||||
std::sort(marked_for_removal.begin(), marked_for_removal.end());
|
||||
marked_for_removal.erase(std::unique(marked_for_removal.begin(), marked_for_removal.end()),
|
||||
marked_for_removal.end());
|
||||
|
||||
std::vector<T*> removed_shaders;
|
||||
removed_shaders.reserve(marked_for_removal.size());
|
||||
|
||||
std::scoped_lock lock{lookup_mutex};
|
||||
|
||||
for (Entry* const entry : marked_for_removal) {
|
||||
if (lookup_cache.erase(entry->addr_start) > 0) {
|
||||
removed_shaders.push_back(entry->data);
|
||||
}
|
||||
removed_shaders.push_back(entry->data);
|
||||
|
||||
const auto it = lookup_cache.find(entry->addr_start);
|
||||
ASSERT(it != lookup_cache.end());
|
||||
lookup_cache.erase(it);
|
||||
}
|
||||
marked_for_removal.clear();
|
||||
|
||||
@@ -154,17 +155,33 @@ private:
|
||||
/// @param addr_end Non-inclusive end address of the invalidation
|
||||
/// @pre invalidation_mutex is locked
|
||||
void InvalidatePageEntries(std::vector<Entry*>& entries, VAddr addr, VAddr addr_end) {
|
||||
auto it = entries.begin();
|
||||
while (it != entries.end()) {
|
||||
Entry* const entry = *it;
|
||||
std::size_t index = 0;
|
||||
while (index < entries.size()) {
|
||||
Entry* const entry = entries[index];
|
||||
if (!entry->Overlaps(addr, addr_end)) {
|
||||
++it;
|
||||
++index;
|
||||
continue;
|
||||
}
|
||||
UnmarkMemory(entry);
|
||||
marked_for_removal.push_back(entry);
|
||||
|
||||
it = entries.erase(it);
|
||||
UnmarkMemory(entry);
|
||||
RemoveEntryFromInvalidationCache(entry);
|
||||
marked_for_removal.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Removes all references to an entry in the invalidation cache
|
||||
/// @param entry Entry to remove from the invalidation cache
|
||||
/// @pre invalidation_mutex is locked
|
||||
void RemoveEntryFromInvalidationCache(const Entry* entry) {
|
||||
const u64 page_end = (entry->addr_end + PAGE_SIZE - 1) >> PAGE_BITS;
|
||||
for (u64 page = entry->addr_start >> PAGE_BITS; page < page_end; ++page) {
|
||||
const auto entries_it = invalidation_cache.find(page);
|
||||
ASSERT(entries_it != invalidation_cache.end());
|
||||
std::vector<Entry*>& entries = entries_it->second;
|
||||
|
||||
const auto entry_it = std::find(entries.begin(), entries.end(), entry);
|
||||
ASSERT(entry_it != entries.end());
|
||||
entries.erase(entry_it);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,16 +199,11 @@ private:
|
||||
}
|
||||
|
||||
/// @brief Removes a vector of shaders from a list
|
||||
/// @param removed_shaders Shaders to be removed from the storage, it can contain duplicates
|
||||
/// @param removed_shaders Shaders to be removed from the storage
|
||||
/// @pre invalidation_mutex is locked
|
||||
/// @pre lookup_mutex is locked
|
||||
void RemoveShadersFromStorage(std::vector<T*> removed_shaders) {
|
||||
// Remove duplicates
|
||||
std::sort(removed_shaders.begin(), removed_shaders.end());
|
||||
removed_shaders.erase(std::unique(removed_shaders.begin(), removed_shaders.end()),
|
||||
removed_shaders.end());
|
||||
|
||||
// Now that there are no duplicates, we can notify removals
|
||||
// Notify removals
|
||||
for (T* const shader : removed_shaders) {
|
||||
OnShaderRemoval(shader);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/settings.h"
|
||||
#include "video_core/compatible_formats.h"
|
||||
#include "video_core/dirty_flags.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
@@ -47,8 +48,8 @@ class RasterizerInterface;
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
using VideoCore::Surface::FormatCompatibility;
|
||||
using VideoCore::Surface::PixelFormat;
|
||||
|
||||
using VideoCore::Surface::SurfaceTarget;
|
||||
using RenderTargetConfig = Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig;
|
||||
|
||||
@@ -595,7 +596,7 @@ private:
|
||||
} else {
|
||||
new_surface = GetUncachedSurface(gpu_addr, params);
|
||||
}
|
||||
const auto& final_params = new_surface->GetSurfaceParams();
|
||||
const SurfaceParams& final_params = new_surface->GetSurfaceParams();
|
||||
if (cr_params.type != final_params.type) {
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
BufferCopy(current_surface, new_surface);
|
||||
@@ -603,7 +604,7 @@ private:
|
||||
} else {
|
||||
std::vector<CopyParams> bricks = current_surface->BreakDown(final_params);
|
||||
for (auto& brick : bricks) {
|
||||
ImageCopy(current_surface, new_surface, brick);
|
||||
TryCopyImage(current_surface, new_surface, brick);
|
||||
}
|
||||
}
|
||||
Unregister(current_surface);
|
||||
@@ -694,7 +695,7 @@ private:
|
||||
}
|
||||
const CopyParams copy_params(0, 0, 0, 0, 0, base_layer, 0, mipmap, width, height,
|
||||
src_params.depth);
|
||||
ImageCopy(surface, new_surface, copy_params);
|
||||
TryCopyImage(surface, new_surface, copy_params);
|
||||
}
|
||||
}
|
||||
if (passed_tests == 0) {
|
||||
@@ -791,7 +792,7 @@ private:
|
||||
const u32 width = params.width;
|
||||
const u32 height = params.height;
|
||||
const CopyParams copy_params(0, 0, 0, 0, 0, slice, 0, 0, width, height, 1);
|
||||
ImageCopy(surface, new_surface, copy_params);
|
||||
TryCopyImage(surface, new_surface, copy_params);
|
||||
}
|
||||
for (const auto& surface : overlaps) {
|
||||
Unregister(surface);
|
||||
@@ -1192,6 +1193,19 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Try to do an image copy logging when formats are incompatible.
|
||||
void TryCopyImage(TSurface& src, TSurface& dst, const CopyParams& copy) {
|
||||
const SurfaceParams& src_params = src->GetSurfaceParams();
|
||||
const SurfaceParams& dst_params = dst->GetSurfaceParams();
|
||||
if (!format_compatibility.TestCopy(src_params.pixel_format, dst_params.pixel_format)) {
|
||||
LOG_ERROR(HW_GPU, "Illegal copy between formats={{{}, {}}}",
|
||||
static_cast<int>(dst_params.pixel_format),
|
||||
static_cast<int>(src_params.pixel_format));
|
||||
return;
|
||||
}
|
||||
ImageCopy(src, dst, copy);
|
||||
}
|
||||
|
||||
constexpr PixelFormat GetSiblingFormat(PixelFormat format) const {
|
||||
return siblings_table[static_cast<std::size_t>(format)];
|
||||
}
|
||||
@@ -1241,6 +1255,7 @@ private:
|
||||
VideoCore::RasterizerInterface& rasterizer;
|
||||
|
||||
FormatLookupTable format_lookup_table;
|
||||
FormatCompatibility format_compatibility;
|
||||
|
||||
u64 ticks{};
|
||||
|
||||
|
||||
@@ -70,6 +70,20 @@ static QString ButtonToText(const Common::ParamPackage& param) {
|
||||
return GetKeyName(param.Get("code", 0));
|
||||
}
|
||||
|
||||
if (param.Get("engine", "") == "gcpad") {
|
||||
if (param.Has("axis")) {
|
||||
const QString axis_str = QString::fromStdString(param.Get("axis", ""));
|
||||
const QString direction_str = QString::fromStdString(param.Get("direction", ""));
|
||||
|
||||
return QObject::tr("GC Axis %1%2").arg(axis_str, direction_str);
|
||||
}
|
||||
if (param.Has("button")) {
|
||||
const QString button_str = QString::number(int(std::log2(param.Get("button", 0))));
|
||||
return QObject::tr("GC Button %1").arg(button_str);
|
||||
}
|
||||
return GetKeyName(param.Get("code", 0));
|
||||
}
|
||||
|
||||
if (param.Get("engine", "") == "sdl") {
|
||||
if (param.Has("hat")) {
|
||||
const QString hat_str = QString::fromStdString(param.Get("hat", ""));
|
||||
@@ -126,6 +140,25 @@ static QString AnalogToText(const Common::ParamPackage& param, const std::string
|
||||
return {};
|
||||
}
|
||||
|
||||
if (param.Get("engine", "") == "gcpad") {
|
||||
if (dir == "modifier") {
|
||||
return QObject::tr("[unused]");
|
||||
}
|
||||
|
||||
if (dir == "left" || dir == "right") {
|
||||
const QString axis_x_str = QString::fromStdString(param.Get("axis_x", ""));
|
||||
|
||||
return QObject::tr("GC Axis %1").arg(axis_x_str);
|
||||
}
|
||||
|
||||
if (dir == "up" || dir == "down") {
|
||||
const QString axis_y_str = QString::fromStdString(param.Get("axis_y", ""));
|
||||
|
||||
return QObject::tr("GC Axis %1").arg(axis_y_str);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
return QObject::tr("[unknown]");
|
||||
}
|
||||
|
||||
@@ -332,7 +365,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
|
||||
connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, [=] {
|
||||
const float slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value();
|
||||
if (analogs_param[analog_id].Get("engine", "") == "sdl") {
|
||||
if (analogs_param[analog_id].Get("engine", "") == "sdl" ||
|
||||
analogs_param[analog_id].Get("engine", "") == "gcpad") {
|
||||
analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
|
||||
tr("Deadzone: %1%").arg(slider_value));
|
||||
analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
|
||||
@@ -352,6 +386,20 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
|
||||
connect(poll_timer.get(), &QTimer::timeout, [this] {
|
||||
Common::ParamPackage params;
|
||||
if (InputCommon::GetGCButtons()->IsPolling()) {
|
||||
params = InputCommon::GetGCButtons()->GetNextInput();
|
||||
if (params.Has("engine")) {
|
||||
SetPollingResult(params, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (InputCommon::GetGCAnalogs()->IsPolling()) {
|
||||
params = InputCommon::GetGCAnalogs()->GetNextInput();
|
||||
if (params.Has("engine")) {
|
||||
SetPollingResult(params, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (auto& poller : device_pollers) {
|
||||
params = poller->GetNextInput();
|
||||
if (params.Has("engine")) {
|
||||
@@ -534,7 +582,7 @@ void ConfigureInputPlayer::UpdateButtonLabels() {
|
||||
analog_map_deadzone_and_modifier_slider_label[analog_id];
|
||||
|
||||
if (param.Has("engine")) {
|
||||
if (param.Get("engine", "") == "sdl") {
|
||||
if (param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad") {
|
||||
if (!param.Has("deadzone")) {
|
||||
param.Set("deadzone", 0.1f);
|
||||
}
|
||||
@@ -583,6 +631,11 @@ void ConfigureInputPlayer::HandleClick(
|
||||
|
||||
grabKeyboard();
|
||||
grabMouse();
|
||||
if (type == InputCommon::Polling::DeviceType::Button) {
|
||||
InputCommon::GetGCButtons()->BeginConfiguration();
|
||||
} else {
|
||||
InputCommon::GetGCAnalogs()->BeginConfiguration();
|
||||
}
|
||||
timeout_timer->start(5000); // Cancel after 5 seconds
|
||||
poll_timer->start(200); // Check for new inputs every 200ms
|
||||
}
|
||||
@@ -596,6 +649,9 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params,
|
||||
poller->Stop();
|
||||
}
|
||||
|
||||
InputCommon::GetGCButtons()->EndConfiguration();
|
||||
InputCommon::GetGCAnalogs()->EndConfiguration();
|
||||
|
||||
if (!abort) {
|
||||
(*input_setter)(params);
|
||||
}
|
||||
|
||||
@@ -689,6 +689,11 @@ void GMainWindow::InitializeHotkeys() {
|
||||
ui.action_Capture_Screenshot->setShortcutContext(
|
||||
hotkey_registry.GetShortcutContext(main_window, capture_screenshot));
|
||||
|
||||
ui.action_Fullscreen->setShortcut(
|
||||
hotkey_registry.GetHotkey(main_window, fullscreen, this)->key());
|
||||
ui.action_Fullscreen->setShortcutContext(
|
||||
hotkey_registry.GetShortcutContext(main_window, fullscreen));
|
||||
|
||||
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load File"), this),
|
||||
&QShortcut::activated, this, &GMainWindow::OnMenuLoadFile);
|
||||
connect(
|
||||
@@ -863,6 +868,9 @@ void GMainWindow::ConnectMenuEvents() {
|
||||
connect(ui.action_Report_Compatibility, &QAction::triggered, this,
|
||||
&GMainWindow::OnMenuReportCompatibility);
|
||||
connect(ui.action_Open_Mods_Page, &QAction::triggered, this, &GMainWindow::OnOpenModsPage);
|
||||
connect(ui.action_Open_Quickstart_Guide, &QAction::triggered, this,
|
||||
&GMainWindow::OnOpenQuickstartGuide);
|
||||
connect(ui.action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnOpenFAQ);
|
||||
connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); });
|
||||
connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure);
|
||||
|
||||
@@ -876,10 +884,6 @@ void GMainWindow::ConnectMenuEvents() {
|
||||
connect(ui.action_Reset_Window_Size, &QAction::triggered, this, &GMainWindow::ResetWindowSize);
|
||||
|
||||
// Fullscreen
|
||||
ui.action_Fullscreen->setShortcut(
|
||||
hotkey_registry
|
||||
.GetHotkey(QStringLiteral("Main Window"), QStringLiteral("Fullscreen"), this)
|
||||
->key());
|
||||
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
|
||||
|
||||
// Movie
|
||||
@@ -1077,17 +1081,19 @@ void GMainWindow::BootGame(const QString& filename) {
|
||||
const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
|
||||
|
||||
std::string title_name;
|
||||
std::string title_version;
|
||||
const auto res = Core::System::GetInstance().GetGameName(title_name);
|
||||
if (res != Loader::ResultStatus::Success) {
|
||||
const auto metadata = FileSys::PatchManager(title_id).GetControlMetadata();
|
||||
if (metadata.first != nullptr)
|
||||
title_name = metadata.first->GetApplicationName();
|
||||
|
||||
if (title_name.empty())
|
||||
title_name = FileUtil::GetFilename(filename.toStdString());
|
||||
const auto metadata = FileSys::PatchManager(title_id).GetControlMetadata();
|
||||
if (metadata.first != nullptr) {
|
||||
title_version = metadata.first->GetVersionString();
|
||||
title_name = metadata.first->GetApplicationName();
|
||||
}
|
||||
LOG_INFO(Frontend, "Booting game: {:016X} | {}", title_id, title_name);
|
||||
UpdateWindowTitle(QString::fromStdString(title_name));
|
||||
if (res != Loader::ResultStatus::Success || title_name.empty()) {
|
||||
title_name = FileUtil::GetFilename(filename.toStdString());
|
||||
}
|
||||
LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version);
|
||||
UpdateWindowTitle(title_name, title_version);
|
||||
|
||||
loading_screen->Prepare(Core::System::GetInstance().GetAppLoader());
|
||||
loading_screen->show();
|
||||
@@ -1838,16 +1844,26 @@ void GMainWindow::OnMenuReportCompatibility() {
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnOpenModsPage() {
|
||||
const auto mods_page_url = QStringLiteral("https://github.com/yuzu-emu/yuzu/wiki/Switch-Mods");
|
||||
const QUrl mods_page(mods_page_url);
|
||||
const bool open = QDesktopServices::openUrl(mods_page);
|
||||
void GMainWindow::OpenURL(const QUrl& url) {
|
||||
const bool open = QDesktopServices::openUrl(url);
|
||||
if (!open) {
|
||||
QMessageBox::warning(this, tr("Error opening URL"),
|
||||
tr("Unable to open the URL \"%1\".").arg(mods_page_url));
|
||||
tr("Unable to open the URL \"%1\".").arg(url.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnOpenModsPage() {
|
||||
OpenURL(QUrl(QStringLiteral("https://github.com/yuzu-emu/yuzu/wiki/Switch-Mods")));
|
||||
}
|
||||
|
||||
void GMainWindow::OnOpenQuickstartGuide() {
|
||||
OpenURL(QUrl(QStringLiteral("https://yuzu-emu.org/help/quickstart/")));
|
||||
}
|
||||
|
||||
void GMainWindow::OnOpenFAQ() {
|
||||
OpenURL(QUrl(QStringLiteral("https://yuzu-emu.org/wiki/faq/")));
|
||||
}
|
||||
|
||||
void GMainWindow::ToggleFullscreen() {
|
||||
if (!emulation_running) {
|
||||
return;
|
||||
@@ -2050,7 +2066,8 @@ void GMainWindow::OnCaptureScreenshot() {
|
||||
OnStartGame();
|
||||
}
|
||||
|
||||
void GMainWindow::UpdateWindowTitle(const QString& title_name) {
|
||||
void GMainWindow::UpdateWindowTitle(const std::string& title_name,
|
||||
const std::string& title_version) {
|
||||
const auto full_name = std::string(Common::g_build_fullname);
|
||||
const auto branch_name = std::string(Common::g_scm_branch);
|
||||
const auto description = std::string(Common::g_scm_desc);
|
||||
@@ -2059,7 +2076,7 @@ void GMainWindow::UpdateWindowTitle(const QString& title_name) {
|
||||
const auto date =
|
||||
QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd")).toStdString();
|
||||
|
||||
if (title_name.isEmpty()) {
|
||||
if (title_name.empty()) {
|
||||
const auto fmt = std::string(Common::g_title_bar_format_idle);
|
||||
setWindowTitle(QString::fromStdString(fmt::format(fmt.empty() ? "yuzu {0}| {1}-{2}" : fmt,
|
||||
full_name, branch_name, description,
|
||||
@@ -2067,8 +2084,8 @@ void GMainWindow::UpdateWindowTitle(const QString& title_name) {
|
||||
} else {
|
||||
const auto fmt = std::string(Common::g_title_bar_format_running);
|
||||
setWindowTitle(QString::fromStdString(
|
||||
fmt::format(fmt.empty() ? "yuzu {0}| {3} | {1}-{2}" : fmt, full_name, branch_name,
|
||||
description, title_name.toStdString(), date, build_id)));
|
||||
fmt::format(fmt.empty() ? "yuzu {0}| {3} | {6} | {1}-{2}" : fmt, full_name, branch_name,
|
||||
description, title_name, date, build_id, title_version)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2209,7 +2226,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
|
||||
"title.keys_autogenerated");
|
||||
}
|
||||
|
||||
Core::Crypto::KeyManager keys{};
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||
if (keys.BaseDeriveNecessary()) {
|
||||
Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory(
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)};
|
||||
|
||||
@@ -182,6 +182,8 @@ private slots:
|
||||
void OnStopGame();
|
||||
void OnMenuReportCompatibility();
|
||||
void OnOpenModsPage();
|
||||
void OnOpenQuickstartGuide();
|
||||
void OnOpenFAQ();
|
||||
/// Called whenever a user selects a game in the game list widget.
|
||||
void OnGameListLoadFile(QString game_path);
|
||||
void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path);
|
||||
@@ -216,10 +218,12 @@ private slots:
|
||||
|
||||
private:
|
||||
std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
|
||||
void UpdateWindowTitle(const QString& title_name = {});
|
||||
void UpdateWindowTitle(const std::string& title_name = {},
|
||||
const std::string& title_version = {});
|
||||
void UpdateStatusBar();
|
||||
void HideMouseCursor();
|
||||
void ShowMouseCursor();
|
||||
void OpenURL(const QUrl& url);
|
||||
|
||||
Ui::MainWindow ui;
|
||||
|
||||
|
||||
@@ -114,6 +114,8 @@
|
||||
</property>
|
||||
<addaction name="action_Report_Compatibility"/>
|
||||
<addaction name="action_Open_Mods_Page"/>
|
||||
<addaction name="action_Open_Quickstart_Guide"/>
|
||||
<addaction name="action_Open_FAQ"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_About"/>
|
||||
</widget>
|
||||
@@ -262,6 +264,16 @@
|
||||
<string>Open Mods Page</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Open_Quickstart_Guide">
|
||||
<property name="text">
|
||||
<string>Open Quickstart Guide</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Open_FAQ">
|
||||
<property name="text">
|
||||
<string>FAQ</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Open_yuzu_Folder">
|
||||
<property name="text">
|
||||
<string>Open yuzu Folder</string>
|
||||
|
||||
Reference in New Issue
Block a user