diff options
71 files changed, 2556 insertions, 535 deletions
diff --git a/.gitmodules b/.gitmodules index 9ba8fe207..79028bbb5 100644 --- a/.gitmodules +++ b/.gitmodules | |||
| @@ -34,3 +34,9 @@ | |||
| 34 | [submodule "xbyak"] | 34 | [submodule "xbyak"] |
| 35 | path = externals/xbyak | 35 | path = externals/xbyak |
| 36 | url = https://github.com/herumi/xbyak.git | 36 | url = https://github.com/herumi/xbyak.git |
| 37 | [submodule "externals/libusb"] | ||
| 38 | path = externals/libusb | ||
| 39 | url = https://github.com/ameerj/libusb | ||
| 40 | [submodule "opus"] | ||
| 41 | path = externals/opus/opus | ||
| 42 | url = https://github.com/xiph/opus.git | ||
diff --git a/CMakeLists.txt b/CMakeLists.txt index b71071271..27383bce8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -156,8 +156,6 @@ macro(yuzu_find_packages) | |||
| 156 | #"libzip 1.5 libzip/1.5.2@bincrafters/stable" | 156 | #"libzip 1.5 libzip/1.5.2@bincrafters/stable" |
| 157 | "lz4 1.8 lz4/1.9.2" | 157 | "lz4 1.8 lz4/1.9.2" |
| 158 | "nlohmann_json 3.7 nlohmann_json/3.7.3" | 158 | "nlohmann_json 3.7 nlohmann_json/3.7.3" |
| 159 | # we need to be careful as the version check might be broken https://github.com/xiph/opus/issues/110 | ||
| 160 | "opus 1.3 opus/1.3.1" | ||
| 161 | "ZLIB 1.2 zlib/1.2.11" | 159 | "ZLIB 1.2 zlib/1.2.11" |
| 162 | "zstd 1.4 zstd/1.4.4" | 160 | "zstd 1.4 zstd/1.4.4" |
| 163 | ) | 161 | ) |
| @@ -214,6 +212,9 @@ if(ENABLE_QT) | |||
| 214 | set(QT_PREFIX_HINT HINTS "${QT_PREFIX}") | 212 | set(QT_PREFIX_HINT HINTS "${QT_PREFIX}") |
| 215 | endif() | 213 | endif() |
| 216 | find_package(Qt5 5.9 COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT}) | 214 | find_package(Qt5 5.9 COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT}) |
| 215 | if (YUZU_USE_QT_WEB_ENGINE) | ||
| 216 | find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets) | ||
| 217 | endif() | ||
| 217 | if (NOT Qt5_FOUND) | 218 | if (NOT Qt5_FOUND) |
| 218 | list(APPEND CONAN_REQUIRED_LIBS "qt/5.14.1@bincrafters/stable") | 219 | list(APPEND CONAN_REQUIRED_LIBS "qt/5.14.1@bincrafters/stable") |
| 219 | endif() | 220 | endif() |
| @@ -328,6 +329,12 @@ elseif(SDL2_FOUND) | |||
| 328 | target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARIES}") | 329 | target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARIES}") |
| 329 | endif() | 330 | endif() |
| 330 | 331 | ||
| 332 | # Ensure libusb is properly configured (based on dolphin libusb include) | ||
| 333 | find_package(LibUSB) | ||
| 334 | add_subdirectory(externals/libusb) | ||
| 335 | set(LIBUSB_LIBRARIES usb) | ||
| 336 | |||
| 337 | |||
| 331 | # Prefer the -pthread flag on Linux. | 338 | # Prefer the -pthread flag on Linux. |
| 332 | set(THREADS_PREFER_PTHREAD_FLAG ON) | 339 | set(THREADS_PREFER_PTHREAD_FLAG ON) |
| 333 | find_package(Threads REQUIRED) | 340 | find_package(Threads REQUIRED) |
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index b80b27605..d1dcc403b 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt | |||
| @@ -91,3 +91,6 @@ if (ENABLE_WEB_SERVICE) | |||
| 91 | target_compile_definitions(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT) | 91 | target_compile_definitions(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT) |
| 92 | target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES}) | 92 | target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES}) |
| 93 | endif() | 93 | endif() |
| 94 | |||
| 95 | # Opus | ||
| 96 | add_subdirectory(opus) | ||
diff --git a/externals/Vulkan-Headers b/externals/Vulkan-Headers | |||
| Subproject 9250d5ae8f50202005233dc0512a1d460c8b483 | Subproject 8188e3fbbc105591064093440f88081fb957d4f | ||
diff --git a/externals/libusb b/externals/libusb new file mode 160000 | |||
| Subproject 3406d72cda879f8792a88bf5f6bd0b7a65636f7 | |||
diff --git a/externals/opus/CMakeLists.txt b/externals/opus/CMakeLists.txt new file mode 100644 index 000000000..94a86551f --- /dev/null +++ b/externals/opus/CMakeLists.txt | |||
| @@ -0,0 +1,254 @@ | |||
| 1 | cmake_minimum_required(VERSION 3.8) | ||
| 2 | |||
| 3 | project(opus) | ||
| 4 | |||
| 5 | option(OPUS_STACK_PROTECTOR "Use stack protection" OFF) | ||
| 6 | option(OPUS_USE_ALLOCA "Use alloca for stack arrays (on non-C99 compilers)" OFF) | ||
| 7 | option(OPUS_CUSTOM_MODES "Enable non-Opus modes, e.g. 44.1 kHz & 2^n frames" OFF) | ||
| 8 | option(OPUS_FIXED_POINT "Compile as fixed-point (for machines without a fast enough FPU)" OFF) | ||
| 9 | option(OPUS_ENABLE_FLOAT_API "Compile with the floating point API (for machines with float library" ON) | ||
| 10 | |||
| 11 | include(opus/opus_functions.cmake) | ||
| 12 | |||
| 13 | if(OPUS_STACK_PROTECTOR) | ||
| 14 | if(NOT MSVC) # GC on by default on MSVC | ||
| 15 | check_and_set_flag(STACK_PROTECTION_STRONG -fstack-protector-strong) | ||
| 16 | endif() | ||
| 17 | else() | ||
| 18 | if(MSVC) | ||
| 19 | check_and_set_flag(BUFFER_SECURITY_CHECK /GS-) | ||
| 20 | endif() | ||
| 21 | endif() | ||
| 22 | |||
| 23 | add_library(opus STATIC | ||
| 24 | # CELT sources | ||
| 25 | opus/celt/bands.c | ||
| 26 | opus/celt/celt.c | ||
| 27 | opus/celt/celt_decoder.c | ||
| 28 | opus/celt/celt_encoder.c | ||
| 29 | opus/celt/celt_lpc.c | ||
| 30 | opus/celt/cwrs.c | ||
| 31 | opus/celt/entcode.c | ||
| 32 | opus/celt/entdec.c | ||
| 33 | opus/celt/entenc.c | ||
| 34 | opus/celt/kiss_fft.c | ||
| 35 | opus/celt/laplace.c | ||
| 36 | opus/celt/mathops.c | ||
| 37 | opus/celt/mdct.c | ||
| 38 | opus/celt/modes.c | ||
| 39 | opus/celt/pitch.c | ||
| 40 | opus/celt/quant_bands.c | ||
| 41 | opus/celt/rate.c | ||
| 42 | opus/celt/vq.c | ||
| 43 | |||
| 44 | # SILK sources | ||
| 45 | opus/silk/A2NLSF.c | ||
| 46 | opus/silk/CNG.c | ||
| 47 | opus/silk/HP_variable_cutoff.c | ||
| 48 | opus/silk/LPC_analysis_filter.c | ||
| 49 | opus/silk/LPC_fit.c | ||
| 50 | opus/silk/LPC_inv_pred_gain.c | ||
| 51 | opus/silk/LP_variable_cutoff.c | ||
| 52 | opus/silk/NLSF2A.c | ||
| 53 | opus/silk/NLSF_VQ.c | ||
| 54 | opus/silk/NLSF_VQ_weights_laroia.c | ||
| 55 | opus/silk/NLSF_decode.c | ||
| 56 | opus/silk/NLSF_del_dec_quant.c | ||
| 57 | opus/silk/NLSF_encode.c | ||
| 58 | opus/silk/NLSF_stabilize.c | ||
| 59 | opus/silk/NLSF_unpack.c | ||
| 60 | opus/silk/NSQ.c | ||
| 61 | opus/silk/NSQ_del_dec.c | ||
| 62 | opus/silk/PLC.c | ||
| 63 | opus/silk/VAD.c | ||
| 64 | opus/silk/VQ_WMat_EC.c | ||
| 65 | opus/silk/ana_filt_bank_1.c | ||
| 66 | opus/silk/biquad_alt.c | ||
| 67 | opus/silk/bwexpander.c | ||
| 68 | opus/silk/bwexpander_32.c | ||
| 69 | opus/silk/check_control_input.c | ||
| 70 | opus/silk/code_signs.c | ||
| 71 | opus/silk/control_SNR.c | ||
| 72 | opus/silk/control_audio_bandwidth.c | ||
| 73 | opus/silk/control_codec.c | ||
| 74 | opus/silk/dec_API.c | ||
| 75 | opus/silk/decode_core.c | ||
| 76 | opus/silk/decode_frame.c | ||
| 77 | opus/silk/decode_indices.c | ||
| 78 | opus/silk/decode_parameters.c | ||
| 79 | opus/silk/decode_pitch.c | ||
| 80 | opus/silk/decode_pulses.c | ||
| 81 | opus/silk/decoder_set_fs.c | ||
| 82 | opus/silk/enc_API.c | ||
| 83 | opus/silk/encode_indices.c | ||
| 84 | opus/silk/encode_pulses.c | ||
| 85 | opus/silk/gain_quant.c | ||
| 86 | opus/silk/init_decoder.c | ||
| 87 | opus/silk/init_encoder.c | ||
| 88 | opus/silk/inner_prod_aligned.c | ||
| 89 | opus/silk/interpolate.c | ||
| 90 | opus/silk/lin2log.c | ||
| 91 | opus/silk/log2lin.c | ||
| 92 | opus/silk/pitch_est_tables.c | ||
| 93 | opus/silk/process_NLSFs.c | ||
| 94 | opus/silk/quant_LTP_gains.c | ||
| 95 | opus/silk/resampler.c | ||
| 96 | opus/silk/resampler_down2.c | ||
| 97 | opus/silk/resampler_down2_3.c | ||
| 98 | opus/silk/resampler_private_AR2.c | ||
| 99 | opus/silk/resampler_private_IIR_FIR.c | ||
| 100 | opus/silk/resampler_private_down_FIR.c | ||
| 101 | opus/silk/resampler_private_up2_HQ.c | ||
| 102 | opus/silk/resampler_rom.c | ||
| 103 | opus/silk/shell_coder.c | ||
| 104 | opus/silk/sigm_Q15.c | ||
| 105 | opus/silk/sort.c | ||
| 106 | opus/silk/stereo_LR_to_MS.c | ||
| 107 | opus/silk/stereo_MS_to_LR.c | ||
| 108 | opus/silk/stereo_decode_pred.c | ||
| 109 | opus/silk/stereo_encode_pred.c | ||
| 110 | opus/silk/stereo_find_predictor.c | ||
| 111 | opus/silk/stereo_quant_pred.c | ||
| 112 | opus/silk/sum_sqr_shift.c | ||
| 113 | opus/silk/table_LSF_cos.c | ||
| 114 | opus/silk/tables_LTP.c | ||
| 115 | opus/silk/tables_NLSF_CB_NB_MB.c | ||
| 116 | opus/silk/tables_NLSF_CB_WB.c | ||
| 117 | opus/silk/tables_gain.c | ||
| 118 | opus/silk/tables_other.c | ||
| 119 | opus/silk/tables_pitch_lag.c | ||
| 120 | opus/silk/tables_pulses_per_block.c | ||
| 121 | |||
| 122 | # Opus sources | ||
| 123 | opus/src/analysis.c | ||
| 124 | opus/src/mapping_matrix.c | ||
| 125 | opus/src/mlp.c | ||
| 126 | opus/src/mlp_data.c | ||
| 127 | opus/src/opus.c | ||
| 128 | opus/src/opus_decoder.c | ||
| 129 | opus/src/opus_encoder.c | ||
| 130 | opus/src/opus_multistream.c | ||
| 131 | opus/src/opus_multistream_decoder.c | ||
| 132 | opus/src/opus_multistream_encoder.c | ||
| 133 | opus/src/opus_projection_decoder.c | ||
| 134 | opus/src/opus_projection_encoder.c | ||
| 135 | opus/src/repacketizer.c | ||
| 136 | ) | ||
| 137 | |||
| 138 | if (DEBUG) | ||
| 139 | target_sources(opus PRIVATE opus/silk/debug.c) | ||
| 140 | endif() | ||
| 141 | |||
| 142 | if (OPUS_FIXED_POINT) | ||
| 143 | target_sources(opus PRIVATE | ||
| 144 | opus/silk/fixed/LTP_analysis_filter_FIX.c | ||
| 145 | opus/silk/fixed/LTP_scale_ctrl_FIX.c | ||
| 146 | opus/silk/fixed/apply_sine_window_FIX.c | ||
| 147 | opus/silk/fixed/autocorr_FIX.c | ||
| 148 | opus/silk/fixed/burg_modified_FIX.c | ||
| 149 | opus/silk/fixed/corrMatrix_FIX.c | ||
| 150 | opus/silk/fixed/encode_frame_FIX.c | ||
| 151 | opus/silk/fixed/find_LPC_FIX.c | ||
| 152 | opus/silk/fixed/find_LTP_FIX.c | ||
| 153 | opus/silk/fixed/find_pitch_lags_FIX.c | ||
| 154 | opus/silk/fixed/find_pred_coefs_FIX.c | ||
| 155 | opus/silk/fixed/k2a_FIX.c | ||
| 156 | opus/silk/fixed/k2a_Q16_FIX.c | ||
| 157 | opus/silk/fixed/noise_shape_analysis_FIX.c | ||
| 158 | opus/silk/fixed/pitch_analysis_core_FIX.c | ||
| 159 | opus/silk/fixed/prefilter_FIX.c | ||
| 160 | opus/silk/fixed/process_gains_FIX.c | ||
| 161 | opus/silk/fixed/regularize_correlations_FIX.c | ||
| 162 | opus/silk/fixed/residual_energy16_FIX.c | ||
| 163 | opus/silk/fixed/residual_energy_FIX.c | ||
| 164 | opus/silk/fixed/schur64_FIX.c | ||
| 165 | opus/silk/fixed/schur_FIX.c | ||
| 166 | opus/silk/fixed/solve_LS_FIX.c | ||
| 167 | opus/silk/fixed/vector_ops_FIX.c | ||
| 168 | opus/silk/fixed/warped_autocorrelation_FIX.c | ||
| 169 | ) | ||
| 170 | else() | ||
| 171 | target_sources(opus PRIVATE | ||
| 172 | opus/silk/float/LPC_analysis_filter_FLP.c | ||
| 173 | opus/silk/float/LPC_inv_pred_gain_FLP.c | ||
| 174 | opus/silk/float/LTP_analysis_filter_FLP.c | ||
| 175 | opus/silk/float/LTP_scale_ctrl_FLP.c | ||
| 176 | opus/silk/float/apply_sine_window_FLP.c | ||
| 177 | opus/silk/float/autocorrelation_FLP.c | ||
| 178 | opus/silk/float/burg_modified_FLP.c | ||
| 179 | opus/silk/float/bwexpander_FLP.c | ||
| 180 | opus/silk/float/corrMatrix_FLP.c | ||
| 181 | opus/silk/float/encode_frame_FLP.c | ||
| 182 | opus/silk/float/energy_FLP.c | ||
| 183 | opus/silk/float/find_LPC_FLP.c | ||
| 184 | opus/silk/float/find_LTP_FLP.c | ||
| 185 | opus/silk/float/find_pitch_lags_FLP.c | ||
| 186 | opus/silk/float/find_pred_coefs_FLP.c | ||
| 187 | opus/silk/float/inner_product_FLP.c | ||
| 188 | opus/silk/float/k2a_FLP.c | ||
| 189 | opus/silk/float/noise_shape_analysis_FLP.c | ||
| 190 | opus/silk/float/pitch_analysis_core_FLP.c | ||
| 191 | opus/silk/float/process_gains_FLP.c | ||
| 192 | opus/silk/float/regularize_correlations_FLP.c | ||
| 193 | opus/silk/float/residual_energy_FLP.c | ||
| 194 | opus/silk/float/scale_copy_vector_FLP.c | ||
| 195 | opus/silk/float/scale_vector_FLP.c | ||
| 196 | opus/silk/float/schur_FLP.c | ||
| 197 | opus/silk/float/sort_FLP.c | ||
| 198 | opus/silk/float/warped_autocorrelation_FLP.c | ||
| 199 | opus/silk/float/wrappers_FLP.c | ||
| 200 | ) | ||
| 201 | endif() | ||
| 202 | |||
| 203 | target_compile_definitions(opus PRIVATE OPUS_BUILD ENABLE_HARDENING) | ||
| 204 | |||
| 205 | if(NOT MSVC) | ||
| 206 | if(MINGW) | ||
| 207 | target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=0) | ||
| 208 | else() | ||
| 209 | target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=2) | ||
| 210 | endif() | ||
| 211 | endif() | ||
| 212 | |||
| 213 | # It is strongly recommended to uncomment one of these VAR_ARRAYS: Use C99 | ||
| 214 | # variable-length arrays for stack allocation USE_ALLOCA: Use alloca() for stack | ||
| 215 | # allocation If none is defined, then the fallback is a non-threadsafe global | ||
| 216 | # array | ||
| 217 | if(OPUS_USE_ALLOCA OR MSVC) | ||
| 218 | target_compile_definitions(opus PRIVATE USE_ALLOCA) | ||
| 219 | else() | ||
| 220 | target_compile_definitions(opus PRIVATE VAR_ARRAYS) | ||
| 221 | endif() | ||
| 222 | |||
| 223 | if(OPUS_CUSTOM_MODES) | ||
| 224 | target_compile_definitions(opus PRIVATE CUSTOM_MODES) | ||
| 225 | endif() | ||
| 226 | |||
| 227 | if(NOT OPUS_ENABLE_FLOAT_API) | ||
| 228 | target_compile_definitions(opus PRIVATE DISABLE_FLOAT_API) | ||
| 229 | endif() | ||
| 230 | |||
| 231 | target_compile_definitions(opus | ||
| 232 | PUBLIC | ||
| 233 | -DOPUS_VERSION="\\"1.3.1\\"" | ||
| 234 | |||
| 235 | PRIVATE | ||
| 236 | # Use C99 intrinsics to speed up float-to-int conversion | ||
| 237 | HAVE_LRINTF | ||
| 238 | ) | ||
| 239 | |||
| 240 | if (FIXED_POINT) | ||
| 241 | target_compile_definitions(opus PRIVATE -DFIXED_POINT=1 -DDISABLE_FLOAT_API) | ||
| 242 | endif() | ||
| 243 | |||
| 244 | target_include_directories(opus | ||
| 245 | PUBLIC | ||
| 246 | opus/include | ||
| 247 | |||
| 248 | PRIVATE | ||
| 249 | opus/celt | ||
| 250 | opus/silk | ||
| 251 | opus/silk/fixed | ||
| 252 | opus/silk/float | ||
| 253 | opus/src | ||
| 254 | ) | ||
diff --git a/externals/opus/opus b/externals/opus/opus new file mode 160000 | |||
| Subproject ad8fe90db79b7d2a135e3dfd2ed6631b0c5662a | |||
diff --git a/src/common/memory_detect.cpp b/src/common/memory_detect.cpp index 3fdc309a2..8cff6ec37 100644 --- a/src/common/memory_detect.cpp +++ b/src/common/memory_detect.cpp | |||
| @@ -9,10 +9,12 @@ | |||
| 9 | // clang-format on | 9 | // clang-format on |
| 10 | #else | 10 | #else |
| 11 | #include <sys/types.h> | 11 | #include <sys/types.h> |
| 12 | #ifdef __APPLE__ | 12 | #if defined(__APPLE__) || defined(__FreeBSD__) |
| 13 | #include <sys/sysctl.h> | 13 | #include <sys/sysctl.h> |
| 14 | #else | 14 | #elif defined(__linux__) |
| 15 | #include <sys/sysinfo.h> | 15 | #include <sys/sysinfo.h> |
| 16 | #else | ||
| 17 | #include <unistd.h> | ||
| 16 | #endif | 18 | #endif |
| 17 | #endif | 19 | #endif |
| 18 | 20 | ||
| @@ -38,15 +40,26 @@ static MemoryInfo Detect() { | |||
| 38 | // hw and vm are defined in sysctl.h | 40 | // hw and vm are defined in sysctl.h |
| 39 | // https://github.com/apple/darwin-xnu/blob/master/bsd/sys/sysctl.h#L471 | 41 | // https://github.com/apple/darwin-xnu/blob/master/bsd/sys/sysctl.h#L471 |
| 40 | // sysctlbyname(const char *, void *, size_t *, void *, size_t); | 42 | // sysctlbyname(const char *, void *, size_t *, void *, size_t); |
| 41 | sysctlbyname("hw.memsize", &ramsize, &sizeof_ramsize, NULL, 0); | 43 | sysctlbyname("hw.memsize", &ramsize, &sizeof_ramsize, nullptr, 0); |
| 42 | sysctlbyname("vm.swapusage", &vmusage, &sizeof_vmusage, NULL, 0); | 44 | sysctlbyname("vm.swapusage", &vmusage, &sizeof_vmusage, nullptr, 0); |
| 43 | mem_info.TotalPhysicalMemory = ramsize; | 45 | mem_info.TotalPhysicalMemory = ramsize; |
| 44 | mem_info.TotalSwapMemory = vmusage.xsu_total; | 46 | mem_info.TotalSwapMemory = vmusage.xsu_total; |
| 45 | #else | 47 | #elif defined(__FreeBSD__) |
| 48 | u_long physmem, swap_total; | ||
| 49 | std::size_t sizeof_u_long = sizeof(u_long); | ||
| 50 | // sysctlbyname(const char *, void *, size_t *, const void *, size_t); | ||
| 51 | sysctlbyname("hw.physmem", &physmem, &sizeof_u_long, nullptr, 0); | ||
| 52 | sysctlbyname("vm.swap_total", &swap_total, &sizeof_u_long, nullptr, 0); | ||
| 53 | mem_info.TotalPhysicalMemory = physmem; | ||
| 54 | mem_info.TotalSwapMemory = swap_total; | ||
| 55 | #elif defined(__linux__) | ||
| 46 | struct sysinfo meminfo; | 56 | struct sysinfo meminfo; |
| 47 | sysinfo(&meminfo); | 57 | sysinfo(&meminfo); |
| 48 | mem_info.TotalPhysicalMemory = meminfo.totalram; | 58 | mem_info.TotalPhysicalMemory = meminfo.totalram; |
| 49 | mem_info.TotalSwapMemory = meminfo.totalswap; | 59 | mem_info.TotalSwapMemory = meminfo.totalswap; |
| 60 | #else | ||
| 61 | mem_info.TotalPhysicalMemory = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE); | ||
| 62 | mem_info.TotalSwapMemory = 0; | ||
| 50 | #endif | 63 | #endif |
| 51 | 64 | ||
| 52 | return mem_info; | 65 | return mem_info; |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f87d67db5..d1f173f42 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -614,7 +614,7 @@ endif() | |||
| 614 | create_target_directory_groups(core) | 614 | create_target_directory_groups(core) |
| 615 | 615 | ||
| 616 | target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) | 616 | target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) |
| 617 | target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus unicorn zip) | 617 | target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls opus unicorn zip) |
| 618 | 618 | ||
| 619 | if (YUZU_ENABLE_BOXCAT) | 619 | if (YUZU_ENABLE_BOXCAT) |
| 620 | target_compile_definitions(core PRIVATE -DYUZU_ENABLE_BOXCAT) | 620 | target_compile_definitions(core PRIVATE -DYUZU_ENABLE_BOXCAT) |
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index 7265c4171..9269a73f2 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h | |||
| @@ -223,7 +223,16 @@ bool operator<(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) { | |||
| 223 | 223 | ||
| 224 | class KeyManager { | 224 | class KeyManager { |
| 225 | public: | 225 | public: |
| 226 | KeyManager(); | 226 | static KeyManager& Instance() { |
| 227 | static KeyManager instance; | ||
| 228 | return instance; | ||
| 229 | } | ||
| 230 | |||
| 231 | KeyManager(const KeyManager&) = delete; | ||
| 232 | KeyManager& operator=(const KeyManager&) = delete; | ||
| 233 | |||
| 234 | KeyManager(KeyManager&&) = delete; | ||
| 235 | KeyManager& operator=(KeyManager&&) = delete; | ||
| 227 | 236 | ||
| 228 | bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; | 237 | bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; |
| 229 | bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; | 238 | bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; |
| @@ -257,6 +266,8 @@ public: | |||
| 257 | bool AddTicketPersonalized(Ticket raw); | 266 | bool AddTicketPersonalized(Ticket raw); |
| 258 | 267 | ||
| 259 | private: | 268 | private: |
| 269 | KeyManager(); | ||
| 270 | |||
| 260 | std::map<KeyIndex<S128KeyType>, Key128> s128_keys; | 271 | std::map<KeyIndex<S128KeyType>, Key128> s128_keys; |
| 261 | std::map<KeyIndex<S256KeyType>, Key256> s256_keys; | 272 | std::map<KeyIndex<S256KeyType>, Key256> s256_keys; |
| 262 | 273 | ||
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index 0af44f340..8935a62c3 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp | |||
| @@ -79,7 +79,7 @@ VirtualDir BISFactory::OpenPartition(BisPartitionId id) const { | |||
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const { | 81 | VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const { |
| 82 | Core::Crypto::KeyManager keys; | 82 | auto& keys = Core::Crypto::KeyManager::Instance(); |
| 83 | Core::Crypto::PartitionDataManager pdm{ | 83 | Core::Crypto::PartitionDataManager pdm{ |
| 84 | Core::System::GetInstance().GetFilesystem()->OpenDirectory( | 84 | Core::System::GetInstance().GetFilesystem()->OpenDirectory( |
| 85 | FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)}; | 85 | FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)}; |
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index 07d0c8d5d..664a47e7f 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp | |||
| @@ -178,7 +178,7 @@ u32 XCI::GetSystemUpdateVersion() { | |||
| 178 | return 0; | 178 | return 0; |
| 179 | 179 | ||
| 180 | for (const auto& file : update->GetFiles()) { | 180 | for (const auto& file : update->GetFiles()) { |
| 181 | NCA nca{file, nullptr, 0, keys}; | 181 | NCA nca{file, nullptr, 0}; |
| 182 | 182 | ||
| 183 | if (nca.GetStatus() != Loader::ResultStatus::Success) | 183 | if (nca.GetStatus() != Loader::ResultStatus::Success) |
| 184 | continue; | 184 | continue; |
| @@ -286,7 +286,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { | |||
| 286 | continue; | 286 | continue; |
| 287 | } | 287 | } |
| 288 | 288 | ||
| 289 | auto nca = std::make_shared<NCA>(file, nullptr, 0, keys); | 289 | auto nca = std::make_shared<NCA>(file, nullptr, 0); |
| 290 | if (nca->IsUpdate()) { | 290 | if (nca->IsUpdate()) { |
| 291 | continue; | 291 | continue; |
| 292 | } | 292 | } |
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index c2ee0ea99..e1b136426 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h | |||
| @@ -140,6 +140,6 @@ private: | |||
| 140 | 140 | ||
| 141 | u64 update_normal_partition_end; | 141 | u64 update_normal_partition_end; |
| 142 | 142 | ||
| 143 | Core::Crypto::KeyManager keys; | 143 | Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); |
| 144 | }; | 144 | }; |
| 145 | } // namespace FileSys | 145 | } // namespace FileSys |
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index b8bbdd1ef..473245d5a 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp | |||
| @@ -118,9 +118,8 @@ static bool IsValidNCA(const NCAHeader& header) { | |||
| 118 | return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); | 118 | return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset, | 121 | NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset) |
| 122 | Core::Crypto::KeyManager keys_) | 122 | : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) { |
| 123 | : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)), keys(std::move(keys_)) { | ||
| 124 | if (file == nullptr) { | 123 | if (file == nullptr) { |
| 125 | status = Loader::ResultStatus::ErrorNullFile; | 124 | status = Loader::ResultStatus::ErrorNullFile; |
| 126 | return; | 125 | return; |
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index e249079b5..d25cbcf91 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h | |||
| @@ -99,8 +99,7 @@ inline bool IsDirectoryLogoPartition(const VirtualDir& pfs) { | |||
| 99 | class NCA : public ReadOnlyVfsDirectory { | 99 | class NCA : public ReadOnlyVfsDirectory { |
| 100 | public: | 100 | public: |
| 101 | explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr, | 101 | explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr, |
| 102 | u64 bktr_base_ivfc_offset = 0, | 102 | u64 bktr_base_ivfc_offset = 0); |
| 103 | Core::Crypto::KeyManager keys = Core::Crypto::KeyManager()); | ||
| 104 | ~NCA() override; | 103 | ~NCA() override; |
| 105 | 104 | ||
| 106 | Loader::ResultStatus GetStatus() const; | 105 | Loader::ResultStatus GetStatus() const; |
| @@ -159,7 +158,7 @@ private: | |||
| 159 | bool encrypted = false; | 158 | bool encrypted = false; |
| 160 | bool is_update = false; | 159 | bool is_update = false; |
| 161 | 160 | ||
| 162 | Core::Crypto::KeyManager keys; | 161 | Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); |
| 163 | }; | 162 | }; |
| 164 | 163 | ||
| 165 | } // namespace FileSys | 164 | } // namespace FileSys |
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index ba5f76288..27c1b0233 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp | |||
| @@ -408,7 +408,7 @@ void RegisteredCache::ProcessFiles(const std::vector<NcaID>& ids) { | |||
| 408 | 408 | ||
| 409 | if (file == nullptr) | 409 | if (file == nullptr) |
| 410 | continue; | 410 | continue; |
| 411 | const auto nca = std::make_shared<NCA>(parser(file, id), nullptr, 0, keys); | 411 | const auto nca = std::make_shared<NCA>(parser(file, id), nullptr, 0); |
| 412 | if (nca->GetStatus() != Loader::ResultStatus::Success || | 412 | if (nca->GetStatus() != Loader::ResultStatus::Success || |
| 413 | nca->GetType() != NCAContentType::Meta) { | 413 | nca->GetType() != NCAContentType::Meta) { |
| 414 | continue; | 414 | continue; |
| @@ -486,7 +486,7 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t | |||
| 486 | const auto raw = GetEntryRaw(title_id, type); | 486 | const auto raw = GetEntryRaw(title_id, type); |
| 487 | if (raw == nullptr) | 487 | if (raw == nullptr) |
| 488 | return nullptr; | 488 | return nullptr; |
| 489 | return std::make_unique<NCA>(raw, nullptr, 0, keys); | 489 | return std::make_unique<NCA>(raw, nullptr, 0); |
| 490 | } | 490 | } |
| 491 | 491 | ||
| 492 | template <typename T> | 492 | template <typename T> |
| @@ -865,7 +865,7 @@ std::unique_ptr<NCA> ManualContentProvider::GetEntry(u64 title_id, ContentRecord | |||
| 865 | const auto res = GetEntryRaw(title_id, type); | 865 | const auto res = GetEntryRaw(title_id, type); |
| 866 | if (res == nullptr) | 866 | if (res == nullptr) |
| 867 | return nullptr; | 867 | return nullptr; |
| 868 | return std::make_unique<NCA>(res, nullptr, 0, keys); | 868 | return std::make_unique<NCA>(res, nullptr, 0); |
| 869 | } | 869 | } |
| 870 | 870 | ||
| 871 | std::vector<ContentProviderEntry> ManualContentProvider::ListEntriesFilter( | 871 | std::vector<ContentProviderEntry> ManualContentProvider::ListEntriesFilter( |
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index d1eec240e..f339cd17b 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h | |||
| @@ -88,7 +88,7 @@ public: | |||
| 88 | 88 | ||
| 89 | protected: | 89 | protected: |
| 90 | // A single instance of KeyManager to be used by GetEntry() | 90 | // A single instance of KeyManager to be used by GetEntry() |
| 91 | Core::Crypto::KeyManager keys; | 91 | Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); |
| 92 | }; | 92 | }; |
| 93 | 93 | ||
| 94 | class PlaceholderCache { | 94 | class PlaceholderCache { |
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index ef3084681..175a8266a 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp | |||
| @@ -21,7 +21,7 @@ | |||
| 21 | namespace FileSys { | 21 | namespace FileSys { |
| 22 | namespace { | 22 | namespace { |
| 23 | void SetTicketKeys(const std::vector<VirtualFile>& files) { | 23 | void SetTicketKeys(const std::vector<VirtualFile>& files) { |
| 24 | Core::Crypto::KeyManager keys; | 24 | auto& keys = Core::Crypto::KeyManager::Instance(); |
| 25 | 25 | ||
| 26 | for (const auto& ticket_file : files) { | 26 | for (const auto& ticket_file : files) { |
| 27 | if (ticket_file == nullptr) { | 27 | if (ticket_file == nullptr) { |
| @@ -285,7 +285,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { | |||
| 285 | continue; | 285 | continue; |
| 286 | } | 286 | } |
| 287 | 287 | ||
| 288 | auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0, keys); | 288 | auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0); |
| 289 | if (next_nca->GetType() == NCAContentType::Program) { | 289 | if (next_nca->GetType() == NCAContentType::Program) { |
| 290 | program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); | 290 | program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); |
| 291 | } | 291 | } |
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index ee9b6ce17..cf89de6a9 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h | |||
| @@ -73,7 +73,7 @@ private: | |||
| 73 | std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas; | 73 | std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas; |
| 74 | std::vector<VirtualFile> ticket_files; | 74 | std::vector<VirtualFile> ticket_files; |
| 75 | 75 | ||
| 76 | Core::Crypto::KeyManager keys; | 76 | Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); |
| 77 | 77 | ||
| 78 | VirtualFile romfs; | 78 | VirtualFile romfs; |
| 79 | VirtualDir exefs; | 79 | VirtualDir exefs; |
diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h index 7704dee90..563531bb6 100644 --- a/src/core/file_sys/xts_archive.h +++ b/src/core/file_sys/xts_archive.h | |||
| @@ -62,6 +62,6 @@ private: | |||
| 62 | 62 | ||
| 63 | VirtualFile dec_file; | 63 | VirtualFile dec_file; |
| 64 | 64 | ||
| 65 | Core::Crypto::KeyManager keys; | 65 | Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); |
| 66 | }; | 66 | }; |
| 67 | } // namespace FileSys | 67 | } // namespace FileSys |
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 94d8c1fc6..8ac856ec3 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp | |||
| @@ -776,6 +776,15 @@ void Module::Interface::ListQualifiedUsers(Kernel::HLERequestContext& ctx) { | |||
| 776 | rb.Push(RESULT_SUCCESS); | 776 | rb.Push(RESULT_SUCCESS); |
| 777 | } | 777 | } |
| 778 | 778 | ||
| 779 | void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) { | ||
| 780 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | ||
| 781 | |||
| 782 | // TODO(ogniK): Handle open contexts | ||
| 783 | ctx.WriteBuffer(profile_manager->GetOpenUsers()); | ||
| 784 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 785 | rb.Push(RESULT_SUCCESS); | ||
| 786 | } | ||
| 787 | |||
| 779 | void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) { | 788 | void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) { |
| 780 | LOG_DEBUG(Service_ACC, "called"); | 789 | LOG_DEBUG(Service_ACC, "called"); |
| 781 | // A u8 is passed into this function which we can safely ignore. It's to determine if we have | 790 | // A u8 is passed into this function which we can safely ignore. It's to determine if we have |
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index 74ca39d6e..d4c6395c6 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h | |||
| @@ -34,6 +34,7 @@ public: | |||
| 34 | void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx); | 34 | void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx); |
| 35 | void GetProfileEditor(Kernel::HLERequestContext& ctx); | 35 | void GetProfileEditor(Kernel::HLERequestContext& ctx); |
| 36 | void ListQualifiedUsers(Kernel::HLERequestContext& ctx); | 36 | void ListQualifiedUsers(Kernel::HLERequestContext& ctx); |
| 37 | void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx); | ||
| 37 | 38 | ||
| 38 | private: | 39 | private: |
| 39 | ResultCode InitializeApplicationInfoBase(); | 40 | ResultCode InitializeApplicationInfoBase(); |
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index 85620bde3..d2bb8c2c8 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp | |||
| @@ -20,7 +20,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 20 | {6, nullptr, "GetProfileDigest"}, // 3.0.0+ | 20 | {6, nullptr, "GetProfileDigest"}, // 3.0.0+ |
| 21 | {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, | 21 | {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, |
| 22 | {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, | 22 | {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, |
| 23 | {60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 | 23 | {60, &ACC_SU::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 |
| 24 | {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ | 24 | {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ |
| 25 | {100, nullptr, "GetUserRegistrationNotifier"}, | 25 | {100, nullptr, "GetUserRegistrationNotifier"}, |
| 26 | {101, nullptr, "GetUserStateChangeNotifier"}, | 26 | {101, nullptr, "GetUserStateChangeNotifier"}, |
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index 49f6e20f1..cb44e06b7 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp | |||
| @@ -20,7 +20,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 20 | {6, nullptr, "GetProfileDigest"}, // 3.0.0+ | 20 | {6, nullptr, "GetProfileDigest"}, // 3.0.0+ |
| 21 | {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, | 21 | {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, |
| 22 | {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, | 22 | {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, |
| 23 | {60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 | 23 | {60, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 |
| 24 | {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ | 24 | {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ |
| 25 | {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, | 25 | {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, |
| 26 | {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, | 26 | {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, |
| @@ -30,7 +30,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 30 | {111, nullptr, "ClearSaveDataThumbnail"}, | 30 | {111, nullptr, "ClearSaveDataThumbnail"}, |
| 31 | {120, nullptr, "CreateGuestLoginRequest"}, | 31 | {120, nullptr, "CreateGuestLoginRequest"}, |
| 32 | {130, nullptr, "LoadOpenContext"}, // 5.0.0+ | 32 | {130, nullptr, "LoadOpenContext"}, // 5.0.0+ |
| 33 | {131, nullptr, "ListOpenContextStoredUsers"}, // 6.0.0+ | 33 | {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+ |
| 34 | {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+ | 34 | {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+ |
| 35 | {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ | 35 | {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ |
| 36 | {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"}, // 6.0.0+ | 36 | {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"}, // 6.0.0+ |
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp index f47004f84..a4aa5316a 100644 --- a/src/core/hle/service/acc/acc_u1.cpp +++ b/src/core/hle/service/acc/acc_u1.cpp | |||
| @@ -20,7 +20,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 20 | {6, nullptr, "GetProfileDigest"}, // 3.0.0+ | 20 | {6, nullptr, "GetProfileDigest"}, // 3.0.0+ |
| 21 | {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, | 21 | {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, |
| 22 | {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, | 22 | {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, |
| 23 | {60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 | 23 | {60, &ACC_U1::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 |
| 24 | {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ | 24 | {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ |
| 25 | {100, nullptr, "GetUserRegistrationNotifier"}, | 25 | {100, nullptr, "GetUserRegistrationNotifier"}, |
| 26 | {101, nullptr, "GetUserStateChangeNotifier"}, | 26 | {101, nullptr, "GetUserStateChangeNotifier"}, |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 20f366635..1bb544dd8 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -841,7 +841,7 @@ public: | |||
| 841 | {110, nullptr, "NeedsToExitProcess"}, | 841 | {110, nullptr, "NeedsToExitProcess"}, |
| 842 | {120, nullptr, "GetLibraryAppletInfo"}, | 842 | {120, nullptr, "GetLibraryAppletInfo"}, |
| 843 | {150, nullptr, "RequestForAppletToGetForeground"}, | 843 | {150, nullptr, "RequestForAppletToGetForeground"}, |
| 844 | {160, nullptr, "GetIndirectLayerConsumerHandle"}, | 844 | {160, &ILibraryAppletAccessor::GetIndirectLayerConsumerHandle, "GetIndirectLayerConsumerHandle"}, |
| 845 | }; | 845 | }; |
| 846 | // clang-format on | 846 | // clang-format on |
| 847 | 847 | ||
| @@ -960,6 +960,18 @@ private: | |||
| 960 | rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent()); | 960 | rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent()); |
| 961 | } | 961 | } |
| 962 | 962 | ||
| 963 | void GetIndirectLayerConsumerHandle(Kernel::HLERequestContext& ctx) { | ||
| 964 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 965 | |||
| 966 | // We require a non-zero handle to be valid. Using 0xdeadbeef allows us to trace if this is | ||
| 967 | // actually used anywhere | ||
| 968 | constexpr u64 handle = 0xdeadbeef; | ||
| 969 | |||
| 970 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 971 | rb.Push(RESULT_SUCCESS); | ||
| 972 | rb.Push(handle); | ||
| 973 | } | ||
| 974 | |||
| 963 | std::shared_ptr<Applets::Applet> applet; | 975 | std::shared_ptr<Applets::Applet> applet; |
| 964 | }; | 976 | }; |
| 965 | 977 | ||
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index 9365f27e1..a41c73c48 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp | |||
| @@ -302,7 +302,7 @@ private: | |||
| 302 | rb.Push<u64>(write_size); | 302 | rb.Push<u64>(write_size); |
| 303 | } | 303 | } |
| 304 | 304 | ||
| 305 | Core::Crypto::KeyManager keys; | 305 | Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); |
| 306 | }; | 306 | }; |
| 307 | 307 | ||
| 308 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 308 | void InstallInterfaces(SM::ServiceManager& service_manager) { |
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index 14309c679..67833d9af 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp | |||
| @@ -75,8 +75,13 @@ private: | |||
| 75 | const auto user_id = rp.PopRaw<u128>(); | 75 | const auto user_id = rp.PopRaw<u128>(); |
| 76 | const auto process_id = rp.PopRaw<u64>(); | 76 | const auto process_id = rp.PopRaw<u64>(); |
| 77 | std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)}; | 77 | std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)}; |
| 78 | |||
| 78 | if constexpr (Type == Core::Reporter::PlayReportType::Old2) { | 79 | if constexpr (Type == Core::Reporter::PlayReportType::Old2) { |
| 79 | data.emplace_back(ctx.ReadBuffer(1)); | 80 | const auto read_buffer_count = |
| 81 | ctx.BufferDescriptorX().size() + ctx.BufferDescriptorA().size(); | ||
| 82 | if (read_buffer_count > 1) { | ||
| 83 | data.emplace_back(ctx.ReadBuffer(1)); | ||
| 84 | } | ||
| 80 | } | 85 | } |
| 81 | 86 | ||
| 82 | LOG_DEBUG( | 87 | LOG_DEBUG( |
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index f3b4b286c..e5cfd2101 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <array> | ||
| 6 | #include <chrono> | 7 | #include <chrono> |
| 7 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 8 | #include "core/hle/ipc_helpers.h" | 9 | #include "core/hle/ipc_helpers.h" |
| @@ -31,6 +32,44 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{ | |||
| 31 | LanguageCode::ZH_HANT, | 32 | LanguageCode::ZH_HANT, |
| 32 | }}; | 33 | }}; |
| 33 | 34 | ||
| 35 | enum class KeyboardLayout : u64 { | ||
| 36 | Japanese = 0, | ||
| 37 | EnglishUs = 1, | ||
| 38 | EnglishUsInternational = 2, | ||
| 39 | EnglishUk = 3, | ||
| 40 | French = 4, | ||
| 41 | FrenchCa = 5, | ||
| 42 | Spanish = 6, | ||
| 43 | SpanishLatin = 7, | ||
| 44 | German = 8, | ||
| 45 | Italian = 9, | ||
| 46 | Portuguese = 10, | ||
| 47 | Russian = 11, | ||
| 48 | Korean = 12, | ||
| 49 | ChineseSimplified = 13, | ||
| 50 | ChineseTraditional = 14, | ||
| 51 | }; | ||
| 52 | |||
| 53 | constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 17> language_to_layout{{ | ||
| 54 | {LanguageCode::JA, KeyboardLayout::Japanese}, | ||
| 55 | {LanguageCode::EN_US, KeyboardLayout::EnglishUs}, | ||
| 56 | {LanguageCode::FR, KeyboardLayout::French}, | ||
| 57 | {LanguageCode::DE, KeyboardLayout::German}, | ||
| 58 | {LanguageCode::IT, KeyboardLayout::Italian}, | ||
| 59 | {LanguageCode::ES, KeyboardLayout::Spanish}, | ||
| 60 | {LanguageCode::ZH_CN, KeyboardLayout::ChineseSimplified}, | ||
| 61 | {LanguageCode::KO, KeyboardLayout::Korean}, | ||
| 62 | {LanguageCode::NL, KeyboardLayout::EnglishUsInternational}, | ||
| 63 | {LanguageCode::PT, KeyboardLayout::Portuguese}, | ||
| 64 | {LanguageCode::RU, KeyboardLayout::Russian}, | ||
| 65 | {LanguageCode::ZH_TW, KeyboardLayout::ChineseTraditional}, | ||
| 66 | {LanguageCode::EN_GB, KeyboardLayout::EnglishUk}, | ||
| 67 | {LanguageCode::FR_CA, KeyboardLayout::FrenchCa}, | ||
| 68 | {LanguageCode::ES_419, KeyboardLayout::SpanishLatin}, | ||
| 69 | {LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified}, | ||
| 70 | {LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional}, | ||
| 71 | }}; | ||
| 72 | |||
| 34 | constexpr std::size_t pre4_0_0_max_entries = 15; | 73 | constexpr std::size_t pre4_0_0_max_entries = 15; |
| 35 | constexpr std::size_t post4_0_0_max_entries = 17; | 74 | constexpr std::size_t post4_0_0_max_entries = 17; |
| 36 | 75 | ||
| @@ -50,6 +89,25 @@ void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t m | |||
| 50 | ctx.WriteBuffer(available_language_codes.data(), copy_size); | 89 | ctx.WriteBuffer(available_language_codes.data(), copy_size); |
| 51 | PushResponseLanguageCode(ctx, copy_amount); | 90 | PushResponseLanguageCode(ctx, copy_amount); |
| 52 | } | 91 | } |
| 92 | |||
| 93 | void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) { | ||
| 94 | const auto language_code = available_language_codes[Settings::values.language_index]; | ||
| 95 | const auto key_code = | ||
| 96 | std::find_if(language_to_layout.cbegin(), language_to_layout.cend(), | ||
| 97 | [=](const auto& element) { return element.first == language_code; }); | ||
| 98 | KeyboardLayout layout = KeyboardLayout::EnglishUs; | ||
| 99 | if (key_code == language_to_layout.cend()) { | ||
| 100 | LOG_ERROR(Service_SET, | ||
| 101 | "Could not find keyboard layout for language index {}, defaulting to English us", | ||
| 102 | Settings::values.language_index); | ||
| 103 | } else { | ||
| 104 | layout = key_code->second; | ||
| 105 | } | ||
| 106 | |||
| 107 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 108 | rb.Push(RESULT_SUCCESS); | ||
| 109 | ctx.WriteBuffer(&layout, sizeof(KeyboardLayout)); | ||
| 110 | } | ||
| 53 | } // Anonymous namespace | 111 | } // Anonymous namespace |
| 54 | 112 | ||
| 55 | LanguageCode GetLanguageCodeFromIndex(std::size_t index) { | 113 | LanguageCode GetLanguageCodeFromIndex(std::size_t index) { |
| @@ -120,6 +178,16 @@ void SET::GetRegionCode(Kernel::HLERequestContext& ctx) { | |||
| 120 | rb.Push(Settings::values.region_index); | 178 | rb.Push(Settings::values.region_index); |
| 121 | } | 179 | } |
| 122 | 180 | ||
| 181 | void SET::GetKeyCodeMap(Kernel::HLERequestContext& ctx) { | ||
| 182 | LOG_DEBUG(Service_SET, "Called {}", ctx.Description()); | ||
| 183 | GetKeyCodeMapImpl(ctx); | ||
| 184 | } | ||
| 185 | |||
| 186 | void SET::GetKeyCodeMap2(Kernel::HLERequestContext& ctx) { | ||
| 187 | LOG_DEBUG(Service_SET, "Called {}", ctx.Description()); | ||
| 188 | GetKeyCodeMapImpl(ctx); | ||
| 189 | } | ||
| 190 | |||
| 123 | SET::SET() : ServiceFramework("set") { | 191 | SET::SET() : ServiceFramework("set") { |
| 124 | // clang-format off | 192 | // clang-format off |
| 125 | static const FunctionInfo functions[] = { | 193 | static const FunctionInfo functions[] = { |
| @@ -130,9 +198,9 @@ SET::SET() : ServiceFramework("set") { | |||
| 130 | {4, &SET::GetRegionCode, "GetRegionCode"}, | 198 | {4, &SET::GetRegionCode, "GetRegionCode"}, |
| 131 | {5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"}, | 199 | {5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"}, |
| 132 | {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"}, | 200 | {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"}, |
| 133 | {7, nullptr, "GetKeyCodeMap"}, | 201 | {7, &SET::GetKeyCodeMap, "GetKeyCodeMap"}, |
| 134 | {8, &SET::GetQuestFlag, "GetQuestFlag"}, | 202 | {8, &SET::GetQuestFlag, "GetQuestFlag"}, |
| 135 | {9, nullptr, "GetKeyCodeMap2"}, | 203 | {9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"}, |
| 136 | {10, nullptr, "GetFirmwareVersionForDebug"}, | 204 | {10, nullptr, "GetFirmwareVersionForDebug"}, |
| 137 | }; | 205 | }; |
| 138 | // clang-format on | 206 | // clang-format on |
diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index 6084b345d..8ac9c169d 100644 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h | |||
| @@ -44,6 +44,8 @@ private: | |||
| 44 | void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx); | 44 | void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx); |
| 45 | void GetQuestFlag(Kernel::HLERequestContext& ctx); | 45 | void GetQuestFlag(Kernel::HLERequestContext& ctx); |
| 46 | void GetRegionCode(Kernel::HLERequestContext& ctx); | 46 | void GetRegionCode(Kernel::HLERequestContext& ctx); |
| 47 | void GetKeyCodeMap(Kernel::HLERequestContext& ctx); | ||
| 48 | void GetKeyCodeMap2(Kernel::HLERequestContext& ctx); | ||
| 47 | }; | 49 | }; |
| 48 | 50 | ||
| 49 | } // namespace Service::Set | 51 | } // namespace Service::Set |
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index a9c2392b1..3bd76dd23 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt | |||
| @@ -7,6 +7,10 @@ add_library(input_common STATIC | |||
| 7 | main.h | 7 | main.h |
| 8 | motion_emu.cpp | 8 | motion_emu.cpp |
| 9 | motion_emu.h | 9 | motion_emu.h |
| 10 | gcadapter/gc_adapter.cpp | ||
| 11 | gcadapter/gc_adapter.h | ||
| 12 | gcadapter/gc_poller.cpp | ||
| 13 | gcadapter/gc_poller.h | ||
| 10 | sdl/sdl.cpp | 14 | sdl/sdl.cpp |
| 11 | sdl/sdl.h | 15 | sdl/sdl.h |
| 12 | udp/client.cpp | 16 | udp/client.cpp |
| @@ -26,5 +30,7 @@ if(SDL2_FOUND) | |||
| 26 | target_compile_definitions(input_common PRIVATE HAVE_SDL2) | 30 | target_compile_definitions(input_common PRIVATE HAVE_SDL2) |
| 27 | endif() | 31 | endif() |
| 28 | 32 | ||
| 33 | target_link_libraries(input_common PUBLIC ${LIBUSB_LIBRARIES}) | ||
| 34 | |||
| 29 | create_target_directory_groups(input_common) | 35 | create_target_directory_groups(input_common) |
| 30 | target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost) | 36 | target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost) |
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp new file mode 100644 index 000000000..b39d2a3fb --- /dev/null +++ b/src/input_common/gcadapter/gc_adapter.cpp | |||
| @@ -0,0 +1,379 @@ | |||
| 1 | // Copyright 2014 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <chrono> | ||
| 6 | #include <thread> | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "input_common/gcadapter/gc_adapter.h" | ||
| 9 | |||
| 10 | namespace GCAdapter { | ||
| 11 | |||
| 12 | /// Used to loop through and assign button in poller | ||
| 13 | constexpr std::array<PadButton, 12> PadButtonArray{ | ||
| 14 | PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, PadButton::PAD_BUTTON_DOWN, | ||
| 15 | PadButton::PAD_BUTTON_UP, PadButton::PAD_TRIGGER_Z, PadButton::PAD_TRIGGER_R, | ||
| 16 | PadButton::PAD_TRIGGER_L, PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B, | ||
| 17 | PadButton::PAD_BUTTON_X, PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_START, | ||
| 18 | }; | ||
| 19 | |||
| 20 | Adapter::Adapter() { | ||
| 21 | if (usb_adapter_handle != nullptr) { | ||
| 22 | return; | ||
| 23 | } | ||
| 24 | LOG_INFO(Input, "GC Adapter Initialization started"); | ||
| 25 | |||
| 26 | current_status = NO_ADAPTER_DETECTED; | ||
| 27 | libusb_init(&libusb_ctx); | ||
| 28 | |||
| 29 | StartScanThread(); | ||
| 30 | } | ||
| 31 | |||
| 32 | GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_payload) { | ||
| 33 | GCPadStatus pad = {}; | ||
| 34 | bool get_origin = false; | ||
| 35 | |||
| 36 | ControllerTypes type = ControllerTypes(adapter_payload[1 + (9 * port)] >> 4); | ||
| 37 | if (type != ControllerTypes::None) { | ||
| 38 | get_origin = true; | ||
| 39 | } | ||
| 40 | |||
| 41 | adapter_controllers_status[port] = type; | ||
| 42 | |||
| 43 | static constexpr std::array<PadButton, 8> b1_buttons{ | ||
| 44 | PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B, PadButton::PAD_BUTTON_X, | ||
| 45 | PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, | ||
| 46 | PadButton::PAD_BUTTON_DOWN, PadButton::PAD_BUTTON_UP, | ||
| 47 | }; | ||
| 48 | |||
| 49 | static constexpr std::array<PadButton, 4> b2_buttons{ | ||
| 50 | PadButton::PAD_BUTTON_START, | ||
| 51 | PadButton::PAD_TRIGGER_Z, | ||
| 52 | PadButton::PAD_TRIGGER_R, | ||
| 53 | PadButton::PAD_TRIGGER_L, | ||
| 54 | }; | ||
| 55 | |||
| 56 | if (adapter_controllers_status[port] != ControllerTypes::None) { | ||
| 57 | const u8 b1 = adapter_payload[1 + (9 * port) + 1]; | ||
| 58 | const u8 b2 = adapter_payload[1 + (9 * port) + 2]; | ||
| 59 | |||
| 60 | for (std::size_t i = 0; i < b1_buttons.size(); ++i) { | ||
| 61 | if ((b1 & (1U << i)) != 0) { | ||
| 62 | pad.button |= static_cast<u16>(b1_buttons[i]); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | for (std::size_t j = 0; j < b2_buttons.size(); ++j) { | ||
| 67 | if ((b2 & (1U << j)) != 0) { | ||
| 68 | pad.button |= static_cast<u16>(b2_buttons[j]); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | if (get_origin) { | ||
| 73 | pad.button |= PAD_GET_ORIGIN; | ||
| 74 | } | ||
| 75 | |||
| 76 | pad.stick_x = adapter_payload[1 + (9 * port) + 3]; | ||
| 77 | pad.stick_y = adapter_payload[1 + (9 * port) + 4]; | ||
| 78 | pad.substick_x = adapter_payload[1 + (9 * port) + 5]; | ||
| 79 | pad.substick_y = adapter_payload[1 + (9 * port) + 6]; | ||
| 80 | pad.trigger_left = adapter_payload[1 + (9 * port) + 7]; | ||
| 81 | pad.trigger_right = adapter_payload[1 + (9 * port) + 8]; | ||
| 82 | } | ||
| 83 | return pad; | ||
| 84 | } | ||
| 85 | |||
| 86 | void Adapter::PadToState(const GCPadStatus& pad, GCState& state) { | ||
| 87 | for (const auto& button : PadButtonArray) { | ||
| 88 | const u16 button_value = static_cast<u16>(button); | ||
| 89 | state.buttons.insert_or_assign(button_value, pad.button & button_value); | ||
| 90 | } | ||
| 91 | |||
| 92 | state.axes.insert_or_assign(static_cast<u8>(PadAxes::StickX), pad.stick_x); | ||
| 93 | state.axes.insert_or_assign(static_cast<u8>(PadAxes::StickY), pad.stick_y); | ||
| 94 | state.axes.insert_or_assign(static_cast<u8>(PadAxes::SubstickX), pad.substick_x); | ||
| 95 | state.axes.insert_or_assign(static_cast<u8>(PadAxes::SubstickY), pad.substick_y); | ||
| 96 | state.axes.insert_or_assign(static_cast<u8>(PadAxes::TriggerLeft), pad.trigger_left); | ||
| 97 | state.axes.insert_or_assign(static_cast<u8>(PadAxes::TriggerRight), pad.trigger_right); | ||
| 98 | } | ||
| 99 | |||
| 100 | void Adapter::Read() { | ||
| 101 | LOG_DEBUG(Input, "GC Adapter Read() thread started"); | ||
| 102 | |||
| 103 | int payload_size_in, payload_size_copy; | ||
| 104 | std::array<u8, 37> adapter_payload; | ||
| 105 | std::array<u8, 37> adapter_payload_copy; | ||
| 106 | std::array<GCPadStatus, 4> pads; | ||
| 107 | |||
| 108 | while (adapter_thread_running) { | ||
| 109 | libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(), | ||
| 110 | sizeof(adapter_payload), &payload_size_in, 16); | ||
| 111 | payload_size_copy = 0; | ||
| 112 | // this mutex might be redundant? | ||
| 113 | { | ||
| 114 | std::lock_guard<std::mutex> lk(s_mutex); | ||
| 115 | std::copy(std::begin(adapter_payload), std::end(adapter_payload), | ||
| 116 | std::begin(adapter_payload_copy)); | ||
| 117 | payload_size_copy = payload_size_in; | ||
| 118 | } | ||
| 119 | |||
| 120 | if (payload_size_copy != sizeof(adapter_payload_copy) || | ||
| 121 | adapter_payload_copy[0] != LIBUSB_DT_HID) { | ||
| 122 | LOG_ERROR(Input, "error reading payload (size: {}, type: {:02x})", payload_size_copy, | ||
| 123 | adapter_payload_copy[0]); | ||
| 124 | adapter_thread_running = false; // error reading from adapter, stop reading. | ||
| 125 | break; | ||
| 126 | } | ||
| 127 | for (std::size_t port = 0; port < pads.size(); ++port) { | ||
| 128 | pads[port] = GetPadStatus(port, adapter_payload_copy); | ||
| 129 | if (DeviceConnected(port) && configuring) { | ||
| 130 | if (pads[port].button != PAD_GET_ORIGIN) { | ||
| 131 | pad_queue[port].Push(pads[port]); | ||
| 132 | } | ||
| 133 | |||
| 134 | // Accounting for a threshold here because of some controller variance | ||
| 135 | if (pads[port].stick_x > pads[port].MAIN_STICK_CENTER_X + pads[port].THRESHOLD || | ||
| 136 | pads[port].stick_x < pads[port].MAIN_STICK_CENTER_X - pads[port].THRESHOLD) { | ||
| 137 | pads[port].axis = GCAdapter::PadAxes::StickX; | ||
| 138 | pads[port].axis_value = pads[port].stick_x; | ||
| 139 | pad_queue[port].Push(pads[port]); | ||
| 140 | } | ||
| 141 | if (pads[port].stick_y > pads[port].MAIN_STICK_CENTER_Y + pads[port].THRESHOLD || | ||
| 142 | pads[port].stick_y < pads[port].MAIN_STICK_CENTER_Y - pads[port].THRESHOLD) { | ||
| 143 | pads[port].axis = GCAdapter::PadAxes::StickY; | ||
| 144 | pads[port].axis_value = pads[port].stick_y; | ||
| 145 | pad_queue[port].Push(pads[port]); | ||
| 146 | } | ||
| 147 | if (pads[port].substick_x > pads[port].C_STICK_CENTER_X + pads[port].THRESHOLD || | ||
| 148 | pads[port].substick_x < pads[port].C_STICK_CENTER_X - pads[port].THRESHOLD) { | ||
| 149 | pads[port].axis = GCAdapter::PadAxes::SubstickX; | ||
| 150 | pads[port].axis_value = pads[port].substick_x; | ||
| 151 | pad_queue[port].Push(pads[port]); | ||
| 152 | } | ||
| 153 | if (pads[port].substick_y > pads[port].C_STICK_CENTER_Y + pads[port].THRESHOLD || | ||
| 154 | pads[port].substick_y < pads[port].C_STICK_CENTER_Y - pads[port].THRESHOLD) { | ||
| 155 | pads[port].axis = GCAdapter::PadAxes::SubstickY; | ||
| 156 | pads[port].axis_value = pads[port].substick_y; | ||
| 157 | pad_queue[port].Push(pads[port]); | ||
| 158 | } | ||
| 159 | if (pads[port].trigger_left > pads[port].TRIGGER_THRESHOLD) { | ||
| 160 | pads[port].axis = GCAdapter::PadAxes::TriggerLeft; | ||
| 161 | pads[port].axis_value = pads[port].trigger_left; | ||
| 162 | pad_queue[port].Push(pads[port]); | ||
| 163 | } | ||
| 164 | if (pads[port].trigger_right > pads[port].TRIGGER_THRESHOLD) { | ||
| 165 | pads[port].axis = GCAdapter::PadAxes::TriggerRight; | ||
| 166 | pads[port].axis_value = pads[port].trigger_right; | ||
| 167 | pad_queue[port].Push(pads[port]); | ||
| 168 | } | ||
| 169 | } | ||
| 170 | PadToState(pads[port], state[port]); | ||
| 171 | } | ||
| 172 | std::this_thread::yield(); | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | void Adapter::ScanThreadFunc() { | ||
| 177 | LOG_INFO(Input, "GC Adapter scanning thread started"); | ||
| 178 | |||
| 179 | while (detect_thread_running) { | ||
| 180 | if (usb_adapter_handle == nullptr) { | ||
| 181 | std::lock_guard<std::mutex> lk(initialization_mutex); | ||
| 182 | Setup(); | ||
| 183 | } | ||
| 184 | std::this_thread::sleep_for(std::chrono::milliseconds(500)); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | void Adapter::StartScanThread() { | ||
| 189 | if (detect_thread_running) { | ||
| 190 | return; | ||
| 191 | } | ||
| 192 | if (!libusb_ctx) { | ||
| 193 | return; | ||
| 194 | } | ||
| 195 | |||
| 196 | detect_thread_running = true; | ||
| 197 | detect_thread = std::thread([=] { ScanThreadFunc(); }); | ||
| 198 | } | ||
| 199 | |||
| 200 | void Adapter::StopScanThread() { | ||
| 201 | detect_thread_running = false; | ||
| 202 | detect_thread.join(); | ||
| 203 | } | ||
| 204 | |||
| 205 | void Adapter::Setup() { | ||
| 206 | // Reset the error status in case the adapter gets unplugged | ||
| 207 | if (current_status < 0) { | ||
| 208 | current_status = NO_ADAPTER_DETECTED; | ||
| 209 | } | ||
| 210 | |||
| 211 | adapter_controllers_status.fill(ControllerTypes::None); | ||
| 212 | |||
| 213 | // pointer to list of connected usb devices | ||
| 214 | libusb_device** devices; | ||
| 215 | |||
| 216 | // populate the list of devices, get the count | ||
| 217 | const std::size_t device_count = libusb_get_device_list(libusb_ctx, &devices); | ||
| 218 | |||
| 219 | for (std::size_t index = 0; index < device_count; ++index) { | ||
| 220 | if (CheckDeviceAccess(devices[index])) { | ||
| 221 | // GC Adapter found and accessible, registering it | ||
| 222 | GetGCEndpoint(devices[index]); | ||
| 223 | break; | ||
| 224 | } | ||
| 225 | } | ||
| 226 | } | ||
| 227 | |||
| 228 | bool Adapter::CheckDeviceAccess(libusb_device* device) { | ||
| 229 | libusb_device_descriptor desc; | ||
| 230 | const int get_descriptor_error = libusb_get_device_descriptor(device, &desc); | ||
| 231 | if (get_descriptor_error) { | ||
| 232 | // could not acquire the descriptor, no point in trying to use it. | ||
| 233 | LOG_ERROR(Input, "libusb_get_device_descriptor failed with error: {}", | ||
| 234 | get_descriptor_error); | ||
| 235 | return false; | ||
| 236 | } | ||
| 237 | |||
| 238 | if (desc.idVendor != 0x057e || desc.idProduct != 0x0337) { | ||
| 239 | // This isn't the device we are looking for. | ||
| 240 | return false; | ||
| 241 | } | ||
| 242 | const int open_error = libusb_open(device, &usb_adapter_handle); | ||
| 243 | |||
| 244 | if (open_error == LIBUSB_ERROR_ACCESS) { | ||
| 245 | LOG_ERROR(Input, "Yuzu can not gain access to this device: ID {:04X}:{:04X}.", | ||
| 246 | desc.idVendor, desc.idProduct); | ||
| 247 | return false; | ||
| 248 | } | ||
| 249 | if (open_error) { | ||
| 250 | LOG_ERROR(Input, "libusb_open failed to open device with error = {}", open_error); | ||
| 251 | return false; | ||
| 252 | } | ||
| 253 | |||
| 254 | int kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0); | ||
| 255 | if (kernel_driver_error == 1) { | ||
| 256 | kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0); | ||
| 257 | if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { | ||
| 258 | LOG_ERROR(Input, "libusb_detach_kernel_driver failed with error = {}", | ||
| 259 | kernel_driver_error); | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 263 | if (kernel_driver_error && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { | ||
| 264 | libusb_close(usb_adapter_handle); | ||
| 265 | usb_adapter_handle = nullptr; | ||
| 266 | return false; | ||
| 267 | } | ||
| 268 | |||
| 269 | const int interface_claim_error = libusb_claim_interface(usb_adapter_handle, 0); | ||
| 270 | if (interface_claim_error) { | ||
| 271 | LOG_ERROR(Input, "libusb_claim_interface failed with error = {}", interface_claim_error); | ||
| 272 | libusb_close(usb_adapter_handle); | ||
| 273 | usb_adapter_handle = nullptr; | ||
| 274 | return false; | ||
| 275 | } | ||
| 276 | |||
| 277 | return true; | ||
| 278 | } | ||
| 279 | |||
| 280 | void Adapter::GetGCEndpoint(libusb_device* device) { | ||
| 281 | libusb_config_descriptor* config = nullptr; | ||
| 282 | libusb_get_config_descriptor(device, 0, &config); | ||
| 283 | for (u8 ic = 0; ic < config->bNumInterfaces; ic++) { | ||
| 284 | const libusb_interface* interfaceContainer = &config->interface[ic]; | ||
| 285 | for (int i = 0; i < interfaceContainer->num_altsetting; i++) { | ||
| 286 | const libusb_interface_descriptor* interface = &interfaceContainer->altsetting[i]; | ||
| 287 | for (u8 e = 0; e < interface->bNumEndpoints; e++) { | ||
| 288 | const libusb_endpoint_descriptor* endpoint = &interface->endpoint[e]; | ||
| 289 | if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) { | ||
| 290 | input_endpoint = endpoint->bEndpointAddress; | ||
| 291 | } else { | ||
| 292 | output_endpoint = endpoint->bEndpointAddress; | ||
| 293 | } | ||
| 294 | } | ||
| 295 | } | ||
| 296 | } | ||
| 297 | // This transfer seems to be responsible for clearing the state of the adapter | ||
| 298 | // Used to clear the "busy" state of when the device is unexpectedly unplugged | ||
| 299 | unsigned char clear_payload = 0x13; | ||
| 300 | libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload, | ||
| 301 | sizeof(clear_payload), nullptr, 16); | ||
| 302 | |||
| 303 | adapter_thread_running = true; | ||
| 304 | current_status = ADAPTER_DETECTED; | ||
| 305 | adapter_input_thread = std::thread([=] { Read(); }); // Read input | ||
| 306 | } | ||
| 307 | |||
| 308 | Adapter::~Adapter() { | ||
| 309 | StopScanThread(); | ||
| 310 | Reset(); | ||
| 311 | } | ||
| 312 | |||
| 313 | void Adapter::Reset() { | ||
| 314 | std::unique_lock<std::mutex> lock(initialization_mutex, std::defer_lock); | ||
| 315 | if (!lock.try_lock()) { | ||
| 316 | return; | ||
| 317 | } | ||
| 318 | if (current_status != ADAPTER_DETECTED) { | ||
| 319 | return; | ||
| 320 | } | ||
| 321 | |||
| 322 | if (adapter_thread_running) { | ||
| 323 | adapter_thread_running = false; | ||
| 324 | } | ||
| 325 | adapter_input_thread.join(); | ||
| 326 | |||
| 327 | adapter_controllers_status.fill(ControllerTypes::None); | ||
| 328 | current_status = NO_ADAPTER_DETECTED; | ||
| 329 | |||
| 330 | if (usb_adapter_handle) { | ||
| 331 | libusb_release_interface(usb_adapter_handle, 1); | ||
| 332 | libusb_close(usb_adapter_handle); | ||
| 333 | usb_adapter_handle = nullptr; | ||
| 334 | } | ||
| 335 | |||
| 336 | if (libusb_ctx) { | ||
| 337 | libusb_exit(libusb_ctx); | ||
| 338 | } | ||
| 339 | } | ||
| 340 | |||
| 341 | bool Adapter::DeviceConnected(int port) { | ||
| 342 | return adapter_controllers_status[port] != ControllerTypes::None; | ||
| 343 | } | ||
| 344 | |||
| 345 | void Adapter::ResetDeviceType(int port) { | ||
| 346 | adapter_controllers_status[port] = ControllerTypes::None; | ||
| 347 | } | ||
| 348 | |||
| 349 | void Adapter::BeginConfiguration() { | ||
| 350 | for (auto& pq : pad_queue) { | ||
| 351 | pq.Clear(); | ||
| 352 | } | ||
| 353 | configuring = true; | ||
| 354 | } | ||
| 355 | |||
| 356 | void Adapter::EndConfiguration() { | ||
| 357 | for (auto& pq : pad_queue) { | ||
| 358 | pq.Clear(); | ||
| 359 | } | ||
| 360 | configuring = false; | ||
| 361 | } | ||
| 362 | |||
| 363 | std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() { | ||
| 364 | return pad_queue; | ||
| 365 | } | ||
| 366 | |||
| 367 | const std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() const { | ||
| 368 | return pad_queue; | ||
| 369 | } | ||
| 370 | |||
| 371 | std::array<GCState, 4>& Adapter::GetPadState() { | ||
| 372 | return state; | ||
| 373 | } | ||
| 374 | |||
| 375 | const std::array<GCState, 4>& Adapter::GetPadState() const { | ||
| 376 | return state; | ||
| 377 | } | ||
| 378 | |||
| 379 | } // namespace GCAdapter | ||
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h new file mode 100644 index 000000000..0ea6263eb --- /dev/null +++ b/src/input_common/gcadapter/gc_adapter.h | |||
| @@ -0,0 +1,160 @@ | |||
| 1 | // Copyright 2014 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | #include <algorithm> | ||
| 7 | #include <functional> | ||
| 8 | #include <mutex> | ||
| 9 | #include <thread> | ||
| 10 | #include <libusb.h> | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/threadsafe_queue.h" | ||
| 13 | |||
| 14 | namespace GCAdapter { | ||
| 15 | |||
| 16 | enum { | ||
| 17 | PAD_USE_ORIGIN = 0x0080, | ||
| 18 | PAD_GET_ORIGIN = 0x2000, | ||
| 19 | PAD_ERR_STATUS = 0x8000, | ||
| 20 | }; | ||
| 21 | |||
| 22 | enum class PadButton { | ||
| 23 | PAD_BUTTON_LEFT = 0x0001, | ||
| 24 | PAD_BUTTON_RIGHT = 0x0002, | ||
| 25 | PAD_BUTTON_DOWN = 0x0004, | ||
| 26 | PAD_BUTTON_UP = 0x0008, | ||
| 27 | PAD_TRIGGER_Z = 0x0010, | ||
| 28 | PAD_TRIGGER_R = 0x0020, | ||
| 29 | PAD_TRIGGER_L = 0x0040, | ||
| 30 | PAD_BUTTON_A = 0x0100, | ||
| 31 | PAD_BUTTON_B = 0x0200, | ||
| 32 | PAD_BUTTON_X = 0x0400, | ||
| 33 | PAD_BUTTON_Y = 0x0800, | ||
| 34 | PAD_BUTTON_START = 0x1000, | ||
| 35 | // Below is for compatibility with "AxisButton" type | ||
| 36 | PAD_STICK = 0x2000, | ||
| 37 | }; | ||
| 38 | |||
| 39 | extern const std::array<PadButton, 12> PadButtonArray; | ||
| 40 | |||
| 41 | enum class PadAxes : u8 { | ||
| 42 | StickX, | ||
| 43 | StickY, | ||
| 44 | SubstickX, | ||
| 45 | SubstickY, | ||
| 46 | TriggerLeft, | ||
| 47 | TriggerRight, | ||
| 48 | Undefined, | ||
| 49 | }; | ||
| 50 | |||
| 51 | struct GCPadStatus { | ||
| 52 | u16 button{}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits | ||
| 53 | u8 stick_x{}; // 0 <= stick_x <= 255 | ||
| 54 | u8 stick_y{}; // 0 <= stick_y <= 255 | ||
| 55 | u8 substick_x{}; // 0 <= substick_x <= 255 | ||
| 56 | u8 substick_y{}; // 0 <= substick_y <= 255 | ||
| 57 | u8 trigger_left{}; // 0 <= trigger_left <= 255 | ||
| 58 | u8 trigger_right{}; // 0 <= trigger_right <= 255 | ||
| 59 | |||
| 60 | static constexpr u8 MAIN_STICK_CENTER_X = 0x80; | ||
| 61 | static constexpr u8 MAIN_STICK_CENTER_Y = 0x80; | ||
| 62 | static constexpr u8 MAIN_STICK_RADIUS = 0x7f; | ||
| 63 | static constexpr u8 C_STICK_CENTER_X = 0x80; | ||
| 64 | static constexpr u8 C_STICK_CENTER_Y = 0x80; | ||
| 65 | static constexpr u8 C_STICK_RADIUS = 0x7f; | ||
| 66 | static constexpr u8 THRESHOLD = 10; | ||
| 67 | |||
| 68 | // 256/4, at least a quarter press to count as a press. For polling mostly | ||
| 69 | static constexpr u8 TRIGGER_THRESHOLD = 64; | ||
| 70 | |||
| 71 | u8 port{}; | ||
| 72 | PadAxes axis{PadAxes::Undefined}; | ||
| 73 | u8 axis_value{255}; | ||
| 74 | }; | ||
| 75 | |||
| 76 | struct GCState { | ||
| 77 | std::unordered_map<int, bool> buttons; | ||
| 78 | std::unordered_map<int, u16> axes; | ||
| 79 | }; | ||
| 80 | |||
| 81 | enum class ControllerTypes { None, Wired, Wireless }; | ||
| 82 | |||
| 83 | enum { | ||
| 84 | NO_ADAPTER_DETECTED = 0, | ||
| 85 | ADAPTER_DETECTED = 1, | ||
| 86 | }; | ||
| 87 | |||
| 88 | class Adapter { | ||
| 89 | public: | ||
| 90 | /// Initialize the GC Adapter capture and read sequence | ||
| 91 | Adapter(); | ||
| 92 | |||
| 93 | /// Close the adapter read thread and release the adapter | ||
| 94 | ~Adapter(); | ||
| 95 | /// Used for polling | ||
| 96 | void BeginConfiguration(); | ||
| 97 | void EndConfiguration(); | ||
| 98 | |||
| 99 | std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue(); | ||
| 100 | const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const; | ||
| 101 | |||
| 102 | std::array<GCState, 4>& GetPadState(); | ||
| 103 | const std::array<GCState, 4>& GetPadState() const; | ||
| 104 | |||
| 105 | private: | ||
| 106 | GCPadStatus GetPadStatus(int port, const std::array<u8, 37>& adapter_payload); | ||
| 107 | |||
| 108 | void PadToState(const GCPadStatus& pad, GCState& state); | ||
| 109 | |||
| 110 | void Read(); | ||
| 111 | void ScanThreadFunc(); | ||
| 112 | /// Begin scanning for the GC Adapter. | ||
| 113 | void StartScanThread(); | ||
| 114 | |||
| 115 | /// Stop scanning for the adapter | ||
| 116 | void StopScanThread(); | ||
| 117 | |||
| 118 | /// Returns true if there is a device connected to port | ||
| 119 | bool DeviceConnected(int port); | ||
| 120 | |||
| 121 | /// Resets status of device connected to port | ||
| 122 | void ResetDeviceType(int port); | ||
| 123 | |||
| 124 | /// Returns true if we successfully gain access to GC Adapter | ||
| 125 | bool CheckDeviceAccess(libusb_device* device); | ||
| 126 | |||
| 127 | /// Captures GC Adapter endpoint address, | ||
| 128 | void GetGCEndpoint(libusb_device* device); | ||
| 129 | |||
| 130 | /// For shutting down, clear all data, join all threads, release usb | ||
| 131 | void Reset(); | ||
| 132 | |||
| 133 | /// For use in initialization, querying devices to find the adapter | ||
| 134 | void Setup(); | ||
| 135 | |||
| 136 | int current_status = NO_ADAPTER_DETECTED; | ||
| 137 | libusb_device_handle* usb_adapter_handle = nullptr; | ||
| 138 | std::array<ControllerTypes, 4> adapter_controllers_status{}; | ||
| 139 | |||
| 140 | std::mutex s_mutex; | ||
| 141 | |||
| 142 | std::thread adapter_input_thread; | ||
| 143 | bool adapter_thread_running; | ||
| 144 | |||
| 145 | std::mutex initialization_mutex; | ||
| 146 | std::thread detect_thread; | ||
| 147 | bool detect_thread_running = false; | ||
| 148 | |||
| 149 | libusb_context* libusb_ctx; | ||
| 150 | |||
| 151 | u8 input_endpoint = 0; | ||
| 152 | u8 output_endpoint = 0; | ||
| 153 | |||
| 154 | bool configuring = false; | ||
| 155 | |||
| 156 | std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue; | ||
| 157 | std::array<GCState, 4> state; | ||
| 158 | }; | ||
| 159 | |||
| 160 | } // namespace GCAdapter | ||
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp new file mode 100644 index 000000000..385ce8430 --- /dev/null +++ b/src/input_common/gcadapter/gc_poller.cpp | |||
| @@ -0,0 +1,272 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <atomic> | ||
| 6 | #include <list> | ||
| 7 | #include <mutex> | ||
| 8 | #include <utility> | ||
| 9 | #include "common/threadsafe_queue.h" | ||
| 10 | #include "input_common/gcadapter/gc_adapter.h" | ||
| 11 | #include "input_common/gcadapter/gc_poller.h" | ||
| 12 | |||
| 13 | namespace InputCommon { | ||
| 14 | |||
| 15 | class GCButton final : public Input::ButtonDevice { | ||
| 16 | public: | ||
| 17 | explicit GCButton(int port_, int button_, GCAdapter::Adapter* adapter) | ||
| 18 | : port(port_), button(button_), gcadapter(adapter) {} | ||
| 19 | |||
| 20 | ~GCButton() override; | ||
| 21 | |||
| 22 | bool GetStatus() const override { | ||
| 23 | return gcadapter->GetPadState()[port].buttons.at(button); | ||
| 24 | } | ||
| 25 | |||
| 26 | private: | ||
| 27 | const int port; | ||
| 28 | const int button; | ||
| 29 | GCAdapter::Adapter* gcadapter; | ||
| 30 | }; | ||
| 31 | |||
| 32 | class GCAxisButton final : public Input::ButtonDevice { | ||
| 33 | public: | ||
| 34 | explicit GCAxisButton(int port_, int axis_, float threshold_, bool trigger_if_greater_, | ||
| 35 | GCAdapter::Adapter* adapter) | ||
| 36 | : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_), | ||
| 37 | gcadapter(adapter) { | ||
| 38 | // L/R triggers range is only in positive direction beginning near 0 | ||
| 39 | // 0.0 threshold equates to near half trigger press, but threshold accounts for variability. | ||
| 40 | if (axis > 3) { | ||
| 41 | threshold *= -0.5; | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | bool GetStatus() const override { | ||
| 46 | const float axis_value = (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 128.0f; | ||
| 47 | if (trigger_if_greater) { | ||
| 48 | // TODO: Might be worthwile to set a slider for the trigger threshold. It is currently | ||
| 49 | // always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick | ||
| 50 | return axis_value > threshold; | ||
| 51 | } | ||
| 52 | return axis_value < -threshold; | ||
| 53 | } | ||
| 54 | |||
| 55 | private: | ||
| 56 | const int port; | ||
| 57 | const int axis; | ||
| 58 | float threshold; | ||
| 59 | bool trigger_if_greater; | ||
| 60 | GCAdapter::Adapter* gcadapter; | ||
| 61 | }; | ||
| 62 | |||
| 63 | GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_) | ||
| 64 | : adapter(std::move(adapter_)) {} | ||
| 65 | |||
| 66 | GCButton::~GCButton() = default; | ||
| 67 | |||
| 68 | std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::ParamPackage& params) { | ||
| 69 | const int button_id = params.Get("button", 0); | ||
| 70 | const int port = params.Get("port", 0); | ||
| 71 | |||
| 72 | constexpr int PAD_STICK_ID = static_cast<u16>(GCAdapter::PadButton::PAD_STICK); | ||
| 73 | |||
| 74 | // button is not an axis/stick button | ||
| 75 | if (button_id != PAD_STICK_ID) { | ||
| 76 | auto button = std::make_unique<GCButton>(port, button_id, adapter.get()); | ||
| 77 | return std::move(button); | ||
| 78 | } | ||
| 79 | |||
| 80 | // For Axis buttons, used by the binary sticks. | ||
| 81 | if (button_id == PAD_STICK_ID) { | ||
| 82 | const int axis = params.Get("axis", 0); | ||
| 83 | const float threshold = params.Get("threshold", 0.25f); | ||
| 84 | const std::string direction_name = params.Get("direction", ""); | ||
| 85 | bool trigger_if_greater; | ||
| 86 | if (direction_name == "+") { | ||
| 87 | trigger_if_greater = true; | ||
| 88 | } else if (direction_name == "-") { | ||
| 89 | trigger_if_greater = false; | ||
| 90 | } else { | ||
| 91 | trigger_if_greater = true; | ||
| 92 | LOG_ERROR(Input, "Unknown direction {}", direction_name); | ||
| 93 | } | ||
| 94 | return std::make_unique<GCAxisButton>(port, axis, threshold, trigger_if_greater, | ||
| 95 | adapter.get()); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | Common::ParamPackage GCButtonFactory::GetNextInput() { | ||
| 100 | Common::ParamPackage params; | ||
| 101 | GCAdapter::GCPadStatus pad; | ||
| 102 | auto& queue = adapter->GetPadQueue(); | ||
| 103 | for (std::size_t port = 0; port < queue.size(); ++port) { | ||
| 104 | while (queue[port].Pop(pad)) { | ||
| 105 | // This while loop will break on the earliest detected button | ||
| 106 | params.Set("engine", "gcpad"); | ||
| 107 | params.Set("port", static_cast<int>(port)); | ||
| 108 | for (const auto& button : GCAdapter::PadButtonArray) { | ||
| 109 | const u16 button_value = static_cast<u16>(button); | ||
| 110 | if (pad.button & button_value) { | ||
| 111 | params.Set("button", button_value); | ||
| 112 | break; | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | // For Axis button implementation | ||
| 117 | if (pad.axis != GCAdapter::PadAxes::Undefined) { | ||
| 118 | params.Set("axis", static_cast<u8>(pad.axis)); | ||
| 119 | params.Set("button", static_cast<u16>(GCAdapter::PadButton::PAD_STICK)); | ||
| 120 | if (pad.axis_value > 128) { | ||
| 121 | params.Set("direction", "+"); | ||
| 122 | params.Set("threshold", "0.25"); | ||
| 123 | } else { | ||
| 124 | params.Set("direction", "-"); | ||
| 125 | params.Set("threshold", "-0.25"); | ||
| 126 | } | ||
| 127 | break; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | } | ||
| 131 | return params; | ||
| 132 | } | ||
| 133 | |||
| 134 | void GCButtonFactory::BeginConfiguration() { | ||
| 135 | polling = true; | ||
| 136 | adapter->BeginConfiguration(); | ||
| 137 | } | ||
| 138 | |||
| 139 | void GCButtonFactory::EndConfiguration() { | ||
| 140 | polling = false; | ||
| 141 | adapter->EndConfiguration(); | ||
| 142 | } | ||
| 143 | |||
| 144 | class GCAnalog final : public Input::AnalogDevice { | ||
| 145 | public: | ||
| 146 | GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter) | ||
| 147 | : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter) {} | ||
| 148 | |||
| 149 | float GetAxis(int axis) const { | ||
| 150 | std::lock_guard lock{mutex}; | ||
| 151 | // division is not by a perfect 128 to account for some variance in center location | ||
| 152 | // e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range | ||
| 153 | // [20-230] | ||
| 154 | return (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 95.0f; | ||
| 155 | } | ||
| 156 | |||
| 157 | std::pair<float, float> GetAnalog(int axis_x, int axis_y) const { | ||
| 158 | float x = GetAxis(axis_x); | ||
| 159 | float y = GetAxis(axis_y); | ||
| 160 | |||
| 161 | // Make sure the coordinates are in the unit circle, | ||
| 162 | // otherwise normalize it. | ||
| 163 | float r = x * x + y * y; | ||
| 164 | if (r > 1.0f) { | ||
| 165 | r = std::sqrt(r); | ||
| 166 | x /= r; | ||
| 167 | y /= r; | ||
| 168 | } | ||
| 169 | |||
| 170 | return {x, y}; | ||
| 171 | } | ||
| 172 | |||
| 173 | std::tuple<float, float> GetStatus() const override { | ||
| 174 | const auto [x, y] = GetAnalog(axis_x, axis_y); | ||
| 175 | const float r = std::sqrt((x * x) + (y * y)); | ||
| 176 | if (r > deadzone) { | ||
| 177 | return {x / r * (r - deadzone) / (1 - deadzone), | ||
| 178 | y / r * (r - deadzone) / (1 - deadzone)}; | ||
| 179 | } | ||
| 180 | return {0.0f, 0.0f}; | ||
| 181 | } | ||
| 182 | |||
| 183 | bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { | ||
| 184 | const auto [x, y] = GetStatus(); | ||
| 185 | const float directional_deadzone = 0.4f; | ||
| 186 | switch (direction) { | ||
| 187 | case Input::AnalogDirection::RIGHT: | ||
| 188 | return x > directional_deadzone; | ||
| 189 | case Input::AnalogDirection::LEFT: | ||
| 190 | return x < -directional_deadzone; | ||
| 191 | case Input::AnalogDirection::UP: | ||
| 192 | return y > directional_deadzone; | ||
| 193 | case Input::AnalogDirection::DOWN: | ||
| 194 | return y < -directional_deadzone; | ||
| 195 | } | ||
| 196 | return false; | ||
| 197 | } | ||
| 198 | |||
| 199 | private: | ||
| 200 | const int port; | ||
| 201 | const int axis_x; | ||
| 202 | const int axis_y; | ||
| 203 | const float deadzone; | ||
| 204 | mutable std::mutex mutex; | ||
| 205 | GCAdapter::Adapter* gcadapter; | ||
| 206 | }; | ||
| 207 | |||
| 208 | /// An analog device factory that creates analog devices from GC Adapter | ||
| 209 | GCAnalogFactory::GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_) | ||
| 210 | : adapter(std::move(adapter_)) {} | ||
| 211 | |||
| 212 | /** | ||
| 213 | * Creates analog device from joystick axes | ||
| 214 | * @param params contains parameters for creating the device: | ||
| 215 | * - "port": the nth gcpad on the adapter | ||
| 216 | * - "axis_x": the index of the axis to be bind as x-axis | ||
| 217 | * - "axis_y": the index of the axis to be bind as y-axis | ||
| 218 | */ | ||
| 219 | std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::ParamPackage& params) { | ||
| 220 | const int port = params.Get("port", 0); | ||
| 221 | const int axis_x = params.Get("axis_x", 0); | ||
| 222 | const int axis_y = params.Get("axis_y", 1); | ||
| 223 | const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); | ||
| 224 | |||
| 225 | return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get()); | ||
| 226 | } | ||
| 227 | |||
| 228 | void GCAnalogFactory::BeginConfiguration() { | ||
| 229 | polling = true; | ||
| 230 | adapter->BeginConfiguration(); | ||
| 231 | } | ||
| 232 | |||
| 233 | void GCAnalogFactory::EndConfiguration() { | ||
| 234 | polling = false; | ||
| 235 | adapter->EndConfiguration(); | ||
| 236 | } | ||
| 237 | |||
| 238 | Common::ParamPackage GCAnalogFactory::GetNextInput() { | ||
| 239 | GCAdapter::GCPadStatus pad; | ||
| 240 | auto& queue = adapter->GetPadQueue(); | ||
| 241 | for (std::size_t port = 0; port < queue.size(); ++port) { | ||
| 242 | while (queue[port].Pop(pad)) { | ||
| 243 | if (pad.axis == GCAdapter::PadAxes::Undefined || | ||
| 244 | std::abs((pad.axis_value - 128.0f) / 128.0f) < 0.1) { | ||
| 245 | continue; | ||
| 246 | } | ||
| 247 | // An analog device needs two axes, so we need to store the axis for later and wait for | ||
| 248 | // a second input event. The axes also must be from the same joystick. | ||
| 249 | const u8 axis = static_cast<u8>(pad.axis); | ||
| 250 | if (analog_x_axis == -1) { | ||
| 251 | analog_x_axis = axis; | ||
| 252 | controller_number = port; | ||
| 253 | } else if (analog_y_axis == -1 && analog_x_axis != axis && controller_number == port) { | ||
| 254 | analog_y_axis = axis; | ||
| 255 | } | ||
| 256 | } | ||
| 257 | } | ||
| 258 | Common::ParamPackage params; | ||
| 259 | if (analog_x_axis != -1 && analog_y_axis != -1) { | ||
| 260 | params.Set("engine", "gcpad"); | ||
| 261 | params.Set("port", controller_number); | ||
| 262 | params.Set("axis_x", analog_x_axis); | ||
| 263 | params.Set("axis_y", analog_y_axis); | ||
| 264 | analog_x_axis = -1; | ||
| 265 | analog_y_axis = -1; | ||
| 266 | controller_number = -1; | ||
| 267 | return params; | ||
| 268 | } | ||
| 269 | return params; | ||
| 270 | } | ||
| 271 | |||
| 272 | } // namespace InputCommon | ||
diff --git a/src/input_common/gcadapter/gc_poller.h b/src/input_common/gcadapter/gc_poller.h new file mode 100644 index 000000000..e96af7d51 --- /dev/null +++ b/src/input_common/gcadapter/gc_poller.h | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include "core/frontend/input.h" | ||
| 9 | #include "input_common/gcadapter/gc_adapter.h" | ||
| 10 | |||
| 11 | namespace InputCommon { | ||
| 12 | |||
| 13 | /** | ||
| 14 | * A button device factory representing a gcpad. It receives gcpad events and forward them | ||
| 15 | * to all button devices it created. | ||
| 16 | */ | ||
| 17 | class GCButtonFactory final : public Input::Factory<Input::ButtonDevice> { | ||
| 18 | public: | ||
| 19 | explicit GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_); | ||
| 20 | |||
| 21 | /** | ||
| 22 | * Creates a button device from a button press | ||
| 23 | * @param params contains parameters for creating the device: | ||
| 24 | * - "code": the code of the key to bind with the button | ||
| 25 | */ | ||
| 26 | std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override; | ||
| 27 | |||
| 28 | Common::ParamPackage GetNextInput(); | ||
| 29 | |||
| 30 | /// For device input configuration/polling | ||
| 31 | void BeginConfiguration(); | ||
| 32 | void EndConfiguration(); | ||
| 33 | |||
| 34 | bool IsPolling() const { | ||
| 35 | return polling; | ||
| 36 | } | ||
| 37 | |||
| 38 | private: | ||
| 39 | std::shared_ptr<GCAdapter::Adapter> adapter; | ||
| 40 | bool polling = false; | ||
| 41 | }; | ||
| 42 | |||
| 43 | /// An analog device factory that creates analog devices from GC Adapter | ||
| 44 | class GCAnalogFactory final : public Input::Factory<Input::AnalogDevice> { | ||
| 45 | public: | ||
| 46 | explicit GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_); | ||
| 47 | |||
| 48 | std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override; | ||
| 49 | Common::ParamPackage GetNextInput(); | ||
| 50 | |||
| 51 | /// For device input configuration/polling | ||
| 52 | void BeginConfiguration(); | ||
| 53 | void EndConfiguration(); | ||
| 54 | |||
| 55 | bool IsPolling() const { | ||
| 56 | return polling; | ||
| 57 | } | ||
| 58 | |||
| 59 | private: | ||
| 60 | std::shared_ptr<GCAdapter::Adapter> adapter; | ||
| 61 | int analog_x_axis = -1; | ||
| 62 | int analog_y_axis = -1; | ||
| 63 | int controller_number = -1; | ||
| 64 | bool polling = false; | ||
| 65 | }; | ||
| 66 | |||
| 67 | } // namespace InputCommon | ||
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index 95e351e24..fd0af1019 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp | |||
| @@ -4,8 +4,11 @@ | |||
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include <thread> | 6 | #include <thread> |
| 7 | #include <libusb.h> | ||
| 7 | #include "common/param_package.h" | 8 | #include "common/param_package.h" |
| 8 | #include "input_common/analog_from_button.h" | 9 | #include "input_common/analog_from_button.h" |
| 10 | #include "input_common/gcadapter/gc_adapter.h" | ||
| 11 | #include "input_common/gcadapter/gc_poller.h" | ||
| 9 | #include "input_common/keyboard.h" | 12 | #include "input_common/keyboard.h" |
| 10 | #include "input_common/main.h" | 13 | #include "input_common/main.h" |
| 11 | #include "input_common/motion_emu.h" | 14 | #include "input_common/motion_emu.h" |
| @@ -22,8 +25,16 @@ static std::shared_ptr<MotionEmu> motion_emu; | |||
| 22 | static std::unique_ptr<SDL::State> sdl; | 25 | static std::unique_ptr<SDL::State> sdl; |
| 23 | #endif | 26 | #endif |
| 24 | static std::unique_ptr<CemuhookUDP::State> udp; | 27 | static std::unique_ptr<CemuhookUDP::State> udp; |
| 28 | static std::shared_ptr<GCButtonFactory> gcbuttons; | ||
| 29 | static std::shared_ptr<GCAnalogFactory> gcanalog; | ||
| 25 | 30 | ||
| 26 | void Init() { | 31 | void Init() { |
| 32 | auto gcadapter = std::make_shared<GCAdapter::Adapter>(); | ||
| 33 | gcbuttons = std::make_shared<GCButtonFactory>(gcadapter); | ||
| 34 | Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons); | ||
| 35 | gcanalog = std::make_shared<GCAnalogFactory>(gcadapter); | ||
| 36 | Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog); | ||
| 37 | |||
| 27 | keyboard = std::make_shared<Keyboard>(); | 38 | keyboard = std::make_shared<Keyboard>(); |
| 28 | Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); | 39 | Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); |
| 29 | Input::RegisterFactory<Input::AnalogDevice>("analog_from_button", | 40 | Input::RegisterFactory<Input::AnalogDevice>("analog_from_button", |
| @@ -48,6 +59,11 @@ void Shutdown() { | |||
| 48 | sdl.reset(); | 59 | sdl.reset(); |
| 49 | #endif | 60 | #endif |
| 50 | udp.reset(); | 61 | udp.reset(); |
| 62 | Input::UnregisterFactory<Input::ButtonDevice>("gcpad"); | ||
| 63 | Input::UnregisterFactory<Input::AnalogDevice>("gcpad"); | ||
| 64 | |||
| 65 | gcbuttons.reset(); | ||
| 66 | gcanalog.reset(); | ||
| 51 | } | 67 | } |
| 52 | 68 | ||
| 53 | Keyboard* GetKeyboard() { | 69 | Keyboard* GetKeyboard() { |
| @@ -58,6 +74,14 @@ MotionEmu* GetMotionEmu() { | |||
| 58 | return motion_emu.get(); | 74 | return motion_emu.get(); |
| 59 | } | 75 | } |
| 60 | 76 | ||
| 77 | GCButtonFactory* GetGCButtons() { | ||
| 78 | return gcbuttons.get(); | ||
| 79 | } | ||
| 80 | |||
| 81 | GCAnalogFactory* GetGCAnalogs() { | ||
| 82 | return gcanalog.get(); | ||
| 83 | } | ||
| 84 | |||
| 61 | std::string GenerateKeyboardParam(int key_code) { | 85 | std::string GenerateKeyboardParam(int key_code) { |
| 62 | Common::ParamPackage param{ | 86 | Common::ParamPackage param{ |
| 63 | {"engine", "keyboard"}, | 87 | {"engine", "keyboard"}, |
diff --git a/src/input_common/main.h b/src/input_common/main.h index 77a0ce90b..0e32856f6 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | #include "input_common/gcadapter/gc_poller.h" | ||
| 10 | 11 | ||
| 11 | namespace Common { | 12 | namespace Common { |
| 12 | class ParamPackage; | 13 | class ParamPackage; |
| @@ -30,6 +31,10 @@ class MotionEmu; | |||
| 30 | /// Gets the motion emulation factory. | 31 | /// Gets the motion emulation factory. |
| 31 | MotionEmu* GetMotionEmu(); | 32 | MotionEmu* GetMotionEmu(); |
| 32 | 33 | ||
| 34 | GCButtonFactory* GetGCButtons(); | ||
| 35 | |||
| 36 | GCAnalogFactory* GetGCAnalogs(); | ||
| 37 | |||
| 33 | /// Generates a serialized param package for creating a keyboard button device | 38 | /// Generates a serialized param package for creating a keyboard button device |
| 34 | std::string GenerateKeyboardParam(int key_code); | 39 | std::string GenerateKeyboardParam(int key_code); |
| 35 | 40 | ||
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 2dc752aa9..21c46a567 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -3,6 +3,8 @@ add_library(video_core STATIC | |||
| 3 | buffer_cache/buffer_cache.h | 3 | buffer_cache/buffer_cache.h |
| 4 | buffer_cache/map_interval.cpp | 4 | buffer_cache/map_interval.cpp |
| 5 | buffer_cache/map_interval.h | 5 | buffer_cache/map_interval.h |
| 6 | compatible_formats.cpp | ||
| 7 | compatible_formats.h | ||
| 6 | dirty_flags.cpp | 8 | dirty_flags.cpp |
| 7 | dirty_flags.h | 9 | dirty_flags.h |
| 8 | dma_pusher.cpp | 10 | dma_pusher.cpp |
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index cf8bdd021..c6479af9f 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h | |||
| @@ -322,8 +322,7 @@ protected: | |||
| 322 | } | 322 | } |
| 323 | 323 | ||
| 324 | private: | 324 | private: |
| 325 | MapInterval* MapAddress(const Buffer* block, GPUVAddr gpu_addr, VAddr cpu_addr, | 325 | MapInterval* MapAddress(Buffer* block, GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size) { |
| 326 | std::size_t size) { | ||
| 327 | const VectorMapInterval overlaps = GetMapsInRange(cpu_addr, size); | 326 | const VectorMapInterval overlaps = GetMapsInRange(cpu_addr, size); |
| 328 | if (overlaps.empty()) { | 327 | if (overlaps.empty()) { |
| 329 | auto& memory_manager = system.GPU().MemoryManager(); | 328 | auto& memory_manager = system.GPU().MemoryManager(); |
| @@ -377,8 +376,7 @@ private: | |||
| 377 | return map; | 376 | return map; |
| 378 | } | 377 | } |
| 379 | 378 | ||
| 380 | void UpdateBlock(const Buffer* block, VAddr start, VAddr end, | 379 | void UpdateBlock(Buffer* block, VAddr start, VAddr end, const VectorMapInterval& overlaps) { |
| 381 | const VectorMapInterval& overlaps) { | ||
| 382 | const IntervalType base_interval{start, end}; | 380 | const IntervalType base_interval{start, end}; |
| 383 | IntervalSet interval_set{}; | 381 | IntervalSet interval_set{}; |
| 384 | interval_set.add(base_interval); | 382 | interval_set.add(base_interval); |
diff --git a/src/video_core/compatible_formats.cpp b/src/video_core/compatible_formats.cpp new file mode 100644 index 000000000..6c426b035 --- /dev/null +++ b/src/video_core/compatible_formats.cpp | |||
| @@ -0,0 +1,162 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <array> | ||
| 6 | #include <bitset> | ||
| 7 | #include <cstddef> | ||
| 8 | |||
| 9 | #include "video_core/compatible_formats.h" | ||
| 10 | #include "video_core/surface.h" | ||
| 11 | |||
| 12 | namespace VideoCore::Surface { | ||
| 13 | |||
| 14 | namespace { | ||
| 15 | |||
| 16 | // Compatibility table taken from Table 3.X.2 in: | ||
| 17 | // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_view.txt | ||
| 18 | |||
| 19 | constexpr std::array VIEW_CLASS_128_BITS = { | ||
| 20 | PixelFormat::RGBA32F, | ||
| 21 | PixelFormat::RGBA32UI, | ||
| 22 | }; | ||
| 23 | // Missing formats: | ||
| 24 | // PixelFormat::RGBA32I | ||
| 25 | |||
| 26 | constexpr std::array VIEW_CLASS_96_BITS = { | ||
| 27 | PixelFormat::RGB32F, | ||
| 28 | }; | ||
| 29 | // Missing formats: | ||
| 30 | // PixelFormat::RGB32UI, | ||
| 31 | // PixelFormat::RGB32I, | ||
| 32 | |||
| 33 | constexpr std::array VIEW_CLASS_64_BITS = { | ||
| 34 | PixelFormat::RGBA16F, PixelFormat::RG32F, PixelFormat::RGBA16UI, PixelFormat::RG32UI, | ||
| 35 | PixelFormat::RGBA16U, PixelFormat::RGBA16F, PixelFormat::RGBA16S, | ||
| 36 | }; | ||
| 37 | // Missing formats: | ||
| 38 | // PixelFormat::RGBA16I | ||
| 39 | // PixelFormat::RG32I | ||
| 40 | |||
| 41 | // TODO: How should we handle 48 bits? | ||
| 42 | |||
| 43 | constexpr std::array VIEW_CLASS_32_BITS = { | ||
| 44 | PixelFormat::RG16F, PixelFormat::R11FG11FB10F, PixelFormat::R32F, | ||
| 45 | PixelFormat::A2B10G10R10U, PixelFormat::RG16UI, PixelFormat::R32UI, | ||
| 46 | PixelFormat::RG16I, PixelFormat::R32I, PixelFormat::ABGR8U, | ||
| 47 | PixelFormat::RG16, PixelFormat::ABGR8S, PixelFormat::RG16S, | ||
| 48 | PixelFormat::RGBA8_SRGB, PixelFormat::E5B9G9R9F, PixelFormat::BGRA8, | ||
| 49 | PixelFormat::BGRA8_SRGB, | ||
| 50 | }; | ||
| 51 | // Missing formats: | ||
| 52 | // PixelFormat::RGBA8UI | ||
| 53 | // PixelFormat::RGBA8I | ||
| 54 | // PixelFormat::RGB10_A2_UI | ||
| 55 | |||
| 56 | // TODO: How should we handle 24 bits? | ||
| 57 | |||
| 58 | constexpr std::array VIEW_CLASS_16_BITS = { | ||
| 59 | PixelFormat::R16F, PixelFormat::RG8UI, PixelFormat::R16UI, PixelFormat::R16I, | ||
| 60 | PixelFormat::RG8U, PixelFormat::R16U, PixelFormat::RG8S, PixelFormat::R16S, | ||
| 61 | }; | ||
| 62 | // Missing formats: | ||
| 63 | // PixelFormat::RG8I | ||
| 64 | |||
| 65 | constexpr std::array VIEW_CLASS_8_BITS = { | ||
| 66 | PixelFormat::R8UI, | ||
| 67 | PixelFormat::R8U, | ||
| 68 | }; | ||
| 69 | // Missing formats: | ||
| 70 | // PixelFormat::R8I | ||
| 71 | // PixelFormat::R8S | ||
| 72 | |||
| 73 | constexpr std::array VIEW_CLASS_RGTC1_RED = { | ||
| 74 | PixelFormat::DXN1, | ||
| 75 | }; | ||
| 76 | // Missing formats: | ||
| 77 | // COMPRESSED_SIGNED_RED_RGTC1 | ||
| 78 | |||
| 79 | constexpr std::array VIEW_CLASS_RGTC2_RG = { | ||
| 80 | PixelFormat::DXN2UNORM, | ||
| 81 | PixelFormat::DXN2SNORM, | ||
| 82 | }; | ||
| 83 | |||
| 84 | constexpr std::array VIEW_CLASS_BPTC_UNORM = { | ||
| 85 | PixelFormat::BC7U, | ||
| 86 | PixelFormat::BC7U_SRGB, | ||
| 87 | }; | ||
| 88 | |||
| 89 | constexpr std::array VIEW_CLASS_BPTC_FLOAT = { | ||
| 90 | PixelFormat::BC6H_SF16, | ||
| 91 | PixelFormat::BC6H_UF16, | ||
| 92 | }; | ||
| 93 | |||
| 94 | // Compatibility table taken from Table 4.X.1 in: | ||
| 95 | // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_copy_image.txt | ||
| 96 | |||
| 97 | constexpr std::array COPY_CLASS_128_BITS = { | ||
| 98 | PixelFormat::RGBA32UI, PixelFormat::RGBA32F, PixelFormat::DXT23, | ||
| 99 | PixelFormat::DXT23_SRGB, PixelFormat::DXT45, PixelFormat::DXT45_SRGB, | ||
| 100 | PixelFormat::DXN2SNORM, PixelFormat::BC7U, PixelFormat::BC7U_SRGB, | ||
| 101 | PixelFormat::BC6H_SF16, PixelFormat::BC6H_UF16, | ||
| 102 | }; | ||
| 103 | // Missing formats: | ||
| 104 | // PixelFormat::RGBA32I | ||
| 105 | // COMPRESSED_RG_RGTC2 | ||
| 106 | |||
| 107 | constexpr std::array COPY_CLASS_64_BITS = { | ||
| 108 | PixelFormat::RGBA16F, PixelFormat::RG32F, PixelFormat::RGBA16UI, PixelFormat::RG32UI, | ||
| 109 | PixelFormat::RGBA16U, PixelFormat::RGBA16S, PixelFormat::DXT1_SRGB, PixelFormat::DXT1, | ||
| 110 | |||
| 111 | }; | ||
| 112 | // Missing formats: | ||
| 113 | // PixelFormat::RGBA16I | ||
| 114 | // PixelFormat::RG32I, | ||
| 115 | // COMPRESSED_RGB_S3TC_DXT1_EXT | ||
| 116 | // COMPRESSED_SRGB_S3TC_DXT1_EXT | ||
| 117 | // COMPRESSED_RGBA_S3TC_DXT1_EXT | ||
| 118 | // COMPRESSED_SIGNED_RED_RGTC1 | ||
| 119 | |||
| 120 | void Enable(FormatCompatibility::Table& compatiblity, size_t format_a, size_t format_b) { | ||
| 121 | compatiblity[format_a][format_b] = true; | ||
| 122 | compatiblity[format_b][format_a] = true; | ||
| 123 | } | ||
| 124 | |||
| 125 | void Enable(FormatCompatibility::Table& compatibility, PixelFormat format_a, PixelFormat format_b) { | ||
| 126 | Enable(compatibility, static_cast<size_t>(format_a), static_cast<size_t>(format_b)); | ||
| 127 | } | ||
| 128 | |||
| 129 | template <typename Range> | ||
| 130 | void EnableRange(FormatCompatibility::Table& compatibility, const Range& range) { | ||
| 131 | for (auto it_a = range.begin(); it_a != range.end(); ++it_a) { | ||
| 132 | for (auto it_b = it_a; it_b != range.end(); ++it_b) { | ||
| 133 | Enable(compatibility, *it_a, *it_b); | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | } // Anonymous namespace | ||
| 139 | |||
| 140 | FormatCompatibility::FormatCompatibility() { | ||
| 141 | for (size_t i = 0; i < MaxPixelFormat; ++i) { | ||
| 142 | // Identity is allowed | ||
| 143 | Enable(view, i, i); | ||
| 144 | } | ||
| 145 | |||
| 146 | EnableRange(view, VIEW_CLASS_128_BITS); | ||
| 147 | EnableRange(view, VIEW_CLASS_96_BITS); | ||
| 148 | EnableRange(view, VIEW_CLASS_64_BITS); | ||
| 149 | EnableRange(view, VIEW_CLASS_32_BITS); | ||
| 150 | EnableRange(view, VIEW_CLASS_16_BITS); | ||
| 151 | EnableRange(view, VIEW_CLASS_8_BITS); | ||
| 152 | EnableRange(view, VIEW_CLASS_RGTC1_RED); | ||
| 153 | EnableRange(view, VIEW_CLASS_RGTC2_RG); | ||
| 154 | EnableRange(view, VIEW_CLASS_BPTC_UNORM); | ||
| 155 | EnableRange(view, VIEW_CLASS_BPTC_FLOAT); | ||
| 156 | |||
| 157 | copy = view; | ||
| 158 | EnableRange(copy, COPY_CLASS_128_BITS); | ||
| 159 | EnableRange(copy, COPY_CLASS_64_BITS); | ||
| 160 | } | ||
| 161 | |||
| 162 | } // namespace VideoCore::Surface | ||
diff --git a/src/video_core/compatible_formats.h b/src/video_core/compatible_formats.h new file mode 100644 index 000000000..d1082566d --- /dev/null +++ b/src/video_core/compatible_formats.h | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <array> | ||
| 6 | #include <bitset> | ||
| 7 | #include <cstddef> | ||
| 8 | |||
| 9 | #include "video_core/surface.h" | ||
| 10 | |||
| 11 | namespace VideoCore::Surface { | ||
| 12 | |||
| 13 | class FormatCompatibility { | ||
| 14 | public: | ||
| 15 | using Table = std::array<std::bitset<MaxPixelFormat>, MaxPixelFormat>; | ||
| 16 | |||
| 17 | explicit FormatCompatibility(); | ||
| 18 | |||
| 19 | bool TestView(PixelFormat format_a, PixelFormat format_b) const noexcept { | ||
| 20 | return view[static_cast<size_t>(format_a)][static_cast<size_t>(format_b)]; | ||
| 21 | } | ||
| 22 | |||
| 23 | bool TestCopy(PixelFormat format_a, PixelFormat format_b) const noexcept { | ||
| 24 | return copy[static_cast<size_t>(format_a)][static_cast<size_t>(format_b)]; | ||
| 25 | } | ||
| 26 | |||
| 27 | private: | ||
| 28 | Table view; | ||
| 29 | Table copy; | ||
| 30 | }; | ||
| 31 | |||
| 32 | } // namespace VideoCore::Surface | ||
diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp index ef7dad349..a50e7b4e0 100644 --- a/src/video_core/macro/macro.cpp +++ b/src/video_core/macro/macro.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <optional> | ||
| 5 | #include <boost/container_hash/hash.hpp> | 6 | #include <boost/container_hash/hash.hpp> |
| 6 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 7 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| @@ -35,22 +36,40 @@ void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method, | |||
| 35 | } | 36 | } |
| 36 | } else { | 37 | } else { |
| 37 | // Macro not compiled, check if it's uploaded and if so, compile it | 38 | // Macro not compiled, check if it's uploaded and if so, compile it |
| 38 | auto macro_code = uploaded_macro_code.find(method); | 39 | std::optional<u32> mid_method = std::nullopt; |
| 40 | const auto macro_code = uploaded_macro_code.find(method); | ||
| 39 | if (macro_code == uploaded_macro_code.end()) { | 41 | if (macro_code == uploaded_macro_code.end()) { |
| 40 | UNREACHABLE_MSG("Macro 0x{0:x} was not uploaded", method); | 42 | for (const auto& [method_base, code] : uploaded_macro_code) { |
| 41 | return; | 43 | if (method >= method_base && (method - method_base) < code.size()) { |
| 44 | mid_method = method_base; | ||
| 45 | break; | ||
| 46 | } | ||
| 47 | } | ||
| 48 | if (!mid_method.has_value()) { | ||
| 49 | UNREACHABLE_MSG("Macro 0x{0:x} was not uploaded", method); | ||
| 50 | return; | ||
| 51 | } | ||
| 42 | } | 52 | } |
| 43 | auto& cache_info = macro_cache[method]; | 53 | auto& cache_info = macro_cache[method]; |
| 44 | cache_info.hash = boost::hash_value(macro_code->second); | 54 | |
| 45 | cache_info.lle_program = Compile(macro_code->second); | 55 | if (!mid_method.has_value()) { |
| 56 | cache_info.lle_program = Compile(macro_code->second); | ||
| 57 | cache_info.hash = boost::hash_value(macro_code->second); | ||
| 58 | } else { | ||
| 59 | const auto& macro_cached = uploaded_macro_code[mid_method.value()]; | ||
| 60 | const auto rebased_method = method - mid_method.value(); | ||
| 61 | auto& code = uploaded_macro_code[method]; | ||
| 62 | code.resize(macro_cached.size() - rebased_method); | ||
| 63 | std::memcpy(code.data(), macro_cached.data() + rebased_method, | ||
| 64 | code.size() * sizeof(u32)); | ||
| 65 | cache_info.hash = boost::hash_value(code); | ||
| 66 | cache_info.lle_program = Compile(code); | ||
| 67 | } | ||
| 46 | 68 | ||
| 47 | auto hle_program = hle_macros->GetHLEProgram(cache_info.hash); | 69 | auto hle_program = hle_macros->GetHLEProgram(cache_info.hash); |
| 48 | if (hle_program.has_value()) { | 70 | if (hle_program.has_value()) { |
| 49 | cache_info.has_hle_program = true; | 71 | cache_info.has_hle_program = true; |
| 50 | cache_info.hle_program = std::move(hle_program.value()); | 72 | cache_info.hle_program = std::move(hle_program.value()); |
| 51 | } | ||
| 52 | |||
| 53 | if (cache_info.has_hle_program) { | ||
| 54 | cache_info.hle_program->Execute(parameters, method); | 73 | cache_info.hle_program->Execute(parameters, method); |
| 55 | } else { | 74 | } else { |
| 56 | cache_info.lle_program->Execute(parameters, method); | 75 | cache_info.lle_program->Execute(parameters, method); |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index d9f7b4cc6..e461e4c70 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp | |||
| @@ -34,20 +34,27 @@ Buffer::Buffer(const Device& device, VAddr cpu_addr, std::size_t size) | |||
| 34 | 34 | ||
| 35 | Buffer::~Buffer() = default; | 35 | Buffer::~Buffer() = default; |
| 36 | 36 | ||
| 37 | void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) const { | 37 | void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) { |
| 38 | glNamedBufferSubData(Handle(), static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size), | 38 | glNamedBufferSubData(Handle(), static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size), |
| 39 | data); | 39 | data); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | void Buffer::Download(std::size_t offset, std::size_t size, u8* data) const { | 42 | void Buffer::Download(std::size_t offset, std::size_t size, u8* data) { |
| 43 | MICROPROFILE_SCOPE(OpenGL_Buffer_Download); | 43 | MICROPROFILE_SCOPE(OpenGL_Buffer_Download); |
| 44 | const GLsizeiptr gl_size = static_cast<GLsizeiptr>(size); | ||
| 45 | const GLintptr gl_offset = static_cast<GLintptr>(offset); | ||
| 46 | if (read_buffer.handle == 0) { | ||
| 47 | read_buffer.Create(); | ||
| 48 | glNamedBufferData(read_buffer.handle, static_cast<GLsizeiptr>(Size()), nullptr, | ||
| 49 | GL_STREAM_READ); | ||
| 50 | } | ||
| 44 | glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); | 51 | glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| 45 | glGetNamedBufferSubData(Handle(), static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size), | 52 | glCopyNamedBufferSubData(gl_buffer.handle, read_buffer.handle, gl_offset, gl_offset, gl_size); |
| 46 | data); | 53 | glGetNamedBufferSubData(read_buffer.handle, gl_offset, gl_size, data); |
| 47 | } | 54 | } |
| 48 | 55 | ||
| 49 | void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, | 56 | void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, |
| 50 | std::size_t size) const { | 57 | std::size_t size) { |
| 51 | glCopyNamedBufferSubData(src.Handle(), Handle(), static_cast<GLintptr>(src_offset), | 58 | glCopyNamedBufferSubData(src.Handle(), Handle(), static_cast<GLintptr>(src_offset), |
| 52 | static_cast<GLintptr>(dst_offset), static_cast<GLsizeiptr>(size)); | 59 | static_cast<GLintptr>(dst_offset), static_cast<GLsizeiptr>(size)); |
| 53 | } | 60 | } |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index 59d95adbc..88fdc0536 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h | |||
| @@ -28,12 +28,12 @@ public: | |||
| 28 | explicit Buffer(const Device& device, VAddr cpu_addr, std::size_t size); | 28 | explicit Buffer(const Device& device, VAddr cpu_addr, std::size_t size); |
| 29 | ~Buffer(); | 29 | ~Buffer(); |
| 30 | 30 | ||
| 31 | void Upload(std::size_t offset, std::size_t size, const u8* data) const; | 31 | void Upload(std::size_t offset, std::size_t size, const u8* data); |
| 32 | 32 | ||
| 33 | void Download(std::size_t offset, std::size_t size, u8* data) const; | 33 | void Download(std::size_t offset, std::size_t size, u8* data); |
| 34 | 34 | ||
| 35 | void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, | 35 | void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, |
| 36 | std::size_t size) const; | 36 | std::size_t size); |
| 37 | 37 | ||
| 38 | GLuint Handle() const noexcept { | 38 | GLuint Handle() const noexcept { |
| 39 | return gl_buffer.handle; | 39 | return gl_buffer.handle; |
| @@ -45,6 +45,7 @@ public: | |||
| 45 | 45 | ||
| 46 | private: | 46 | private: |
| 47 | OGLBuffer gl_buffer; | 47 | OGLBuffer gl_buffer; |
| 48 | OGLBuffer read_buffer; | ||
| 48 | u64 gpu_address = 0; | 49 | u64 gpu_address = 0; |
| 49 | }; | 50 | }; |
| 50 | 51 | ||
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index b6b6659c1..208fc6167 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp | |||
| @@ -188,20 +188,6 @@ bool IsASTCSupported() { | |||
| 188 | return true; | 188 | return true; |
| 189 | } | 189 | } |
| 190 | 190 | ||
| 191 | /// @brief Returns true when a GL_RENDERER is a Turing GPU | ||
| 192 | /// @param renderer GL_RENDERER string | ||
| 193 | bool IsTuring(std::string_view renderer) { | ||
| 194 | static constexpr std::array<std::string_view, 12> TURING_GPUS = { | ||
| 195 | "GTX 1650", "GTX 1660", "RTX 2060", "RTX 2070", | ||
| 196 | "RTX 2080", "TITAN RTX", "Quadro RTX 3000", "Quadro RTX 4000", | ||
| 197 | "Quadro RTX 5000", "Quadro RTX 6000", "Quadro RTX 8000", "Tesla T4", | ||
| 198 | }; | ||
| 199 | return std::any_of(TURING_GPUS.begin(), TURING_GPUS.end(), | ||
| 200 | [renderer](std::string_view candidate) { | ||
| 201 | return renderer.find(candidate) != std::string_view::npos; | ||
| 202 | }); | ||
| 203 | } | ||
| 204 | |||
| 205 | } // Anonymous namespace | 191 | } // Anonymous namespace |
| 206 | 192 | ||
| 207 | Device::Device() | 193 | Device::Device() |
| @@ -213,7 +199,6 @@ Device::Device() | |||
| 213 | 199 | ||
| 214 | const bool is_nvidia = vendor == "NVIDIA Corporation"; | 200 | const bool is_nvidia = vendor == "NVIDIA Corporation"; |
| 215 | const bool is_amd = vendor == "ATI Technologies Inc."; | 201 | const bool is_amd = vendor == "ATI Technologies Inc."; |
| 216 | const bool is_turing = is_nvidia && IsTuring(renderer); | ||
| 217 | 202 | ||
| 218 | bool disable_fast_buffer_sub_data = false; | 203 | bool disable_fast_buffer_sub_data = false; |
| 219 | if (is_nvidia && version == "4.6.0 NVIDIA 443.24") { | 204 | if (is_nvidia && version == "4.6.0 NVIDIA 443.24") { |
| @@ -238,15 +223,12 @@ Device::Device() | |||
| 238 | has_component_indexing_bug = is_amd; | 223 | has_component_indexing_bug = is_amd; |
| 239 | has_precise_bug = TestPreciseBug(); | 224 | has_precise_bug = TestPreciseBug(); |
| 240 | has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2; | 225 | has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2; |
| 226 | has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory; | ||
| 241 | 227 | ||
| 242 | // At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive | 228 | // At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive |
| 243 | // uniform buffers as "push constants" | 229 | // uniform buffers as "push constants" |
| 244 | has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data; | 230 | has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data; |
| 245 | 231 | ||
| 246 | // Nvidia's driver on Turing GPUs randomly crashes when the buffer is made resident, or on | ||
| 247 | // DeleteBuffers. Disable unified memory on these devices. | ||
| 248 | has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory && !is_turing; | ||
| 249 | |||
| 250 | use_assembly_shaders = Settings::values.use_assembly_shaders && GLAD_GL_NV_gpu_program5 && | 232 | use_assembly_shaders = Settings::values.use_assembly_shaders && GLAD_GL_NV_gpu_program5 && |
| 251 | GLAD_GL_NV_compute_program5 && GLAD_GL_NV_transform_feedback && | 233 | GLAD_GL_NV_compute_program5 && GLAD_GL_NV_transform_feedback && |
| 252 | GLAD_GL_NV_transform_feedback2; | 234 | GLAD_GL_NV_transform_feedback2; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 362457ffe..e960a0ef1 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -213,9 +213,10 @@ void RasterizerOpenGL::SetupVertexFormat() { | |||
| 213 | if (attrib.type == Maxwell::VertexAttribute::Type::SignedInt || | 213 | if (attrib.type == Maxwell::VertexAttribute::Type::SignedInt || |
| 214 | attrib.type == Maxwell::VertexAttribute::Type::UnsignedInt) { | 214 | attrib.type == Maxwell::VertexAttribute::Type::UnsignedInt) { |
| 215 | glVertexAttribIFormat(gl_index, attrib.ComponentCount(), | 215 | glVertexAttribIFormat(gl_index, attrib.ComponentCount(), |
| 216 | MaxwellToGL::VertexType(attrib), attrib.offset); | 216 | MaxwellToGL::VertexFormat(attrib), attrib.offset); |
| 217 | } else { | 217 | } else { |
| 218 | glVertexAttribFormat(gl_index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib), | 218 | glVertexAttribFormat(gl_index, attrib.ComponentCount(), |
| 219 | MaxwellToGL::VertexFormat(attrib), | ||
| 219 | attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset); | 220 | attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset); |
| 220 | } | 221 | } |
| 221 | glVertexAttribBinding(gl_index, attrib.buffer); | 222 | glVertexAttribBinding(gl_index, attrib.buffer); |
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 35e329240..fe9bd4b5a 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h | |||
| @@ -24,10 +24,11 @@ namespace MaxwellToGL { | |||
| 24 | 24 | ||
| 25 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | 25 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| 26 | 26 | ||
| 27 | inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | 27 | inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) { |
| 28 | switch (attrib.type) { | 28 | switch (attrib.type) { |
| 29 | case Maxwell::VertexAttribute::Type::UnsignedInt: | ||
| 30 | case Maxwell::VertexAttribute::Type::UnsignedNorm: | 29 | case Maxwell::VertexAttribute::Type::UnsignedNorm: |
| 30 | case Maxwell::VertexAttribute::Type::UnsignedScaled: | ||
| 31 | case Maxwell::VertexAttribute::Type::UnsignedInt: | ||
| 31 | switch (attrib.size) { | 32 | switch (attrib.size) { |
| 32 | case Maxwell::VertexAttribute::Size::Size_8: | 33 | case Maxwell::VertexAttribute::Size::Size_8: |
| 33 | case Maxwell::VertexAttribute::Size::Size_8_8: | 34 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| @@ -48,8 +49,9 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | |||
| 48 | return GL_UNSIGNED_INT_2_10_10_10_REV; | 49 | return GL_UNSIGNED_INT_2_10_10_10_REV; |
| 49 | } | 50 | } |
| 50 | break; | 51 | break; |
| 51 | case Maxwell::VertexAttribute::Type::SignedInt: | ||
| 52 | case Maxwell::VertexAttribute::Type::SignedNorm: | 52 | case Maxwell::VertexAttribute::Type::SignedNorm: |
| 53 | case Maxwell::VertexAttribute::Type::SignedScaled: | ||
| 54 | case Maxwell::VertexAttribute::Type::SignedInt: | ||
| 53 | switch (attrib.size) { | 55 | switch (attrib.size) { |
| 54 | case Maxwell::VertexAttribute::Size::Size_8: | 56 | case Maxwell::VertexAttribute::Size::Size_8: |
| 55 | case Maxwell::VertexAttribute::Size::Size_8_8: | 57 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| @@ -84,36 +86,8 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | |||
| 84 | return GL_FLOAT; | 86 | return GL_FLOAT; |
| 85 | } | 87 | } |
| 86 | break; | 88 | break; |
| 87 | case Maxwell::VertexAttribute::Type::UnsignedScaled: | ||
| 88 | switch (attrib.size) { | ||
| 89 | case Maxwell::VertexAttribute::Size::Size_8: | ||
| 90 | case Maxwell::VertexAttribute::Size::Size_8_8: | ||
| 91 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | ||
| 92 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | ||
| 93 | return GL_UNSIGNED_BYTE; | ||
| 94 | case Maxwell::VertexAttribute::Size::Size_16: | ||
| 95 | case Maxwell::VertexAttribute::Size::Size_16_16: | ||
| 96 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | ||
| 97 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | ||
| 98 | return GL_UNSIGNED_SHORT; | ||
| 99 | } | ||
| 100 | break; | ||
| 101 | case Maxwell::VertexAttribute::Type::SignedScaled: | ||
| 102 | switch (attrib.size) { | ||
| 103 | case Maxwell::VertexAttribute::Size::Size_8: | ||
| 104 | case Maxwell::VertexAttribute::Size::Size_8_8: | ||
| 105 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | ||
| 106 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | ||
| 107 | return GL_BYTE; | ||
| 108 | case Maxwell::VertexAttribute::Size::Size_16: | ||
| 109 | case Maxwell::VertexAttribute::Size::Size_16_16: | ||
| 110 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | ||
| 111 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | ||
| 112 | return GL_SHORT; | ||
| 113 | } | ||
| 114 | break; | ||
| 115 | } | 89 | } |
| 116 | UNIMPLEMENTED_MSG("Unimplemented vertex type={} and size={}", attrib.TypeString(), | 90 | UNIMPLEMENTED_MSG("Unimplemented vertex format of type={} and size={}", attrib.TypeString(), |
| 117 | attrib.SizeString()); | 91 | attrib.SizeString()); |
| 118 | return {}; | 92 | return {}; |
| 119 | } | 93 | } |
| @@ -217,6 +191,12 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { | |||
| 217 | } else { | 191 | } else { |
| 218 | return GL_MIRROR_CLAMP_TO_EDGE; | 192 | return GL_MIRROR_CLAMP_TO_EDGE; |
| 219 | } | 193 | } |
| 194 | case Tegra::Texture::WrapMode::MirrorOnceClampOGL: | ||
| 195 | if (GL_EXT_texture_mirror_clamp) { | ||
| 196 | return GL_MIRROR_CLAMP_EXT; | ||
| 197 | } else { | ||
| 198 | return GL_MIRROR_CLAMP_TO_EDGE; | ||
| 199 | } | ||
| 220 | } | 200 | } |
| 221 | UNIMPLEMENTED_MSG("Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); | 201 | UNIMPLEMENTED_MSG("Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); |
| 222 | return GL_REPEAT; | 202 | return GL_REPEAT; |
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 424278816..d1f0ea932 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp | |||
| @@ -39,52 +39,18 @@ constexpr std::array POLYGON_OFFSET_ENABLE_LUT = { | |||
| 39 | 39 | ||
| 40 | } // Anonymous namespace | 40 | } // Anonymous namespace |
| 41 | 41 | ||
| 42 | void FixedPipelineState::DepthStencil::Fill(const Maxwell& regs) noexcept { | 42 | void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_state) { |
| 43 | raw = 0; | ||
| 44 | front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail)); | ||
| 45 | front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail)); | ||
| 46 | front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass)); | ||
| 47 | front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func)); | ||
| 48 | if (regs.stencil_two_side_enable) { | ||
| 49 | back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail)); | ||
| 50 | back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail)); | ||
| 51 | back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass)); | ||
| 52 | back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func)); | ||
| 53 | } else { | ||
| 54 | back.action_stencil_fail.Assign(front.action_stencil_fail); | ||
| 55 | back.action_depth_fail.Assign(front.action_depth_fail); | ||
| 56 | back.action_depth_pass.Assign(front.action_depth_pass); | ||
| 57 | back.test_func.Assign(front.test_func); | ||
| 58 | } | ||
| 59 | depth_test_enable.Assign(regs.depth_test_enable); | ||
| 60 | depth_write_enable.Assign(regs.depth_write_enabled); | ||
| 61 | depth_bounds_enable.Assign(regs.depth_bounds_enable); | ||
| 62 | stencil_enable.Assign(regs.stencil_enable); | ||
| 63 | depth_test_func.Assign(PackComparisonOp(regs.depth_test_func)); | ||
| 64 | } | ||
| 65 | |||
| 66 | void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept { | ||
| 67 | const auto& clip = regs.view_volume_clip_control; | 43 | const auto& clip = regs.view_volume_clip_control; |
| 68 | const std::array enabled_lut = {regs.polygon_offset_point_enable, | 44 | const std::array enabled_lut = {regs.polygon_offset_point_enable, |
| 69 | regs.polygon_offset_line_enable, | 45 | regs.polygon_offset_line_enable, |
| 70 | regs.polygon_offset_fill_enable}; | 46 | regs.polygon_offset_fill_enable}; |
| 71 | const u32 topology_index = static_cast<u32>(regs.draw.topology.Value()); | 47 | const u32 topology_index = static_cast<u32>(regs.draw.topology.Value()); |
| 72 | 48 | ||
| 73 | u32 packed_front_face = PackFrontFace(regs.front_face); | ||
| 74 | if (regs.screen_y_control.triangle_rast_flip != 0) { | ||
| 75 | // Flip front face | ||
| 76 | packed_front_face = 1 - packed_front_face; | ||
| 77 | } | ||
| 78 | |||
| 79 | raw = 0; | 49 | raw = 0; |
| 80 | topology.Assign(topology_index); | ||
| 81 | primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0); | 50 | primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0); |
| 82 | cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0); | ||
| 83 | depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); | 51 | depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); |
| 84 | depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value()); | 52 | depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value()); |
| 85 | ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); | 53 | ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); |
| 86 | cull_face.Assign(PackCullFace(regs.cull_face)); | ||
| 87 | front_face.Assign(packed_front_face); | ||
| 88 | polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front)); | 54 | polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front)); |
| 89 | patch_control_points_minus_one.Assign(regs.patch_vertices - 1); | 55 | patch_control_points_minus_one.Assign(regs.patch_vertices - 1); |
| 90 | tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value())); | 56 | tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value())); |
| @@ -93,19 +59,37 @@ void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept { | |||
| 93 | logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); | 59 | logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); |
| 94 | logic_op.Assign(PackLogicOp(regs.logic_op.operation)); | 60 | logic_op.Assign(PackLogicOp(regs.logic_op.operation)); |
| 95 | rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0); | 61 | rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0); |
| 62 | |||
| 96 | std::memcpy(&point_size, ®s.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast | 63 | std::memcpy(&point_size, ®s.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast |
| 97 | } | ||
| 98 | 64 | ||
| 99 | void FixedPipelineState::ColorBlending::Fill(const Maxwell& regs) noexcept { | 65 | for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { |
| 66 | binding_divisors[index] = | ||
| 67 | regs.instanced_arrays.IsInstancingEnabled(index) ? regs.vertex_array[index].divisor : 0; | ||
| 68 | } | ||
| 69 | |||
| 70 | for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { | ||
| 71 | const auto& input = regs.vertex_attrib_format[index]; | ||
| 72 | auto& attribute = attributes[index]; | ||
| 73 | attribute.raw = 0; | ||
| 74 | attribute.enabled.Assign(input.IsConstant() ? 0 : 1); | ||
| 75 | attribute.buffer.Assign(input.buffer); | ||
| 76 | attribute.offset.Assign(input.offset); | ||
| 77 | attribute.type.Assign(static_cast<u32>(input.type.Value())); | ||
| 78 | attribute.size.Assign(static_cast<u32>(input.size.Value())); | ||
| 79 | } | ||
| 80 | |||
| 100 | for (std::size_t index = 0; index < std::size(attachments); ++index) { | 81 | for (std::size_t index = 0; index < std::size(attachments); ++index) { |
| 101 | attachments[index].Fill(regs, index); | 82 | attachments[index].Fill(regs, index); |
| 102 | } | 83 | } |
| 103 | } | ||
| 104 | 84 | ||
| 105 | void FixedPipelineState::ViewportSwizzles::Fill(const Maxwell& regs) noexcept { | ||
| 106 | const auto& transform = regs.viewport_transform; | 85 | const auto& transform = regs.viewport_transform; |
| 107 | std::transform(transform.begin(), transform.end(), swizzles.begin(), | 86 | std::transform(transform.begin(), transform.end(), viewport_swizzles.begin(), |
| 108 | [](const auto& viewport) { return static_cast<u16>(viewport.swizzle.raw); }); | 87 | [](const auto& viewport) { return static_cast<u16>(viewport.swizzle.raw); }); |
| 88 | |||
| 89 | if (!has_extended_dynamic_state) { | ||
| 90 | no_extended_dynamic_state.Assign(1); | ||
| 91 | dynamic_state.Fill(regs); | ||
| 92 | } | ||
| 109 | } | 93 | } |
| 110 | 94 | ||
| 111 | void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) { | 95 | void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) { |
| @@ -147,20 +131,57 @@ void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size | |||
| 147 | enable.Assign(1); | 131 | enable.Assign(1); |
| 148 | } | 132 | } |
| 149 | 133 | ||
| 150 | void FixedPipelineState::Fill(const Maxwell& regs) { | 134 | void FixedPipelineState::DynamicState::Fill(const Maxwell& regs) { |
| 151 | rasterizer.Fill(regs); | 135 | const u32 topology_index = static_cast<u32>(regs.draw.topology.Value()); |
| 152 | depth_stencil.Fill(regs); | 136 | u32 packed_front_face = PackFrontFace(regs.front_face); |
| 153 | color_blending.Fill(regs); | 137 | if (regs.screen_y_control.triangle_rast_flip != 0) { |
| 154 | viewport_swizzles.Fill(regs); | 138 | // Flip front face |
| 139 | packed_front_face = 1 - packed_front_face; | ||
| 140 | } | ||
| 141 | |||
| 142 | raw1 = 0; | ||
| 143 | raw2 = 0; | ||
| 144 | front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail)); | ||
| 145 | front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail)); | ||
| 146 | front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass)); | ||
| 147 | front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func)); | ||
| 148 | if (regs.stencil_two_side_enable) { | ||
| 149 | back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail)); | ||
| 150 | back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail)); | ||
| 151 | back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass)); | ||
| 152 | back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func)); | ||
| 153 | } else { | ||
| 154 | back.action_stencil_fail.Assign(front.action_stencil_fail); | ||
| 155 | back.action_depth_fail.Assign(front.action_depth_fail); | ||
| 156 | back.action_depth_pass.Assign(front.action_depth_pass); | ||
| 157 | back.test_func.Assign(front.test_func); | ||
| 158 | } | ||
| 159 | stencil_enable.Assign(regs.stencil_enable); | ||
| 160 | depth_write_enable.Assign(regs.depth_write_enabled); | ||
| 161 | depth_bounds_enable.Assign(regs.depth_bounds_enable); | ||
| 162 | depth_test_enable.Assign(regs.depth_test_enable); | ||
| 163 | front_face.Assign(packed_front_face); | ||
| 164 | depth_test_func.Assign(PackComparisonOp(regs.depth_test_func)); | ||
| 165 | topology.Assign(topology_index); | ||
| 166 | cull_face.Assign(PackCullFace(regs.cull_face)); | ||
| 167 | cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0); | ||
| 168 | |||
| 169 | for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { | ||
| 170 | const auto& input = regs.vertex_array[index]; | ||
| 171 | VertexBinding& binding = vertex_bindings[index]; | ||
| 172 | binding.raw = 0; | ||
| 173 | binding.enabled.Assign(input.IsEnabled() ? 1 : 0); | ||
| 174 | binding.stride.Assign(static_cast<u16>(input.stride.Value())); | ||
| 175 | } | ||
| 155 | } | 176 | } |
| 156 | 177 | ||
| 157 | std::size_t FixedPipelineState::Hash() const noexcept { | 178 | std::size_t FixedPipelineState::Hash() const noexcept { |
| 158 | const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this); | 179 | const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size()); |
| 159 | return static_cast<std::size_t>(hash); | 180 | return static_cast<std::size_t>(hash); |
| 160 | } | 181 | } |
| 161 | 182 | ||
| 162 | bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept { | 183 | bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept { |
| 163 | return std::memcmp(this, &rhs, sizeof *this) == 0; | 184 | return std::memcmp(this, &rhs, Size()) == 0; |
| 164 | } | 185 | } |
| 165 | 186 | ||
| 166 | u32 FixedPipelineState::PackComparisonOp(Maxwell::ComparisonOp op) noexcept { | 187 | u32 FixedPipelineState::PackComparisonOp(Maxwell::ComparisonOp op) noexcept { |
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 31a6398f2..cdcbb65f5 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h | |||
| @@ -60,14 +60,6 @@ struct FixedPipelineState { | |||
| 60 | 60 | ||
| 61 | void Fill(const Maxwell& regs, std::size_t index); | 61 | void Fill(const Maxwell& regs, std::size_t index); |
| 62 | 62 | ||
| 63 | std::size_t Hash() const noexcept; | ||
| 64 | |||
| 65 | bool operator==(const BlendingAttachment& rhs) const noexcept; | ||
| 66 | |||
| 67 | bool operator!=(const BlendingAttachment& rhs) const noexcept { | ||
| 68 | return !operator==(rhs); | ||
| 69 | } | ||
| 70 | |||
| 71 | constexpr std::array<bool, 4> Mask() const noexcept { | 63 | constexpr std::array<bool, 4> Mask() const noexcept { |
| 72 | return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0}; | 64 | return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0}; |
| 73 | } | 65 | } |
| @@ -97,156 +89,116 @@ struct FixedPipelineState { | |||
| 97 | } | 89 | } |
| 98 | }; | 90 | }; |
| 99 | 91 | ||
| 100 | struct VertexInput { | 92 | union VertexAttribute { |
| 101 | union Binding { | 93 | u32 raw; |
| 102 | u16 raw; | 94 | BitField<0, 1, u32> enabled; |
| 103 | BitField<0, 1, u16> enabled; | 95 | BitField<1, 5, u32> buffer; |
| 104 | BitField<1, 12, u16> stride; | 96 | BitField<6, 14, u32> offset; |
| 105 | }; | 97 | BitField<20, 3, u32> type; |
| 98 | BitField<23, 6, u32> size; | ||
| 106 | 99 | ||
| 107 | union Attribute { | 100 | constexpr Maxwell::VertexAttribute::Type Type() const noexcept { |
| 108 | u32 raw; | 101 | return static_cast<Maxwell::VertexAttribute::Type>(type.Value()); |
| 109 | BitField<0, 1, u32> enabled; | ||
| 110 | BitField<1, 5, u32> buffer; | ||
| 111 | BitField<6, 14, u32> offset; | ||
| 112 | BitField<20, 3, u32> type; | ||
| 113 | BitField<23, 6, u32> size; | ||
| 114 | |||
| 115 | constexpr Maxwell::VertexAttribute::Type Type() const noexcept { | ||
| 116 | return static_cast<Maxwell::VertexAttribute::Type>(type.Value()); | ||
| 117 | } | ||
| 118 | |||
| 119 | constexpr Maxwell::VertexAttribute::Size Size() const noexcept { | ||
| 120 | return static_cast<Maxwell::VertexAttribute::Size>(size.Value()); | ||
| 121 | } | ||
| 122 | }; | ||
| 123 | |||
| 124 | std::array<Binding, Maxwell::NumVertexArrays> bindings; | ||
| 125 | std::array<u32, Maxwell::NumVertexArrays> binding_divisors; | ||
| 126 | std::array<Attribute, Maxwell::NumVertexAttributes> attributes; | ||
| 127 | |||
| 128 | void SetBinding(std::size_t index, bool enabled, u32 stride, u32 divisor) noexcept { | ||
| 129 | auto& binding = bindings[index]; | ||
| 130 | binding.raw = 0; | ||
| 131 | binding.enabled.Assign(enabled ? 1 : 0); | ||
| 132 | binding.stride.Assign(static_cast<u16>(stride)); | ||
| 133 | binding_divisors[index] = divisor; | ||
| 134 | } | 102 | } |
| 135 | 103 | ||
| 136 | void SetAttribute(std::size_t index, bool enabled, u32 buffer, u32 offset, | 104 | constexpr Maxwell::VertexAttribute::Size Size() const noexcept { |
| 137 | Maxwell::VertexAttribute::Type type, | 105 | return static_cast<Maxwell::VertexAttribute::Size>(size.Value()); |
| 138 | Maxwell::VertexAttribute::Size size) noexcept { | ||
| 139 | auto& attribute = attributes[index]; | ||
| 140 | attribute.raw = 0; | ||
| 141 | attribute.enabled.Assign(enabled ? 1 : 0); | ||
| 142 | attribute.buffer.Assign(buffer); | ||
| 143 | attribute.offset.Assign(offset); | ||
| 144 | attribute.type.Assign(static_cast<u32>(type)); | ||
| 145 | attribute.size.Assign(static_cast<u32>(size)); | ||
| 146 | } | 106 | } |
| 147 | }; | 107 | }; |
| 148 | 108 | ||
| 149 | struct Rasterizer { | 109 | template <std::size_t Position> |
| 150 | union { | 110 | union StencilFace { |
| 151 | u32 raw; | 111 | BitField<Position + 0, 3, u32> action_stencil_fail; |
| 152 | BitField<0, 4, u32> topology; | 112 | BitField<Position + 3, 3, u32> action_depth_fail; |
| 153 | BitField<4, 1, u32> primitive_restart_enable; | 113 | BitField<Position + 6, 3, u32> action_depth_pass; |
| 154 | BitField<5, 1, u32> cull_enable; | 114 | BitField<Position + 9, 3, u32> test_func; |
| 155 | BitField<6, 1, u32> depth_bias_enable; | ||
| 156 | BitField<7, 1, u32> depth_clamp_disabled; | ||
| 157 | BitField<8, 1, u32> ndc_minus_one_to_one; | ||
| 158 | BitField<9, 2, u32> cull_face; | ||
| 159 | BitField<11, 1, u32> front_face; | ||
| 160 | BitField<12, 2, u32> polygon_mode; | ||
| 161 | BitField<14, 5, u32> patch_control_points_minus_one; | ||
| 162 | BitField<19, 2, u32> tessellation_primitive; | ||
| 163 | BitField<21, 2, u32> tessellation_spacing; | ||
| 164 | BitField<23, 1, u32> tessellation_clockwise; | ||
| 165 | BitField<24, 1, u32> logic_op_enable; | ||
| 166 | BitField<25, 4, u32> logic_op; | ||
| 167 | BitField<29, 1, u32> rasterize_enable; | ||
| 168 | }; | ||
| 169 | |||
| 170 | // TODO(Rodrigo): Move this to push constants | ||
| 171 | u32 point_size; | ||
| 172 | 115 | ||
| 173 | void Fill(const Maxwell& regs) noexcept; | 116 | Maxwell::StencilOp ActionStencilFail() const noexcept { |
| 117 | return UnpackStencilOp(action_stencil_fail); | ||
| 118 | } | ||
| 174 | 119 | ||
| 175 | constexpr Maxwell::PrimitiveTopology Topology() const noexcept { | 120 | Maxwell::StencilOp ActionDepthFail() const noexcept { |
| 176 | return static_cast<Maxwell::PrimitiveTopology>(topology.Value()); | 121 | return UnpackStencilOp(action_depth_fail); |
| 177 | } | 122 | } |
| 178 | 123 | ||
| 179 | Maxwell::CullFace CullFace() const noexcept { | 124 | Maxwell::StencilOp ActionDepthPass() const noexcept { |
| 180 | return UnpackCullFace(cull_face.Value()); | 125 | return UnpackStencilOp(action_depth_pass); |
| 181 | } | 126 | } |
| 182 | 127 | ||
| 183 | Maxwell::FrontFace FrontFace() const noexcept { | 128 | Maxwell::ComparisonOp TestFunc() const noexcept { |
| 184 | return UnpackFrontFace(front_face.Value()); | 129 | return UnpackComparisonOp(test_func); |
| 185 | } | 130 | } |
| 186 | }; | 131 | }; |
| 187 | 132 | ||
| 188 | struct DepthStencil { | 133 | union VertexBinding { |
| 189 | template <std::size_t Position> | 134 | u16 raw; |
| 190 | union StencilFace { | 135 | BitField<0, 12, u16> stride; |
| 191 | BitField<Position + 0, 3, u32> action_stencil_fail; | 136 | BitField<12, 1, u16> enabled; |
| 192 | BitField<Position + 3, 3, u32> action_depth_fail; | 137 | }; |
| 193 | BitField<Position + 6, 3, u32> action_depth_pass; | ||
| 194 | BitField<Position + 9, 3, u32> test_func; | ||
| 195 | |||
| 196 | Maxwell::StencilOp ActionStencilFail() const noexcept { | ||
| 197 | return UnpackStencilOp(action_stencil_fail); | ||
| 198 | } | ||
| 199 | |||
| 200 | Maxwell::StencilOp ActionDepthFail() const noexcept { | ||
| 201 | return UnpackStencilOp(action_depth_fail); | ||
| 202 | } | ||
| 203 | |||
| 204 | Maxwell::StencilOp ActionDepthPass() const noexcept { | ||
| 205 | return UnpackStencilOp(action_depth_pass); | ||
| 206 | } | ||
| 207 | |||
| 208 | Maxwell::ComparisonOp TestFunc() const noexcept { | ||
| 209 | return UnpackComparisonOp(test_func); | ||
| 210 | } | ||
| 211 | }; | ||
| 212 | 138 | ||
| 139 | struct DynamicState { | ||
| 213 | union { | 140 | union { |
| 214 | u32 raw; | 141 | u32 raw1; |
| 215 | StencilFace<0> front; | 142 | StencilFace<0> front; |
| 216 | StencilFace<12> back; | 143 | StencilFace<12> back; |
| 217 | BitField<24, 1, u32> depth_test_enable; | 144 | BitField<24, 1, u32> stencil_enable; |
| 218 | BitField<25, 1, u32> depth_write_enable; | 145 | BitField<25, 1, u32> depth_write_enable; |
| 219 | BitField<26, 1, u32> depth_bounds_enable; | 146 | BitField<26, 1, u32> depth_bounds_enable; |
| 220 | BitField<27, 1, u32> stencil_enable; | 147 | BitField<27, 1, u32> depth_test_enable; |
| 221 | BitField<28, 3, u32> depth_test_func; | 148 | BitField<28, 1, u32> front_face; |
| 149 | BitField<29, 3, u32> depth_test_func; | ||
| 150 | }; | ||
| 151 | union { | ||
| 152 | u32 raw2; | ||
| 153 | BitField<0, 4, u32> topology; | ||
| 154 | BitField<4, 2, u32> cull_face; | ||
| 155 | BitField<6, 1, u32> cull_enable; | ||
| 222 | }; | 156 | }; |
| 157 | std::array<VertexBinding, Maxwell::NumVertexArrays> vertex_bindings; | ||
| 223 | 158 | ||
| 224 | void Fill(const Maxwell& regs) noexcept; | 159 | void Fill(const Maxwell& regs); |
| 225 | 160 | ||
| 226 | Maxwell::ComparisonOp DepthTestFunc() const noexcept { | 161 | Maxwell::ComparisonOp DepthTestFunc() const noexcept { |
| 227 | return UnpackComparisonOp(depth_test_func); | 162 | return UnpackComparisonOp(depth_test_func); |
| 228 | } | 163 | } |
| 229 | }; | ||
| 230 | |||
| 231 | struct ColorBlending { | ||
| 232 | std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments; | ||
| 233 | 164 | ||
| 234 | void Fill(const Maxwell& regs) noexcept; | 165 | Maxwell::CullFace CullFace() const noexcept { |
| 235 | }; | 166 | return UnpackCullFace(cull_face.Value()); |
| 167 | } | ||
| 236 | 168 | ||
| 237 | struct ViewportSwizzles { | 169 | Maxwell::FrontFace FrontFace() const noexcept { |
| 238 | std::array<u16, Maxwell::NumViewports> swizzles; | 170 | return UnpackFrontFace(front_face.Value()); |
| 171 | } | ||
| 239 | 172 | ||
| 240 | void Fill(const Maxwell& regs) noexcept; | 173 | constexpr Maxwell::PrimitiveTopology Topology() const noexcept { |
| 174 | return static_cast<Maxwell::PrimitiveTopology>(topology.Value()); | ||
| 175 | } | ||
| 241 | }; | 176 | }; |
| 242 | 177 | ||
| 243 | VertexInput vertex_input; | 178 | union { |
| 244 | Rasterizer rasterizer; | 179 | u32 raw; |
| 245 | DepthStencil depth_stencil; | 180 | BitField<0, 1, u32> no_extended_dynamic_state; |
| 246 | ColorBlending color_blending; | 181 | BitField<2, 1, u32> primitive_restart_enable; |
| 247 | ViewportSwizzles viewport_swizzles; | 182 | BitField<3, 1, u32> depth_bias_enable; |
| 183 | BitField<4, 1, u32> depth_clamp_disabled; | ||
| 184 | BitField<5, 1, u32> ndc_minus_one_to_one; | ||
| 185 | BitField<6, 2, u32> polygon_mode; | ||
| 186 | BitField<8, 5, u32> patch_control_points_minus_one; | ||
| 187 | BitField<13, 2, u32> tessellation_primitive; | ||
| 188 | BitField<15, 2, u32> tessellation_spacing; | ||
| 189 | BitField<17, 1, u32> tessellation_clockwise; | ||
| 190 | BitField<18, 1, u32> logic_op_enable; | ||
| 191 | BitField<19, 4, u32> logic_op; | ||
| 192 | BitField<23, 1, u32> rasterize_enable; | ||
| 193 | }; | ||
| 194 | u32 point_size; | ||
| 195 | std::array<u32, Maxwell::NumVertexArrays> binding_divisors; | ||
| 196 | std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes; | ||
| 197 | std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments; | ||
| 198 | std::array<u16, Maxwell::NumViewports> viewport_swizzles; | ||
| 199 | DynamicState dynamic_state; | ||
| 248 | 200 | ||
| 249 | void Fill(const Maxwell& regs); | 201 | void Fill(const Maxwell& regs, bool has_extended_dynamic_state); |
| 250 | 202 | ||
| 251 | std::size_t Hash() const noexcept; | 203 | std::size_t Hash() const noexcept; |
| 252 | 204 | ||
| @@ -255,6 +207,11 @@ struct FixedPipelineState { | |||
| 255 | bool operator!=(const FixedPipelineState& rhs) const noexcept { | 207 | bool operator!=(const FixedPipelineState& rhs) const noexcept { |
| 256 | return !operator==(rhs); | 208 | return !operator==(rhs); |
| 257 | } | 209 | } |
| 210 | |||
| 211 | std::size_t Size() const noexcept { | ||
| 212 | const std::size_t total_size = sizeof *this; | ||
| 213 | return total_size - (no_extended_dynamic_state != 0 ? 0 : sizeof(DynamicState)); | ||
| 214 | } | ||
| 258 | }; | 215 | }; |
| 259 | static_assert(std::has_unique_object_representations_v<FixedPipelineState>); | 216 | static_assert(std::has_unique_object_representations_v<FixedPipelineState>); |
| 260 | static_assert(std::is_trivially_copyable_v<FixedPipelineState>); | 217 | static_assert(std::is_trivially_copyable_v<FixedPipelineState>); |
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 1f2b6734b..d7f1ae89f 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp | |||
| @@ -294,6 +294,28 @@ VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const VKDevice& device, | |||
| 294 | 294 | ||
| 295 | VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) { | 295 | VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) { |
| 296 | switch (type) { | 296 | switch (type) { |
| 297 | case Maxwell::VertexAttribute::Type::UnsignedNorm: | ||
| 298 | switch (size) { | ||
| 299 | case Maxwell::VertexAttribute::Size::Size_8: | ||
| 300 | return VK_FORMAT_R8_UNORM; | ||
| 301 | case Maxwell::VertexAttribute::Size::Size_8_8: | ||
| 302 | return VK_FORMAT_R8G8_UNORM; | ||
| 303 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | ||
| 304 | return VK_FORMAT_R8G8B8_UNORM; | ||
| 305 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | ||
| 306 | return VK_FORMAT_R8G8B8A8_UNORM; | ||
| 307 | case Maxwell::VertexAttribute::Size::Size_16: | ||
| 308 | return VK_FORMAT_R16_UNORM; | ||
| 309 | case Maxwell::VertexAttribute::Size::Size_16_16: | ||
| 310 | return VK_FORMAT_R16G16_UNORM; | ||
| 311 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | ||
| 312 | return VK_FORMAT_R16G16B16_UNORM; | ||
| 313 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | ||
| 314 | return VK_FORMAT_R16G16B16A16_UNORM; | ||
| 315 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | ||
| 316 | return VK_FORMAT_A2B10G10R10_UNORM_PACK32; | ||
| 317 | } | ||
| 318 | break; | ||
| 297 | case Maxwell::VertexAttribute::Type::SignedNorm: | 319 | case Maxwell::VertexAttribute::Type::SignedNorm: |
| 298 | switch (size) { | 320 | switch (size) { |
| 299 | case Maxwell::VertexAttribute::Size::Size_8: | 321 | case Maxwell::VertexAttribute::Size::Size_8: |
| @@ -314,62 +336,50 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib | |||
| 314 | return VK_FORMAT_R16G16B16A16_SNORM; | 336 | return VK_FORMAT_R16G16B16A16_SNORM; |
| 315 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | 337 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 316 | return VK_FORMAT_A2B10G10R10_SNORM_PACK32; | 338 | return VK_FORMAT_A2B10G10R10_SNORM_PACK32; |
| 317 | default: | ||
| 318 | break; | ||
| 319 | } | 339 | } |
| 320 | break; | 340 | break; |
| 321 | case Maxwell::VertexAttribute::Type::UnsignedNorm: | 341 | case Maxwell::VertexAttribute::Type::UnsignedScaled: |
| 322 | switch (size) { | 342 | switch (size) { |
| 323 | case Maxwell::VertexAttribute::Size::Size_8: | 343 | case Maxwell::VertexAttribute::Size::Size_8: |
| 324 | return VK_FORMAT_R8_UNORM; | 344 | return VK_FORMAT_R8_USCALED; |
| 325 | case Maxwell::VertexAttribute::Size::Size_8_8: | 345 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| 326 | return VK_FORMAT_R8G8_UNORM; | 346 | return VK_FORMAT_R8G8_USCALED; |
| 327 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | 347 | case Maxwell::VertexAttribute::Size::Size_8_8_8: |
| 328 | return VK_FORMAT_R8G8B8_UNORM; | 348 | return VK_FORMAT_R8G8B8_USCALED; |
| 329 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | 349 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: |
| 330 | return VK_FORMAT_R8G8B8A8_UNORM; | 350 | return VK_FORMAT_R8G8B8A8_USCALED; |
| 331 | case Maxwell::VertexAttribute::Size::Size_16: | 351 | case Maxwell::VertexAttribute::Size::Size_16: |
| 332 | return VK_FORMAT_R16_UNORM; | 352 | return VK_FORMAT_R16_USCALED; |
| 333 | case Maxwell::VertexAttribute::Size::Size_16_16: | 353 | case Maxwell::VertexAttribute::Size::Size_16_16: |
| 334 | return VK_FORMAT_R16G16_UNORM; | 354 | return VK_FORMAT_R16G16_USCALED; |
| 335 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | 355 | case Maxwell::VertexAttribute::Size::Size_16_16_16: |
| 336 | return VK_FORMAT_R16G16B16_UNORM; | 356 | return VK_FORMAT_R16G16B16_USCALED; |
| 337 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | 357 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: |
| 338 | return VK_FORMAT_R16G16B16A16_UNORM; | 358 | return VK_FORMAT_R16G16B16A16_USCALED; |
| 339 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | 359 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 340 | return VK_FORMAT_A2B10G10R10_UNORM_PACK32; | 360 | return VK_FORMAT_A2B10G10R10_USCALED_PACK32; |
| 341 | default: | ||
| 342 | break; | ||
| 343 | } | 361 | } |
| 344 | break; | 362 | break; |
| 345 | case Maxwell::VertexAttribute::Type::SignedInt: | 363 | case Maxwell::VertexAttribute::Type::SignedScaled: |
| 346 | switch (size) { | 364 | switch (size) { |
| 347 | case Maxwell::VertexAttribute::Size::Size_8: | 365 | case Maxwell::VertexAttribute::Size::Size_8: |
| 348 | return VK_FORMAT_R8_SINT; | 366 | return VK_FORMAT_R8_SSCALED; |
| 349 | case Maxwell::VertexAttribute::Size::Size_8_8: | 367 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| 350 | return VK_FORMAT_R8G8_SINT; | 368 | return VK_FORMAT_R8G8_SSCALED; |
| 351 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | 369 | case Maxwell::VertexAttribute::Size::Size_8_8_8: |
| 352 | return VK_FORMAT_R8G8B8_SINT; | 370 | return VK_FORMAT_R8G8B8_SSCALED; |
| 353 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | 371 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: |
| 354 | return VK_FORMAT_R8G8B8A8_SINT; | 372 | return VK_FORMAT_R8G8B8A8_SSCALED; |
| 355 | case Maxwell::VertexAttribute::Size::Size_16: | 373 | case Maxwell::VertexAttribute::Size::Size_16: |
| 356 | return VK_FORMAT_R16_SINT; | 374 | return VK_FORMAT_R16_SSCALED; |
| 357 | case Maxwell::VertexAttribute::Size::Size_16_16: | 375 | case Maxwell::VertexAttribute::Size::Size_16_16: |
| 358 | return VK_FORMAT_R16G16_SINT; | 376 | return VK_FORMAT_R16G16_SSCALED; |
| 359 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | 377 | case Maxwell::VertexAttribute::Size::Size_16_16_16: |
| 360 | return VK_FORMAT_R16G16B16_SINT; | 378 | return VK_FORMAT_R16G16B16_SSCALED; |
| 361 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | 379 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: |
| 362 | return VK_FORMAT_R16G16B16A16_SINT; | 380 | return VK_FORMAT_R16G16B16A16_SSCALED; |
| 363 | case Maxwell::VertexAttribute::Size::Size_32: | 381 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 364 | return VK_FORMAT_R32_SINT; | 382 | return VK_FORMAT_A2B10G10R10_SSCALED_PACK32; |
| 365 | case Maxwell::VertexAttribute::Size::Size_32_32: | ||
| 366 | return VK_FORMAT_R32G32_SINT; | ||
| 367 | case Maxwell::VertexAttribute::Size::Size_32_32_32: | ||
| 368 | return VK_FORMAT_R32G32B32_SINT; | ||
| 369 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | ||
| 370 | return VK_FORMAT_R32G32B32A32_SINT; | ||
| 371 | default: | ||
| 372 | break; | ||
| 373 | } | 383 | } |
| 374 | break; | 384 | break; |
| 375 | case Maxwell::VertexAttribute::Type::UnsignedInt: | 385 | case Maxwell::VertexAttribute::Type::UnsignedInt: |
| @@ -398,56 +408,50 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib | |||
| 398 | return VK_FORMAT_R32G32B32_UINT; | 408 | return VK_FORMAT_R32G32B32_UINT; |
| 399 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | 409 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: |
| 400 | return VK_FORMAT_R32G32B32A32_UINT; | 410 | return VK_FORMAT_R32G32B32A32_UINT; |
| 401 | default: | 411 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 402 | break; | 412 | return VK_FORMAT_A2B10G10R10_UINT_PACK32; |
| 403 | } | 413 | } |
| 404 | break; | 414 | break; |
| 405 | case Maxwell::VertexAttribute::Type::UnsignedScaled: | 415 | case Maxwell::VertexAttribute::Type::SignedInt: |
| 406 | switch (size) { | 416 | switch (size) { |
| 407 | case Maxwell::VertexAttribute::Size::Size_8: | 417 | case Maxwell::VertexAttribute::Size::Size_8: |
| 408 | return VK_FORMAT_R8_USCALED; | 418 | return VK_FORMAT_R8_SINT; |
| 409 | case Maxwell::VertexAttribute::Size::Size_8_8: | 419 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| 410 | return VK_FORMAT_R8G8_USCALED; | 420 | return VK_FORMAT_R8G8_SINT; |
| 411 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | 421 | case Maxwell::VertexAttribute::Size::Size_8_8_8: |
| 412 | return VK_FORMAT_R8G8B8_USCALED; | 422 | return VK_FORMAT_R8G8B8_SINT; |
| 413 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | 423 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: |
| 414 | return VK_FORMAT_R8G8B8A8_USCALED; | 424 | return VK_FORMAT_R8G8B8A8_SINT; |
| 415 | case Maxwell::VertexAttribute::Size::Size_16: | 425 | case Maxwell::VertexAttribute::Size::Size_16: |
| 416 | return VK_FORMAT_R16_USCALED; | 426 | return VK_FORMAT_R16_SINT; |
| 417 | case Maxwell::VertexAttribute::Size::Size_16_16: | 427 | case Maxwell::VertexAttribute::Size::Size_16_16: |
| 418 | return VK_FORMAT_R16G16_USCALED; | 428 | return VK_FORMAT_R16G16_SINT; |
| 419 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | 429 | case Maxwell::VertexAttribute::Size::Size_16_16_16: |
| 420 | return VK_FORMAT_R16G16B16_USCALED; | 430 | return VK_FORMAT_R16G16B16_SINT; |
| 421 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | 431 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: |
| 422 | return VK_FORMAT_R16G16B16A16_USCALED; | 432 | return VK_FORMAT_R16G16B16A16_SINT; |
| 423 | default: | 433 | case Maxwell::VertexAttribute::Size::Size_32: |
| 424 | break; | 434 | return VK_FORMAT_R32_SINT; |
| 435 | case Maxwell::VertexAttribute::Size::Size_32_32: | ||
| 436 | return VK_FORMAT_R32G32_SINT; | ||
| 437 | case Maxwell::VertexAttribute::Size::Size_32_32_32: | ||
| 438 | return VK_FORMAT_R32G32B32_SINT; | ||
| 439 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | ||
| 440 | return VK_FORMAT_R32G32B32A32_SINT; | ||
| 441 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | ||
| 442 | return VK_FORMAT_A2B10G10R10_SINT_PACK32; | ||
| 425 | } | 443 | } |
| 426 | break; | 444 | break; |
| 427 | case Maxwell::VertexAttribute::Type::SignedScaled: | 445 | case Maxwell::VertexAttribute::Type::Float: |
| 428 | switch (size) { | 446 | switch (size) { |
| 429 | case Maxwell::VertexAttribute::Size::Size_8: | ||
| 430 | return VK_FORMAT_R8_SSCALED; | ||
| 431 | case Maxwell::VertexAttribute::Size::Size_8_8: | ||
| 432 | return VK_FORMAT_R8G8_SSCALED; | ||
| 433 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | ||
| 434 | return VK_FORMAT_R8G8B8_SSCALED; | ||
| 435 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | ||
| 436 | return VK_FORMAT_R8G8B8A8_SSCALED; | ||
| 437 | case Maxwell::VertexAttribute::Size::Size_16: | 447 | case Maxwell::VertexAttribute::Size::Size_16: |
| 438 | return VK_FORMAT_R16_SSCALED; | 448 | return VK_FORMAT_R16_SFLOAT; |
| 439 | case Maxwell::VertexAttribute::Size::Size_16_16: | 449 | case Maxwell::VertexAttribute::Size::Size_16_16: |
| 440 | return VK_FORMAT_R16G16_SSCALED; | 450 | return VK_FORMAT_R16G16_SFLOAT; |
| 441 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | 451 | case Maxwell::VertexAttribute::Size::Size_16_16_16: |
| 442 | return VK_FORMAT_R16G16B16_SSCALED; | 452 | return VK_FORMAT_R16G16B16_SFLOAT; |
| 443 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | 453 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: |
| 444 | return VK_FORMAT_R16G16B16A16_SSCALED; | 454 | return VK_FORMAT_R16G16B16A16_SFLOAT; |
| 445 | default: | ||
| 446 | break; | ||
| 447 | } | ||
| 448 | break; | ||
| 449 | case Maxwell::VertexAttribute::Type::Float: | ||
| 450 | switch (size) { | ||
| 451 | case Maxwell::VertexAttribute::Size::Size_32: | 455 | case Maxwell::VertexAttribute::Size::Size_32: |
| 452 | return VK_FORMAT_R32_SFLOAT; | 456 | return VK_FORMAT_R32_SFLOAT; |
| 453 | case Maxwell::VertexAttribute::Size::Size_32_32: | 457 | case Maxwell::VertexAttribute::Size::Size_32_32: |
| @@ -456,16 +460,6 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib | |||
| 456 | return VK_FORMAT_R32G32B32_SFLOAT; | 460 | return VK_FORMAT_R32G32B32_SFLOAT; |
| 457 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | 461 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: |
| 458 | return VK_FORMAT_R32G32B32A32_SFLOAT; | 462 | return VK_FORMAT_R32G32B32A32_SFLOAT; |
| 459 | case Maxwell::VertexAttribute::Size::Size_16: | ||
| 460 | return VK_FORMAT_R16_SFLOAT; | ||
| 461 | case Maxwell::VertexAttribute::Size::Size_16_16: | ||
| 462 | return VK_FORMAT_R16G16_SFLOAT; | ||
| 463 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | ||
| 464 | return VK_FORMAT_R16G16B16_SFLOAT; | ||
| 465 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | ||
| 466 | return VK_FORMAT_R16G16B16A16_SFLOAT; | ||
| 467 | default: | ||
| 468 | break; | ||
| 469 | } | 463 | } |
| 470 | break; | 464 | break; |
| 471 | } | 465 | } |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index cd9673d1f..2d9b18ed9 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp | |||
| @@ -155,11 +155,31 @@ vk::Instance CreateInstance(Common::DynamicLibrary& library, vk::InstanceDispatc | |||
| 155 | } | 155 | } |
| 156 | } | 156 | } |
| 157 | 157 | ||
| 158 | static constexpr std::array layers_data{"VK_LAYER_LUNARG_standard_validation"}; | 158 | std::vector<const char*> layers; |
| 159 | vk::Span<const char*> layers = layers_data; | 159 | layers.reserve(1); |
| 160 | if (!enable_layers) { | 160 | if (enable_layers) { |
| 161 | layers = {}; | 161 | layers.push_back("VK_LAYER_KHRONOS_validation"); |
| 162 | } | ||
| 163 | |||
| 164 | const std::optional layer_properties = vk::EnumerateInstanceLayerProperties(dld); | ||
| 165 | if (!layer_properties) { | ||
| 166 | LOG_ERROR(Render_Vulkan, "Failed to query layer properties, disabling layers"); | ||
| 167 | layers.clear(); | ||
| 168 | } | ||
| 169 | |||
| 170 | for (auto layer_it = layers.begin(); layer_it != layers.end();) { | ||
| 171 | const char* const layer = *layer_it; | ||
| 172 | const auto it = std::find_if( | ||
| 173 | layer_properties->begin(), layer_properties->end(), | ||
| 174 | [layer](const VkLayerProperties& prop) { return !std::strcmp(layer, prop.layerName); }); | ||
| 175 | if (it == layer_properties->end()) { | ||
| 176 | LOG_ERROR(Render_Vulkan, "Layer {} not available, removing it", layer); | ||
| 177 | layer_it = layers.erase(layer_it); | ||
| 178 | } else { | ||
| 179 | ++layer_it; | ||
| 180 | } | ||
| 162 | } | 181 | } |
| 182 | |||
| 163 | vk::Instance instance = vk::Instance::Create(layers, extensions, dld); | 183 | vk::Instance instance = vk::Instance::Create(layers, extensions, dld); |
| 164 | if (!instance) { | 184 | if (!instance) { |
| 165 | LOG_ERROR(Render_Vulkan, "Failed to create Vulkan instance"); | 185 | LOG_ERROR(Render_Vulkan, "Failed to create Vulkan instance"); |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index f10f96cd8..2be38d419 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp | |||
| @@ -56,7 +56,7 @@ Buffer::Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VKSchedu | |||
| 56 | 56 | ||
| 57 | Buffer::~Buffer() = default; | 57 | Buffer::~Buffer() = default; |
| 58 | 58 | ||
| 59 | void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) const { | 59 | void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) { |
| 60 | const auto& staging = staging_pool.GetUnusedBuffer(size, true); | 60 | const auto& staging = staging_pool.GetUnusedBuffer(size, true); |
| 61 | std::memcpy(staging.commit->Map(size), data, size); | 61 | std::memcpy(staging.commit->Map(size), data, size); |
| 62 | 62 | ||
| @@ -81,7 +81,7 @@ void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) const | |||
| 81 | }); | 81 | }); |
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | void Buffer::Download(std::size_t offset, std::size_t size, u8* data) const { | 84 | void Buffer::Download(std::size_t offset, std::size_t size, u8* data) { |
| 85 | const auto& staging = staging_pool.GetUnusedBuffer(size, true); | 85 | const auto& staging = staging_pool.GetUnusedBuffer(size, true); |
| 86 | scheduler.RequestOutsideRenderPassOperationContext(); | 86 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 87 | 87 | ||
| @@ -110,7 +110,7 @@ void Buffer::Download(std::size_t offset, std::size_t size, u8* data) const { | |||
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, | 112 | void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, |
| 113 | std::size_t size) const { | 113 | std::size_t size) { |
| 114 | scheduler.RequestOutsideRenderPassOperationContext(); | 114 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 115 | 115 | ||
| 116 | const VkBuffer dst_buffer = Handle(); | 116 | const VkBuffer dst_buffer = Handle(); |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 3630aca77..991ee451c 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h | |||
| @@ -29,12 +29,12 @@ public: | |||
| 29 | VKStagingBufferPool& staging_pool, VAddr cpu_addr, std::size_t size); | 29 | VKStagingBufferPool& staging_pool, VAddr cpu_addr, std::size_t size); |
| 30 | ~Buffer(); | 30 | ~Buffer(); |
| 31 | 31 | ||
| 32 | void Upload(std::size_t offset, std::size_t size, const u8* data) const; | 32 | void Upload(std::size_t offset, std::size_t size, const u8* data); |
| 33 | 33 | ||
| 34 | void Download(std::size_t offset, std::size_t size, u8* data) const; | 34 | void Download(std::size_t offset, std::size_t size, u8* data); |
| 35 | 35 | ||
| 36 | void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, | 36 | void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, |
| 37 | std::size_t size) const; | 37 | std::size_t size); |
| 38 | 38 | ||
| 39 | VkBuffer Handle() const { | 39 | VkBuffer Handle() const { |
| 40 | return *buffer.handle; | 40 | return *buffer.handle; |
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp index 9fd8ac3f6..fdaea4210 100644 --- a/src/video_core/renderer_vulkan/vk_device.cpp +++ b/src/video_core/renderer_vulkan/vk_device.cpp | |||
| @@ -313,6 +313,16 @@ bool VKDevice::Create() { | |||
| 313 | LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors"); | 313 | LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors"); |
| 314 | } | 314 | } |
| 315 | 315 | ||
| 316 | VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state; | ||
| 317 | if (ext_extended_dynamic_state) { | ||
| 318 | dynamic_state.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT; | ||
| 319 | dynamic_state.pNext = nullptr; | ||
| 320 | dynamic_state.extendedDynamicState = VK_TRUE; | ||
| 321 | SetNext(next, dynamic_state); | ||
| 322 | } else { | ||
| 323 | LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state"); | ||
| 324 | } | ||
| 325 | |||
| 316 | if (!ext_depth_range_unrestricted) { | 326 | if (!ext_depth_range_unrestricted) { |
| 317 | LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); | 327 | LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); |
| 318 | } | 328 | } |
| @@ -541,6 +551,7 @@ std::vector<const char*> VKDevice::LoadExtensions() { | |||
| 541 | bool has_ext_subgroup_size_control{}; | 551 | bool has_ext_subgroup_size_control{}; |
| 542 | bool has_ext_transform_feedback{}; | 552 | bool has_ext_transform_feedback{}; |
| 543 | bool has_ext_custom_border_color{}; | 553 | bool has_ext_custom_border_color{}; |
| 554 | bool has_ext_extended_dynamic_state{}; | ||
| 544 | for (const auto& extension : physical.EnumerateDeviceExtensionProperties()) { | 555 | for (const auto& extension : physical.EnumerateDeviceExtensionProperties()) { |
| 545 | Test(extension, nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true); | 556 | Test(extension, nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true); |
| 546 | Test(extension, khr_uniform_buffer_standard_layout, | 557 | Test(extension, khr_uniform_buffer_standard_layout, |
| @@ -558,6 +569,8 @@ std::vector<const char*> VKDevice::LoadExtensions() { | |||
| 558 | false); | 569 | false); |
| 559 | Test(extension, has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, | 570 | Test(extension, has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, |
| 560 | false); | 571 | false); |
| 572 | Test(extension, has_ext_extended_dynamic_state, | ||
| 573 | VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false); | ||
| 561 | if (Settings::values.renderer_debug) { | 574 | if (Settings::values.renderer_debug) { |
| 562 | Test(extension, nv_device_diagnostics_config, | 575 | Test(extension, nv_device_diagnostics_config, |
| 563 | VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true); | 576 | VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true); |
| @@ -643,6 +656,19 @@ std::vector<const char*> VKDevice::LoadExtensions() { | |||
| 643 | } | 656 | } |
| 644 | } | 657 | } |
| 645 | 658 | ||
| 659 | if (has_ext_extended_dynamic_state) { | ||
| 660 | VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state; | ||
| 661 | dynamic_state.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT; | ||
| 662 | dynamic_state.pNext = nullptr; | ||
| 663 | features.pNext = &dynamic_state; | ||
| 664 | physical.GetFeatures2KHR(features); | ||
| 665 | |||
| 666 | if (dynamic_state.extendedDynamicState) { | ||
| 667 | extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); | ||
| 668 | ext_extended_dynamic_state = true; | ||
| 669 | } | ||
| 670 | } | ||
| 671 | |||
| 646 | return extensions; | 672 | return extensions; |
| 647 | } | 673 | } |
| 648 | 674 | ||
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h index 6b9227b09..ae5c21baa 100644 --- a/src/video_core/renderer_vulkan/vk_device.h +++ b/src/video_core/renderer_vulkan/vk_device.h | |||
| @@ -182,6 +182,11 @@ public: | |||
| 182 | return ext_custom_border_color; | 182 | return ext_custom_border_color; |
| 183 | } | 183 | } |
| 184 | 184 | ||
| 185 | /// Returns true if the device supports VK_EXT_extended_dynamic_state. | ||
| 186 | bool IsExtExtendedDynamicStateSupported() const { | ||
| 187 | return ext_extended_dynamic_state; | ||
| 188 | } | ||
| 189 | |||
| 185 | /// Returns the vendor name reported from Vulkan. | 190 | /// Returns the vendor name reported from Vulkan. |
| 186 | std::string_view GetVendorName() const { | 191 | std::string_view GetVendorName() const { |
| 187 | return vendor_name; | 192 | return vendor_name; |
| @@ -239,6 +244,7 @@ private: | |||
| 239 | bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. | 244 | bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. |
| 240 | bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. | 245 | bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. |
| 241 | bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color. | 246 | bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color. |
| 247 | bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state. | ||
| 242 | bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. | 248 | bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. |
| 243 | 249 | ||
| 244 | // Telemetry parameters | 250 | // Telemetry parameters |
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 69b6bba00..844445105 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp | |||
| @@ -176,20 +176,32 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules( | |||
| 176 | 176 | ||
| 177 | vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params, | 177 | vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params, |
| 178 | const SPIRVProgram& program) const { | 178 | const SPIRVProgram& program) const { |
| 179 | const auto& vi = fixed_state.vertex_input; | 179 | const auto& state = fixed_state; |
| 180 | const auto& ds = fixed_state.depth_stencil; | 180 | const auto& viewport_swizzles = state.viewport_swizzles; |
| 181 | const auto& cd = fixed_state.color_blending; | 181 | |
| 182 | const auto& rs = fixed_state.rasterizer; | 182 | FixedPipelineState::DynamicState dynamic; |
| 183 | const auto& viewport_swizzles = fixed_state.viewport_swizzles.swizzles; | 183 | if (device.IsExtExtendedDynamicStateSupported()) { |
| 184 | // Insert dummy values, as long as they are valid they don't matter as extended dynamic | ||
| 185 | // state is ignored | ||
| 186 | dynamic.raw1 = 0; | ||
| 187 | dynamic.raw2 = 0; | ||
| 188 | for (FixedPipelineState::VertexBinding& binding : dynamic.vertex_bindings) { | ||
| 189 | // Enable all vertex bindings | ||
| 190 | binding.raw = 0; | ||
| 191 | binding.enabled.Assign(1); | ||
| 192 | } | ||
| 193 | } else { | ||
| 194 | dynamic = state.dynamic_state; | ||
| 195 | } | ||
| 184 | 196 | ||
| 185 | std::vector<VkVertexInputBindingDescription> vertex_bindings; | 197 | std::vector<VkVertexInputBindingDescription> vertex_bindings; |
| 186 | std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors; | 198 | std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors; |
| 187 | for (std::size_t index = 0; index < std::size(vi.bindings); ++index) { | 199 | for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { |
| 188 | const auto& binding = vi.bindings[index]; | 200 | const auto& binding = dynamic.vertex_bindings[index]; |
| 189 | if (!binding.enabled) { | 201 | if (!binding.enabled) { |
| 190 | continue; | 202 | continue; |
| 191 | } | 203 | } |
| 192 | const bool instanced = vi.binding_divisors[index] != 0; | 204 | const bool instanced = state.binding_divisors[index] != 0; |
| 193 | const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; | 205 | const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; |
| 194 | 206 | ||
| 195 | auto& vertex_binding = vertex_bindings.emplace_back(); | 207 | auto& vertex_binding = vertex_bindings.emplace_back(); |
| @@ -200,14 +212,14 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa | |||
| 200 | if (instanced) { | 212 | if (instanced) { |
| 201 | auto& binding_divisor = vertex_binding_divisors.emplace_back(); | 213 | auto& binding_divisor = vertex_binding_divisors.emplace_back(); |
| 202 | binding_divisor.binding = static_cast<u32>(index); | 214 | binding_divisor.binding = static_cast<u32>(index); |
| 203 | binding_divisor.divisor = vi.binding_divisors[index]; | 215 | binding_divisor.divisor = state.binding_divisors[index]; |
| 204 | } | 216 | } |
| 205 | } | 217 | } |
| 206 | 218 | ||
| 207 | std::vector<VkVertexInputAttributeDescription> vertex_attributes; | 219 | std::vector<VkVertexInputAttributeDescription> vertex_attributes; |
| 208 | const auto& input_attributes = program[0]->entries.attributes; | 220 | const auto& input_attributes = program[0]->entries.attributes; |
| 209 | for (std::size_t index = 0; index < std::size(vi.attributes); ++index) { | 221 | for (std::size_t index = 0; index < state.attributes.size(); ++index) { |
| 210 | const auto& attribute = vi.attributes[index]; | 222 | const auto& attribute = state.attributes[index]; |
| 211 | if (!attribute.enabled) { | 223 | if (!attribute.enabled) { |
| 212 | continue; | 224 | continue; |
| 213 | } | 225 | } |
| @@ -244,15 +256,15 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa | |||
| 244 | input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; | 256 | input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; |
| 245 | input_assembly_ci.pNext = nullptr; | 257 | input_assembly_ci.pNext = nullptr; |
| 246 | input_assembly_ci.flags = 0; | 258 | input_assembly_ci.flags = 0; |
| 247 | input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, rs.Topology()); | 259 | input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, dynamic.Topology()); |
| 248 | input_assembly_ci.primitiveRestartEnable = | 260 | input_assembly_ci.primitiveRestartEnable = |
| 249 | rs.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology); | 261 | state.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology); |
| 250 | 262 | ||
| 251 | VkPipelineTessellationStateCreateInfo tessellation_ci; | 263 | VkPipelineTessellationStateCreateInfo tessellation_ci; |
| 252 | tessellation_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; | 264 | tessellation_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; |
| 253 | tessellation_ci.pNext = nullptr; | 265 | tessellation_ci.pNext = nullptr; |
| 254 | tessellation_ci.flags = 0; | 266 | tessellation_ci.flags = 0; |
| 255 | tessellation_ci.patchControlPoints = rs.patch_control_points_minus_one.Value() + 1; | 267 | tessellation_ci.patchControlPoints = state.patch_control_points_minus_one.Value() + 1; |
| 256 | 268 | ||
| 257 | VkPipelineViewportStateCreateInfo viewport_ci; | 269 | VkPipelineViewportStateCreateInfo viewport_ci; |
| 258 | viewport_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; | 270 | viewport_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; |
| @@ -280,13 +292,13 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa | |||
| 280 | rasterization_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; | 292 | rasterization_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; |
| 281 | rasterization_ci.pNext = nullptr; | 293 | rasterization_ci.pNext = nullptr; |
| 282 | rasterization_ci.flags = 0; | 294 | rasterization_ci.flags = 0; |
| 283 | rasterization_ci.depthClampEnable = rs.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE; | 295 | rasterization_ci.depthClampEnable = state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE; |
| 284 | rasterization_ci.rasterizerDiscardEnable = rs.rasterize_enable == 0 ? VK_TRUE : VK_FALSE; | 296 | rasterization_ci.rasterizerDiscardEnable = state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE; |
| 285 | rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL; | 297 | rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL; |
| 286 | rasterization_ci.cullMode = | 298 | rasterization_ci.cullMode = |
| 287 | rs.cull_enable ? MaxwellToVK::CullFace(rs.CullFace()) : VK_CULL_MODE_NONE; | 299 | dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE; |
| 288 | rasterization_ci.frontFace = MaxwellToVK::FrontFace(rs.FrontFace()); | 300 | rasterization_ci.frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()); |
| 289 | rasterization_ci.depthBiasEnable = rs.depth_bias_enable; | 301 | rasterization_ci.depthBiasEnable = state.depth_bias_enable; |
| 290 | rasterization_ci.depthBiasConstantFactor = 0.0f; | 302 | rasterization_ci.depthBiasConstantFactor = 0.0f; |
| 291 | rasterization_ci.depthBiasClamp = 0.0f; | 303 | rasterization_ci.depthBiasClamp = 0.0f; |
| 292 | rasterization_ci.depthBiasSlopeFactor = 0.0f; | 304 | rasterization_ci.depthBiasSlopeFactor = 0.0f; |
| @@ -307,14 +319,15 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa | |||
| 307 | depth_stencil_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; | 319 | depth_stencil_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; |
| 308 | depth_stencil_ci.pNext = nullptr; | 320 | depth_stencil_ci.pNext = nullptr; |
| 309 | depth_stencil_ci.flags = 0; | 321 | depth_stencil_ci.flags = 0; |
| 310 | depth_stencil_ci.depthTestEnable = ds.depth_test_enable; | 322 | depth_stencil_ci.depthTestEnable = dynamic.depth_test_enable; |
| 311 | depth_stencil_ci.depthWriteEnable = ds.depth_write_enable; | 323 | depth_stencil_ci.depthWriteEnable = dynamic.depth_write_enable; |
| 312 | depth_stencil_ci.depthCompareOp = | 324 | depth_stencil_ci.depthCompareOp = dynamic.depth_test_enable |
| 313 | ds.depth_test_enable ? MaxwellToVK::ComparisonOp(ds.DepthTestFunc()) : VK_COMPARE_OP_ALWAYS; | 325 | ? MaxwellToVK::ComparisonOp(dynamic.DepthTestFunc()) |
| 314 | depth_stencil_ci.depthBoundsTestEnable = ds.depth_bounds_enable; | 326 | : VK_COMPARE_OP_ALWAYS; |
| 315 | depth_stencil_ci.stencilTestEnable = ds.stencil_enable; | 327 | depth_stencil_ci.depthBoundsTestEnable = dynamic.depth_bounds_enable; |
| 316 | depth_stencil_ci.front = GetStencilFaceState(ds.front); | 328 | depth_stencil_ci.stencilTestEnable = dynamic.stencil_enable; |
| 317 | depth_stencil_ci.back = GetStencilFaceState(ds.back); | 329 | depth_stencil_ci.front = GetStencilFaceState(dynamic.front); |
| 330 | depth_stencil_ci.back = GetStencilFaceState(dynamic.back); | ||
| 318 | depth_stencil_ci.minDepthBounds = 0.0f; | 331 | depth_stencil_ci.minDepthBounds = 0.0f; |
| 319 | depth_stencil_ci.maxDepthBounds = 0.0f; | 332 | depth_stencil_ci.maxDepthBounds = 0.0f; |
| 320 | 333 | ||
| @@ -324,7 +337,7 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa | |||
| 324 | static constexpr std::array COMPONENT_TABLE = { | 337 | static constexpr std::array COMPONENT_TABLE = { |
| 325 | VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT, | 338 | VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT, |
| 326 | VK_COLOR_COMPONENT_A_BIT}; | 339 | VK_COLOR_COMPONENT_A_BIT}; |
| 327 | const auto& blend = cd.attachments[index]; | 340 | const auto& blend = state.attachments[index]; |
| 328 | 341 | ||
| 329 | VkColorComponentFlags color_components = 0; | 342 | VkColorComponentFlags color_components = 0; |
| 330 | for (std::size_t i = 0; i < COMPONENT_TABLE.size(); ++i) { | 343 | for (std::size_t i = 0; i < COMPONENT_TABLE.size(); ++i) { |
| @@ -354,11 +367,27 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa | |||
| 354 | color_blend_ci.pAttachments = cb_attachments.data(); | 367 | color_blend_ci.pAttachments = cb_attachments.data(); |
| 355 | std::memset(color_blend_ci.blendConstants, 0, sizeof(color_blend_ci.blendConstants)); | 368 | std::memset(color_blend_ci.blendConstants, 0, sizeof(color_blend_ci.blendConstants)); |
| 356 | 369 | ||
| 357 | static constexpr std::array dynamic_states = { | 370 | std::vector dynamic_states = { |
| 358 | VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, | 371 | VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, |
| 359 | VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, | 372 | VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, |
| 360 | VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, | 373 | VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, |
| 361 | VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE}; | 374 | VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, |
| 375 | }; | ||
| 376 | if (device.IsExtExtendedDynamicStateSupported()) { | ||
| 377 | static constexpr std::array extended = { | ||
| 378 | VK_DYNAMIC_STATE_CULL_MODE_EXT, | ||
| 379 | VK_DYNAMIC_STATE_FRONT_FACE_EXT, | ||
| 380 | VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT, | ||
| 381 | VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT, | ||
| 382 | VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT, | ||
| 383 | VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT, | ||
| 384 | VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT, | ||
| 385 | VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT, | ||
| 386 | VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT, | ||
| 387 | VK_DYNAMIC_STATE_STENCIL_OP_EXT, | ||
| 388 | }; | ||
| 389 | dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end()); | ||
| 390 | } | ||
| 362 | 391 | ||
| 363 | VkPipelineDynamicStateCreateInfo dynamic_state_ci; | 392 | VkPipelineDynamicStateCreateInfo dynamic_state_ci; |
| 364 | dynamic_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; | 393 | dynamic_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index ea66e621e..3da835324 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -116,12 +116,12 @@ u32 FillDescriptorLayout(const ShaderEntries& entries, | |||
| 116 | } // Anonymous namespace | 116 | } // Anonymous namespace |
| 117 | 117 | ||
| 118 | std::size_t GraphicsPipelineCacheKey::Hash() const noexcept { | 118 | std::size_t GraphicsPipelineCacheKey::Hash() const noexcept { |
| 119 | const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this); | 119 | const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size()); |
| 120 | return static_cast<std::size_t>(hash); | 120 | return static_cast<std::size_t>(hash); |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | bool GraphicsPipelineCacheKey::operator==(const GraphicsPipelineCacheKey& rhs) const noexcept { | 123 | bool GraphicsPipelineCacheKey::operator==(const GraphicsPipelineCacheKey& rhs) const noexcept { |
| 124 | return std::memcmp(&rhs, this, sizeof *this) == 0; | 124 | return std::memcmp(&rhs, this, Size()) == 0; |
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | std::size_t ComputePipelineCacheKey::Hash() const noexcept { | 127 | std::size_t ComputePipelineCacheKey::Hash() const noexcept { |
| @@ -312,18 +312,19 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) { | |||
| 312 | const auto& gpu = system.GPU().Maxwell3D(); | 312 | const auto& gpu = system.GPU().Maxwell3D(); |
| 313 | 313 | ||
| 314 | Specialization specialization; | 314 | Specialization specialization; |
| 315 | if (fixed_state.rasterizer.Topology() == Maxwell::PrimitiveTopology::Points) { | 315 | if (fixed_state.dynamic_state.Topology() == Maxwell::PrimitiveTopology::Points || |
| 316 | device.IsExtExtendedDynamicStateSupported()) { | ||
| 316 | float point_size; | 317 | float point_size; |
| 317 | std::memcpy(&point_size, &fixed_state.rasterizer.point_size, sizeof(float)); | 318 | std::memcpy(&point_size, &fixed_state.point_size, sizeof(float)); |
| 318 | specialization.point_size = point_size; | 319 | specialization.point_size = point_size; |
| 319 | ASSERT(point_size != 0.0f); | 320 | ASSERT(point_size != 0.0f); |
| 320 | } | 321 | } |
| 321 | for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) { | 322 | for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) { |
| 322 | const auto& attribute = fixed_state.vertex_input.attributes[i]; | 323 | const auto& attribute = fixed_state.attributes[i]; |
| 323 | specialization.enabled_attributes[i] = attribute.enabled.Value() != 0; | 324 | specialization.enabled_attributes[i] = attribute.enabled.Value() != 0; |
| 324 | specialization.attribute_types[i] = attribute.Type(); | 325 | specialization.attribute_types[i] = attribute.Type(); |
| 325 | } | 326 | } |
| 326 | specialization.ndc_minus_one_to_one = fixed_state.rasterizer.ndc_minus_one_to_one; | 327 | specialization.ndc_minus_one_to_one = fixed_state.ndc_minus_one_to_one; |
| 327 | 328 | ||
| 328 | SPIRVProgram program; | 329 | SPIRVProgram program; |
| 329 | std::vector<VkDescriptorSetLayoutBinding> bindings; | 330 | std::vector<VkDescriptorSetLayoutBinding> bindings; |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 0a36e5112..0a3fe65fb 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h | |||
| @@ -44,10 +44,10 @@ class VKUpdateDescriptorQueue; | |||
| 44 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | 44 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| 45 | 45 | ||
| 46 | struct GraphicsPipelineCacheKey { | 46 | struct GraphicsPipelineCacheKey { |
| 47 | FixedPipelineState fixed_state; | ||
| 48 | RenderPassParams renderpass_params; | 47 | RenderPassParams renderpass_params; |
| 48 | u32 padding; | ||
| 49 | std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders; | 49 | std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders; |
| 50 | u64 padding; // This is necessary for unique object representations | 50 | FixedPipelineState fixed_state; |
| 51 | 51 | ||
| 52 | std::size_t Hash() const noexcept; | 52 | std::size_t Hash() const noexcept; |
| 53 | 53 | ||
| @@ -56,6 +56,10 @@ struct GraphicsPipelineCacheKey { | |||
| 56 | bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept { | 56 | bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept { |
| 57 | return !operator==(rhs); | 57 | return !operator==(rhs); |
| 58 | } | 58 | } |
| 59 | |||
| 60 | std::size_t Size() const noexcept { | ||
| 61 | return sizeof(renderpass_params) + sizeof(padding) + sizeof(shaders) + fixed_state.Size(); | ||
| 62 | } | ||
| 59 | }; | 63 | }; |
| 60 | static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>); | 64 | static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>); |
| 61 | static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>); | 65 | static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>); |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a8d94eac3..380ed532b 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -186,13 +186,22 @@ bool HasToPreserveDepthContents(bool is_clear, const Maxwell& regs) { | |||
| 186 | scissor.max_y < regs.zeta_height; | 186 | scissor.max_y < regs.zeta_height; |
| 187 | } | 187 | } |
| 188 | 188 | ||
| 189 | template <std::size_t N> | ||
| 190 | std::array<VkDeviceSize, N> ExpandStrides(const std::array<u16, N>& strides) { | ||
| 191 | std::array<VkDeviceSize, N> expanded; | ||
| 192 | std::copy(strides.begin(), strides.end(), expanded.begin()); | ||
| 193 | return expanded; | ||
| 194 | } | ||
| 195 | |||
| 189 | } // Anonymous namespace | 196 | } // Anonymous namespace |
| 190 | 197 | ||
| 191 | class BufferBindings final { | 198 | class BufferBindings final { |
| 192 | public: | 199 | public: |
| 193 | void AddVertexBinding(VkBuffer buffer, VkDeviceSize offset) { | 200 | void AddVertexBinding(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, u32 stride) { |
| 194 | vertex.buffers[vertex.num_buffers] = buffer; | 201 | vertex.buffers[vertex.num_buffers] = buffer; |
| 195 | vertex.offsets[vertex.num_buffers] = offset; | 202 | vertex.offsets[vertex.num_buffers] = offset; |
| 203 | vertex.sizes[vertex.num_buffers] = size; | ||
| 204 | vertex.strides[vertex.num_buffers] = static_cast<u16>(stride); | ||
| 196 | ++vertex.num_buffers; | 205 | ++vertex.num_buffers; |
| 197 | } | 206 | } |
| 198 | 207 | ||
| @@ -202,76 +211,76 @@ public: | |||
| 202 | index.type = type; | 211 | index.type = type; |
| 203 | } | 212 | } |
| 204 | 213 | ||
| 205 | void Bind(VKScheduler& scheduler) const { | 214 | void Bind(const VKDevice& device, VKScheduler& scheduler) const { |
| 206 | // Use this large switch case to avoid dispatching more memory in the record lambda than | 215 | // Use this large switch case to avoid dispatching more memory in the record lambda than |
| 207 | // what we need. It looks horrible, but it's the best we can do on standard C++. | 216 | // what we need. It looks horrible, but it's the best we can do on standard C++. |
| 208 | switch (vertex.num_buffers) { | 217 | switch (vertex.num_buffers) { |
| 209 | case 0: | 218 | case 0: |
| 210 | return BindStatic<0>(scheduler); | 219 | return BindStatic<0>(device, scheduler); |
| 211 | case 1: | 220 | case 1: |
| 212 | return BindStatic<1>(scheduler); | 221 | return BindStatic<1>(device, scheduler); |
| 213 | case 2: | 222 | case 2: |
| 214 | return BindStatic<2>(scheduler); | 223 | return BindStatic<2>(device, scheduler); |
| 215 | case 3: | 224 | case 3: |
| 216 | return BindStatic<3>(scheduler); | 225 | return BindStatic<3>(device, scheduler); |
| 217 | case 4: | 226 | case 4: |
| 218 | return BindStatic<4>(scheduler); | 227 | return BindStatic<4>(device, scheduler); |
| 219 | case 5: | 228 | case 5: |
| 220 | return BindStatic<5>(scheduler); | 229 | return BindStatic<5>(device, scheduler); |
| 221 | case 6: | 230 | case 6: |
| 222 | return BindStatic<6>(scheduler); | 231 | return BindStatic<6>(device, scheduler); |
| 223 | case 7: | 232 | case 7: |
| 224 | return BindStatic<7>(scheduler); | 233 | return BindStatic<7>(device, scheduler); |
| 225 | case 8: | 234 | case 8: |
| 226 | return BindStatic<8>(scheduler); | 235 | return BindStatic<8>(device, scheduler); |
| 227 | case 9: | 236 | case 9: |
| 228 | return BindStatic<9>(scheduler); | 237 | return BindStatic<9>(device, scheduler); |
| 229 | case 10: | 238 | case 10: |
| 230 | return BindStatic<10>(scheduler); | 239 | return BindStatic<10>(device, scheduler); |
| 231 | case 11: | 240 | case 11: |
| 232 | return BindStatic<11>(scheduler); | 241 | return BindStatic<11>(device, scheduler); |
| 233 | case 12: | 242 | case 12: |
| 234 | return BindStatic<12>(scheduler); | 243 | return BindStatic<12>(device, scheduler); |
| 235 | case 13: | 244 | case 13: |
| 236 | return BindStatic<13>(scheduler); | 245 | return BindStatic<13>(device, scheduler); |
| 237 | case 14: | 246 | case 14: |
| 238 | return BindStatic<14>(scheduler); | 247 | return BindStatic<14>(device, scheduler); |
| 239 | case 15: | 248 | case 15: |
| 240 | return BindStatic<15>(scheduler); | 249 | return BindStatic<15>(device, scheduler); |
| 241 | case 16: | 250 | case 16: |
| 242 | return BindStatic<16>(scheduler); | 251 | return BindStatic<16>(device, scheduler); |
| 243 | case 17: | 252 | case 17: |
| 244 | return BindStatic<17>(scheduler); | 253 | return BindStatic<17>(device, scheduler); |
| 245 | case 18: | 254 | case 18: |
| 246 | return BindStatic<18>(scheduler); | 255 | return BindStatic<18>(device, scheduler); |
| 247 | case 19: | 256 | case 19: |
| 248 | return BindStatic<19>(scheduler); | 257 | return BindStatic<19>(device, scheduler); |
| 249 | case 20: | 258 | case 20: |
| 250 | return BindStatic<20>(scheduler); | 259 | return BindStatic<20>(device, scheduler); |
| 251 | case 21: | 260 | case 21: |
| 252 | return BindStatic<21>(scheduler); | 261 | return BindStatic<21>(device, scheduler); |
| 253 | case 22: | 262 | case 22: |
| 254 | return BindStatic<22>(scheduler); | 263 | return BindStatic<22>(device, scheduler); |
| 255 | case 23: | 264 | case 23: |
| 256 | return BindStatic<23>(scheduler); | 265 | return BindStatic<23>(device, scheduler); |
| 257 | case 24: | 266 | case 24: |
| 258 | return BindStatic<24>(scheduler); | 267 | return BindStatic<24>(device, scheduler); |
| 259 | case 25: | 268 | case 25: |
| 260 | return BindStatic<25>(scheduler); | 269 | return BindStatic<25>(device, scheduler); |
| 261 | case 26: | 270 | case 26: |
| 262 | return BindStatic<26>(scheduler); | 271 | return BindStatic<26>(device, scheduler); |
| 263 | case 27: | 272 | case 27: |
| 264 | return BindStatic<27>(scheduler); | 273 | return BindStatic<27>(device, scheduler); |
| 265 | case 28: | 274 | case 28: |
| 266 | return BindStatic<28>(scheduler); | 275 | return BindStatic<28>(device, scheduler); |
| 267 | case 29: | 276 | case 29: |
| 268 | return BindStatic<29>(scheduler); | 277 | return BindStatic<29>(device, scheduler); |
| 269 | case 30: | 278 | case 30: |
| 270 | return BindStatic<30>(scheduler); | 279 | return BindStatic<30>(device, scheduler); |
| 271 | case 31: | 280 | case 31: |
| 272 | return BindStatic<31>(scheduler); | 281 | return BindStatic<31>(device, scheduler); |
| 273 | case 32: | 282 | case 32: |
| 274 | return BindStatic<32>(scheduler); | 283 | return BindStatic<32>(device, scheduler); |
| 275 | } | 284 | } |
| 276 | UNREACHABLE(); | 285 | UNREACHABLE(); |
| 277 | } | 286 | } |
| @@ -282,6 +291,8 @@ private: | |||
| 282 | std::size_t num_buffers = 0; | 291 | std::size_t num_buffers = 0; |
| 283 | std::array<VkBuffer, Maxwell::NumVertexArrays> buffers; | 292 | std::array<VkBuffer, Maxwell::NumVertexArrays> buffers; |
| 284 | std::array<VkDeviceSize, Maxwell::NumVertexArrays> offsets; | 293 | std::array<VkDeviceSize, Maxwell::NumVertexArrays> offsets; |
| 294 | std::array<VkDeviceSize, Maxwell::NumVertexArrays> sizes; | ||
| 295 | std::array<u16, Maxwell::NumVertexArrays> strides; | ||
| 285 | } vertex; | 296 | } vertex; |
| 286 | 297 | ||
| 287 | struct { | 298 | struct { |
| @@ -291,15 +302,23 @@ private: | |||
| 291 | } index; | 302 | } index; |
| 292 | 303 | ||
| 293 | template <std::size_t N> | 304 | template <std::size_t N> |
| 294 | void BindStatic(VKScheduler& scheduler) const { | 305 | void BindStatic(const VKDevice& device, VKScheduler& scheduler) const { |
| 295 | if (index.buffer) { | 306 | if (device.IsExtExtendedDynamicStateSupported()) { |
| 296 | BindStatic<N, true>(scheduler); | 307 | if (index.buffer) { |
| 308 | BindStatic<N, true, true>(scheduler); | ||
| 309 | } else { | ||
| 310 | BindStatic<N, false, true>(scheduler); | ||
| 311 | } | ||
| 297 | } else { | 312 | } else { |
| 298 | BindStatic<N, false>(scheduler); | 313 | if (index.buffer) { |
| 314 | BindStatic<N, true, false>(scheduler); | ||
| 315 | } else { | ||
| 316 | BindStatic<N, false, false>(scheduler); | ||
| 317 | } | ||
| 299 | } | 318 | } |
| 300 | } | 319 | } |
| 301 | 320 | ||
| 302 | template <std::size_t N, bool is_indexed> | 321 | template <std::size_t N, bool is_indexed, bool has_extended_dynamic_state> |
| 303 | void BindStatic(VKScheduler& scheduler) const { | 322 | void BindStatic(VKScheduler& scheduler) const { |
| 304 | static_assert(N <= Maxwell::NumVertexArrays); | 323 | static_assert(N <= Maxwell::NumVertexArrays); |
| 305 | if constexpr (N == 0) { | 324 | if constexpr (N == 0) { |
| @@ -311,6 +330,31 @@ private: | |||
| 311 | std::copy(vertex.buffers.begin(), vertex.buffers.begin() + N, buffers.begin()); | 330 | std::copy(vertex.buffers.begin(), vertex.buffers.begin() + N, buffers.begin()); |
| 312 | std::copy(vertex.offsets.begin(), vertex.offsets.begin() + N, offsets.begin()); | 331 | std::copy(vertex.offsets.begin(), vertex.offsets.begin() + N, offsets.begin()); |
| 313 | 332 | ||
| 333 | if constexpr (has_extended_dynamic_state) { | ||
| 334 | // With extended dynamic states we can specify the length and stride of a vertex buffer | ||
| 335 | // std::array<VkDeviceSize, N> sizes; | ||
| 336 | std::array<u16, N> strides; | ||
| 337 | // std::copy(vertex.sizes.begin(), vertex.sizes.begin() + N, sizes.begin()); | ||
| 338 | std::copy(vertex.strides.begin(), vertex.strides.begin() + N, strides.begin()); | ||
| 339 | |||
| 340 | if constexpr (is_indexed) { | ||
| 341 | scheduler.Record( | ||
| 342 | [buffers, offsets, strides, index = index](vk::CommandBuffer cmdbuf) { | ||
| 343 | cmdbuf.BindIndexBuffer(index.buffer, index.offset, index.type); | ||
| 344 | cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(), | ||
| 345 | offsets.data(), nullptr, | ||
| 346 | ExpandStrides(strides).data()); | ||
| 347 | }); | ||
| 348 | } else { | ||
| 349 | scheduler.Record([buffers, offsets, strides](vk::CommandBuffer cmdbuf) { | ||
| 350 | cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(), | ||
| 351 | offsets.data(), nullptr, | ||
| 352 | ExpandStrides(strides).data()); | ||
| 353 | }); | ||
| 354 | } | ||
| 355 | return; | ||
| 356 | } | ||
| 357 | |||
| 314 | if constexpr (is_indexed) { | 358 | if constexpr (is_indexed) { |
| 315 | // Indexed draw | 359 | // Indexed draw |
| 316 | scheduler.Record([buffers, offsets, index = index](vk::CommandBuffer cmdbuf) { | 360 | scheduler.Record([buffers, offsets, index = index](vk::CommandBuffer cmdbuf) { |
| @@ -369,7 +413,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { | |||
| 369 | 413 | ||
| 370 | const auto& gpu = system.GPU().Maxwell3D(); | 414 | const auto& gpu = system.GPU().Maxwell3D(); |
| 371 | GraphicsPipelineCacheKey key; | 415 | GraphicsPipelineCacheKey key; |
| 372 | key.fixed_state.Fill(gpu.regs); | 416 | key.fixed_state.Fill(gpu.regs, device.IsExtExtendedDynamicStateSupported()); |
| 373 | 417 | ||
| 374 | buffer_cache.Map(CalculateGraphicsStreamBufferSize(is_indexed)); | 418 | buffer_cache.Map(CalculateGraphicsStreamBufferSize(is_indexed)); |
| 375 | 419 | ||
| @@ -402,7 +446,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { | |||
| 402 | 446 | ||
| 403 | UpdateDynamicStates(); | 447 | UpdateDynamicStates(); |
| 404 | 448 | ||
| 405 | buffer_bindings.Bind(scheduler); | 449 | buffer_bindings.Bind(device, scheduler); |
| 406 | 450 | ||
| 407 | BeginTransformFeedback(); | 451 | BeginTransformFeedback(); |
| 408 | 452 | ||
| @@ -822,7 +866,7 @@ RasterizerVulkan::DrawParameters RasterizerVulkan::SetupGeometry(FixedPipelineSt | |||
| 822 | const auto& gpu = system.GPU().Maxwell3D(); | 866 | const auto& gpu = system.GPU().Maxwell3D(); |
| 823 | const auto& regs = gpu.regs; | 867 | const auto& regs = gpu.regs; |
| 824 | 868 | ||
| 825 | SetupVertexArrays(fixed_state.vertex_input, buffer_bindings); | 869 | SetupVertexArrays(buffer_bindings); |
| 826 | 870 | ||
| 827 | const u32 base_instance = regs.vb_base_instance; | 871 | const u32 base_instance = regs.vb_base_instance; |
| 828 | const u32 num_instances = is_instanced ? gpu.mme_draw.instance_count : 1; | 872 | const u32 num_instances = is_instanced ? gpu.mme_draw.instance_count : 1; |
| @@ -893,6 +937,17 @@ void RasterizerVulkan::UpdateDynamicStates() { | |||
| 893 | UpdateBlendConstants(regs); | 937 | UpdateBlendConstants(regs); |
| 894 | UpdateDepthBounds(regs); | 938 | UpdateDepthBounds(regs); |
| 895 | UpdateStencilFaces(regs); | 939 | UpdateStencilFaces(regs); |
| 940 | if (device.IsExtExtendedDynamicStateSupported()) { | ||
| 941 | UpdateCullMode(regs); | ||
| 942 | UpdateDepthBoundsTestEnable(regs); | ||
| 943 | UpdateDepthTestEnable(regs); | ||
| 944 | UpdateDepthWriteEnable(regs); | ||
| 945 | UpdateDepthCompareOp(regs); | ||
| 946 | UpdateFrontFace(regs); | ||
| 947 | UpdatePrimitiveTopology(regs); | ||
| 948 | UpdateStencilOp(regs); | ||
| 949 | UpdateStencilTestEnable(regs); | ||
| 950 | } | ||
| 896 | } | 951 | } |
| 897 | 952 | ||
| 898 | void RasterizerVulkan::BeginTransformFeedback() { | 953 | void RasterizerVulkan::BeginTransformFeedback() { |
| @@ -940,41 +995,25 @@ void RasterizerVulkan::EndTransformFeedback() { | |||
| 940 | [](vk::CommandBuffer cmdbuf) { cmdbuf.EndTransformFeedbackEXT(0, 0, nullptr, nullptr); }); | 995 | [](vk::CommandBuffer cmdbuf) { cmdbuf.EndTransformFeedbackEXT(0, 0, nullptr, nullptr); }); |
| 941 | } | 996 | } |
| 942 | 997 | ||
| 943 | void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input, | 998 | void RasterizerVulkan::SetupVertexArrays(BufferBindings& buffer_bindings) { |
| 944 | BufferBindings& buffer_bindings) { | ||
| 945 | const auto& regs = system.GPU().Maxwell3D().regs; | 999 | const auto& regs = system.GPU().Maxwell3D().regs; |
| 946 | 1000 | ||
| 947 | for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { | ||
| 948 | const auto& attrib = regs.vertex_attrib_format[index]; | ||
| 949 | if (attrib.IsConstant()) { | ||
| 950 | vertex_input.SetAttribute(index, false, 0, 0, {}, {}); | ||
| 951 | continue; | ||
| 952 | } | ||
| 953 | vertex_input.SetAttribute(index, true, attrib.buffer, attrib.offset, attrib.type.Value(), | ||
| 954 | attrib.size.Value()); | ||
| 955 | } | ||
| 956 | |||
| 957 | for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { | 1001 | for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { |
| 958 | const auto& vertex_array = regs.vertex_array[index]; | 1002 | const auto& vertex_array = regs.vertex_array[index]; |
| 959 | if (!vertex_array.IsEnabled()) { | 1003 | if (!vertex_array.IsEnabled()) { |
| 960 | vertex_input.SetBinding(index, false, 0, 0); | ||
| 961 | continue; | 1004 | continue; |
| 962 | } | 1005 | } |
| 963 | vertex_input.SetBinding( | ||
| 964 | index, true, vertex_array.stride, | ||
| 965 | regs.instanced_arrays.IsInstancingEnabled(index) ? vertex_array.divisor : 0); | ||
| 966 | |||
| 967 | const GPUVAddr start{vertex_array.StartAddress()}; | 1006 | const GPUVAddr start{vertex_array.StartAddress()}; |
| 968 | const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()}; | 1007 | const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()}; |
| 969 | 1008 | ||
| 970 | ASSERT(end >= start); | 1009 | ASSERT(end >= start); |
| 971 | const std::size_t size{end - start}; | 1010 | const std::size_t size = end - start; |
| 972 | if (size == 0) { | 1011 | if (size == 0) { |
| 973 | buffer_bindings.AddVertexBinding(DefaultBuffer(), 0); | 1012 | buffer_bindings.AddVertexBinding(DefaultBuffer(), 0, DEFAULT_BUFFER_SIZE, 0); |
| 974 | continue; | 1013 | continue; |
| 975 | } | 1014 | } |
| 976 | const auto info = buffer_cache.UploadMemory(start, size); | 1015 | const auto info = buffer_cache.UploadMemory(start, size); |
| 977 | buffer_bindings.AddVertexBinding(info.handle, info.offset); | 1016 | buffer_bindings.AddVertexBinding(info.handle, info.offset, size, vertex_array.stride); |
| 978 | } | 1017 | } |
| 979 | } | 1018 | } |
| 980 | 1019 | ||
| @@ -1326,6 +1365,117 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) | |||
| 1326 | } | 1365 | } |
| 1327 | } | 1366 | } |
| 1328 | 1367 | ||
| 1368 | void RasterizerVulkan::UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1369 | if (!state_tracker.TouchCullMode()) { | ||
| 1370 | return; | ||
| 1371 | } | ||
| 1372 | scheduler.Record( | ||
| 1373 | [enabled = regs.cull_test_enabled, cull_face = regs.cull_face](vk::CommandBuffer cmdbuf) { | ||
| 1374 | cmdbuf.SetCullModeEXT(enabled ? MaxwellToVK::CullFace(cull_face) : VK_CULL_MODE_NONE); | ||
| 1375 | }); | ||
| 1376 | } | ||
| 1377 | |||
| 1378 | void RasterizerVulkan::UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1379 | if (!state_tracker.TouchDepthBoundsTestEnable()) { | ||
| 1380 | return; | ||
| 1381 | } | ||
| 1382 | scheduler.Record([enable = regs.depth_bounds_enable](vk::CommandBuffer cmdbuf) { | ||
| 1383 | cmdbuf.SetDepthBoundsTestEnableEXT(enable); | ||
| 1384 | }); | ||
| 1385 | } | ||
| 1386 | |||
| 1387 | void RasterizerVulkan::UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1388 | if (!state_tracker.TouchDepthTestEnable()) { | ||
| 1389 | return; | ||
| 1390 | } | ||
| 1391 | scheduler.Record([enable = regs.depth_test_enable](vk::CommandBuffer cmdbuf) { | ||
| 1392 | cmdbuf.SetDepthTestEnableEXT(enable); | ||
| 1393 | }); | ||
| 1394 | } | ||
| 1395 | |||
| 1396 | void RasterizerVulkan::UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1397 | if (!state_tracker.TouchDepthWriteEnable()) { | ||
| 1398 | return; | ||
| 1399 | } | ||
| 1400 | scheduler.Record([enable = regs.depth_write_enabled](vk::CommandBuffer cmdbuf) { | ||
| 1401 | cmdbuf.SetDepthWriteEnableEXT(enable); | ||
| 1402 | }); | ||
| 1403 | } | ||
| 1404 | |||
| 1405 | void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1406 | if (!state_tracker.TouchDepthCompareOp()) { | ||
| 1407 | return; | ||
| 1408 | } | ||
| 1409 | scheduler.Record([func = regs.depth_test_func](vk::CommandBuffer cmdbuf) { | ||
| 1410 | cmdbuf.SetDepthCompareOpEXT(MaxwellToVK::ComparisonOp(func)); | ||
| 1411 | }); | ||
| 1412 | } | ||
| 1413 | |||
| 1414 | void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1415 | if (!state_tracker.TouchFrontFace()) { | ||
| 1416 | return; | ||
| 1417 | } | ||
| 1418 | |||
| 1419 | VkFrontFace front_face = MaxwellToVK::FrontFace(regs.front_face); | ||
| 1420 | if (regs.screen_y_control.triangle_rast_flip != 0) { | ||
| 1421 | front_face = front_face == VK_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_COUNTER_CLOCKWISE | ||
| 1422 | : VK_FRONT_FACE_CLOCKWISE; | ||
| 1423 | } | ||
| 1424 | scheduler.Record( | ||
| 1425 | [front_face](vk::CommandBuffer cmdbuf) { cmdbuf.SetFrontFaceEXT(front_face); }); | ||
| 1426 | } | ||
| 1427 | |||
| 1428 | void RasterizerVulkan::UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1429 | if (!state_tracker.TouchPrimitiveTopology()) { | ||
| 1430 | return; | ||
| 1431 | } | ||
| 1432 | const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value(); | ||
| 1433 | scheduler.Record([this, primitive_topology](vk::CommandBuffer cmdbuf) { | ||
| 1434 | cmdbuf.SetPrimitiveTopologyEXT(MaxwellToVK::PrimitiveTopology(device, primitive_topology)); | ||
| 1435 | }); | ||
| 1436 | } | ||
| 1437 | |||
| 1438 | void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1439 | if (!state_tracker.TouchStencilOp()) { | ||
| 1440 | return; | ||
| 1441 | } | ||
| 1442 | const Maxwell::StencilOp fail = regs.stencil_front_op_fail; | ||
| 1443 | const Maxwell::StencilOp zfail = regs.stencil_front_op_zfail; | ||
| 1444 | const Maxwell::StencilOp zpass = regs.stencil_front_op_zpass; | ||
| 1445 | const Maxwell::ComparisonOp compare = regs.stencil_front_func_func; | ||
| 1446 | if (regs.stencil_two_side_enable) { | ||
| 1447 | scheduler.Record([fail, zfail, zpass, compare](vk::CommandBuffer cmdbuf) { | ||
| 1448 | cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_AND_BACK, MaxwellToVK::StencilOp(fail), | ||
| 1449 | MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail), | ||
| 1450 | MaxwellToVK::ComparisonOp(compare)); | ||
| 1451 | }); | ||
| 1452 | } else { | ||
| 1453 | const Maxwell::StencilOp back_fail = regs.stencil_back_op_fail; | ||
| 1454 | const Maxwell::StencilOp back_zfail = regs.stencil_back_op_zfail; | ||
| 1455 | const Maxwell::StencilOp back_zpass = regs.stencil_back_op_zpass; | ||
| 1456 | const Maxwell::ComparisonOp back_compare = regs.stencil_back_func_func; | ||
| 1457 | scheduler.Record([fail, zfail, zpass, compare, back_fail, back_zfail, back_zpass, | ||
| 1458 | back_compare](vk::CommandBuffer cmdbuf) { | ||
| 1459 | cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_BIT, MaxwellToVK::StencilOp(fail), | ||
| 1460 | MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail), | ||
| 1461 | MaxwellToVK::ComparisonOp(compare)); | ||
| 1462 | cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_BACK_BIT, MaxwellToVK::StencilOp(back_fail), | ||
| 1463 | MaxwellToVK::StencilOp(back_zpass), | ||
| 1464 | MaxwellToVK::StencilOp(back_zfail), | ||
| 1465 | MaxwellToVK::ComparisonOp(back_compare)); | ||
| 1466 | }); | ||
| 1467 | } | ||
| 1468 | } | ||
| 1469 | |||
| 1470 | void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { | ||
| 1471 | if (!state_tracker.TouchStencilTestEnable()) { | ||
| 1472 | return; | ||
| 1473 | } | ||
| 1474 | scheduler.Record([enable = regs.stencil_enable](vk::CommandBuffer cmdbuf) { | ||
| 1475 | cmdbuf.SetStencilTestEnableEXT(enable); | ||
| 1476 | }); | ||
| 1477 | } | ||
| 1478 | |||
| 1329 | std::size_t RasterizerVulkan::CalculateGraphicsStreamBufferSize(bool is_indexed) const { | 1479 | std::size_t RasterizerVulkan::CalculateGraphicsStreamBufferSize(bool is_indexed) const { |
| 1330 | std::size_t size = CalculateVertexArraysSize(); | 1480 | std::size_t size = CalculateVertexArraysSize(); |
| 1331 | if (is_indexed) { | 1481 | if (is_indexed) { |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 83e00e7e9..923178b0b 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h | |||
| @@ -185,8 +185,7 @@ private: | |||
| 185 | 185 | ||
| 186 | bool WalkAttachmentOverlaps(const CachedSurfaceView& attachment); | 186 | bool WalkAttachmentOverlaps(const CachedSurfaceView& attachment); |
| 187 | 187 | ||
| 188 | void SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input, | 188 | void SetupVertexArrays(BufferBindings& buffer_bindings); |
| 189 | BufferBindings& buffer_bindings); | ||
| 190 | 189 | ||
| 191 | void SetupIndexBuffer(BufferBindings& buffer_bindings, DrawParameters& params, bool is_indexed); | 190 | void SetupIndexBuffer(BufferBindings& buffer_bindings, DrawParameters& params, bool is_indexed); |
| 192 | 191 | ||
| @@ -246,6 +245,16 @@ private: | |||
| 246 | void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs); | 245 | void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs); |
| 247 | void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs); | 246 | void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs); |
| 248 | 247 | ||
| 248 | void UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 249 | void UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 250 | void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 251 | void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 252 | void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 253 | void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 254 | void UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 255 | void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 256 | void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); | ||
| 257 | |||
| 249 | std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const; | 258 | std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const; |
| 250 | 259 | ||
| 251 | std::size_t CalculateComputeStreamBufferSize() const; | 260 | std::size_t CalculateComputeStreamBufferSize() const; |
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index 94a89e388..e5a583dd5 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp | |||
| @@ -36,6 +36,15 @@ Flags MakeInvalidationFlags() { | |||
| 36 | flags[BlendConstants] = true; | 36 | flags[BlendConstants] = true; |
| 37 | flags[DepthBounds] = true; | 37 | flags[DepthBounds] = true; |
| 38 | flags[StencilProperties] = true; | 38 | flags[StencilProperties] = true; |
| 39 | flags[CullMode] = true; | ||
| 40 | flags[DepthBoundsEnable] = true; | ||
| 41 | flags[DepthTestEnable] = true; | ||
| 42 | flags[DepthWriteEnable] = true; | ||
| 43 | flags[DepthCompareOp] = true; | ||
| 44 | flags[FrontFace] = true; | ||
| 45 | flags[PrimitiveTopology] = true; | ||
| 46 | flags[StencilOp] = true; | ||
| 47 | flags[StencilTestEnable] = true; | ||
| 39 | return flags; | 48 | return flags; |
| 40 | } | 49 | } |
| 41 | 50 | ||
| @@ -75,6 +84,57 @@ void SetupDirtyStencilProperties(Tables& tables) { | |||
| 75 | table[OFF(stencil_back_func_mask)] = StencilProperties; | 84 | table[OFF(stencil_back_func_mask)] = StencilProperties; |
| 76 | } | 85 | } |
| 77 | 86 | ||
| 87 | void SetupDirtyCullMode(Tables& tables) { | ||
| 88 | auto& table = tables[0]; | ||
| 89 | table[OFF(cull_face)] = CullMode; | ||
| 90 | table[OFF(cull_test_enabled)] = CullMode; | ||
| 91 | } | ||
| 92 | |||
| 93 | void SetupDirtyDepthBoundsEnable(Tables& tables) { | ||
| 94 | tables[0][OFF(depth_bounds_enable)] = DepthBoundsEnable; | ||
| 95 | } | ||
| 96 | |||
| 97 | void SetupDirtyDepthTestEnable(Tables& tables) { | ||
| 98 | tables[0][OFF(depth_test_enable)] = DepthTestEnable; | ||
| 99 | } | ||
| 100 | |||
| 101 | void SetupDirtyDepthWriteEnable(Tables& tables) { | ||
| 102 | tables[0][OFF(depth_write_enabled)] = DepthWriteEnable; | ||
| 103 | } | ||
| 104 | |||
| 105 | void SetupDirtyDepthCompareOp(Tables& tables) { | ||
| 106 | tables[0][OFF(depth_test_func)] = DepthCompareOp; | ||
| 107 | } | ||
| 108 | |||
| 109 | void SetupDirtyFrontFace(Tables& tables) { | ||
| 110 | auto& table = tables[0]; | ||
| 111 | table[OFF(front_face)] = FrontFace; | ||
| 112 | table[OFF(screen_y_control)] = FrontFace; | ||
| 113 | } | ||
| 114 | |||
| 115 | void SetupDirtyPrimitiveTopology(Tables& tables) { | ||
| 116 | tables[0][OFF(draw.topology)] = PrimitiveTopology; | ||
| 117 | } | ||
| 118 | |||
| 119 | void SetupDirtyStencilOp(Tables& tables) { | ||
| 120 | auto& table = tables[0]; | ||
| 121 | table[OFF(stencil_front_op_fail)] = StencilOp; | ||
| 122 | table[OFF(stencil_front_op_zfail)] = StencilOp; | ||
| 123 | table[OFF(stencil_front_op_zpass)] = StencilOp; | ||
| 124 | table[OFF(stencil_front_func_func)] = StencilOp; | ||
| 125 | table[OFF(stencil_back_op_fail)] = StencilOp; | ||
| 126 | table[OFF(stencil_back_op_zfail)] = StencilOp; | ||
| 127 | table[OFF(stencil_back_op_zpass)] = StencilOp; | ||
| 128 | table[OFF(stencil_back_func_func)] = StencilOp; | ||
| 129 | |||
| 130 | // Table 0 is used by StencilProperties | ||
| 131 | tables[1][OFF(stencil_two_side_enable)] = StencilOp; | ||
| 132 | } | ||
| 133 | |||
| 134 | void SetupDirtyStencilTestEnable(Tables& tables) { | ||
| 135 | tables[0][OFF(stencil_enable)] = StencilTestEnable; | ||
| 136 | } | ||
| 137 | |||
| 78 | } // Anonymous namespace | 138 | } // Anonymous namespace |
| 79 | 139 | ||
| 80 | StateTracker::StateTracker(Core::System& system) | 140 | StateTracker::StateTracker(Core::System& system) |
| @@ -90,6 +150,14 @@ void StateTracker::Initialize() { | |||
| 90 | SetupDirtyBlendConstants(tables); | 150 | SetupDirtyBlendConstants(tables); |
| 91 | SetupDirtyDepthBounds(tables); | 151 | SetupDirtyDepthBounds(tables); |
| 92 | SetupDirtyStencilProperties(tables); | 152 | SetupDirtyStencilProperties(tables); |
| 153 | SetupDirtyCullMode(tables); | ||
| 154 | SetupDirtyDepthBoundsEnable(tables); | ||
| 155 | SetupDirtyDepthTestEnable(tables); | ||
| 156 | SetupDirtyDepthWriteEnable(tables); | ||
| 157 | SetupDirtyDepthCompareOp(tables); | ||
| 158 | SetupDirtyFrontFace(tables); | ||
| 159 | SetupDirtyPrimitiveTopology(tables); | ||
| 160 | SetupDirtyStencilOp(tables); | ||
| 93 | } | 161 | } |
| 94 | 162 | ||
| 95 | void StateTracker::InvalidateCommandBufferState() { | 163 | void StateTracker::InvalidateCommandBufferState() { |
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index 03bc415b2..54ca0d6c6 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.h +++ b/src/video_core/renderer_vulkan/vk_state_tracker.h | |||
| @@ -26,6 +26,16 @@ enum : u8 { | |||
| 26 | DepthBounds, | 26 | DepthBounds, |
| 27 | StencilProperties, | 27 | StencilProperties, |
| 28 | 28 | ||
| 29 | CullMode, | ||
| 30 | DepthBoundsEnable, | ||
| 31 | DepthTestEnable, | ||
| 32 | DepthWriteEnable, | ||
| 33 | DepthCompareOp, | ||
| 34 | FrontFace, | ||
| 35 | PrimitiveTopology, | ||
| 36 | StencilOp, | ||
| 37 | StencilTestEnable, | ||
| 38 | |||
| 29 | Last | 39 | Last |
| 30 | }; | 40 | }; |
| 31 | static_assert(Last <= std::numeric_limits<u8>::max()); | 41 | static_assert(Last <= std::numeric_limits<u8>::max()); |
| @@ -64,6 +74,46 @@ public: | |||
| 64 | return Exchange(Dirty::StencilProperties, false); | 74 | return Exchange(Dirty::StencilProperties, false); |
| 65 | } | 75 | } |
| 66 | 76 | ||
| 77 | bool TouchCullMode() { | ||
| 78 | return Exchange(Dirty::CullMode, false); | ||
| 79 | } | ||
| 80 | |||
| 81 | bool TouchDepthBoundsTestEnable() { | ||
| 82 | return Exchange(Dirty::DepthBoundsEnable, false); | ||
| 83 | } | ||
| 84 | |||
| 85 | bool TouchDepthTestEnable() { | ||
| 86 | return Exchange(Dirty::DepthTestEnable, false); | ||
| 87 | } | ||
| 88 | |||
| 89 | bool TouchDepthBoundsEnable() { | ||
| 90 | return Exchange(Dirty::DepthBoundsEnable, false); | ||
| 91 | } | ||
| 92 | |||
| 93 | bool TouchDepthWriteEnable() { | ||
| 94 | return Exchange(Dirty::DepthWriteEnable, false); | ||
| 95 | } | ||
| 96 | |||
| 97 | bool TouchDepthCompareOp() { | ||
| 98 | return Exchange(Dirty::DepthCompareOp, false); | ||
| 99 | } | ||
| 100 | |||
| 101 | bool TouchFrontFace() { | ||
| 102 | return Exchange(Dirty::FrontFace, false); | ||
| 103 | } | ||
| 104 | |||
| 105 | bool TouchPrimitiveTopology() { | ||
| 106 | return Exchange(Dirty::PrimitiveTopology, false); | ||
| 107 | } | ||
| 108 | |||
| 109 | bool TouchStencilOp() { | ||
| 110 | return Exchange(Dirty::StencilOp, false); | ||
| 111 | } | ||
| 112 | |||
| 113 | bool TouchStencilTestEnable() { | ||
| 114 | return Exchange(Dirty::StencilTestEnable, false); | ||
| 115 | } | ||
| 116 | |||
| 67 | private: | 117 | private: |
| 68 | bool Exchange(std::size_t id, bool new_value) const noexcept { | 118 | bool Exchange(std::size_t id, bool new_value) const noexcept { |
| 69 | auto& flags = system.GPU().Maxwell3D().dirty.flags; | 119 | auto& flags = system.GPU().Maxwell3D().dirty.flags; |
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp index 42eff85d3..051298cc8 100644 --- a/src/video_core/renderer_vulkan/wrapper.cpp +++ b/src/video_core/renderer_vulkan/wrapper.cpp | |||
| @@ -88,6 +88,16 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { | |||
| 88 | X(vkCmdSetStencilWriteMask); | 88 | X(vkCmdSetStencilWriteMask); |
| 89 | X(vkCmdSetViewport); | 89 | X(vkCmdSetViewport); |
| 90 | X(vkCmdWaitEvents); | 90 | X(vkCmdWaitEvents); |
| 91 | X(vkCmdBindVertexBuffers2EXT); | ||
| 92 | X(vkCmdSetCullModeEXT); | ||
| 93 | X(vkCmdSetDepthBoundsTestEnableEXT); | ||
| 94 | X(vkCmdSetDepthCompareOpEXT); | ||
| 95 | X(vkCmdSetDepthTestEnableEXT); | ||
| 96 | X(vkCmdSetDepthWriteEnableEXT); | ||
| 97 | X(vkCmdSetFrontFaceEXT); | ||
| 98 | X(vkCmdSetPrimitiveTopologyEXT); | ||
| 99 | X(vkCmdSetStencilOpEXT); | ||
| 100 | X(vkCmdSetStencilTestEnableEXT); | ||
| 91 | X(vkCreateBuffer); | 101 | X(vkCreateBuffer); |
| 92 | X(vkCreateBufferView); | 102 | X(vkCreateBufferView); |
| 93 | X(vkCreateCommandPool); | 103 | X(vkCreateCommandPool); |
| @@ -153,7 +163,8 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { | |||
| 153 | 163 | ||
| 154 | bool Load(InstanceDispatch& dld) noexcept { | 164 | bool Load(InstanceDispatch& dld) noexcept { |
| 155 | #define X(name) Proc(dld.name, dld, #name) | 165 | #define X(name) Proc(dld.name, dld, #name) |
| 156 | return X(vkCreateInstance) && X(vkEnumerateInstanceExtensionProperties); | 166 | return X(vkCreateInstance) && X(vkEnumerateInstanceExtensionProperties) && |
| 167 | X(vkEnumerateInstanceLayerProperties); | ||
| 157 | #undef X | 168 | #undef X |
| 158 | } | 169 | } |
| 159 | 170 | ||
| @@ -770,4 +781,17 @@ std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProp | |||
| 770 | return properties; | 781 | return properties; |
| 771 | } | 782 | } |
| 772 | 783 | ||
| 784 | std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties( | ||
| 785 | const InstanceDispatch& dld) { | ||
| 786 | u32 num; | ||
| 787 | if (dld.vkEnumerateInstanceLayerProperties(&num, nullptr) != VK_SUCCESS) { | ||
| 788 | return std::nullopt; | ||
| 789 | } | ||
| 790 | std::vector<VkLayerProperties> properties(num); | ||
| 791 | if (dld.vkEnumerateInstanceLayerProperties(&num, properties.data()) != VK_SUCCESS) { | ||
| 792 | return std::nullopt; | ||
| 793 | } | ||
| 794 | return properties; | ||
| 795 | } | ||
| 796 | |||
| 773 | } // namespace Vulkan::vk | 797 | } // namespace Vulkan::vk |
diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/renderer_vulkan/wrapper.h index da42ca88e..71daac9d7 100644 --- a/src/video_core/renderer_vulkan/wrapper.h +++ b/src/video_core/renderer_vulkan/wrapper.h | |||
| @@ -141,6 +141,7 @@ struct InstanceDispatch { | |||
| 141 | PFN_vkCreateInstance vkCreateInstance; | 141 | PFN_vkCreateInstance vkCreateInstance; |
| 142 | PFN_vkDestroyInstance vkDestroyInstance; | 142 | PFN_vkDestroyInstance vkDestroyInstance; |
| 143 | PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties; | 143 | PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties; |
| 144 | PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties; | ||
| 144 | 145 | ||
| 145 | PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT; | 146 | PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT; |
| 146 | PFN_vkCreateDevice vkCreateDevice; | 147 | PFN_vkCreateDevice vkCreateDevice; |
| @@ -206,6 +207,16 @@ struct DeviceDispatch : public InstanceDispatch { | |||
| 206 | PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask; | 207 | PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask; |
| 207 | PFN_vkCmdSetViewport vkCmdSetViewport; | 208 | PFN_vkCmdSetViewport vkCmdSetViewport; |
| 208 | PFN_vkCmdWaitEvents vkCmdWaitEvents; | 209 | PFN_vkCmdWaitEvents vkCmdWaitEvents; |
| 210 | PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT; | ||
| 211 | PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT; | ||
| 212 | PFN_vkCmdSetDepthBoundsTestEnableEXT vkCmdSetDepthBoundsTestEnableEXT; | ||
| 213 | PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT; | ||
| 214 | PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT; | ||
| 215 | PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT; | ||
| 216 | PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT; | ||
| 217 | PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT; | ||
| 218 | PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT; | ||
| 219 | PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT; | ||
| 209 | PFN_vkCreateBuffer vkCreateBuffer; | 220 | PFN_vkCreateBuffer vkCreateBuffer; |
| 210 | PFN_vkCreateBufferView vkCreateBufferView; | 221 | PFN_vkCreateBufferView vkCreateBufferView; |
| 211 | PFN_vkCreateCommandPool vkCreateCommandPool; | 222 | PFN_vkCreateCommandPool vkCreateCommandPool; |
| @@ -968,6 +979,50 @@ public: | |||
| 968 | buffer_barriers.data(), image_barriers.size(), image_barriers.data()); | 979 | buffer_barriers.data(), image_barriers.size(), image_barriers.data()); |
| 969 | } | 980 | } |
| 970 | 981 | ||
| 982 | void BindVertexBuffers2EXT(u32 first_binding, u32 binding_count, const VkBuffer* buffers, | ||
| 983 | const VkDeviceSize* offsets, const VkDeviceSize* sizes, | ||
| 984 | const VkDeviceSize* strides) const noexcept { | ||
| 985 | dld->vkCmdBindVertexBuffers2EXT(handle, first_binding, binding_count, buffers, offsets, | ||
| 986 | sizes, strides); | ||
| 987 | } | ||
| 988 | |||
| 989 | void SetCullModeEXT(VkCullModeFlags cull_mode) const noexcept { | ||
| 990 | dld->vkCmdSetCullModeEXT(handle, cull_mode); | ||
| 991 | } | ||
| 992 | |||
| 993 | void SetDepthBoundsTestEnableEXT(bool enable) const noexcept { | ||
| 994 | dld->vkCmdSetDepthBoundsTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); | ||
| 995 | } | ||
| 996 | |||
| 997 | void SetDepthCompareOpEXT(VkCompareOp compare_op) const noexcept { | ||
| 998 | dld->vkCmdSetDepthCompareOpEXT(handle, compare_op); | ||
| 999 | } | ||
| 1000 | |||
| 1001 | void SetDepthTestEnableEXT(bool enable) const noexcept { | ||
| 1002 | dld->vkCmdSetDepthTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | void SetDepthWriteEnableEXT(bool enable) const noexcept { | ||
| 1006 | dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | void SetFrontFaceEXT(VkFrontFace front_face) const noexcept { | ||
| 1010 | dld->vkCmdSetFrontFaceEXT(handle, front_face); | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | void SetPrimitiveTopologyEXT(VkPrimitiveTopology primitive_topology) const noexcept { | ||
| 1014 | dld->vkCmdSetPrimitiveTopologyEXT(handle, primitive_topology); | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | void SetStencilOpEXT(VkStencilFaceFlags face_mask, VkStencilOp fail_op, VkStencilOp pass_op, | ||
| 1018 | VkStencilOp depth_fail_op, VkCompareOp compare_op) const noexcept { | ||
| 1019 | dld->vkCmdSetStencilOpEXT(handle, face_mask, fail_op, pass_op, depth_fail_op, compare_op); | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | void SetStencilTestEnableEXT(bool enable) const noexcept { | ||
| 1023 | dld->vkCmdSetStencilTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); | ||
| 1024 | } | ||
| 1025 | |||
| 971 | void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers, | 1026 | void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers, |
| 972 | const VkDeviceSize* offsets, | 1027 | const VkDeviceSize* offsets, |
| 973 | const VkDeviceSize* sizes) const noexcept { | 1028 | const VkDeviceSize* sizes) const noexcept { |
| @@ -996,4 +1051,7 @@ private: | |||
| 996 | std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProperties( | 1051 | std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProperties( |
| 997 | const InstanceDispatch& dld); | 1052 | const InstanceDispatch& dld); |
| 998 | 1053 | ||
| 1054 | std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties( | ||
| 1055 | const InstanceDispatch& dld); | ||
| 1056 | |||
| 999 | } // namespace Vulkan::vk | 1057 | } // namespace Vulkan::vk |
diff --git a/src/video_core/shader_cache.h b/src/video_core/shader_cache.h index 2dd270e99..b7608fc7b 100644 --- a/src/video_core/shader_cache.h +++ b/src/video_core/shader_cache.h | |||
| @@ -20,6 +20,7 @@ namespace VideoCommon { | |||
| 20 | template <class T> | 20 | template <class T> |
| 21 | class ShaderCache { | 21 | class ShaderCache { |
| 22 | static constexpr u64 PAGE_BITS = 14; | 22 | static constexpr u64 PAGE_BITS = 14; |
| 23 | static constexpr u64 PAGE_SIZE = u64(1) << PAGE_BITS; | ||
| 23 | 24 | ||
| 24 | struct Entry { | 25 | struct Entry { |
| 25 | VAddr addr_start; | 26 | VAddr addr_start; |
| @@ -87,8 +88,8 @@ protected: | |||
| 87 | const VAddr addr_end = addr + size; | 88 | const VAddr addr_end = addr + size; |
| 88 | Entry* const entry = NewEntry(addr, addr_end, data.get()); | 89 | Entry* const entry = NewEntry(addr, addr_end, data.get()); |
| 89 | 90 | ||
| 90 | const u64 page_end = addr_end >> PAGE_BITS; | 91 | const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS; |
| 91 | for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) { | 92 | for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) { |
| 92 | invalidation_cache[page].push_back(entry); | 93 | invalidation_cache[page].push_back(entry); |
| 93 | } | 94 | } |
| 94 | 95 | ||
| @@ -108,20 +109,13 @@ private: | |||
| 108 | /// @pre invalidation_mutex is locked | 109 | /// @pre invalidation_mutex is locked |
| 109 | void InvalidatePagesInRegion(VAddr addr, std::size_t size) { | 110 | void InvalidatePagesInRegion(VAddr addr, std::size_t size) { |
| 110 | const VAddr addr_end = addr + size; | 111 | const VAddr addr_end = addr + size; |
| 111 | const u64 page_end = addr_end >> PAGE_BITS; | 112 | const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS; |
| 112 | for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) { | 113 | for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) { |
| 113 | const auto it = invalidation_cache.find(page); | 114 | auto it = invalidation_cache.find(page); |
| 114 | if (it == invalidation_cache.end()) { | 115 | if (it == invalidation_cache.end()) { |
| 115 | continue; | 116 | continue; |
| 116 | } | 117 | } |
| 117 | 118 | InvalidatePageEntries(it->second, addr, addr_end); | |
| 118 | std::vector<Entry*>& entries = it->second; | ||
| 119 | InvalidatePageEntries(entries, addr, addr_end); | ||
| 120 | |||
| 121 | // If there's nothing else in this page, remove it to avoid overpopulating the hash map. | ||
| 122 | if (entries.empty()) { | ||
| 123 | invalidation_cache.erase(it); | ||
| 124 | } | ||
| 125 | } | 119 | } |
| 126 | } | 120 | } |
| 127 | 121 | ||
| @@ -131,15 +125,22 @@ private: | |||
| 131 | if (marked_for_removal.empty()) { | 125 | if (marked_for_removal.empty()) { |
| 132 | return; | 126 | return; |
| 133 | } | 127 | } |
| 134 | std::scoped_lock lock{lookup_mutex}; | 128 | // Remove duplicates |
| 129 | std::sort(marked_for_removal.begin(), marked_for_removal.end()); | ||
| 130 | marked_for_removal.erase(std::unique(marked_for_removal.begin(), marked_for_removal.end()), | ||
| 131 | marked_for_removal.end()); | ||
| 135 | 132 | ||
| 136 | std::vector<T*> removed_shaders; | 133 | std::vector<T*> removed_shaders; |
| 137 | removed_shaders.reserve(marked_for_removal.size()); | 134 | removed_shaders.reserve(marked_for_removal.size()); |
| 138 | 135 | ||
| 136 | std::scoped_lock lock{lookup_mutex}; | ||
| 137 | |||
| 139 | for (Entry* const entry : marked_for_removal) { | 138 | for (Entry* const entry : marked_for_removal) { |
| 140 | if (lookup_cache.erase(entry->addr_start) > 0) { | 139 | removed_shaders.push_back(entry->data); |
| 141 | removed_shaders.push_back(entry->data); | 140 | |
| 142 | } | 141 | const auto it = lookup_cache.find(entry->addr_start); |
| 142 | ASSERT(it != lookup_cache.end()); | ||
| 143 | lookup_cache.erase(it); | ||
| 143 | } | 144 | } |
| 144 | marked_for_removal.clear(); | 145 | marked_for_removal.clear(); |
| 145 | 146 | ||
| @@ -154,17 +155,33 @@ private: | |||
| 154 | /// @param addr_end Non-inclusive end address of the invalidation | 155 | /// @param addr_end Non-inclusive end address of the invalidation |
| 155 | /// @pre invalidation_mutex is locked | 156 | /// @pre invalidation_mutex is locked |
| 156 | void InvalidatePageEntries(std::vector<Entry*>& entries, VAddr addr, VAddr addr_end) { | 157 | void InvalidatePageEntries(std::vector<Entry*>& entries, VAddr addr, VAddr addr_end) { |
| 157 | auto it = entries.begin(); | 158 | std::size_t index = 0; |
| 158 | while (it != entries.end()) { | 159 | while (index < entries.size()) { |
| 159 | Entry* const entry = *it; | 160 | Entry* const entry = entries[index]; |
| 160 | if (!entry->Overlaps(addr, addr_end)) { | 161 | if (!entry->Overlaps(addr, addr_end)) { |
| 161 | ++it; | 162 | ++index; |
| 162 | continue; | 163 | continue; |
| 163 | } | 164 | } |
| 165 | |||
| 164 | UnmarkMemory(entry); | 166 | UnmarkMemory(entry); |
| 167 | RemoveEntryFromInvalidationCache(entry); | ||
| 165 | marked_for_removal.push_back(entry); | 168 | marked_for_removal.push_back(entry); |
| 169 | } | ||
| 170 | } | ||
| 166 | 171 | ||
| 167 | it = entries.erase(it); | 172 | /// @brief Removes all references to an entry in the invalidation cache |
| 173 | /// @param entry Entry to remove from the invalidation cache | ||
| 174 | /// @pre invalidation_mutex is locked | ||
| 175 | void RemoveEntryFromInvalidationCache(const Entry* entry) { | ||
| 176 | const u64 page_end = (entry->addr_end + PAGE_SIZE - 1) >> PAGE_BITS; | ||
| 177 | for (u64 page = entry->addr_start >> PAGE_BITS; page < page_end; ++page) { | ||
| 178 | const auto entries_it = invalidation_cache.find(page); | ||
| 179 | ASSERT(entries_it != invalidation_cache.end()); | ||
| 180 | std::vector<Entry*>& entries = entries_it->second; | ||
| 181 | |||
| 182 | const auto entry_it = std::find(entries.begin(), entries.end(), entry); | ||
| 183 | ASSERT(entry_it != entries.end()); | ||
| 184 | entries.erase(entry_it); | ||
| 168 | } | 185 | } |
| 169 | } | 186 | } |
| 170 | 187 | ||
| @@ -182,16 +199,11 @@ private: | |||
| 182 | } | 199 | } |
| 183 | 200 | ||
| 184 | /// @brief Removes a vector of shaders from a list | 201 | /// @brief Removes a vector of shaders from a list |
| 185 | /// @param removed_shaders Shaders to be removed from the storage, it can contain duplicates | 202 | /// @param removed_shaders Shaders to be removed from the storage |
| 186 | /// @pre invalidation_mutex is locked | 203 | /// @pre invalidation_mutex is locked |
| 187 | /// @pre lookup_mutex is locked | 204 | /// @pre lookup_mutex is locked |
| 188 | void RemoveShadersFromStorage(std::vector<T*> removed_shaders) { | 205 | void RemoveShadersFromStorage(std::vector<T*> removed_shaders) { |
| 189 | // Remove duplicates | 206 | // Notify removals |
| 190 | std::sort(removed_shaders.begin(), removed_shaders.end()); | ||
| 191 | removed_shaders.erase(std::unique(removed_shaders.begin(), removed_shaders.end()), | ||
| 192 | removed_shaders.end()); | ||
| 193 | |||
| 194 | // Now that there are no duplicates, we can notify removals | ||
| 195 | for (T* const shader : removed_shaders) { | 207 | for (T* const shader : removed_shaders) { |
| 196 | OnShaderRemoval(shader); | 208 | OnShaderRemoval(shader); |
| 197 | } | 209 | } |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 85075e868..6207d8dfe 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include "core/core.h" | 24 | #include "core/core.h" |
| 25 | #include "core/memory.h" | 25 | #include "core/memory.h" |
| 26 | #include "core/settings.h" | 26 | #include "core/settings.h" |
| 27 | #include "video_core/compatible_formats.h" | ||
| 27 | #include "video_core/dirty_flags.h" | 28 | #include "video_core/dirty_flags.h" |
| 28 | #include "video_core/engines/fermi_2d.h" | 29 | #include "video_core/engines/fermi_2d.h" |
| 29 | #include "video_core/engines/maxwell_3d.h" | 30 | #include "video_core/engines/maxwell_3d.h" |
| @@ -47,8 +48,8 @@ class RasterizerInterface; | |||
| 47 | 48 | ||
| 48 | namespace VideoCommon { | 49 | namespace VideoCommon { |
| 49 | 50 | ||
| 51 | using VideoCore::Surface::FormatCompatibility; | ||
| 50 | using VideoCore::Surface::PixelFormat; | 52 | using VideoCore::Surface::PixelFormat; |
| 51 | |||
| 52 | using VideoCore::Surface::SurfaceTarget; | 53 | using VideoCore::Surface::SurfaceTarget; |
| 53 | using RenderTargetConfig = Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig; | 54 | using RenderTargetConfig = Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig; |
| 54 | 55 | ||
| @@ -595,7 +596,7 @@ private: | |||
| 595 | } else { | 596 | } else { |
| 596 | new_surface = GetUncachedSurface(gpu_addr, params); | 597 | new_surface = GetUncachedSurface(gpu_addr, params); |
| 597 | } | 598 | } |
| 598 | const auto& final_params = new_surface->GetSurfaceParams(); | 599 | const SurfaceParams& final_params = new_surface->GetSurfaceParams(); |
| 599 | if (cr_params.type != final_params.type) { | 600 | if (cr_params.type != final_params.type) { |
| 600 | if (Settings::IsGPULevelExtreme()) { | 601 | if (Settings::IsGPULevelExtreme()) { |
| 601 | BufferCopy(current_surface, new_surface); | 602 | BufferCopy(current_surface, new_surface); |
| @@ -603,7 +604,7 @@ private: | |||
| 603 | } else { | 604 | } else { |
| 604 | std::vector<CopyParams> bricks = current_surface->BreakDown(final_params); | 605 | std::vector<CopyParams> bricks = current_surface->BreakDown(final_params); |
| 605 | for (auto& brick : bricks) { | 606 | for (auto& brick : bricks) { |
| 606 | ImageCopy(current_surface, new_surface, brick); | 607 | TryCopyImage(current_surface, new_surface, brick); |
| 607 | } | 608 | } |
| 608 | } | 609 | } |
| 609 | Unregister(current_surface); | 610 | Unregister(current_surface); |
| @@ -694,7 +695,7 @@ private: | |||
| 694 | } | 695 | } |
| 695 | const CopyParams copy_params(0, 0, 0, 0, 0, base_layer, 0, mipmap, width, height, | 696 | const CopyParams copy_params(0, 0, 0, 0, 0, base_layer, 0, mipmap, width, height, |
| 696 | src_params.depth); | 697 | src_params.depth); |
| 697 | ImageCopy(surface, new_surface, copy_params); | 698 | TryCopyImage(surface, new_surface, copy_params); |
| 698 | } | 699 | } |
| 699 | } | 700 | } |
| 700 | if (passed_tests == 0) { | 701 | if (passed_tests == 0) { |
| @@ -791,7 +792,7 @@ private: | |||
| 791 | const u32 width = params.width; | 792 | const u32 width = params.width; |
| 792 | const u32 height = params.height; | 793 | const u32 height = params.height; |
| 793 | const CopyParams copy_params(0, 0, 0, 0, 0, slice, 0, 0, width, height, 1); | 794 | const CopyParams copy_params(0, 0, 0, 0, 0, slice, 0, 0, width, height, 1); |
| 794 | ImageCopy(surface, new_surface, copy_params); | 795 | TryCopyImage(surface, new_surface, copy_params); |
| 795 | } | 796 | } |
| 796 | for (const auto& surface : overlaps) { | 797 | for (const auto& surface : overlaps) { |
| 797 | Unregister(surface); | 798 | Unregister(surface); |
| @@ -1192,6 +1193,19 @@ private: | |||
| 1192 | return {}; | 1193 | return {}; |
| 1193 | } | 1194 | } |
| 1194 | 1195 | ||
| 1196 | /// Try to do an image copy logging when formats are incompatible. | ||
| 1197 | void TryCopyImage(TSurface& src, TSurface& dst, const CopyParams& copy) { | ||
| 1198 | const SurfaceParams& src_params = src->GetSurfaceParams(); | ||
| 1199 | const SurfaceParams& dst_params = dst->GetSurfaceParams(); | ||
| 1200 | if (!format_compatibility.TestCopy(src_params.pixel_format, dst_params.pixel_format)) { | ||
| 1201 | LOG_ERROR(HW_GPU, "Illegal copy between formats={{{}, {}}}", | ||
| 1202 | static_cast<int>(dst_params.pixel_format), | ||
| 1203 | static_cast<int>(src_params.pixel_format)); | ||
| 1204 | return; | ||
| 1205 | } | ||
| 1206 | ImageCopy(src, dst, copy); | ||
| 1207 | } | ||
| 1208 | |||
| 1195 | constexpr PixelFormat GetSiblingFormat(PixelFormat format) const { | 1209 | constexpr PixelFormat GetSiblingFormat(PixelFormat format) const { |
| 1196 | return siblings_table[static_cast<std::size_t>(format)]; | 1210 | return siblings_table[static_cast<std::size_t>(format)]; |
| 1197 | } | 1211 | } |
| @@ -1241,6 +1255,7 @@ private: | |||
| 1241 | VideoCore::RasterizerInterface& rasterizer; | 1255 | VideoCore::RasterizerInterface& rasterizer; |
| 1242 | 1256 | ||
| 1243 | FormatLookupTable format_lookup_table; | 1257 | FormatLookupTable format_lookup_table; |
| 1258 | FormatCompatibility format_compatibility; | ||
| 1244 | 1259 | ||
| 1245 | u64 ticks{}; | 1260 | u64 ticks{}; |
| 1246 | 1261 | ||
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index bbbd96113..5e0d0e7af 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -212,7 +212,7 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default | |||
| 212 | // UISetting::values.shortcuts, which is alphabetically ordered. | 212 | // UISetting::values.shortcuts, which is alphabetically ordered. |
| 213 | // clang-format off | 213 | // clang-format off |
| 214 | const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{ | 214 | const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{ |
| 215 | {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}}, | 215 | {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}}, |
| 216 | {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}}, | 216 | {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}}, |
| 217 | {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, | 217 | {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, |
| 218 | {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}}, | 218 | {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}}, |
| @@ -220,8 +220,8 @@ const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{ | |||
| 220 | {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}}, | 220 | {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}}, |
| 221 | {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}}, | 221 | {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}}, |
| 222 | {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}}, | 222 | {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}}, |
| 223 | {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}}, | 223 | {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}}, |
| 224 | {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WindowShortcut}}, | 224 | {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}}, |
| 225 | {QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}}, | 225 | {QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}}, |
| 226 | {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, | 226 | {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, |
| 227 | {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, | 227 | {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, |
| @@ -665,11 +665,13 @@ void Config::ReadShortcutValues() { | |||
| 665 | const auto& [keyseq, context] = shortcut; | 665 | const auto& [keyseq, context] = shortcut; |
| 666 | qt_config->beginGroup(group); | 666 | qt_config->beginGroup(group); |
| 667 | qt_config->beginGroup(name); | 667 | qt_config->beginGroup(name); |
| 668 | // No longer using ReadSetting for shortcut.second as it innacurately returns a value of 1 | ||
| 669 | // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open | ||
| 670 | // a file dialog in windowed mode | ||
| 668 | UISettings::values.shortcuts.push_back( | 671 | UISettings::values.shortcuts.push_back( |
| 669 | {name, | 672 | {name, |
| 670 | group, | 673 | group, |
| 671 | {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), | 674 | {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), shortcut.second}}); |
| 672 | ReadSetting(QStringLiteral("Context"), context).toInt()}}); | ||
| 673 | qt_config->endGroup(); | 675 | qt_config->endGroup(); |
| 674 | qt_config->endGroup(); | 676 | qt_config->endGroup(); |
| 675 | } | 677 | } |
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index a05fa64ba..00433926d 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp | |||
| @@ -70,6 +70,20 @@ static QString ButtonToText(const Common::ParamPackage& param) { | |||
| 70 | return GetKeyName(param.Get("code", 0)); | 70 | return GetKeyName(param.Get("code", 0)); |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | if (param.Get("engine", "") == "gcpad") { | ||
| 74 | if (param.Has("axis")) { | ||
| 75 | const QString axis_str = QString::fromStdString(param.Get("axis", "")); | ||
| 76 | const QString direction_str = QString::fromStdString(param.Get("direction", "")); | ||
| 77 | |||
| 78 | return QObject::tr("GC Axis %1%2").arg(axis_str, direction_str); | ||
| 79 | } | ||
| 80 | if (param.Has("button")) { | ||
| 81 | const QString button_str = QString::number(int(std::log2(param.Get("button", 0)))); | ||
| 82 | return QObject::tr("GC Button %1").arg(button_str); | ||
| 83 | } | ||
| 84 | return GetKeyName(param.Get("code", 0)); | ||
| 85 | } | ||
| 86 | |||
| 73 | if (param.Get("engine", "") == "sdl") { | 87 | if (param.Get("engine", "") == "sdl") { |
| 74 | if (param.Has("hat")) { | 88 | if (param.Has("hat")) { |
| 75 | const QString hat_str = QString::fromStdString(param.Get("hat", "")); | 89 | const QString hat_str = QString::fromStdString(param.Get("hat", "")); |
| @@ -126,6 +140,25 @@ static QString AnalogToText(const Common::ParamPackage& param, const std::string | |||
| 126 | return {}; | 140 | return {}; |
| 127 | } | 141 | } |
| 128 | 142 | ||
| 143 | if (param.Get("engine", "") == "gcpad") { | ||
| 144 | if (dir == "modifier") { | ||
| 145 | return QObject::tr("[unused]"); | ||
| 146 | } | ||
| 147 | |||
| 148 | if (dir == "left" || dir == "right") { | ||
| 149 | const QString axis_x_str = QString::fromStdString(param.Get("axis_x", "")); | ||
| 150 | |||
| 151 | return QObject::tr("GC Axis %1").arg(axis_x_str); | ||
| 152 | } | ||
| 153 | |||
| 154 | if (dir == "up" || dir == "down") { | ||
| 155 | const QString axis_y_str = QString::fromStdString(param.Get("axis_y", "")); | ||
| 156 | |||
| 157 | return QObject::tr("GC Axis %1").arg(axis_y_str); | ||
| 158 | } | ||
| 159 | |||
| 160 | return {}; | ||
| 161 | } | ||
| 129 | return QObject::tr("[unknown]"); | 162 | return QObject::tr("[unknown]"); |
| 130 | } | 163 | } |
| 131 | 164 | ||
| @@ -332,7 +365,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 332 | 365 | ||
| 333 | connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, [=] { | 366 | connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, [=] { |
| 334 | const float slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value(); | 367 | const float slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value(); |
| 335 | if (analogs_param[analog_id].Get("engine", "") == "sdl") { | 368 | if (analogs_param[analog_id].Get("engine", "") == "sdl" || |
| 369 | analogs_param[analog_id].Get("engine", "") == "gcpad") { | ||
| 336 | analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( | 370 | analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( |
| 337 | tr("Deadzone: %1%").arg(slider_value)); | 371 | tr("Deadzone: %1%").arg(slider_value)); |
| 338 | analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); | 372 | analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); |
| @@ -352,6 +386,20 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 352 | 386 | ||
| 353 | connect(poll_timer.get(), &QTimer::timeout, [this] { | 387 | connect(poll_timer.get(), &QTimer::timeout, [this] { |
| 354 | Common::ParamPackage params; | 388 | Common::ParamPackage params; |
| 389 | if (InputCommon::GetGCButtons()->IsPolling()) { | ||
| 390 | params = InputCommon::GetGCButtons()->GetNextInput(); | ||
| 391 | if (params.Has("engine")) { | ||
| 392 | SetPollingResult(params, false); | ||
| 393 | return; | ||
| 394 | } | ||
| 395 | } | ||
| 396 | if (InputCommon::GetGCAnalogs()->IsPolling()) { | ||
| 397 | params = InputCommon::GetGCAnalogs()->GetNextInput(); | ||
| 398 | if (params.Has("engine")) { | ||
| 399 | SetPollingResult(params, false); | ||
| 400 | return; | ||
| 401 | } | ||
| 402 | } | ||
| 355 | for (auto& poller : device_pollers) { | 403 | for (auto& poller : device_pollers) { |
| 356 | params = poller->GetNextInput(); | 404 | params = poller->GetNextInput(); |
| 357 | if (params.Has("engine")) { | 405 | if (params.Has("engine")) { |
| @@ -534,7 +582,7 @@ void ConfigureInputPlayer::UpdateButtonLabels() { | |||
| 534 | analog_map_deadzone_and_modifier_slider_label[analog_id]; | 582 | analog_map_deadzone_and_modifier_slider_label[analog_id]; |
| 535 | 583 | ||
| 536 | if (param.Has("engine")) { | 584 | if (param.Has("engine")) { |
| 537 | if (param.Get("engine", "") == "sdl") { | 585 | if (param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad") { |
| 538 | if (!param.Has("deadzone")) { | 586 | if (!param.Has("deadzone")) { |
| 539 | param.Set("deadzone", 0.1f); | 587 | param.Set("deadzone", 0.1f); |
| 540 | } | 588 | } |
| @@ -583,6 +631,11 @@ void ConfigureInputPlayer::HandleClick( | |||
| 583 | 631 | ||
| 584 | grabKeyboard(); | 632 | grabKeyboard(); |
| 585 | grabMouse(); | 633 | grabMouse(); |
| 634 | if (type == InputCommon::Polling::DeviceType::Button) { | ||
| 635 | InputCommon::GetGCButtons()->BeginConfiguration(); | ||
| 636 | } else { | ||
| 637 | InputCommon::GetGCAnalogs()->BeginConfiguration(); | ||
| 638 | } | ||
| 586 | timeout_timer->start(5000); // Cancel after 5 seconds | 639 | timeout_timer->start(5000); // Cancel after 5 seconds |
| 587 | poll_timer->start(200); // Check for new inputs every 200ms | 640 | poll_timer->start(200); // Check for new inputs every 200ms |
| 588 | } | 641 | } |
| @@ -596,6 +649,9 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, | |||
| 596 | poller->Stop(); | 649 | poller->Stop(); |
| 597 | } | 650 | } |
| 598 | 651 | ||
| 652 | InputCommon::GetGCButtons()->EndConfiguration(); | ||
| 653 | InputCommon::GetGCAnalogs()->EndConfiguration(); | ||
| 654 | |||
| 599 | if (!abort) { | 655 | if (!abort) { |
| 600 | (*input_setter)(params); | 656 | (*input_setter)(params); |
| 601 | } | 657 | } |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 82625e67f..9844e4764 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -689,6 +689,11 @@ void GMainWindow::InitializeHotkeys() { | |||
| 689 | ui.action_Capture_Screenshot->setShortcutContext( | 689 | ui.action_Capture_Screenshot->setShortcutContext( |
| 690 | hotkey_registry.GetShortcutContext(main_window, capture_screenshot)); | 690 | hotkey_registry.GetShortcutContext(main_window, capture_screenshot)); |
| 691 | 691 | ||
| 692 | ui.action_Fullscreen->setShortcut( | ||
| 693 | hotkey_registry.GetHotkey(main_window, fullscreen, this)->key()); | ||
| 694 | ui.action_Fullscreen->setShortcutContext( | ||
| 695 | hotkey_registry.GetShortcutContext(main_window, fullscreen)); | ||
| 696 | |||
| 692 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load File"), this), | 697 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load File"), this), |
| 693 | &QShortcut::activated, this, &GMainWindow::OnMenuLoadFile); | 698 | &QShortcut::activated, this, &GMainWindow::OnMenuLoadFile); |
| 694 | connect( | 699 | connect( |
| @@ -748,7 +753,7 @@ void GMainWindow::InitializeHotkeys() { | |||
| 748 | }); | 753 | }); |
| 749 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Capture Screenshot"), this), | 754 | connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Capture Screenshot"), this), |
| 750 | &QShortcut::activated, this, [&] { | 755 | &QShortcut::activated, this, [&] { |
| 751 | if (emu_thread->IsRunning()) { | 756 | if (emu_thread != nullptr && emu_thread->IsRunning()) { |
| 752 | OnCaptureScreenshot(); | 757 | OnCaptureScreenshot(); |
| 753 | } | 758 | } |
| 754 | }); | 759 | }); |
| @@ -863,6 +868,9 @@ void GMainWindow::ConnectMenuEvents() { | |||
| 863 | connect(ui.action_Report_Compatibility, &QAction::triggered, this, | 868 | connect(ui.action_Report_Compatibility, &QAction::triggered, this, |
| 864 | &GMainWindow::OnMenuReportCompatibility); | 869 | &GMainWindow::OnMenuReportCompatibility); |
| 865 | connect(ui.action_Open_Mods_Page, &QAction::triggered, this, &GMainWindow::OnOpenModsPage); | 870 | connect(ui.action_Open_Mods_Page, &QAction::triggered, this, &GMainWindow::OnOpenModsPage); |
| 871 | connect(ui.action_Open_Quickstart_Guide, &QAction::triggered, this, | ||
| 872 | &GMainWindow::OnOpenQuickstartGuide); | ||
| 873 | connect(ui.action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnOpenFAQ); | ||
| 866 | connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); }); | 874 | connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); }); |
| 867 | connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure); | 875 | connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure); |
| 868 | 876 | ||
| @@ -876,10 +884,6 @@ void GMainWindow::ConnectMenuEvents() { | |||
| 876 | connect(ui.action_Reset_Window_Size, &QAction::triggered, this, &GMainWindow::ResetWindowSize); | 884 | connect(ui.action_Reset_Window_Size, &QAction::triggered, this, &GMainWindow::ResetWindowSize); |
| 877 | 885 | ||
| 878 | // Fullscreen | 886 | // Fullscreen |
| 879 | ui.action_Fullscreen->setShortcut( | ||
| 880 | hotkey_registry | ||
| 881 | .GetHotkey(QStringLiteral("Main Window"), QStringLiteral("Fullscreen"), this) | ||
| 882 | ->key()); | ||
| 883 | connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); | 887 | connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); |
| 884 | 888 | ||
| 885 | // Movie | 889 | // Movie |
| @@ -1077,17 +1081,19 @@ void GMainWindow::BootGame(const QString& filename) { | |||
| 1077 | const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); | 1081 | const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); |
| 1078 | 1082 | ||
| 1079 | std::string title_name; | 1083 | std::string title_name; |
| 1084 | std::string title_version; | ||
| 1080 | const auto res = Core::System::GetInstance().GetGameName(title_name); | 1085 | const auto res = Core::System::GetInstance().GetGameName(title_name); |
| 1081 | if (res != Loader::ResultStatus::Success) { | ||
| 1082 | const auto metadata = FileSys::PatchManager(title_id).GetControlMetadata(); | ||
| 1083 | if (metadata.first != nullptr) | ||
| 1084 | title_name = metadata.first->GetApplicationName(); | ||
| 1085 | 1086 | ||
| 1086 | if (title_name.empty()) | 1087 | const auto metadata = FileSys::PatchManager(title_id).GetControlMetadata(); |
| 1087 | title_name = FileUtil::GetFilename(filename.toStdString()); | 1088 | if (metadata.first != nullptr) { |
| 1089 | title_version = metadata.first->GetVersionString(); | ||
| 1090 | title_name = metadata.first->GetApplicationName(); | ||
| 1088 | } | 1091 | } |
| 1089 | LOG_INFO(Frontend, "Booting game: {:016X} | {}", title_id, title_name); | 1092 | if (res != Loader::ResultStatus::Success || title_name.empty()) { |
| 1090 | UpdateWindowTitle(QString::fromStdString(title_name)); | 1093 | title_name = FileUtil::GetFilename(filename.toStdString()); |
| 1094 | } | ||
| 1095 | LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version); | ||
| 1096 | UpdateWindowTitle(title_name, title_version); | ||
| 1091 | 1097 | ||
| 1092 | loading_screen->Prepare(Core::System::GetInstance().GetAppLoader()); | 1098 | loading_screen->Prepare(Core::System::GetInstance().GetAppLoader()); |
| 1093 | loading_screen->show(); | 1099 | loading_screen->show(); |
| @@ -1838,16 +1844,26 @@ void GMainWindow::OnMenuReportCompatibility() { | |||
| 1838 | } | 1844 | } |
| 1839 | } | 1845 | } |
| 1840 | 1846 | ||
| 1841 | void GMainWindow::OnOpenModsPage() { | 1847 | void GMainWindow::OpenURL(const QUrl& url) { |
| 1842 | const auto mods_page_url = QStringLiteral("https://github.com/yuzu-emu/yuzu/wiki/Switch-Mods"); | 1848 | const bool open = QDesktopServices::openUrl(url); |
| 1843 | const QUrl mods_page(mods_page_url); | ||
| 1844 | const bool open = QDesktopServices::openUrl(mods_page); | ||
| 1845 | if (!open) { | 1849 | if (!open) { |
| 1846 | QMessageBox::warning(this, tr("Error opening URL"), | 1850 | QMessageBox::warning(this, tr("Error opening URL"), |
| 1847 | tr("Unable to open the URL \"%1\".").arg(mods_page_url)); | 1851 | tr("Unable to open the URL \"%1\".").arg(url.toString())); |
| 1848 | } | 1852 | } |
| 1849 | } | 1853 | } |
| 1850 | 1854 | ||
| 1855 | void GMainWindow::OnOpenModsPage() { | ||
| 1856 | OpenURL(QUrl(QStringLiteral("https://github.com/yuzu-emu/yuzu/wiki/Switch-Mods"))); | ||
| 1857 | } | ||
| 1858 | |||
| 1859 | void GMainWindow::OnOpenQuickstartGuide() { | ||
| 1860 | OpenURL(QUrl(QStringLiteral("https://yuzu-emu.org/help/quickstart/"))); | ||
| 1861 | } | ||
| 1862 | |||
| 1863 | void GMainWindow::OnOpenFAQ() { | ||
| 1864 | OpenURL(QUrl(QStringLiteral("https://yuzu-emu.org/wiki/faq/"))); | ||
| 1865 | } | ||
| 1866 | |||
| 1851 | void GMainWindow::ToggleFullscreen() { | 1867 | void GMainWindow::ToggleFullscreen() { |
| 1852 | if (!emulation_running) { | 1868 | if (!emulation_running) { |
| 1853 | return; | 1869 | return; |
| @@ -2050,7 +2066,8 @@ void GMainWindow::OnCaptureScreenshot() { | |||
| 2050 | OnStartGame(); | 2066 | OnStartGame(); |
| 2051 | } | 2067 | } |
| 2052 | 2068 | ||
| 2053 | void GMainWindow::UpdateWindowTitle(const QString& title_name) { | 2069 | void GMainWindow::UpdateWindowTitle(const std::string& title_name, |
| 2070 | const std::string& title_version) { | ||
| 2054 | const auto full_name = std::string(Common::g_build_fullname); | 2071 | const auto full_name = std::string(Common::g_build_fullname); |
| 2055 | const auto branch_name = std::string(Common::g_scm_branch); | 2072 | const auto branch_name = std::string(Common::g_scm_branch); |
| 2056 | const auto description = std::string(Common::g_scm_desc); | 2073 | const auto description = std::string(Common::g_scm_desc); |
| @@ -2059,7 +2076,7 @@ void GMainWindow::UpdateWindowTitle(const QString& title_name) { | |||
| 2059 | const auto date = | 2076 | const auto date = |
| 2060 | QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd")).toStdString(); | 2077 | QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd")).toStdString(); |
| 2061 | 2078 | ||
| 2062 | if (title_name.isEmpty()) { | 2079 | if (title_name.empty()) { |
| 2063 | const auto fmt = std::string(Common::g_title_bar_format_idle); | 2080 | const auto fmt = std::string(Common::g_title_bar_format_idle); |
| 2064 | setWindowTitle(QString::fromStdString(fmt::format(fmt.empty() ? "yuzu {0}| {1}-{2}" : fmt, | 2081 | setWindowTitle(QString::fromStdString(fmt::format(fmt.empty() ? "yuzu {0}| {1}-{2}" : fmt, |
| 2065 | full_name, branch_name, description, | 2082 | full_name, branch_name, description, |
| @@ -2067,8 +2084,8 @@ void GMainWindow::UpdateWindowTitle(const QString& title_name) { | |||
| 2067 | } else { | 2084 | } else { |
| 2068 | const auto fmt = std::string(Common::g_title_bar_format_running); | 2085 | const auto fmt = std::string(Common::g_title_bar_format_running); |
| 2069 | setWindowTitle(QString::fromStdString( | 2086 | setWindowTitle(QString::fromStdString( |
| 2070 | fmt::format(fmt.empty() ? "yuzu {0}| {3} | {1}-{2}" : fmt, full_name, branch_name, | 2087 | fmt::format(fmt.empty() ? "yuzu {0}| {3} | {6} | {1}-{2}" : fmt, full_name, branch_name, |
| 2071 | description, title_name.toStdString(), date, build_id))); | 2088 | description, title_name, date, build_id, title_version))); |
| 2072 | } | 2089 | } |
| 2073 | } | 2090 | } |
| 2074 | 2091 | ||
| @@ -2209,7 +2226,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | |||
| 2209 | "title.keys_autogenerated"); | 2226 | "title.keys_autogenerated"); |
| 2210 | } | 2227 | } |
| 2211 | 2228 | ||
| 2212 | Core::Crypto::KeyManager keys{}; | 2229 | Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); |
| 2213 | if (keys.BaseDeriveNecessary()) { | 2230 | if (keys.BaseDeriveNecessary()) { |
| 2214 | Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory( | 2231 | Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory( |
| 2215 | FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)}; | 2232 | FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)}; |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 5581874ed..66c84e5c0 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -182,6 +182,8 @@ private slots: | |||
| 182 | void OnStopGame(); | 182 | void OnStopGame(); |
| 183 | void OnMenuReportCompatibility(); | 183 | void OnMenuReportCompatibility(); |
| 184 | void OnOpenModsPage(); | 184 | void OnOpenModsPage(); |
| 185 | void OnOpenQuickstartGuide(); | ||
| 186 | void OnOpenFAQ(); | ||
| 185 | /// Called whenever a user selects a game in the game list widget. | 187 | /// Called whenever a user selects a game in the game list widget. |
| 186 | void OnGameListLoadFile(QString game_path); | 188 | void OnGameListLoadFile(QString game_path); |
| 187 | void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path); | 189 | void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path); |
| @@ -216,10 +218,12 @@ private slots: | |||
| 216 | 218 | ||
| 217 | private: | 219 | private: |
| 218 | std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); | 220 | std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); |
| 219 | void UpdateWindowTitle(const QString& title_name = {}); | 221 | void UpdateWindowTitle(const std::string& title_name = {}, |
| 222 | const std::string& title_version = {}); | ||
| 220 | void UpdateStatusBar(); | 223 | void UpdateStatusBar(); |
| 221 | void HideMouseCursor(); | 224 | void HideMouseCursor(); |
| 222 | void ShowMouseCursor(); | 225 | void ShowMouseCursor(); |
| 226 | void OpenURL(const QUrl& url); | ||
| 223 | 227 | ||
| 224 | Ui::MainWindow ui; | 228 | Ui::MainWindow ui; |
| 225 | 229 | ||
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index b5745dfd5..bee6e107e 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui | |||
| @@ -114,6 +114,8 @@ | |||
| 114 | </property> | 114 | </property> |
| 115 | <addaction name="action_Report_Compatibility"/> | 115 | <addaction name="action_Report_Compatibility"/> |
| 116 | <addaction name="action_Open_Mods_Page"/> | 116 | <addaction name="action_Open_Mods_Page"/> |
| 117 | <addaction name="action_Open_Quickstart_Guide"/> | ||
| 118 | <addaction name="action_Open_FAQ"/> | ||
| 117 | <addaction name="separator"/> | 119 | <addaction name="separator"/> |
| 118 | <addaction name="action_About"/> | 120 | <addaction name="action_About"/> |
| 119 | </widget> | 121 | </widget> |
| @@ -262,6 +264,16 @@ | |||
| 262 | <string>Open Mods Page</string> | 264 | <string>Open Mods Page</string> |
| 263 | </property> | 265 | </property> |
| 264 | </action> | 266 | </action> |
| 267 | <action name="action_Open_Quickstart_Guide"> | ||
| 268 | <property name="text"> | ||
| 269 | <string>Open Quickstart Guide</string> | ||
| 270 | </property> | ||
| 271 | </action> | ||
| 272 | <action name="action_Open_FAQ"> | ||
| 273 | <property name="text"> | ||
| 274 | <string>FAQ</string> | ||
| 275 | </property> | ||
| 276 | </action> | ||
| 265 | <action name="action_Open_yuzu_Folder"> | 277 | <action name="action_Open_yuzu_Folder"> |
| 266 | <property name="text"> | 278 | <property name="text"> |
| 267 | <string>Open yuzu Folder</string> | 279 | <string>Open yuzu Folder</string> |