summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--externals/CMakeLists.txt4
-rw-r--r--externals/gamemode/CMakeLists.txt11
-rw-r--r--externals/gamemode/include/gamemode_client.h379
-rw-r--r--src/common/CMakeLists.txt9
-rw-r--r--src/common/linux/gamemode.cpp39
-rw-r--r--src/common/linux/gamemode.h24
-rw-r--r--src/common/settings.cpp2
-rw-r--r--src/common/settings.h3
-rw-r--r--src/common/settings_common.h1
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp9
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.cpp5
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp4
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp2
-rw-r--r--src/shader_recompiler/host_translate_info.h1
-rw-r--r--src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp13
-rw-r--r--src/shader_recompiler/ir_opt/passes.h2
-rw-r--r--src/shader_recompiler/profile.h2
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h20
-rw-r--r--src/video_core/buffer_cache/usage_tracker.h4
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h4
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp18
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp2
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp34
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/configuration/configure_general.cpp43
-rw-r--r--src/yuzu/configuration/configure_general.ui27
-rw-r--r--src/yuzu/configuration/configure_system.ui2
-rw-r--r--src/yuzu/configuration/shared_translation.cpp3
-rw-r--r--src/yuzu/main.cpp30
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu_cmd/yuzu.cpp12
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()
190endif() 190endif()
191 191
192if (UNIX)
193 add_subdirectory(gamemode)
194endif()
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
194if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client) 198if (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
4project(gamemode LANGUAGES CXX C)
5
6add_library(gamemode include/gamemode_client.h)
7
8target_link_libraries(gamemode PRIVATE common)
9
10target_include_directories(gamemode PUBLIC include)
11set_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
6Copyright (c) 2017-2019, Feral Interactive
7All rights reserved.
8
9Redistribution and use in source and binary forms, with or without
10modification, 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
21THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31POSSIBILITY 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
90static 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 */
98static volatile int internal_libgamemode_loaded = 1;
99
100/* Typedefs for the functions to load */
101typedef int (*api_call_return_int)(void);
102typedef const char *(*api_call_return_cstring)(void);
103typedef int (*api_call_pid_return_int)(pid_t);
104
105/* Storage for functors */
106static api_call_return_int REAL_internal_gamemode_request_start = NULL;
107static api_call_return_int REAL_internal_gamemode_request_end = NULL;
108static api_call_return_int REAL_internal_gamemode_query_status = NULL;
109static api_call_return_cstring REAL_internal_gamemode_error_string = NULL;
110static api_call_pid_return_int REAL_internal_gamemode_request_start_for = NULL;
111static api_call_pid_return_int REAL_internal_gamemode_request_end_for = NULL;
112static 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
255int 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
284int 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 )
175endif() 175endif()
176 176
177if (UNIX)
178 target_sources(common PRIVATE
179 linux/gamemode.cpp
180 linux/gamemode.h
181 )
182
183 target_link_libraries(common PRIVATE gamemode)
184endif()
185
177if(ARCHITECTURE_x86_64) 186if(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
9namespace Common::Linux {
10
11void 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
21void 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
31void 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
6namespace Common::Linux {
7
8/**
9 * Start the (Feral Interactive) Linux gamemode if it is installed and it is activated
10 */
11void StartGamemode();
12
13/**
14 * Stop the (Feral Interactive) Linux gamemode if it is installed and it is activated
15 */
16void 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 */
22void 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
10namespace Shader::Backend::GLASM { 11namespace 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
16namespace Shader::Optimization { 17namespace 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
411IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer) { 412IR::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
519void GlobalMemoryToStorageBufferPass(IR::Program& program) { 523void 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);
16void ConditionalBarrierPass(IR::Program& program); 16void ConditionalBarrierPass(IR::Program& program);
17void ConstantPropagationPass(Environment& env, IR::Program& program); 17void ConstantPropagationPass(Environment& env, IR::Program& program);
18void DeadCodeEliminationPass(IR::Program& program); 18void DeadCodeEliminationPass(IR::Program& program);
19void GlobalMemoryToStorageBufferPass(IR::Program& program); 19void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info);
20void IdentityRemovalPass(IR::Program& program); 20void IdentityRemovalPass(IR::Program& program);
21void LowerFp64ToFp32(IR::Program& program); 21void LowerFp64ToFp32(IR::Program& program);
22void LowerFp16ToFp32(IR::Program& program); 22void 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
194private: 198private:
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
358u32 BufferCacheRuntime::GetStorageBufferAlignment() const {
359 return static_cast<u32>(device.GetStorageBufferAlignment());
360}
361
358void BufferCacheRuntime::TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept { 362void 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})
387endif() 387endif()
388if (UNIX AND NOT APPLE) 388if (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)
390endif() 390endif()
391 391
392target_compile_definitions(yuzu PRIVATE 392target_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;
36void ConfigureGeneral::SetConfiguration() {} 36void ConfigureGeneral::SetConfiguration() {}
37 37
38void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) { 38void 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
3507void GMainWindow::OnRestartGame() { 3517void 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
3527void GMainWindow::OnPauseContinueGame() { 3541void 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__
5198void GMainWindow::SetGamemodeEnabled(bool state) {
5199 if (emulation_running) {
5200 Common::Linux::SetGamemodeState(state);
5201 }
5202}
5203#endif
5204
5175void GMainWindow::changeEvent(QEvent* event) { 5205void 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
345private slots: 346private 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
66static void PrintHelp(const char* argv0) { 70static 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}