diff options
34 files changed, 682 insertions, 38 deletions
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index fc922c31b..070151bec 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt | |||
| @@ -189,6 +189,10 @@ if (ANDROID) | |||
| 189 | endif() | 189 | endif() |
| 190 | endif() | 190 | endif() |
| 191 | 191 | ||
| 192 | if (UNIX) | ||
| 193 | add_subdirectory(gamemode) | ||
| 194 | endif() | ||
| 195 | |||
| 192 | # Breakpad | 196 | # Breakpad |
| 193 | # https://github.com/microsoft/vcpkg/blob/master/ports/breakpad/CMakeLists.txt | 197 | # https://github.com/microsoft/vcpkg/blob/master/ports/breakpad/CMakeLists.txt |
| 194 | if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client) | 198 | if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client) |
diff --git a/externals/gamemode/CMakeLists.txt b/externals/gamemode/CMakeLists.txt new file mode 100644 index 000000000..87095642e --- /dev/null +++ b/externals/gamemode/CMakeLists.txt | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | # SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | project(gamemode LANGUAGES CXX C) | ||
| 5 | |||
| 6 | add_library(gamemode include/gamemode_client.h) | ||
| 7 | |||
| 8 | target_link_libraries(gamemode PRIVATE common) | ||
| 9 | |||
| 10 | target_include_directories(gamemode PUBLIC include) | ||
| 11 | set_target_properties(gamemode PROPERTIES LINKER_LANGUAGE C) | ||
diff --git a/externals/gamemode/include/gamemode_client.h b/externals/gamemode/include/gamemode_client.h new file mode 100644 index 000000000..184812334 --- /dev/null +++ b/externals/gamemode/include/gamemode_client.h | |||
| @@ -0,0 +1,379 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2017-2019 Feral Interactive | ||
| 2 | // SPDX-License-Identifier: BSD-3-Clause | ||
| 3 | |||
| 4 | /* | ||
| 5 | |||
| 6 | Copyright (c) 2017-2019, Feral Interactive | ||
| 7 | All rights reserved. | ||
| 8 | |||
| 9 | Redistribution and use in source and binary forms, with or without | ||
| 10 | modification, are permitted provided that the following conditions are met: | ||
| 11 | |||
| 12 | * Redistributions of source code must retain the above copyright notice, | ||
| 13 | this list of conditions and the following disclaimer. | ||
| 14 | * Redistributions in binary form must reproduce the above copyright | ||
| 15 | notice, this list of conditions and the following disclaimer in the | ||
| 16 | documentation and/or other materials provided with the distribution. | ||
| 17 | * Neither the name of Feral Interactive nor the names of its contributors | ||
| 18 | may be used to endorse or promote products derived from this software | ||
| 19 | without specific prior written permission. | ||
| 20 | |||
| 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| 22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| 23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
| 24 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | ||
| 25 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
| 26 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
| 27 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
| 28 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
| 29 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| 30 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| 31 | POSSIBILITY OF SUCH DAMAGE. | ||
| 32 | |||
| 33 | */ | ||
| 34 | #ifndef CLIENT_GAMEMODE_H | ||
| 35 | #define CLIENT_GAMEMODE_H | ||
| 36 | /* | ||
| 37 | * GameMode supports the following client functions | ||
| 38 | * Requests are refcounted in the daemon | ||
| 39 | * | ||
| 40 | * int gamemode_request_start() - Request gamemode starts | ||
| 41 | * 0 if the request was sent successfully | ||
| 42 | * -1 if the request failed | ||
| 43 | * | ||
| 44 | * int gamemode_request_end() - Request gamemode ends | ||
| 45 | * 0 if the request was sent successfully | ||
| 46 | * -1 if the request failed | ||
| 47 | * | ||
| 48 | * GAMEMODE_AUTO can be defined to make the above two functions apply during static init and | ||
| 49 | * destruction, as appropriate. In this configuration, errors will be printed to stderr | ||
| 50 | * | ||
| 51 | * int gamemode_query_status() - Query the current status of gamemode | ||
| 52 | * 0 if gamemode is inactive | ||
| 53 | * 1 if gamemode is active | ||
| 54 | * 2 if gamemode is active and this client is registered | ||
| 55 | * -1 if the query failed | ||
| 56 | * | ||
| 57 | * int gamemode_request_start_for(pid_t pid) - Request gamemode starts for another process | ||
| 58 | * 0 if the request was sent successfully | ||
| 59 | * -1 if the request failed | ||
| 60 | * -2 if the request was rejected | ||
| 61 | * | ||
| 62 | * int gamemode_request_end_for(pid_t pid) - Request gamemode ends for another process | ||
| 63 | * 0 if the request was sent successfully | ||
| 64 | * -1 if the request failed | ||
| 65 | * -2 if the request was rejected | ||
| 66 | * | ||
| 67 | * int gamemode_query_status_for(pid_t pid) - Query status of gamemode for another process | ||
| 68 | * 0 if gamemode is inactive | ||
| 69 | * 1 if gamemode is active | ||
| 70 | * 2 if gamemode is active and this client is registered | ||
| 71 | * -1 if the query failed | ||
| 72 | * | ||
| 73 | * const char* gamemode_error_string() - Get an error string | ||
| 74 | * returns a string describing any of the above errors | ||
| 75 | * | ||
| 76 | * Note: All the above requests can be blocking - dbus requests can and will block while the daemon | ||
| 77 | * handles the request. It is not recommended to make these calls in performance critical code | ||
| 78 | */ | ||
| 79 | |||
| 80 | #include <stdbool.h> | ||
| 81 | #include <stdio.h> | ||
| 82 | |||
| 83 | #include <dlfcn.h> | ||
| 84 | #include <string.h> | ||
| 85 | |||
| 86 | #include <assert.h> | ||
| 87 | |||
| 88 | #include <sys/types.h> | ||
| 89 | |||
| 90 | static char internal_gamemode_client_error_string[512] = { 0 }; | ||
| 91 | |||
| 92 | /** | ||
| 93 | * Load libgamemode dynamically to dislodge us from most dependencies. | ||
| 94 | * This allows clients to link and/or use this regardless of runtime. | ||
| 95 | * See SDL2 for an example of the reasoning behind this in terms of | ||
| 96 | * dynamic versioning as well. | ||
| 97 | */ | ||
| 98 | static volatile int internal_libgamemode_loaded = 1; | ||
| 99 | |||
| 100 | /* Typedefs for the functions to load */ | ||
| 101 | typedef int (*api_call_return_int)(void); | ||
| 102 | typedef const char *(*api_call_return_cstring)(void); | ||
| 103 | typedef int (*api_call_pid_return_int)(pid_t); | ||
| 104 | |||
| 105 | /* Storage for functors */ | ||
| 106 | static api_call_return_int REAL_internal_gamemode_request_start = NULL; | ||
| 107 | static api_call_return_int REAL_internal_gamemode_request_end = NULL; | ||
| 108 | static api_call_return_int REAL_internal_gamemode_query_status = NULL; | ||
| 109 | static api_call_return_cstring REAL_internal_gamemode_error_string = NULL; | ||
| 110 | static api_call_pid_return_int REAL_internal_gamemode_request_start_for = NULL; | ||
| 111 | static api_call_pid_return_int REAL_internal_gamemode_request_end_for = NULL; | ||
| 112 | static api_call_pid_return_int REAL_internal_gamemode_query_status_for = NULL; | ||
| 113 | |||
| 114 | /** | ||
| 115 | * Internal helper to perform the symbol binding safely. | ||
| 116 | * | ||
| 117 | * Returns 0 on success and -1 on failure | ||
| 118 | */ | ||
| 119 | __attribute__((always_inline)) static inline int internal_bind_libgamemode_symbol( | ||
| 120 | void *handle, const char *name, void **out_func, size_t func_size, bool required) | ||
| 121 | { | ||
| 122 | void *symbol_lookup = NULL; | ||
| 123 | char *dl_error = NULL; | ||
| 124 | |||
| 125 | /* Safely look up the symbol */ | ||
| 126 | symbol_lookup = dlsym(handle, name); | ||
| 127 | dl_error = dlerror(); | ||
| 128 | if (required && (dl_error || !symbol_lookup)) { | ||
| 129 | snprintf(internal_gamemode_client_error_string, | ||
| 130 | sizeof(internal_gamemode_client_error_string), | ||
| 131 | "dlsym failed - %s", | ||
| 132 | dl_error); | ||
| 133 | return -1; | ||
| 134 | } | ||
| 135 | |||
| 136 | /* Have the symbol correctly, copy it to make it usable */ | ||
| 137 | memcpy(out_func, &symbol_lookup, func_size); | ||
| 138 | return 0; | ||
| 139 | } | ||
| 140 | |||
| 141 | /** | ||
| 142 | * Loads libgamemode and needed functions | ||
| 143 | * | ||
| 144 | * Returns 0 on success and -1 on failure | ||
| 145 | */ | ||
| 146 | __attribute__((always_inline)) static inline int internal_load_libgamemode(void) | ||
| 147 | { | ||
| 148 | /* We start at 1, 0 is a success and -1 is a fail */ | ||
| 149 | if (internal_libgamemode_loaded != 1) { | ||
| 150 | return internal_libgamemode_loaded; | ||
| 151 | } | ||
| 152 | |||
| 153 | /* Anonymous struct type to define our bindings */ | ||
| 154 | struct binding { | ||
| 155 | const char *name; | ||
| 156 | void **functor; | ||
| 157 | size_t func_size; | ||
| 158 | bool required; | ||
| 159 | } bindings[] = { | ||
| 160 | { "real_gamemode_request_start", | ||
| 161 | (void **)&REAL_internal_gamemode_request_start, | ||
| 162 | sizeof(REAL_internal_gamemode_request_start), | ||
| 163 | true }, | ||
| 164 | { "real_gamemode_request_end", | ||
| 165 | (void **)&REAL_internal_gamemode_request_end, | ||
| 166 | sizeof(REAL_internal_gamemode_request_end), | ||
| 167 | true }, | ||
| 168 | { "real_gamemode_query_status", | ||
| 169 | (void **)&REAL_internal_gamemode_query_status, | ||
| 170 | sizeof(REAL_internal_gamemode_query_status), | ||
| 171 | false }, | ||
| 172 | { "real_gamemode_error_string", | ||
| 173 | (void **)&REAL_internal_gamemode_error_string, | ||
| 174 | sizeof(REAL_internal_gamemode_error_string), | ||
| 175 | true }, | ||
| 176 | { "real_gamemode_request_start_for", | ||
| 177 | (void **)&REAL_internal_gamemode_request_start_for, | ||
| 178 | sizeof(REAL_internal_gamemode_request_start_for), | ||
| 179 | false }, | ||
| 180 | { "real_gamemode_request_end_for", | ||
| 181 | (void **)&REAL_internal_gamemode_request_end_for, | ||
| 182 | sizeof(REAL_internal_gamemode_request_end_for), | ||
| 183 | false }, | ||
| 184 | { "real_gamemode_query_status_for", | ||
| 185 | (void **)&REAL_internal_gamemode_query_status_for, | ||
| 186 | sizeof(REAL_internal_gamemode_query_status_for), | ||
| 187 | false }, | ||
| 188 | }; | ||
| 189 | |||
| 190 | void *libgamemode = NULL; | ||
| 191 | |||
| 192 | /* Try and load libgamemode */ | ||
| 193 | libgamemode = dlopen("libgamemode.so.0", RTLD_NOW); | ||
| 194 | if (!libgamemode) { | ||
| 195 | /* Attempt to load unversioned library for compatibility with older | ||
| 196 | * versions (as of writing, there are no ABI changes between the two - | ||
| 197 | * this may need to change if ever ABI-breaking changes are made) */ | ||
| 198 | libgamemode = dlopen("libgamemode.so", RTLD_NOW); | ||
| 199 | if (!libgamemode) { | ||
| 200 | snprintf(internal_gamemode_client_error_string, | ||
| 201 | sizeof(internal_gamemode_client_error_string), | ||
| 202 | "dlopen failed - %s", | ||
| 203 | dlerror()); | ||
| 204 | internal_libgamemode_loaded = -1; | ||
| 205 | return -1; | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | /* Attempt to bind all symbols */ | ||
| 210 | for (size_t i = 0; i < sizeof(bindings) / sizeof(bindings[0]); i++) { | ||
| 211 | struct binding *binder = &bindings[i]; | ||
| 212 | |||
| 213 | if (internal_bind_libgamemode_symbol(libgamemode, | ||
| 214 | binder->name, | ||
| 215 | binder->functor, | ||
| 216 | binder->func_size, | ||
| 217 | binder->required)) { | ||
| 218 | internal_libgamemode_loaded = -1; | ||
| 219 | return -1; | ||
| 220 | }; | ||
| 221 | } | ||
| 222 | |||
| 223 | /* Success */ | ||
| 224 | internal_libgamemode_loaded = 0; | ||
| 225 | return 0; | ||
| 226 | } | ||
| 227 | |||
| 228 | /** | ||
| 229 | * Redirect to the real libgamemode | ||
| 230 | */ | ||
| 231 | __attribute__((always_inline)) static inline const char *gamemode_error_string(void) | ||
| 232 | { | ||
| 233 | /* If we fail to load the system gamemode, or we have an error string already, return our error | ||
| 234 | * string instead of diverting to the system version */ | ||
| 235 | if (internal_load_libgamemode() < 0 || internal_gamemode_client_error_string[0] != '\0') { | ||
| 236 | return internal_gamemode_client_error_string; | ||
| 237 | } | ||
| 238 | |||
| 239 | /* Assert for static analyser that the function is not NULL */ | ||
| 240 | assert(REAL_internal_gamemode_error_string != NULL); | ||
| 241 | |||
| 242 | return REAL_internal_gamemode_error_string(); | ||
| 243 | } | ||
| 244 | |||
| 245 | /** | ||
| 246 | * Redirect to the real libgamemode | ||
| 247 | * Allow automatically requesting game mode | ||
| 248 | * Also prints errors as they happen. | ||
| 249 | */ | ||
| 250 | #ifdef GAMEMODE_AUTO | ||
| 251 | __attribute__((constructor)) | ||
| 252 | #else | ||
| 253 | __attribute__((always_inline)) static inline | ||
| 254 | #endif | ||
| 255 | int gamemode_request_start(void) | ||
| 256 | { | ||
| 257 | /* Need to load gamemode */ | ||
| 258 | if (internal_load_libgamemode() < 0) { | ||
| 259 | #ifdef GAMEMODE_AUTO | ||
| 260 | fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); | ||
| 261 | #endif | ||
| 262 | return -1; | ||
| 263 | } | ||
| 264 | |||
| 265 | /* Assert for static analyser that the function is not NULL */ | ||
| 266 | assert(REAL_internal_gamemode_request_start != NULL); | ||
| 267 | |||
| 268 | if (REAL_internal_gamemode_request_start() < 0) { | ||
| 269 | #ifdef GAMEMODE_AUTO | ||
| 270 | fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); | ||
| 271 | #endif | ||
| 272 | return -1; | ||
| 273 | } | ||
| 274 | |||
| 275 | return 0; | ||
| 276 | } | ||
| 277 | |||
| 278 | /* Redirect to the real libgamemode */ | ||
| 279 | #ifdef GAMEMODE_AUTO | ||
| 280 | __attribute__((destructor)) | ||
| 281 | #else | ||
| 282 | __attribute__((always_inline)) static inline | ||
| 283 | #endif | ||
| 284 | int gamemode_request_end(void) | ||
| 285 | { | ||
| 286 | /* Need to load gamemode */ | ||
| 287 | if (internal_load_libgamemode() < 0) { | ||
| 288 | #ifdef GAMEMODE_AUTO | ||
| 289 | fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); | ||
| 290 | #endif | ||
| 291 | return -1; | ||
| 292 | } | ||
| 293 | |||
| 294 | /* Assert for static analyser that the function is not NULL */ | ||
| 295 | assert(REAL_internal_gamemode_request_end != NULL); | ||
| 296 | |||
| 297 | if (REAL_internal_gamemode_request_end() < 0) { | ||
| 298 | #ifdef GAMEMODE_AUTO | ||
| 299 | fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); | ||
| 300 | #endif | ||
| 301 | return -1; | ||
| 302 | } | ||
| 303 | |||
| 304 | return 0; | ||
| 305 | } | ||
| 306 | |||
| 307 | /* Redirect to the real libgamemode */ | ||
| 308 | __attribute__((always_inline)) static inline int gamemode_query_status(void) | ||
| 309 | { | ||
| 310 | /* Need to load gamemode */ | ||
| 311 | if (internal_load_libgamemode() < 0) { | ||
| 312 | return -1; | ||
| 313 | } | ||
| 314 | |||
| 315 | if (REAL_internal_gamemode_query_status == NULL) { | ||
| 316 | snprintf(internal_gamemode_client_error_string, | ||
| 317 | sizeof(internal_gamemode_client_error_string), | ||
| 318 | "gamemode_query_status missing (older host?)"); | ||
| 319 | return -1; | ||
| 320 | } | ||
| 321 | |||
| 322 | return REAL_internal_gamemode_query_status(); | ||
| 323 | } | ||
| 324 | |||
| 325 | /* Redirect to the real libgamemode */ | ||
| 326 | __attribute__((always_inline)) static inline int gamemode_request_start_for(pid_t pid) | ||
| 327 | { | ||
| 328 | /* Need to load gamemode */ | ||
| 329 | if (internal_load_libgamemode() < 0) { | ||
| 330 | return -1; | ||
| 331 | } | ||
| 332 | |||
| 333 | if (REAL_internal_gamemode_request_start_for == NULL) { | ||
| 334 | snprintf(internal_gamemode_client_error_string, | ||
| 335 | sizeof(internal_gamemode_client_error_string), | ||
| 336 | "gamemode_request_start_for missing (older host?)"); | ||
| 337 | return -1; | ||
| 338 | } | ||
| 339 | |||
| 340 | return REAL_internal_gamemode_request_start_for(pid); | ||
| 341 | } | ||
| 342 | |||
| 343 | /* Redirect to the real libgamemode */ | ||
| 344 | __attribute__((always_inline)) static inline int gamemode_request_end_for(pid_t pid) | ||
| 345 | { | ||
| 346 | /* Need to load gamemode */ | ||
| 347 | if (internal_load_libgamemode() < 0) { | ||
| 348 | return -1; | ||
| 349 | } | ||
| 350 | |||
| 351 | if (REAL_internal_gamemode_request_end_for == NULL) { | ||
| 352 | snprintf(internal_gamemode_client_error_string, | ||
| 353 | sizeof(internal_gamemode_client_error_string), | ||
| 354 | "gamemode_request_end_for missing (older host?)"); | ||
| 355 | return -1; | ||
| 356 | } | ||
| 357 | |||
| 358 | return REAL_internal_gamemode_request_end_for(pid); | ||
| 359 | } | ||
| 360 | |||
| 361 | /* Redirect to the real libgamemode */ | ||
| 362 | __attribute__((always_inline)) static inline int gamemode_query_status_for(pid_t pid) | ||
| 363 | { | ||
| 364 | /* Need to load gamemode */ | ||
| 365 | if (internal_load_libgamemode() < 0) { | ||
| 366 | return -1; | ||
| 367 | } | ||
| 368 | |||
| 369 | if (REAL_internal_gamemode_query_status_for == NULL) { | ||
| 370 | snprintf(internal_gamemode_client_error_string, | ||
| 371 | sizeof(internal_gamemode_client_error_string), | ||
| 372 | "gamemode_query_status_for missing (older host?)"); | ||
| 373 | return -1; | ||
| 374 | } | ||
| 375 | |||
| 376 | return REAL_internal_gamemode_query_status_for(pid); | ||
| 377 | } | ||
| 378 | |||
| 379 | #endif // CLIENT_GAMEMODE_H | ||
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index e216eb3de..57cbb9d07 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -174,6 +174,15 @@ if(ANDROID) | |||
| 174 | ) | 174 | ) |
| 175 | endif() | 175 | endif() |
| 176 | 176 | ||
| 177 | if (UNIX) | ||
| 178 | target_sources(common PRIVATE | ||
| 179 | linux/gamemode.cpp | ||
| 180 | linux/gamemode.h | ||
| 181 | ) | ||
| 182 | |||
| 183 | target_link_libraries(common PRIVATE gamemode) | ||
| 184 | endif() | ||
| 185 | |||
| 177 | if(ARCHITECTURE_x86_64) | 186 | if(ARCHITECTURE_x86_64) |
| 178 | target_sources(common | 187 | target_sources(common |
| 179 | PRIVATE | 188 | PRIVATE |
diff --git a/src/common/linux/gamemode.cpp b/src/common/linux/gamemode.cpp new file mode 100644 index 000000000..8876d8dc4 --- /dev/null +++ b/src/common/linux/gamemode.cpp | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <gamemode_client.h> | ||
| 5 | |||
| 6 | #include "common/linux/gamemode.h" | ||
| 7 | #include "common/settings.h" | ||
| 8 | |||
| 9 | namespace Common::Linux { | ||
| 10 | |||
| 11 | void StartGamemode() { | ||
| 12 | if (Settings::values.enable_gamemode) { | ||
| 13 | if (gamemode_request_start() < 0) { | ||
| 14 | LOG_WARNING(Frontend, "Failed to start gamemode: {}", gamemode_error_string()); | ||
| 15 | } else { | ||
| 16 | LOG_INFO(Frontend, "Started gamemode"); | ||
| 17 | } | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 21 | void StopGamemode() { | ||
| 22 | if (Settings::values.enable_gamemode) { | ||
| 23 | if (gamemode_request_end() < 0) { | ||
| 24 | LOG_WARNING(Frontend, "Failed to stop gamemode: {}", gamemode_error_string()); | ||
| 25 | } else { | ||
| 26 | LOG_INFO(Frontend, "Stopped gamemode"); | ||
| 27 | } | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | void SetGamemodeState(bool state) { | ||
| 32 | if (state) { | ||
| 33 | StartGamemode(); | ||
| 34 | } else { | ||
| 35 | StopGamemode(); | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | } // namespace Common::Linux | ||
diff --git a/src/common/linux/gamemode.h b/src/common/linux/gamemode.h new file mode 100644 index 000000000..b80646ae2 --- /dev/null +++ b/src/common/linux/gamemode.h | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | namespace Common::Linux { | ||
| 7 | |||
| 8 | /** | ||
| 9 | * Start the (Feral Interactive) Linux gamemode if it is installed and it is activated | ||
| 10 | */ | ||
| 11 | void StartGamemode(); | ||
| 12 | |||
| 13 | /** | ||
| 14 | * Stop the (Feral Interactive) Linux gamemode if it is installed and it is activated | ||
| 15 | */ | ||
| 16 | void StopGamemode(); | ||
| 17 | |||
| 18 | /** | ||
| 19 | * Start or stop the (Feral Interactive) Linux gamemode if it is installed and it is activated | ||
| 20 | * @param state The new state the gamemode should have | ||
| 21 | */ | ||
| 22 | void SetGamemodeState(bool state); | ||
| 23 | |||
| 24 | } // namespace Common::Linux | ||
diff --git a/src/common/settings.cpp b/src/common/settings.cpp index a10131eb2..3e829253f 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp | |||
| @@ -219,6 +219,8 @@ const char* TranslateCategory(Category category) { | |||
| 219 | return "Services"; | 219 | return "Services"; |
| 220 | case Category::Paths: | 220 | case Category::Paths: |
| 221 | return "Paths"; | 221 | return "Paths"; |
| 222 | case Category::Linux: | ||
| 223 | return "Linux"; | ||
| 222 | case Category::MaxEnum: | 224 | case Category::MaxEnum: |
| 223 | break; | 225 | break; |
| 224 | } | 226 | } |
diff --git a/src/common/settings.h b/src/common/settings.h index b929fd957..6425cd98f 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -429,6 +429,9 @@ struct Values { | |||
| 429 | true, | 429 | true, |
| 430 | true}; | 430 | true}; |
| 431 | 431 | ||
| 432 | // Linux | ||
| 433 | SwitchableSetting<bool> enable_gamemode{linkage, true, "enable_gamemode", Category::Linux}; | ||
| 434 | |||
| 432 | // Controls | 435 | // Controls |
| 433 | InputSetting<std::array<PlayerInput, 10>> players; | 436 | InputSetting<std::array<PlayerInput, 10>> players; |
| 434 | 437 | ||
diff --git a/src/common/settings_common.h b/src/common/settings_common.h index 7943223eb..344c04439 100644 --- a/src/common/settings_common.h +++ b/src/common/settings_common.h | |||
| @@ -41,6 +41,7 @@ enum class Category : u32 { | |||
| 41 | Multiplayer, | 41 | Multiplayer, |
| 42 | Services, | 42 | Services, |
| 43 | Paths, | 43 | Paths, |
| 44 | Linux, | ||
| 44 | MaxEnum, | 45 | MaxEnum, |
| 45 | }; | 46 | }; |
| 46 | 47 | ||
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp index 2705ab140..9319ea007 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" | 5 | #include "shader_recompiler/backend/glasm/glasm_emit_context.h" |
| 6 | #include "shader_recompiler/frontend/ir/program.h" | 6 | #include "shader_recompiler/frontend/ir/program.h" |
| 7 | #include "shader_recompiler/frontend/ir/value.h" | 7 | #include "shader_recompiler/frontend/ir/value.h" |
| 8 | #include "shader_recompiler/profile.h" | ||
| 8 | #include "shader_recompiler/runtime_info.h" | 9 | #include "shader_recompiler/runtime_info.h" |
| 9 | 10 | ||
| 10 | namespace Shader::Backend::GLASM { | 11 | namespace Shader::Backend::GLASM { |
| @@ -35,7 +36,9 @@ void GlobalStorageOp(EmitContext& ctx, Register address, bool pointer_based, std | |||
| 35 | continue; | 36 | continue; |
| 36 | } | 37 | } |
| 37 | const auto& ssbo{ctx.info.storage_buffers_descriptors[index]}; | 38 | const auto& ssbo{ctx.info.storage_buffers_descriptors[index]}; |
| 38 | ctx.Add("LDC.U64 DC.x,c{}[{}];" // ssbo_addr | 39 | const u64 ssbo_align_mask{~(ctx.profile.min_ssbo_alignment - 1U)}; |
| 40 | ctx.Add("LDC.U64 DC.x,c{}[{}];" // unaligned_ssbo_addr | ||
| 41 | "AND.U64 DC.x,DC.x,{};" // ssbo_addr = unaligned_ssbo_addr & ssbo_align_mask | ||
| 39 | "LDC.U32 RC.x,c{}[{}];" // ssbo_size_u32 | 42 | "LDC.U32 RC.x,c{}[{}];" // ssbo_size_u32 |
| 40 | "CVT.U64.U32 DC.y,RC.x;" // ssbo_size = ssbo_size_u32 | 43 | "CVT.U64.U32 DC.y,RC.x;" // ssbo_size = ssbo_size_u32 |
| 41 | "ADD.U64 DC.y,DC.y,DC.x;" // ssbo_end = ssbo_addr + ssbo_size | 44 | "ADD.U64 DC.y,DC.y,DC.x;" // ssbo_end = ssbo_addr + ssbo_size |
| @@ -44,8 +47,8 @@ void GlobalStorageOp(EmitContext& ctx, Register address, bool pointer_based, std | |||
| 44 | "AND.U.CC RC.x,RC.x,RC.y;" // cond = a && b | 47 | "AND.U.CC RC.x,RC.x,RC.y;" // cond = a && b |
| 45 | "IF NE.x;" // if cond | 48 | "IF NE.x;" // if cond |
| 46 | "SUB.U64 DC.x,{}.x,DC.x;", // offset = input_addr - ssbo_addr | 49 | "SUB.U64 DC.x,{}.x,DC.x;", // offset = input_addr - ssbo_addr |
| 47 | ssbo.cbuf_index, ssbo.cbuf_offset, ssbo.cbuf_index, ssbo.cbuf_offset + 8, address, | 50 | ssbo.cbuf_index, ssbo.cbuf_offset, ssbo_align_mask, ssbo.cbuf_index, |
| 48 | address, address); | 51 | ssbo.cbuf_offset + 8, address, address, address); |
| 49 | if (pointer_based) { | 52 | if (pointer_based) { |
| 50 | ctx.Add("PK64.U DC.y,c[{}];" // host_ssbo = cbuf | 53 | ctx.Add("PK64.U DC.y,c[{}];" // host_ssbo = cbuf |
| 51 | "ADD.U64 DC.x,DC.x,DC.y;" // host_addr = host_ssbo + offset | 54 | "ADD.U64 DC.x,DC.x,DC.y;" // host_addr = host_ssbo + offset |
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp index 9ff4028c2..fd9a99449 100644 --- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp +++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp | |||
| @@ -601,7 +601,10 @@ std::string EmitContext::DefineGlobalMemoryFunctions() { | |||
| 601 | addr_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, addr_loc / 16, Swizzle(addr_loc)); | 601 | addr_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, addr_loc / 16, Swizzle(addr_loc)); |
| 602 | size_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, size_loc / 16, Swizzle(size_loc)); | 602 | size_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, size_loc / 16, Swizzle(size_loc)); |
| 603 | } | 603 | } |
| 604 | const auto addr_pack{fmt::format("packUint2x32(uvec2({},{}))", addr_xy[0], addr_xy[1])}; | 604 | const u32 ssbo_align_mask{~(static_cast<u32>(profile.min_ssbo_alignment) - 1U)}; |
| 605 | const auto aligned_low_addr{fmt::format("{}&{}", addr_xy[0], ssbo_align_mask)}; | ||
| 606 | const auto aligned_addr{fmt::format("uvec2({},{})", aligned_low_addr, addr_xy[1])}; | ||
| 607 | const auto addr_pack{fmt::format("packUint2x32({})", aligned_addr)}; | ||
| 605 | const auto addr_statment{fmt::format("uint64_t {}={};", ssbo_addr, addr_pack)}; | 608 | const auto addr_statment{fmt::format("uint64_t {}={};", ssbo_addr, addr_pack)}; |
| 606 | func += addr_statment; | 609 | func += addr_statment; |
| 607 | 610 | ||
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 57df6fc34..3350f1f85 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp | |||
| @@ -891,7 +891,9 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) { | |||
| 891 | const Id ssbo_size_pointer{OpAccessChain(uniform_types.U32, cbufs[ssbo.cbuf_index].U32, | 891 | const Id ssbo_size_pointer{OpAccessChain(uniform_types.U32, cbufs[ssbo.cbuf_index].U32, |
| 892 | zero, ssbo_size_cbuf_offset)}; | 892 | zero, ssbo_size_cbuf_offset)}; |
| 893 | 893 | ||
| 894 | const Id ssbo_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))}; | 894 | const u64 ssbo_align_mask{~(profile.min_ssbo_alignment - 1U)}; |
| 895 | const Id unaligned_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))}; | ||
| 896 | const Id ssbo_addr{OpBitwiseAnd(U64, unaligned_addr, Constant(U64, ssbo_align_mask))}; | ||
| 895 | const Id ssbo_size{OpUConvert(U64, OpLoad(U32[1], ssbo_size_pointer))}; | 897 | const Id ssbo_size{OpUConvert(U64, OpLoad(U32[1], ssbo_size_pointer))}; |
| 896 | const Id ssbo_end{OpIAdd(U64, ssbo_addr, ssbo_size)}; | 898 | const Id ssbo_end{OpIAdd(U64, ssbo_addr, ssbo_size)}; |
| 897 | const Id cond{OpLogicalAnd(U1, OpUGreaterThanEqual(U1, addr, ssbo_addr), | 899 | const Id cond{OpLogicalAnd(U1, OpUGreaterThanEqual(U1, addr, ssbo_addr), |
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index 8fac6bad3..321ea625b 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp | |||
| @@ -298,7 +298,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo | |||
| 298 | 298 | ||
| 299 | Optimization::PositionPass(env, program); | 299 | Optimization::PositionPass(env, program); |
| 300 | 300 | ||
| 301 | Optimization::GlobalMemoryToStorageBufferPass(program); | 301 | Optimization::GlobalMemoryToStorageBufferPass(program, host_info); |
| 302 | Optimization::TexturePass(env, program, host_info); | 302 | Optimization::TexturePass(env, program, host_info); |
| 303 | 303 | ||
| 304 | if (Settings::values.resolution_info.active) { | 304 | if (Settings::values.resolution_info.active) { |
diff --git a/src/shader_recompiler/host_translate_info.h b/src/shader_recompiler/host_translate_info.h index 7d2ded907..1b53404fc 100644 --- a/src/shader_recompiler/host_translate_info.h +++ b/src/shader_recompiler/host_translate_info.h | |||
| @@ -16,6 +16,7 @@ struct HostTranslateInfo { | |||
| 16 | bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered | 16 | bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered |
| 17 | bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers | 17 | bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers |
| 18 | bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS | 18 | bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS |
| 19 | u32 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs | ||
| 19 | bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry | 20 | bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry |
| 20 | ///< passthrough shaders | 21 | ///< passthrough shaders |
| 21 | bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional | 22 | bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional |
diff --git a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp index d1e59f22e..0cea79945 100644 --- a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp +++ b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include "shader_recompiler/frontend/ir/breadth_first_search.h" | 11 | #include "shader_recompiler/frontend/ir/breadth_first_search.h" |
| 12 | #include "shader_recompiler/frontend/ir/ir_emitter.h" | 12 | #include "shader_recompiler/frontend/ir/ir_emitter.h" |
| 13 | #include "shader_recompiler/frontend/ir/value.h" | 13 | #include "shader_recompiler/frontend/ir/value.h" |
| 14 | #include "shader_recompiler/host_translate_info.h" | ||
| 14 | #include "shader_recompiler/ir_opt/passes.h" | 15 | #include "shader_recompiler/ir_opt/passes.h" |
| 15 | 16 | ||
| 16 | namespace Shader::Optimization { | 17 | namespace Shader::Optimization { |
| @@ -408,7 +409,7 @@ void CollectStorageBuffers(IR::Block& block, IR::Inst& inst, StorageInfo& info) | |||
| 408 | } | 409 | } |
| 409 | 410 | ||
| 410 | /// Returns the offset in indices (not bytes) for an equivalent storage instruction | 411 | /// Returns the offset in indices (not bytes) for an equivalent storage instruction |
| 411 | IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer) { | 412 | IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer, u32 alignment) { |
| 412 | IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; | 413 | IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; |
| 413 | IR::U32 offset; | 414 | IR::U32 offset; |
| 414 | if (const std::optional<LowAddrInfo> low_addr{TrackLowAddress(&inst)}) { | 415 | if (const std::optional<LowAddrInfo> low_addr{TrackLowAddress(&inst)}) { |
| @@ -421,7 +422,10 @@ IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer | |||
| 421 | } | 422 | } |
| 422 | // Subtract the least significant 32 bits from the guest offset. The result is the storage | 423 | // Subtract the least significant 32 bits from the guest offset. The result is the storage |
| 423 | // buffer offset in bytes. | 424 | // buffer offset in bytes. |
| 424 | const IR::U32 low_cbuf{ir.GetCbuf(ir.Imm32(buffer.index), ir.Imm32(buffer.offset))}; | 425 | IR::U32 low_cbuf{ir.GetCbuf(ir.Imm32(buffer.index), ir.Imm32(buffer.offset))}; |
| 426 | |||
| 427 | // Align the offset base to match the host alignment requirements | ||
| 428 | low_cbuf = ir.BitwiseAnd(low_cbuf, ir.Imm32(~(alignment - 1U))); | ||
| 425 | return ir.ISub(offset, low_cbuf); | 429 | return ir.ISub(offset, low_cbuf); |
| 426 | } | 430 | } |
| 427 | 431 | ||
| @@ -516,7 +520,7 @@ void Replace(IR::Block& block, IR::Inst& inst, const IR::U32& storage_index, | |||
| 516 | } | 520 | } |
| 517 | } // Anonymous namespace | 521 | } // Anonymous namespace |
| 518 | 522 | ||
| 519 | void GlobalMemoryToStorageBufferPass(IR::Program& program) { | 523 | void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info) { |
| 520 | StorageInfo info; | 524 | StorageInfo info; |
| 521 | for (IR::Block* const block : program.post_order_blocks) { | 525 | for (IR::Block* const block : program.post_order_blocks) { |
| 522 | for (IR::Inst& inst : block->Instructions()) { | 526 | for (IR::Inst& inst : block->Instructions()) { |
| @@ -540,7 +544,8 @@ void GlobalMemoryToStorageBufferPass(IR::Program& program) { | |||
| 540 | const IR::U32 index{IR::Value{static_cast<u32>(info.set.index_of(it))}}; | 544 | const IR::U32 index{IR::Value{static_cast<u32>(info.set.index_of(it))}}; |
| 541 | IR::Block* const block{storage_inst.block}; | 545 | IR::Block* const block{storage_inst.block}; |
| 542 | IR::Inst* const inst{storage_inst.inst}; | 546 | IR::Inst* const inst{storage_inst.inst}; |
| 543 | const IR::U32 offset{StorageOffset(*block, *inst, storage_buffer)}; | 547 | const IR::U32 offset{ |
| 548 | StorageOffset(*block, *inst, storage_buffer, host_info.min_ssbo_alignment)}; | ||
| 544 | Replace(*block, *inst, index, offset); | 549 | Replace(*block, *inst, index, offset); |
| 545 | } | 550 | } |
| 546 | } | 551 | } |
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h index d4d5285e5..1e637cb23 100644 --- a/src/shader_recompiler/ir_opt/passes.h +++ b/src/shader_recompiler/ir_opt/passes.h | |||
| @@ -16,7 +16,7 @@ void CollectShaderInfoPass(Environment& env, IR::Program& program); | |||
| 16 | void ConditionalBarrierPass(IR::Program& program); | 16 | void ConditionalBarrierPass(IR::Program& program); |
| 17 | void ConstantPropagationPass(Environment& env, IR::Program& program); | 17 | void ConstantPropagationPass(Environment& env, IR::Program& program); |
| 18 | void DeadCodeEliminationPass(IR::Program& program); | 18 | void DeadCodeEliminationPass(IR::Program& program); |
| 19 | void GlobalMemoryToStorageBufferPass(IR::Program& program); | 19 | void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info); |
| 20 | void IdentityRemovalPass(IR::Program& program); | 20 | void IdentityRemovalPass(IR::Program& program); |
| 21 | void LowerFp64ToFp32(IR::Program& program); | 21 | void LowerFp64ToFp32(IR::Program& program); |
| 22 | void LowerFp16ToFp32(IR::Program& program); | 22 | void LowerFp16ToFp32(IR::Program& program); |
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index a9de9f4a9..66901a965 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h | |||
| @@ -85,6 +85,8 @@ struct Profile { | |||
| 85 | 85 | ||
| 86 | /// Maxwell and earlier nVidia architectures have broken robust support | 86 | /// Maxwell and earlier nVidia architectures have broken robust support |
| 87 | bool has_broken_robust{}; | 87 | bool has_broken_robust{}; |
| 88 | |||
| 89 | u64 min_ssbo_alignment{}; | ||
| 88 | }; | 90 | }; |
| 89 | 91 | ||
| 90 | } // namespace Shader | 92 | } // namespace Shader |
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 90dbd352f..6d1fc3887 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h | |||
| @@ -1753,15 +1753,25 @@ Binding BufferCache<P>::StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index, | |||
| 1753 | const u32 memory_layout_size = static_cast<u32>(gpu_memory->GetMemoryLayoutSize(gpu_addr)); | 1753 | const u32 memory_layout_size = static_cast<u32>(gpu_memory->GetMemoryLayoutSize(gpu_addr)); |
| 1754 | return std::min(memory_layout_size, static_cast<u32>(8_MiB)); | 1754 | return std::min(memory_layout_size, static_cast<u32>(8_MiB)); |
| 1755 | }(); | 1755 | }(); |
| 1756 | const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); | 1756 | // Alignment only applies to the offset of the buffer |
| 1757 | if (!cpu_addr || size == 0) { | 1757 | const u32 alignment = runtime.GetStorageBufferAlignment(); |
| 1758 | const GPUVAddr aligned_gpu_addr = Common::AlignDown(gpu_addr, alignment); | ||
| 1759 | const u32 aligned_size = static_cast<u32>(gpu_addr - aligned_gpu_addr) + size; | ||
| 1760 | |||
| 1761 | const std::optional<VAddr> aligned_cpu_addr = gpu_memory->GpuToCpuAddress(aligned_gpu_addr); | ||
| 1762 | if (!aligned_cpu_addr || size == 0) { | ||
| 1758 | LOG_WARNING(HW_GPU, "Failed to find storage buffer for cbuf index {}", cbuf_index); | 1763 | LOG_WARNING(HW_GPU, "Failed to find storage buffer for cbuf index {}", cbuf_index); |
| 1759 | return NULL_BINDING; | 1764 | return NULL_BINDING; |
| 1760 | } | 1765 | } |
| 1761 | const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, YUZU_PAGESIZE); | 1766 | const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); |
| 1767 | ASSERT_MSG(cpu_addr, "Unaligned storage buffer address not found for cbuf index {}", | ||
| 1768 | cbuf_index); | ||
| 1769 | // The end address used for size calculation does not need to be aligned | ||
| 1770 | const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, Core::Memory::YUZU_PAGESIZE); | ||
| 1771 | |||
| 1762 | const Binding binding{ | 1772 | const Binding binding{ |
| 1763 | .cpu_addr = *cpu_addr, | 1773 | .cpu_addr = *aligned_cpu_addr, |
| 1764 | .size = is_written ? size : static_cast<u32>(cpu_end - *cpu_addr), | 1774 | .size = is_written ? aligned_size : static_cast<u32>(cpu_end - *aligned_cpu_addr), |
| 1765 | .buffer_id = BufferId{}, | 1775 | .buffer_id = BufferId{}, |
| 1766 | }; | 1776 | }; |
| 1767 | return binding; | 1777 | return binding; |
diff --git a/src/video_core/buffer_cache/usage_tracker.h b/src/video_core/buffer_cache/usage_tracker.h index ab05fe415..5f8688d31 100644 --- a/src/video_core/buffer_cache/usage_tracker.h +++ b/src/video_core/buffer_cache/usage_tracker.h | |||
| @@ -58,7 +58,7 @@ private: | |||
| 58 | void TrackPage(u64 page, u64 offset, u64 size) noexcept { | 58 | void TrackPage(u64 page, u64 offset, u64 size) noexcept { |
| 59 | const size_t offset_in_page = offset % PAGE_BYTES; | 59 | const size_t offset_in_page = offset % PAGE_BYTES; |
| 60 | const size_t first_bit = offset_in_page >> BYTES_PER_BIT_SHIFT; | 60 | const size_t first_bit = offset_in_page >> BYTES_PER_BIT_SHIFT; |
| 61 | const size_t num_bits = std::min(size, PAGE_BYTES) >> BYTES_PER_BIT_SHIFT; | 61 | const size_t num_bits = std::min<size_t>(size, PAGE_BYTES) >> BYTES_PER_BIT_SHIFT; |
| 62 | const size_t mask = ~u64{0} >> (64 - num_bits); | 62 | const size_t mask = ~u64{0} >> (64 - num_bits); |
| 63 | pages[page] |= (~u64{0} & mask) << first_bit; | 63 | pages[page] |= (~u64{0} & mask) << first_bit; |
| 64 | } | 64 | } |
| @@ -66,7 +66,7 @@ private: | |||
| 66 | bool IsPageUsed(u64 page, u64 offset, u64 size) const noexcept { | 66 | bool IsPageUsed(u64 page, u64 offset, u64 size) const noexcept { |
| 67 | const size_t offset_in_page = offset % PAGE_BYTES; | 67 | const size_t offset_in_page = offset % PAGE_BYTES; |
| 68 | const size_t first_bit = offset_in_page >> BYTES_PER_BIT_SHIFT; | 68 | const size_t first_bit = offset_in_page >> BYTES_PER_BIT_SHIFT; |
| 69 | const size_t num_bits = std::min(size, PAGE_BYTES) >> BYTES_PER_BIT_SHIFT; | 69 | const size_t num_bits = std::min<size_t>(size, PAGE_BYTES) >> BYTES_PER_BIT_SHIFT; |
| 70 | const size_t mask = ~u64{0} >> (64 - num_bits); | 70 | const size_t mask = ~u64{0} >> (64 - num_bits); |
| 71 | const size_t mask2 = (~u64{0} & mask) << first_bit; | 71 | const size_t mask2 = (~u64{0} & mask) << first_bit; |
| 72 | return (pages[page] & mask2) != 0; | 72 | return (pages[page] & mask2) != 0; |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index feccf06f9..000f29a82 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h | |||
| @@ -191,6 +191,10 @@ public: | |||
| 191 | return device.CanReportMemoryUsage(); | 191 | return device.CanReportMemoryUsage(); |
| 192 | } | 192 | } |
| 193 | 193 | ||
| 194 | u32 GetStorageBufferAlignment() const { | ||
| 195 | return static_cast<u32>(device.GetShaderStorageBufferAlignment()); | ||
| 196 | } | ||
| 197 | |||
| 194 | private: | 198 | private: |
| 195 | static constexpr std::array PABO_LUT{ | 199 | static constexpr std::array PABO_LUT{ |
| 196 | GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV, | 200 | GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV, |
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 94258ccd0..993438a27 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp | |||
| @@ -265,33 +265,33 @@ std::string Device::GetVendorName() const { | |||
| 265 | if (vendor_name == "Intel") { | 265 | if (vendor_name == "Intel") { |
| 266 | // For Mesa, `Intel` is an overloaded vendor string that could mean crocus or iris. | 266 | // For Mesa, `Intel` is an overloaded vendor string that could mean crocus or iris. |
| 267 | // Simply return `INTEL` for those as well as the Windows driver. | 267 | // Simply return `INTEL` for those as well as the Windows driver. |
| 268 | return "INTEL"; | 268 | return "Intel"; |
| 269 | } | 269 | } |
| 270 | if (vendor_name == "Intel Open Source Technology Center") { | 270 | if (vendor_name == "Intel Open Source Technology Center") { |
| 271 | return "I965"; | 271 | return "i965"; |
| 272 | } | 272 | } |
| 273 | if (vendor_name == "Mesa Project") { | 273 | if (vendor_name == "Mesa Project") { |
| 274 | return "I915"; | 274 | return "i915"; |
| 275 | } | 275 | } |
| 276 | if (vendor_name == "Mesa/X.org") { | 276 | if (vendor_name == "Mesa/X.org") { |
| 277 | // This vendor string is overloaded between llvmpipe, softpipe, and virgl, so just return | 277 | // This vendor string is overloaded between llvmpipe, softpipe, and virgl, so just return |
| 278 | // MESA instead of one of those driver names. | 278 | // MESA instead of one of those driver names. |
| 279 | return "MESA"; | 279 | return "Mesa"; |
| 280 | } | 280 | } |
| 281 | if (vendor_name == "AMD") { | 281 | if (vendor_name == "AMD") { |
| 282 | return "RADEONSI"; | 282 | return "RadeonSI"; |
| 283 | } | 283 | } |
| 284 | if (vendor_name == "nouveau") { | 284 | if (vendor_name == "nouveau") { |
| 285 | return "NOUVEAU"; | 285 | return "Nouveau"; |
| 286 | } | 286 | } |
| 287 | if (vendor_name == "X.Org") { | 287 | if (vendor_name == "X.Org") { |
| 288 | return "R600"; | 288 | return "R600"; |
| 289 | } | 289 | } |
| 290 | if (vendor_name == "Collabora Ltd") { | 290 | if (vendor_name == "Collabora Ltd") { |
| 291 | return "ZINK"; | 291 | return "Zink"; |
| 292 | } | 292 | } |
| 293 | if (vendor_name == "Intel Corporation") { | 293 | if (vendor_name == "Intel Corporation") { |
| 294 | return "OPENSWR"; | 294 | return "OpenSWR"; |
| 295 | } | 295 | } |
| 296 | if (vendor_name == "Microsoft Corporation") { | 296 | if (vendor_name == "Microsoft Corporation") { |
| 297 | return "D3D12"; | 297 | return "D3D12"; |
| @@ -300,7 +300,7 @@ std::string Device::GetVendorName() const { | |||
| 300 | // Mesa's tegra driver reports `NVIDIA`. Only present in this list because the default | 300 | // Mesa's tegra driver reports `NVIDIA`. Only present in this list because the default |
| 301 | // strategy would have returned `NVIDIA` here for this driver, the same result as the | 301 | // strategy would have returned `NVIDIA` here for this driver, the same result as the |
| 302 | // proprietary driver. | 302 | // proprietary driver. |
| 303 | return "TEGRA"; | 303 | return "Tegra"; |
| 304 | } | 304 | } |
| 305 | return vendor_name; | 305 | return vendor_name; |
| 306 | } | 306 | } |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 2888e0238..26f2d0ea7 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -232,6 +232,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo | |||
| 232 | .has_gl_bool_ref_bug = device.HasBoolRefBug(), | 232 | .has_gl_bool_ref_bug = device.HasBoolRefBug(), |
| 233 | .ignore_nan_fp_comparisons = true, | 233 | .ignore_nan_fp_comparisons = true, |
| 234 | .gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(), | 234 | .gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(), |
| 235 | .min_ssbo_alignment = device.GetShaderStorageBufferAlignment(), | ||
| 235 | }, | 236 | }, |
| 236 | host_info{ | 237 | host_info{ |
| 237 | .support_float64 = true, | 238 | .support_float64 = true, |
| @@ -240,6 +241,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo | |||
| 240 | .needs_demote_reorder = device.IsAmd(), | 241 | .needs_demote_reorder = device.IsAmd(), |
| 241 | .support_snorm_render_buffer = false, | 242 | .support_snorm_render_buffer = false, |
| 242 | .support_viewport_index_layer = device.HasVertexViewportLayer(), | 243 | .support_viewport_index_layer = device.HasVertexViewportLayer(), |
| 244 | .min_ssbo_alignment = static_cast<u32>(device.GetShaderStorageBufferAlignment()), | ||
| 243 | .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(), | 245 | .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(), |
| 244 | .support_conditional_barrier = device.SupportsConditionalBarriers(), | 246 | .support_conditional_barrier = device.SupportsConditionalBarriers(), |
| 245 | } { | 247 | } { |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 7691cc2ba..5958f52f7 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp | |||
| @@ -355,6 +355,10 @@ bool BufferCacheRuntime::CanReportMemoryUsage() const { | |||
| 355 | return device.CanReportMemoryUsage(); | 355 | return device.CanReportMemoryUsage(); |
| 356 | } | 356 | } |
| 357 | 357 | ||
| 358 | u32 BufferCacheRuntime::GetStorageBufferAlignment() const { | ||
| 359 | return static_cast<u32>(device.GetStorageBufferAlignment()); | ||
| 360 | } | ||
| 361 | |||
| 358 | void BufferCacheRuntime::TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept { | 362 | void BufferCacheRuntime::TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept { |
| 359 | for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) { | 363 | for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) { |
| 360 | it->ResetUsageTracking(); | 364 | it->ResetUsageTracking(); |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 4416a902f..0b3fbd6d0 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h | |||
| @@ -91,6 +91,8 @@ public: | |||
| 91 | 91 | ||
| 92 | bool CanReportMemoryUsage() const; | 92 | bool CanReportMemoryUsage() const; |
| 93 | 93 | ||
| 94 | u32 GetStorageBufferAlignment() const; | ||
| 95 | |||
| 94 | [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size); | 96 | [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size); |
| 95 | 97 | ||
| 96 | [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false); | 98 | [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false); |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 89b455bff..2a13b2a72 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -373,6 +373,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device | |||
| 373 | driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, | 373 | driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, |
| 374 | .has_broken_robust = | 374 | .has_broken_robust = |
| 375 | device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal, | 375 | device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal, |
| 376 | .min_ssbo_alignment = device.GetStorageBufferAlignment(), | ||
| 376 | }; | 377 | }; |
| 377 | 378 | ||
| 378 | host_info = Shader::HostTranslateInfo{ | 379 | host_info = Shader::HostTranslateInfo{ |
| @@ -383,6 +384,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device | |||
| 383 | driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE, | 384 | driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE, |
| 384 | .support_snorm_render_buffer = true, | 385 | .support_snorm_render_buffer = true, |
| 385 | .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), | 386 | .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), |
| 387 | .min_ssbo_alignment = static_cast<u32>(device.GetStorageBufferAlignment()), | ||
| 386 | .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), | 388 | .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), |
| 387 | .support_conditional_barrier = device.SupportsConditionalBarriers(), | 389 | .support_conditional_barrier = device.SupportsConditionalBarriers(), |
| 388 | }; | 390 | }; |
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index fde36a49c..188ceeed7 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp | |||
| @@ -847,11 +847,41 @@ std::string Device::GetDriverName() const { | |||
| 847 | case VK_DRIVER_ID_NVIDIA_PROPRIETARY: | 847 | case VK_DRIVER_ID_NVIDIA_PROPRIETARY: |
| 848 | return "NVIDIA"; | 848 | return "NVIDIA"; |
| 849 | case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS: | 849 | case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS: |
| 850 | return "INTEL"; | 850 | return "Intel"; |
| 851 | case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA: | 851 | case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA: |
| 852 | return "ANV"; | 852 | return "ANV"; |
| 853 | case VK_DRIVER_ID_IMAGINATION_PROPRIETARY: | ||
| 854 | return "PowerVR"; | ||
| 855 | case VK_DRIVER_ID_QUALCOMM_PROPRIETARY: | ||
| 856 | return "Qualcomm"; | ||
| 857 | case VK_DRIVER_ID_ARM_PROPRIETARY: | ||
| 858 | return "Mali"; | ||
| 859 | case VK_DRIVER_ID_GOOGLE_SWIFTSHADER: | ||
| 860 | return "SwiftShader"; | ||
| 861 | case VK_DRIVER_ID_BROADCOM_PROPRIETARY: | ||
| 862 | return "Broadcom"; | ||
| 853 | case VK_DRIVER_ID_MESA_LLVMPIPE: | 863 | case VK_DRIVER_ID_MESA_LLVMPIPE: |
| 854 | return "LAVAPIPE"; | 864 | return "Lavapipe"; |
| 865 | case VK_DRIVER_ID_MOLTENVK: | ||
| 866 | return "MoltenVK"; | ||
| 867 | case VK_DRIVER_ID_VERISILICON_PROPRIETARY: | ||
| 868 | return "Vivante"; | ||
| 869 | case VK_DRIVER_ID_MESA_TURNIP: | ||
| 870 | return "Turnip"; | ||
| 871 | case VK_DRIVER_ID_MESA_V3DV: | ||
| 872 | return "V3DV"; | ||
| 873 | case VK_DRIVER_ID_MESA_PANVK: | ||
| 874 | return "PanVK"; | ||
| 875 | case VK_DRIVER_ID_MESA_VENUS: | ||
| 876 | return "Venus"; | ||
| 877 | case VK_DRIVER_ID_MESA_DOZEN: | ||
| 878 | return "Dozen"; | ||
| 879 | case VK_DRIVER_ID_MESA_NVK: | ||
| 880 | return "NVK"; | ||
| 881 | case VK_DRIVER_ID_IMAGINATION_OPEN_SOURCE_MESA: | ||
| 882 | return "PVR"; | ||
| 883 | // case VK_DRIVER_ID_MESA_AGXV: | ||
| 884 | // return "Asahi"; | ||
| 855 | default: | 885 | default: |
| 856 | return properties.driver.driverName; | 886 | return properties.driver.driverName; |
| 857 | } | 887 | } |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 90278052a..f3ad2214b 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -386,7 +386,7 @@ if (NOT WIN32) | |||
| 386 | target_include_directories(yuzu PRIVATE ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS}) | 386 | target_include_directories(yuzu PRIVATE ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS}) |
| 387 | endif() | 387 | endif() |
| 388 | if (UNIX AND NOT APPLE) | 388 | if (UNIX AND NOT APPLE) |
| 389 | target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::DBus) | 389 | target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::DBus gamemode) |
| 390 | endif() | 390 | endif() |
| 391 | 391 | ||
| 392 | target_compile_definitions(yuzu PRIVATE | 392 | target_compile_definitions(yuzu PRIVATE |
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index c727fadd1..701b895e7 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp | |||
| @@ -36,12 +36,29 @@ ConfigureGeneral::~ConfigureGeneral() = default; | |||
| 36 | void ConfigureGeneral::SetConfiguration() {} | 36 | void ConfigureGeneral::SetConfiguration() {} |
| 37 | 37 | ||
| 38 | void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) { | 38 | void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) { |
| 39 | QLayout& layout = *ui->general_widget->layout(); | 39 | QLayout& general_layout = *ui->general_widget->layout(); |
| 40 | QLayout& linux_layout = *ui->linux_widget->layout(); | ||
| 40 | 41 | ||
| 41 | std::map<u32, QWidget*> hold{}; | 42 | std::map<u32, QWidget*> general_hold{}; |
| 43 | std::map<u32, QWidget*> linux_hold{}; | ||
| 42 | 44 | ||
| 43 | for (const auto setting : | 45 | std::vector<Settings::BasicSetting*> settings; |
| 44 | UISettings::values.linkage.by_category[Settings::Category::UiGeneral]) { | 46 | |
| 47 | auto push = [&settings](auto& list) { | ||
| 48 | for (auto setting : list) { | ||
| 49 | settings.push_back(setting); | ||
| 50 | } | ||
| 51 | }; | ||
| 52 | |||
| 53 | push(UISettings::values.linkage.by_category[Settings::Category::UiGeneral]); | ||
| 54 | push(Settings::values.linkage.by_category[Settings::Category::Linux]); | ||
| 55 | |||
| 56 | // Only show Linux group on Unix | ||
| 57 | #ifndef __unix__ | ||
| 58 | ui->LinuxGroupBox->setVisible(false); | ||
| 59 | #endif | ||
| 60 | |||
| 61 | for (const auto setting : settings) { | ||
| 45 | auto* widget = builder.BuildWidget(setting, apply_funcs); | 62 | auto* widget = builder.BuildWidget(setting, apply_funcs); |
| 46 | 63 | ||
| 47 | if (widget == nullptr) { | 64 | if (widget == nullptr) { |
| @@ -52,11 +69,23 @@ void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) { | |||
| 52 | continue; | 69 | continue; |
| 53 | } | 70 | } |
| 54 | 71 | ||
| 55 | hold.emplace(setting->Id(), widget); | 72 | switch (setting->GetCategory()) { |
| 73 | case Settings::Category::UiGeneral: | ||
| 74 | general_hold.emplace(setting->Id(), widget); | ||
| 75 | break; | ||
| 76 | case Settings::Category::Linux: | ||
| 77 | linux_hold.emplace(setting->Id(), widget); | ||
| 78 | break; | ||
| 79 | default: | ||
| 80 | widget->deleteLater(); | ||
| 81 | } | ||
| 56 | } | 82 | } |
| 57 | 83 | ||
| 58 | for (const auto& [id, widget] : hold) { | 84 | for (const auto& [id, widget] : general_hold) { |
| 59 | layout.addWidget(widget); | 85 | general_layout.addWidget(widget); |
| 86 | } | ||
| 87 | for (const auto& [id, widget] : linux_hold) { | ||
| 88 | linux_layout.addWidget(widget); | ||
| 60 | } | 89 | } |
| 61 | } | 90 | } |
| 62 | 91 | ||
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index a10e7d3a5..ef20891a3 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui | |||
| @@ -47,6 +47,33 @@ | |||
| 47 | </widget> | 47 | </widget> |
| 48 | </item> | 48 | </item> |
| 49 | <item> | 49 | <item> |
| 50 | <widget class="QGroupBox" name="LinuxGroupBox"> | ||
| 51 | <property name="title"> | ||
| 52 | <string>Linux</string> | ||
| 53 | </property> | ||
| 54 | <layout class="QVBoxLayout" name="LinuxVerticalLayout_1"> | ||
| 55 | <item> | ||
| 56 | <widget class="QWidget" name="linux_widget" native="true"> | ||
| 57 | <layout class="QVBoxLayout" name="LinuxVerticalLayout_2"> | ||
| 58 | <property name="leftMargin"> | ||
| 59 | <number>0</number> | ||
| 60 | </property> | ||
| 61 | <property name="topMargin"> | ||
| 62 | <number>0</number> | ||
| 63 | </property> | ||
| 64 | <property name="rightMargin"> | ||
| 65 | <number>0</number> | ||
| 66 | </property> | ||
| 67 | <property name="bottomMargin"> | ||
| 68 | <number>0</number> | ||
| 69 | </property> | ||
| 70 | </layout> | ||
| 71 | </widget> | ||
| 72 | </item> | ||
| 73 | </layout> | ||
| 74 | </widget> | ||
| 75 | </item> | ||
| 76 | <item> | ||
| 50 | <spacer name="verticalSpacer"> | 77 | <spacer name="verticalSpacer"> |
| 51 | <property name="orientation"> | 78 | <property name="orientation"> |
| 52 | <enum>Qt::Vertical</enum> | 79 | <enum>Qt::Vertical</enum> |
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui index 2a735836e..04b771129 100644 --- a/src/yuzu/configuration/configure_system.ui +++ b/src/yuzu/configuration/configure_system.ui | |||
| @@ -57,7 +57,7 @@ | |||
| 57 | </widget> | 57 | </widget> |
| 58 | </item> | 58 | </item> |
| 59 | <item> | 59 | <item> |
| 60 | <widget class="QGroupBox" name="groupBox"> | 60 | <widget class="QGroupBox" name="coreGroup"> |
| 61 | <property name="title"> | 61 | <property name="title"> |
| 62 | <string>Core</string> | 62 | <string>Core</string> |
| 63 | </property> | 63 | </property> |
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp index a7b5def32..ee0ca4aa7 100644 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/yuzu/configuration/shared_translation.cpp | |||
| @@ -176,6 +176,9 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) { | |||
| 176 | INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"), | 176 | INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"), |
| 177 | QStringLiteral()); | 177 | QStringLiteral()); |
| 178 | 178 | ||
| 179 | // Linux | ||
| 180 | INSERT(Settings, enable_gamemode, tr("Enable Gamemode"), QStringLiteral()); | ||
| 181 | |||
| 179 | // Ui Debugging | 182 | // Ui Debugging |
| 180 | 183 | ||
| 181 | // Ui Multiplayer | 184 | // Ui Multiplayer |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 31aabb78a..10c788290 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #ifdef __unix__ | 17 | #ifdef __unix__ |
| 18 | #include <csignal> | 18 | #include <csignal> |
| 19 | #include <sys/socket.h> | 19 | #include <sys/socket.h> |
| 20 | #include "common/linux/gamemode.h" | ||
| 20 | #endif | 21 | #endif |
| 21 | 22 | ||
| 22 | #include <boost/container/flat_set.hpp> | 23 | #include <boost/container/flat_set.hpp> |
| @@ -319,6 +320,7 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk | |||
| 319 | provider{std::make_unique<FileSys::ManualContentProvider>()} { | 320 | provider{std::make_unique<FileSys::ManualContentProvider>()} { |
| 320 | #ifdef __unix__ | 321 | #ifdef __unix__ |
| 321 | SetupSigInterrupts(); | 322 | SetupSigInterrupts(); |
| 323 | SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue()); | ||
| 322 | #endif | 324 | #endif |
| 323 | system->Initialize(); | 325 | system->Initialize(); |
| 324 | 326 | ||
| @@ -2120,6 +2122,10 @@ void GMainWindow::OnEmulationStopped() { | |||
| 2120 | 2122 | ||
| 2121 | discord_rpc->Update(); | 2123 | discord_rpc->Update(); |
| 2122 | 2124 | ||
| 2125 | #ifdef __unix__ | ||
| 2126 | Common::Linux::StopGamemode(); | ||
| 2127 | #endif | ||
| 2128 | |||
| 2123 | // The emulation is stopped, so closing the window or not does not matter anymore | 2129 | // The emulation is stopped, so closing the window or not does not matter anymore |
| 2124 | disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); | 2130 | disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); |
| 2125 | 2131 | ||
| @@ -3502,6 +3508,10 @@ void GMainWindow::OnStartGame() { | |||
| 3502 | play_time_manager->Start(); | 3508 | play_time_manager->Start(); |
| 3503 | 3509 | ||
| 3504 | discord_rpc->Update(); | 3510 | discord_rpc->Update(); |
| 3511 | |||
| 3512 | #ifdef __unix__ | ||
| 3513 | Common::Linux::StartGamemode(); | ||
| 3514 | #endif | ||
| 3505 | } | 3515 | } |
| 3506 | 3516 | ||
| 3507 | void GMainWindow::OnRestartGame() { | 3517 | void GMainWindow::OnRestartGame() { |
| @@ -3522,6 +3532,10 @@ void GMainWindow::OnPauseGame() { | |||
| 3522 | play_time_manager->Stop(); | 3532 | play_time_manager->Stop(); |
| 3523 | UpdateMenuState(); | 3533 | UpdateMenuState(); |
| 3524 | AllowOSSleep(); | 3534 | AllowOSSleep(); |
| 3535 | |||
| 3536 | #ifdef __unix__ | ||
| 3537 | Common::Linux::StopGamemode(); | ||
| 3538 | #endif | ||
| 3525 | } | 3539 | } |
| 3526 | 3540 | ||
| 3527 | void GMainWindow::OnPauseContinueGame() { | 3541 | void GMainWindow::OnPauseContinueGame() { |
| @@ -3803,6 +3817,9 @@ void GMainWindow::OnConfigure() { | |||
| 3803 | const auto old_theme = UISettings::values.theme; | 3817 | const auto old_theme = UISettings::values.theme; |
| 3804 | const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue(); | 3818 | const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue(); |
| 3805 | const auto old_language_index = Settings::values.language_index.GetValue(); | 3819 | const auto old_language_index = Settings::values.language_index.GetValue(); |
| 3820 | #ifdef __unix__ | ||
| 3821 | const bool old_gamemode = Settings::values.enable_gamemode.GetValue(); | ||
| 3822 | #endif | ||
| 3806 | 3823 | ||
| 3807 | Settings::SetConfiguringGlobal(true); | 3824 | Settings::SetConfiguringGlobal(true); |
| 3808 | ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), | 3825 | ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), |
| @@ -3864,6 +3881,11 @@ void GMainWindow::OnConfigure() { | |||
| 3864 | if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) { | 3881 | if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) { |
| 3865 | SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue()); | 3882 | SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue()); |
| 3866 | } | 3883 | } |
| 3884 | #ifdef __unix__ | ||
| 3885 | if (Settings::values.enable_gamemode.GetValue() != old_gamemode) { | ||
| 3886 | SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue()); | ||
| 3887 | } | ||
| 3888 | #endif | ||
| 3867 | 3889 | ||
| 3868 | if (!multiplayer_state->IsHostingPublicRoom()) { | 3890 | if (!multiplayer_state->IsHostingPublicRoom()) { |
| 3869 | multiplayer_state->UpdateCredentials(); | 3891 | multiplayer_state->UpdateCredentials(); |
| @@ -5172,6 +5194,14 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { | |||
| 5172 | discord_rpc->Update(); | 5194 | discord_rpc->Update(); |
| 5173 | } | 5195 | } |
| 5174 | 5196 | ||
| 5197 | #ifdef __unix__ | ||
| 5198 | void GMainWindow::SetGamemodeEnabled(bool state) { | ||
| 5199 | if (emulation_running) { | ||
| 5200 | Common::Linux::SetGamemodeState(state); | ||
| 5201 | } | ||
| 5202 | } | ||
| 5203 | #endif | ||
| 5204 | |||
| 5175 | void GMainWindow::changeEvent(QEvent* event) { | 5205 | void GMainWindow::changeEvent(QEvent* event) { |
| 5176 | #ifdef __unix__ | 5206 | #ifdef __unix__ |
| 5177 | // PaletteChange event appears to only reach so far into the GUI, explicitly asking to | 5207 | // PaletteChange event appears to only reach so far into the GUI, explicitly asking to |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 733d6291e..530e445f9 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -340,6 +340,7 @@ private: | |||
| 340 | void SetupSigInterrupts(); | 340 | void SetupSigInterrupts(); |
| 341 | static void HandleSigInterrupt(int); | 341 | static void HandleSigInterrupt(int); |
| 342 | void OnSigInterruptNotifierActivated(); | 342 | void OnSigInterruptNotifierActivated(); |
| 343 | void SetGamemodeEnabled(bool state); | ||
| 343 | #endif | 344 | #endif |
| 344 | 345 | ||
| 345 | private slots: | 346 | private slots: |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 0416d5951..a81635fa4 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -63,6 +63,10 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; | |||
| 63 | } | 63 | } |
| 64 | #endif | 64 | #endif |
| 65 | 65 | ||
| 66 | #ifdef __unix__ | ||
| 67 | #include "common/linux/gamemode.h" | ||
| 68 | #endif | ||
| 69 | |||
| 66 | static void PrintHelp(const char* argv0) { | 70 | static void PrintHelp(const char* argv0) { |
| 67 | std::cout << "Usage: " << argv0 | 71 | std::cout << "Usage: " << argv0 |
| 68 | << " [options] <filename>\n" | 72 | << " [options] <filename>\n" |
| @@ -425,6 +429,10 @@ int main(int argc, char** argv) { | |||
| 425 | exit(0); | 429 | exit(0); |
| 426 | }); | 430 | }); |
| 427 | 431 | ||
| 432 | #ifdef __unix__ | ||
| 433 | Common::Linux::StartGamemode(); | ||
| 434 | #endif | ||
| 435 | |||
| 428 | void(system.Run()); | 436 | void(system.Run()); |
| 429 | if (system.DebuggerEnabled()) { | 437 | if (system.DebuggerEnabled()) { |
| 430 | system.InitializeDebugger(); | 438 | system.InitializeDebugger(); |
| @@ -436,6 +444,10 @@ int main(int argc, char** argv) { | |||
| 436 | void(system.Pause()); | 444 | void(system.Pause()); |
| 437 | system.ShutdownMainProcess(); | 445 | system.ShutdownMainProcess(); |
| 438 | 446 | ||
| 447 | #ifdef __unix__ | ||
| 448 | Common::Linux::StopGamemode(); | ||
| 449 | #endif | ||
| 450 | |||
| 439 | detached_tasks.WaitForAllTasks(); | 451 | detached_tasks.WaitForAllTasks(); |
| 440 | return 0; | 452 | return 0; |
| 441 | } | 453 | } |