Compare commits

...

37 Commits

Author SHA1 Message Date
Liam
5eec980a2d general: conditionally compile gamemode on linux only 2023-11-29 14:26:11 -05:00
liamwhite
aded28f276 Merge pull request #12204 from t895/config-migration
android: Multi directory UI
2023-11-29 12:34:09 -05:00
liamwhite
80c4743754 Merge pull request #12203 from liamwhite/crash-fix
set: don't load version nca with null romfs
2023-11-29 12:34:02 -05:00
liamwhite
498159d719 Merge pull request #12201 from Macj0rdan/controller-applet-fix
Fixed controller applet crashing when on FW17+
2023-11-29 12:33:50 -05:00
liamwhite
91ad6b7098 Merge pull request #12194 from liamwhite/fruit-company
video_core: fix mac compile
2023-11-29 12:33:41 -05:00
liamwhite
df49795bcb Merge pull request #12154 from liamwhite/more-drivers
vulkan_device: add names for more driverID enumeration values
2023-11-29 12:33:34 -05:00
liamwhite
337e37f91d Merge pull request #11946 from flodavid/gamemode
Enable (Feral Interactive) Gamemode on Linux
2023-11-29 12:33:09 -05:00
liamwhite
992ca8c358 Merge pull request #11902 from ameerj/ssbo-align
shader_recompiler: Align SSBO offsets to meet host requirements
2023-11-29 12:32:52 -05:00
Liam
fd29227bc4 set: don't load version nca with null romfs 2023-11-27 15:20:18 -05:00
FrozenAra
e21f96ffde Fixed controller applet crashing when on FW17+ 2023-11-27 20:01:30 +01:00
liamwhite
c7649a0cdb Merge pull request #12183 from german77/justmii
service: nfc: Validate mii data
2023-11-27 11:56:37 -05:00
liamwhite
5a96c525e3 Merge pull request #12160 from german77/mouse_constrain
yuzu: Constrain mouse in render window when emulated
2023-11-27 11:56:24 -05:00
Ameer J
1d11fe00a3 Merge branch 'master' into ssbo-align 2023-11-26 21:08:53 -05:00
Liam
7c1cb5e8c9 video_core: fix mac compile 2023-11-26 20:04:06 -05:00
Fernando S
f21340f7aa Merge pull request #11535 from GPUCode/upload_cmdbuf
renderer_vulkan: Introduce separate cmd buffer for uploads
2023-11-26 18:33:05 +01:00
liamwhite
e0c894408a Merge pull request #12180 from german77/cabinetmii
service: am: Create random mii for cabinet applet
2023-11-26 09:50:08 -05:00
liamwhite
257a1c884d Merge pull request #12156 from german77/firmwarevs
yuzu: Display firmware version
2023-11-26 09:49:53 -05:00
liamwhite
c100d7e802 Merge pull request #12175 from abouvier/unbundle-simpleini
cmake: prefer system simpleini library
2023-11-26 09:49:43 -05:00
german77
281eb020ea service: nfc: Validate mii data 2023-11-25 23:40:01 -06:00
german77
4ce6762945 service: am: Create random mii for cabinet applet 2023-11-25 21:31:05 -06:00
Alexandre Bouvier
fe3702223f cmake: prefer system simpleini library 2023-11-26 03:45:10 +01:00
liamwhite
83aa66b17d Merge pull request #12166 from german77/redmagic
android: input: Flip abxy on redmagic controllers
2023-11-25 20:56:40 -05:00
liamwhite
8d7a55be5b Merge pull request #12169 from liamwhite/its-time-to-stop
time: undef GetCurrentTime for Windows
2023-11-25 20:56:31 -05:00
german77
de58618421 android: input: Flip abxy on backbone labs controllers 2023-11-25 18:22:03 -06:00
Liam
e6847c65a8 time: undef GetCurrentTime for Windows 2023-11-25 16:18:02 -05:00
flodavid
ac11f6e4c5 cmake: move gamemode target include into its file 2023-11-25 19:30:45 +01:00
flodavid
40644d43f7 yuzu: create linux group in general settings
- Create files dedicated to starting and stopping gamemode functions
  - Use them in yuzu and yuzu_cmd modules
2023-11-25 19:30:37 +01:00
xcfrg
dfa56765d6 yuzu: integrate gamemode support on linux 2023-11-25 19:30:29 +01:00
german77
b5bde8451c android: input: Flip abxy on redmagic controllers 2023-11-25 10:53:59 -06:00
Narr the Reg
f61cf14646 yuzu: Constrain mouse in render window when emulated 2023-11-24 19:32:35 -06:00
Narr the Reg
2d4e7c8264 yuzu: Display firmware version 2023-11-24 12:58:07 -06:00
Liam
dee792937f vulkan_device: add names for more driverID enumeration values 2023-11-24 12:00:41 -05:00
Fernando Sahmkow
50bcfa5fb9 Vulkan: Add a final barrier to the upload command buffer 2023-11-12 20:58:30 +01:00
GPUCode
efc50485b8 renderer_vulkan: Introduce separate cmd buffer for uploads 2023-11-12 20:27:39 +01:00
Ameer J
75c5be55af shader_recompiler: Align SSBO offsets in GlobalMemory functions 2023-10-31 20:14:18 -04:00
Ameer J
735612c9b3 buffer_cache: Apply storage buffer alignment only to the offset 2023-10-31 20:10:54 -04:00
Ameer J
7d34800531 shader_recompiler: Align SSBO offsets to meet host requirements
Co-Authored-By: Billy Laws <blaws05@gmail.com>
2023-10-31 20:10:54 -04:00
66 changed files with 1331 additions and 336 deletions

View File

@@ -290,6 +290,7 @@ find_package(lz4 REQUIRED)
find_package(nlohmann_json 3.8 REQUIRED)
find_package(Opus 1.3 MODULE)
find_package(RenderDoc MODULE)
find_package(SimpleIni MODULE)
find_package(stb MODULE)
find_package(VulkanMemoryAllocator CONFIG)
find_package(ZLIB 1.2 REQUIRED)

View File

@@ -0,0 +1,19 @@
# SPDX-FileCopyrightText: 2023 Alexandre Bouvier <contact@amb.tf>
#
# SPDX-License-Identifier: GPL-3.0-or-later
find_path(SimpleIni_INCLUDE_DIR SimpleIni.h)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SimpleIni
REQUIRED_VARS SimpleIni_INCLUDE_DIR
)
if (SimpleIni_FOUND AND NOT TARGET SimpleIni::SimpleIni)
add_library(SimpleIni::SimpleIni INTERFACE IMPORTED)
set_target_properties(SimpleIni::SimpleIni PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${SimpleIni_INCLUDE_DIR}"
)
endif()
mark_as_advanced(SimpleIni_INCLUDE_DIR)

View File

@@ -189,6 +189,10 @@ if (ANDROID)
endif()
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
add_subdirectory(gamemode)
endif()
# Breakpad
# https://github.com/microsoft/vcpkg/blob/master/ports/breakpad/CMakeLists.txt
if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
@@ -292,4 +296,6 @@ if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
endif()
# SimpleIni
add_subdirectory(simpleini)
if (NOT TARGET SimpleIni::SimpleIni)
add_subdirectory(simpleini)
endif()

11
externals/gamemode/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,11 @@
# SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
project(gamemode LANGUAGES CXX C)
add_library(gamemode include/gamemode_client.h)
target_link_libraries(gamemode PRIVATE common)
target_include_directories(gamemode PUBLIC include)
set_target_properties(gamemode PROPERTIES LINKER_LANGUAGE C)

View File

@@ -0,0 +1,379 @@
// SPDX-FileCopyrightText: Copyright 2017-2019 Feral Interactive
// SPDX-License-Identifier: BSD-3-Clause
/*
Copyright (c) 2017-2019, Feral Interactive
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CLIENT_GAMEMODE_H
#define CLIENT_GAMEMODE_H
/*
* GameMode supports the following client functions
* Requests are refcounted in the daemon
*
* int gamemode_request_start() - Request gamemode starts
* 0 if the request was sent successfully
* -1 if the request failed
*
* int gamemode_request_end() - Request gamemode ends
* 0 if the request was sent successfully
* -1 if the request failed
*
* GAMEMODE_AUTO can be defined to make the above two functions apply during static init and
* destruction, as appropriate. In this configuration, errors will be printed to stderr
*
* int gamemode_query_status() - Query the current status of gamemode
* 0 if gamemode is inactive
* 1 if gamemode is active
* 2 if gamemode is active and this client is registered
* -1 if the query failed
*
* int gamemode_request_start_for(pid_t pid) - Request gamemode starts for another process
* 0 if the request was sent successfully
* -1 if the request failed
* -2 if the request was rejected
*
* int gamemode_request_end_for(pid_t pid) - Request gamemode ends for another process
* 0 if the request was sent successfully
* -1 if the request failed
* -2 if the request was rejected
*
* int gamemode_query_status_for(pid_t pid) - Query status of gamemode for another process
* 0 if gamemode is inactive
* 1 if gamemode is active
* 2 if gamemode is active and this client is registered
* -1 if the query failed
*
* const char* gamemode_error_string() - Get an error string
* returns a string describing any of the above errors
*
* Note: All the above requests can be blocking - dbus requests can and will block while the daemon
* handles the request. It is not recommended to make these calls in performance critical code
*/
#include <stdbool.h>
#include <stdio.h>
#include <dlfcn.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
static char internal_gamemode_client_error_string[512] = { 0 };
/**
* Load libgamemode dynamically to dislodge us from most dependencies.
* This allows clients to link and/or use this regardless of runtime.
* See SDL2 for an example of the reasoning behind this in terms of
* dynamic versioning as well.
*/
static volatile int internal_libgamemode_loaded = 1;
/* Typedefs for the functions to load */
typedef int (*api_call_return_int)(void);
typedef const char *(*api_call_return_cstring)(void);
typedef int (*api_call_pid_return_int)(pid_t);
/* Storage for functors */
static api_call_return_int REAL_internal_gamemode_request_start = NULL;
static api_call_return_int REAL_internal_gamemode_request_end = NULL;
static api_call_return_int REAL_internal_gamemode_query_status = NULL;
static api_call_return_cstring REAL_internal_gamemode_error_string = NULL;
static api_call_pid_return_int REAL_internal_gamemode_request_start_for = NULL;
static api_call_pid_return_int REAL_internal_gamemode_request_end_for = NULL;
static api_call_pid_return_int REAL_internal_gamemode_query_status_for = NULL;
/**
* Internal helper to perform the symbol binding safely.
*
* Returns 0 on success and -1 on failure
*/
__attribute__((always_inline)) static inline int internal_bind_libgamemode_symbol(
void *handle, const char *name, void **out_func, size_t func_size, bool required)
{
void *symbol_lookup = NULL;
char *dl_error = NULL;
/* Safely look up the symbol */
symbol_lookup = dlsym(handle, name);
dl_error = dlerror();
if (required && (dl_error || !symbol_lookup)) {
snprintf(internal_gamemode_client_error_string,
sizeof(internal_gamemode_client_error_string),
"dlsym failed - %s",
dl_error);
return -1;
}
/* Have the symbol correctly, copy it to make it usable */
memcpy(out_func, &symbol_lookup, func_size);
return 0;
}
/**
* Loads libgamemode and needed functions
*
* Returns 0 on success and -1 on failure
*/
__attribute__((always_inline)) static inline int internal_load_libgamemode(void)
{
/* We start at 1, 0 is a success and -1 is a fail */
if (internal_libgamemode_loaded != 1) {
return internal_libgamemode_loaded;
}
/* Anonymous struct type to define our bindings */
struct binding {
const char *name;
void **functor;
size_t func_size;
bool required;
} bindings[] = {
{ "real_gamemode_request_start",
(void **)&REAL_internal_gamemode_request_start,
sizeof(REAL_internal_gamemode_request_start),
true },
{ "real_gamemode_request_end",
(void **)&REAL_internal_gamemode_request_end,
sizeof(REAL_internal_gamemode_request_end),
true },
{ "real_gamemode_query_status",
(void **)&REAL_internal_gamemode_query_status,
sizeof(REAL_internal_gamemode_query_status),
false },
{ "real_gamemode_error_string",
(void **)&REAL_internal_gamemode_error_string,
sizeof(REAL_internal_gamemode_error_string),
true },
{ "real_gamemode_request_start_for",
(void **)&REAL_internal_gamemode_request_start_for,
sizeof(REAL_internal_gamemode_request_start_for),
false },
{ "real_gamemode_request_end_for",
(void **)&REAL_internal_gamemode_request_end_for,
sizeof(REAL_internal_gamemode_request_end_for),
false },
{ "real_gamemode_query_status_for",
(void **)&REAL_internal_gamemode_query_status_for,
sizeof(REAL_internal_gamemode_query_status_for),
false },
};
void *libgamemode = NULL;
/* Try and load libgamemode */
libgamemode = dlopen("libgamemode.so.0", RTLD_NOW);
if (!libgamemode) {
/* Attempt to load unversioned library for compatibility with older
* versions (as of writing, there are no ABI changes between the two -
* this may need to change if ever ABI-breaking changes are made) */
libgamemode = dlopen("libgamemode.so", RTLD_NOW);
if (!libgamemode) {
snprintf(internal_gamemode_client_error_string,
sizeof(internal_gamemode_client_error_string),
"dlopen failed - %s",
dlerror());
internal_libgamemode_loaded = -1;
return -1;
}
}
/* Attempt to bind all symbols */
for (size_t i = 0; i < sizeof(bindings) / sizeof(bindings[0]); i++) {
struct binding *binder = &bindings[i];
if (internal_bind_libgamemode_symbol(libgamemode,
binder->name,
binder->functor,
binder->func_size,
binder->required)) {
internal_libgamemode_loaded = -1;
return -1;
};
}
/* Success */
internal_libgamemode_loaded = 0;
return 0;
}
/**
* Redirect to the real libgamemode
*/
__attribute__((always_inline)) static inline const char *gamemode_error_string(void)
{
/* If we fail to load the system gamemode, or we have an error string already, return our error
* string instead of diverting to the system version */
if (internal_load_libgamemode() < 0 || internal_gamemode_client_error_string[0] != '\0') {
return internal_gamemode_client_error_string;
}
/* Assert for static analyser that the function is not NULL */
assert(REAL_internal_gamemode_error_string != NULL);
return REAL_internal_gamemode_error_string();
}
/**
* Redirect to the real libgamemode
* Allow automatically requesting game mode
* Also prints errors as they happen.
*/
#ifdef GAMEMODE_AUTO
__attribute__((constructor))
#else
__attribute__((always_inline)) static inline
#endif
int gamemode_request_start(void)
{
/* Need to load gamemode */
if (internal_load_libgamemode() < 0) {
#ifdef GAMEMODE_AUTO
fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
#endif
return -1;
}
/* Assert for static analyser that the function is not NULL */
assert(REAL_internal_gamemode_request_start != NULL);
if (REAL_internal_gamemode_request_start() < 0) {
#ifdef GAMEMODE_AUTO
fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
#endif
return -1;
}
return 0;
}
/* Redirect to the real libgamemode */
#ifdef GAMEMODE_AUTO
__attribute__((destructor))
#else
__attribute__((always_inline)) static inline
#endif
int gamemode_request_end(void)
{
/* Need to load gamemode */
if (internal_load_libgamemode() < 0) {
#ifdef GAMEMODE_AUTO
fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
#endif
return -1;
}
/* Assert for static analyser that the function is not NULL */
assert(REAL_internal_gamemode_request_end != NULL);
if (REAL_internal_gamemode_request_end() < 0) {
#ifdef GAMEMODE_AUTO
fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
#endif
return -1;
}
return 0;
}
/* Redirect to the real libgamemode */
__attribute__((always_inline)) static inline int gamemode_query_status(void)
{
/* Need to load gamemode */
if (internal_load_libgamemode() < 0) {
return -1;
}
if (REAL_internal_gamemode_query_status == NULL) {
snprintf(internal_gamemode_client_error_string,
sizeof(internal_gamemode_client_error_string),
"gamemode_query_status missing (older host?)");
return -1;
}
return REAL_internal_gamemode_query_status();
}
/* Redirect to the real libgamemode */
__attribute__((always_inline)) static inline int gamemode_request_start_for(pid_t pid)
{
/* Need to load gamemode */
if (internal_load_libgamemode() < 0) {
return -1;
}
if (REAL_internal_gamemode_request_start_for == NULL) {
snprintf(internal_gamemode_client_error_string,
sizeof(internal_gamemode_client_error_string),
"gamemode_request_start_for missing (older host?)");
return -1;
}
return REAL_internal_gamemode_request_start_for(pid);
}
/* Redirect to the real libgamemode */
__attribute__((always_inline)) static inline int gamemode_request_end_for(pid_t pid)
{
/* Need to load gamemode */
if (internal_load_libgamemode() < 0) {
return -1;
}
if (REAL_internal_gamemode_request_end_for == NULL) {
snprintf(internal_gamemode_client_error_string,
sizeof(internal_gamemode_client_error_string),
"gamemode_request_end_for missing (older host?)");
return -1;
}
return REAL_internal_gamemode_request_end_for(pid);
}
/* Redirect to the real libgamemode */
__attribute__((always_inline)) static inline int gamemode_query_status_for(pid_t pid)
{
/* Need to load gamemode */
if (internal_load_libgamemode() < 0) {
return -1;
}
if (REAL_internal_gamemode_query_status_for == NULL) {
snprintf(internal_gamemode_client_error_string,
sizeof(internal_gamemode_client_error_string),
"gamemode_query_status_for missing (older host?)");
return -1;
}
return REAL_internal_gamemode_query_status_for(pid);
}
#endif // CLIENT_GAMEMODE_H

View File

@@ -27,6 +27,8 @@ object InputHandler {
0x054C -> getInputDS5ButtonKey(event.keyCode)
0x057E -> getInputJoyconButtonKey(event.keyCode)
0x1532 -> getInputRazerButtonKey(event.keyCode)
0x3537 -> getInputRedmagicButtonKey(event.keyCode)
0x358A -> getInputBackboneLabsButtonKey(event.keyCode)
else -> getInputGenericButtonKey(event.keyCode)
}
@@ -227,6 +229,42 @@ object InputHandler {
}
}
private fun getInputRedmagicButtonKey(key: Int): Int {
return when (key) {
KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y
KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X
KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL
KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR
KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
else -> -1
}
}
private fun getInputBackboneLabsButtonKey(key: Int): Int {
return when (key) {
KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y
KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X
KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL
KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR
KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
else -> -1
}
}
private fun getInputGenericButtonKey(key: Int): Int {
return when (key) {
KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A

View File

@@ -174,6 +174,15 @@ if(ANDROID)
)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_sources(common PRIVATE
linux/gamemode.cpp
linux/gamemode.h
)
target_link_libraries(common PRIVATE gamemode)
endif()
if(ARCHITECTURE_x86_64)
target_sources(common
PRIVATE

View File

@@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <gamemode_client.h>
#include "common/linux/gamemode.h"
#include "common/logging/log.h"
#include "common/settings.h"
namespace Common::Linux {
void StartGamemode() {
if (Settings::values.enable_gamemode) {
if (gamemode_request_start() < 0) {
LOG_WARNING(Frontend, "Failed to start gamemode: {}", gamemode_error_string());
} else {
LOG_INFO(Frontend, "Started gamemode");
}
}
}
void StopGamemode() {
if (Settings::values.enable_gamemode) {
if (gamemode_request_end() < 0) {
LOG_WARNING(Frontend, "Failed to stop gamemode: {}", gamemode_error_string());
} else {
LOG_INFO(Frontend, "Stopped gamemode");
}
}
}
void SetGamemodeState(bool state) {
if (state) {
StartGamemode();
} else {
StopGamemode();
}
}
} // namespace Common::Linux

View File

@@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
namespace Common::Linux {
/**
* Start the (Feral Interactive) Linux gamemode if it is installed and it is activated
*/
void StartGamemode();
/**
* Stop the (Feral Interactive) Linux gamemode if it is installed and it is activated
*/
void StopGamemode();
/**
* Start or stop the (Feral Interactive) Linux gamemode if it is installed and it is activated
* @param state The new state the gamemode should have
*/
void SetGamemodeState(bool state);
} // namespace Common::Linux

View File

@@ -219,6 +219,8 @@ const char* TranslateCategory(Category category) {
return "Services";
case Category::Paths:
return "Paths";
case Category::Linux:
return "Linux";
case Category::MaxEnum:
break;
}

View File

@@ -384,6 +384,8 @@ struct Values {
Category::RendererDebug};
// TODO: remove this once AMDVLK supports VK_EXT_depth_bias_control
bool renderer_amdvlk_depth_bias_workaround{};
Setting<bool> disable_buffer_reorder{linkage, false, "disable_buffer_reorder",
Category::RendererDebug};
// System
SwitchableSetting<Language, true> language_index{linkage,
@@ -427,6 +429,9 @@ struct Values {
true,
true};
// Linux
SwitchableSetting<bool> enable_gamemode{linkage, true, "enable_gamemode", Category::Linux};
// Controls
InputSetting<std::array<PlayerInput, 10>> players;

View File

@@ -41,6 +41,7 @@ enum class Category : u32 {
Multiplayer,
Services,
Paths,
Linux,
MaxEnum,
};

View File

@@ -167,6 +167,11 @@ protected:
*/
std::pair<f32, f32> MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const;
/**
* Clip the provided coordinates to be inside the touchscreen area.
*/
std::pair<u32, u32> ClipToTouchScreen(u32 new_x, u32 new_y) const;
WindowSystemInfo window_info;
bool strict_context_required = false;
@@ -181,11 +186,6 @@ private:
// By default, ignore this request and do nothing.
}
/**
* Clip the provided coordinates to be inside the touchscreen area.
*/
std::pair<u32, u32> ClipToTouchScreen(u32 new_x, u32 new_y) const;
Layout::FramebufferLayout framebuffer_layout; ///< Current framebuffer layout
u32 client_area_width; ///< Current client width, should be set by window impl.

View File

@@ -122,7 +122,8 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
Service::NFP::RegisterInfoPrivate register_info{};
std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(),
std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1));
register_info.mii_store_data.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All);
register_info.mii_store_data.SetNickname({u'y', u'u', u'z', u'u'});
nfp_device->SetRegisterInfoPrivate(register_info);
break;
}
@@ -130,7 +131,7 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
nfp_device->DeleteApplicationArea();
break;
case Service::NFP::CabinetMode::StartRestorer:
nfp_device->RestoreAmiibo();
nfp_device->Restore();
break;
case Service::NFP::CabinetMode::StartFormatter:
nfp_device->Format();

View File

@@ -16,7 +16,8 @@ namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
: ControllerBase{hid_core_}, touchscreen_width(Layout::ScreenUndocked::Width),
touchscreen_height(Layout::ScreenUndocked::Height) {
static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size,
"TouchSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
@@ -95,8 +96,8 @@ void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (id < active_fingers_count) {
const auto& [active_x, active_y] = active_fingers[id].position;
touch_entry.position = {
.x = static_cast<u16>(active_x * Layout::ScreenUndocked::Width),
.y = static_cast<u16>(active_y * Layout::ScreenUndocked::Height),
.x = static_cast<u16>(active_x * static_cast<float>(touchscreen_width)),
.y = static_cast<u16>(active_y * static_cast<float>(touchscreen_height)),
};
touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
@@ -120,4 +121,9 @@ void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
shared_memory->touch_screen_lifo.WriteNextEntry(next_state);
}
void TouchScreen::SetTouchscreenDimensions(u32 width, u32 height) {
touchscreen_width = width;
touchscreen_height = height;
}
} // namespace Service::HID

View File

@@ -28,6 +28,8 @@ public:
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
void SetTouchscreenDimensions(u32 width, u32 height);
private:
static constexpr std::size_t MAX_FINGERS = 16;
@@ -53,5 +55,7 @@ private:
Core::HID::EmulatedConsole* console = nullptr;
std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{};
u32 touchscreen_width;
u32 touchscreen_height;
};
} // namespace Service::HID

View File

@@ -208,6 +208,7 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r
{1001, &IHidServer::GetNpadCommunicationMode, "GetNpadCommunicationMode"},
{1002, &IHidServer::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"},
{1003, &IHidServer::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"},
{1004, &IHidServer::SetTouchScreenResolution, "SetTouchScreenResolution"},
{2000, nullptr, "ActivateDigitizer"},
};
// clang-format on
@@ -2363,6 +2364,21 @@ void IHidServer::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) {
rb.Push(false);
}
void IHidServer::SetTouchScreenResolution(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto width{rp.Pop<u32>()};
const auto height{rp.Pop<u32>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
GetResourceManager()->GetTouchScreen()->SetTouchscreenDimensions(width, height);
LOG_INFO(Service_HID, "called, width={}, height={}, applet_resource_user_id={}", width, height,
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
std::shared_ptr<ResourceManager> IHidServer::GetResourceManager() {
resource_manager->Initialize();
return resource_manager;

View File

@@ -141,6 +141,7 @@ private:
void GetNpadCommunicationMode(HLERequestContext& ctx);
void SetTouchScreenConfiguration(HLERequestContext& ctx);
void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx);
void SetTouchScreenResolution(HLERequestContext& ctx);
std::shared_ptr<ResourceManager> resource_manager;
std::shared_ptr<HidFirmwareSettings> firmware_settings;

View File

@@ -98,7 +98,7 @@ void Ver3StoreData::BuildToStoreData(StoreData& out_store_data) const {
}
void Ver3StoreData::BuildFromStoreData(const StoreData& store_data) {
version = 1;
version = 3;
mii_information.gender.Assign(static_cast<u8>(store_data.GetGender()));
mii_information.favorite_color.Assign(static_cast<u8>(store_data.GetFavoriteColor()));
height = store_data.GetHeight();

View File

@@ -401,6 +401,12 @@ Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& time
}
Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target_) {
bool is_corrupted = false;
if (model_type != NFP::ModelType::Amiibo) {
return ResultInvalidArgument;
}
if (device_state != DeviceState::TagFound) {
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
return ResultWrongDeviceState;
@@ -420,26 +426,32 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target
if (is_plain_amiibo) {
std::vector<u8> data(sizeof(NFP::NTAG215File));
memcpy(data.data(), &tag_data, sizeof(tag_data));
WriteBackupData(tag_data.uid, data);
device_state = DeviceState::TagMounted;
mount_target = mount_target_;
return ResultSuccess;
}
if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) {
bool has_backup = HasBackup(encrypted_tag_data.uuid).IsSuccess();
LOG_ERROR(Service_NFP, "Can't decode amiibo, has_backup= {}", has_backup);
return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData;
if (!is_plain_amiibo && !NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) {
LOG_ERROR(Service_NFP, "Can't decode amiibo");
is_corrupted = true;
}
std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File));
memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
WriteBackupData(encrypted_tag_data.uuid, data);
if (tag_data.settings.settings.amiibo_initialized && !tag_data.owner_mii.IsValid()) {
LOG_ERROR(Service_NFP, "Invalid mii data");
is_corrupted = true;
}
if (!is_corrupted) {
std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File));
memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
WriteBackupData(encrypted_tag_data.uuid, data);
}
device_state = DeviceState::TagMounted;
mount_target = mount_target_;
if (is_corrupted) {
bool has_backup = HasBackup(encrypted_tag_data.uuid).IsSuccess();
return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData;
}
return ResultSuccess;
}
@@ -606,6 +618,17 @@ Result NfcDevice::Restore() {
}
}
// Restore mii data in case is corrupted by previous instances of yuzu
if (tag_data.settings.settings.amiibo_initialized && !tag_data.owner_mii.IsValid()) {
LOG_ERROR(Service_NFP, "Regenerating mii data");
Mii::StoreData new_mii{};
new_mii.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All);
new_mii.SetNickname({u'y', u'u', u'z', u'u', u'\0'});
tag_data.owner_mii.BuildFromStoreData(new_mii);
tag_data.mii_extension.SetFromStoreData(new_mii);
}
// Overwrite tag contents with backup and mount the tag
tag_data = temporary_tag_data;
encrypted_tag_data = temporary_encrypted_tag_data;
@@ -851,25 +874,6 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe
return Flush();
}
Result NfcDevice::RestoreAmiibo() {
if (device_state != DeviceState::TagMounted) {
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
if (device_state == DeviceState::TagRemoved) {
return ResultTagRemoved;
}
return ResultWrongDeviceState;
}
if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
return ResultWrongDeviceState;
}
// TODO: Load amiibo from backup on system
LOG_ERROR(Service_NFP, "Not Implemented");
return ResultSuccess;
}
Result NfcDevice::Format() {
Result result = ResultSuccess;
@@ -877,7 +881,9 @@ Result NfcDevice::Format() {
result = Mount(NFP::ModelType::Amiibo, NFP::MountTarget::All);
}
if (result.IsError()) {
// We are formatting all data. Corruption is not an issue.
if (result.IsError() &&
(result != ResultCorruptedData && result != ResultCorruptedDataWithBackup)) {
return result;
}

View File

@@ -68,7 +68,6 @@ public:
Result DeleteRegisterInfo();
Result SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& register_info);
Result RestoreAmiibo();
Result Format();
Result OpenApplicationArea(u32 access_id);

View File

@@ -19,19 +19,8 @@
namespace Service::Set {
namespace {
constexpr u64 SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET = 0x05;
enum class GetFirmwareVersionType {
Version1,
Version2,
};
void GetFirmwareVersionImpl(Core::System& system, HLERequestContext& ctx,
GetFirmwareVersionType type) {
ASSERT_MSG(ctx.GetWriteBufferSize() == 0x100,
"FirmwareVersion output buffer must be 0x100 bytes in size!");
Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system,
GetFirmwareVersionType type) {
constexpr u64 FirmwareVersionSystemDataId = 0x0100000000000809;
auto& fsc = system.GetFileSystemController();
@@ -45,46 +34,43 @@ void GetFirmwareVersionImpl(Core::System& system, HLERequestContext& ctx,
nca = bis_system->GetEntry(FirmwareVersionSystemDataId, FileSys::ContentRecordType::Data);
}
if (nca) {
romfs = FileSys::ExtractRomFS(nca->GetRomFS());
if (auto nca_romfs = nca->GetRomFS(); nca_romfs) {
romfs = FileSys::ExtractRomFS(nca_romfs);
}
}
if (!romfs) {
romfs = FileSys::ExtractRomFS(
FileSys::SystemArchive::SynthesizeSystemArchive(FirmwareVersionSystemDataId));
}
const auto early_exit_failure = [&ctx](std::string_view desc, Result code) {
const auto early_exit_failure = [](std::string_view desc, Result code) {
LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).",
desc);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(code);
return code;
};
const auto ver_file = romfs->GetFile("file");
if (ver_file == nullptr) {
early_exit_failure("The system version archive didn't contain the file 'file'.",
FileSys::ERROR_INVALID_ARGUMENT);
return;
return early_exit_failure("The system version archive didn't contain the file 'file'.",
FileSys::ERROR_INVALID_ARGUMENT);
}
auto data = ver_file->ReadAllBytes();
if (data.size() != 0x100) {
early_exit_failure("The system version file 'file' was not the correct size.",
FileSys::ERROR_OUT_OF_BOUNDS);
return;
if (data.size() != sizeof(FirmwareVersionFormat)) {
return early_exit_failure("The system version file 'file' was not the correct size.",
FileSys::ERROR_OUT_OF_BOUNDS);
}
std::memcpy(&out_firmware, data.data(), sizeof(FirmwareVersionFormat));
// If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will
// zero out the REVISION_MINOR field.
if (type == GetFirmwareVersionType::Version1) {
data[SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET] = 0;
out_firmware.revision_minor = 0;
}
ctx.WriteBuffer(data);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return ResultSuccess;
}
} // Anonymous namespace
void SET_SYS::SetLanguageCode(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -98,12 +84,32 @@ void SET_SYS::SetLanguageCode(HLERequestContext& ctx) {
void SET_SYS::GetFirmwareVersion(HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
GetFirmwareVersionImpl(system, ctx, GetFirmwareVersionType::Version1);
FirmwareVersionFormat firmware_data{};
const auto result =
GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version1);
if (result.IsSuccess()) {
ctx.WriteBuffer(firmware_data);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void SET_SYS::GetFirmwareVersion2(HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
GetFirmwareVersionImpl(system, ctx, GetFirmwareVersionType::Version2);
FirmwareVersionFormat firmware_data{};
const auto result =
GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version2);
if (result.IsSuccess()) {
ctx.WriteBuffer(firmware_data);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void SET_SYS::GetAccountSettings(HLERequestContext& ctx) {

View File

@@ -4,6 +4,7 @@
#pragma once
#include "common/uuid.h"
#include "core/hle/result.h"
#include "core/hle/service/service.h"
#include "core/hle/service/time/clock_types.h"
@@ -12,6 +13,29 @@ class System;
}
namespace Service::Set {
enum class LanguageCode : u64;
enum class GetFirmwareVersionType {
Version1,
Version2,
};
struct FirmwareVersionFormat {
u8 major;
u8 minor;
u8 micro;
INSERT_PADDING_BYTES(1);
u8 revision_major;
u8 revision_minor;
INSERT_PADDING_BYTES(2);
std::array<char, 0x20> platform;
std::array<u8, 0x40> version_hash;
std::array<char, 0x18> display_version;
std::array<char, 0x80> display_title;
};
static_assert(sizeof(FirmwareVersionFormat) == 0x100, "FirmwareVersionFormat is an invalid size");
Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system,
GetFirmwareVersionType type);
class SET_SYS final : public ServiceFramework<SET_SYS> {
public:

View File

@@ -11,6 +11,11 @@
#include "core/hle/service/time/errors.h"
#include "core/hle/service/time/time_zone_types.h"
// Defined by WinBase.h on Windows
#ifdef GetCurrentTime
#undef GetCurrentTime
#endif
namespace Service::Time::Clock {
enum class TimeType : u8 {

View File

@@ -7,4 +7,4 @@ add_library(frontend_common STATIC
)
create_target_directory_groups(frontend_common)
target_link_libraries(frontend_common PUBLIC core SimpleIni PRIVATE common Boost::headers)
target_link_libraries(frontend_common PUBLIC core SimpleIni::SimpleIni PRIVATE common Boost::headers)

View File

@@ -7,6 +7,7 @@
#include <string>
#include "common/settings.h"
#define SI_NO_CONVERSION
#include <SimpleIni.h>
#include <boost/algorithm/string/replace.hpp>

View File

@@ -5,6 +5,7 @@
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/program.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/profile.h"
#include "shader_recompiler/runtime_info.h"
namespace Shader::Backend::GLASM {
@@ -35,7 +36,9 @@ void GlobalStorageOp(EmitContext& ctx, Register address, bool pointer_based, std
continue;
}
const auto& ssbo{ctx.info.storage_buffers_descriptors[index]};
ctx.Add("LDC.U64 DC.x,c{}[{}];" // ssbo_addr
const u64 ssbo_align_mask{~(ctx.profile.min_ssbo_alignment - 1U)};
ctx.Add("LDC.U64 DC.x,c{}[{}];" // unaligned_ssbo_addr
"AND.U64 DC.x,DC.x,{};" // ssbo_addr = unaligned_ssbo_addr & ssbo_align_mask
"LDC.U32 RC.x,c{}[{}];" // ssbo_size_u32
"CVT.U64.U32 DC.y,RC.x;" // ssbo_size = ssbo_size_u32
"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
"AND.U.CC RC.x,RC.x,RC.y;" // cond = a && b
"IF NE.x;" // if cond
"SUB.U64 DC.x,{}.x,DC.x;", // offset = input_addr - ssbo_addr
ssbo.cbuf_index, ssbo.cbuf_offset, ssbo.cbuf_index, ssbo.cbuf_offset + 8, address,
address, address);
ssbo.cbuf_index, ssbo.cbuf_offset, ssbo_align_mask, ssbo.cbuf_index,
ssbo.cbuf_offset + 8, address, address, address);
if (pointer_based) {
ctx.Add("PK64.U DC.y,c[{}];" // host_ssbo = cbuf
"ADD.U64 DC.x,DC.x,DC.y;" // host_addr = host_ssbo + offset

View File

@@ -601,7 +601,10 @@ std::string EmitContext::DefineGlobalMemoryFunctions() {
addr_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, addr_loc / 16, Swizzle(addr_loc));
size_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, size_loc / 16, Swizzle(size_loc));
}
const auto addr_pack{fmt::format("packUint2x32(uvec2({},{}))", addr_xy[0], addr_xy[1])};
const u32 ssbo_align_mask{~(static_cast<u32>(profile.min_ssbo_alignment) - 1U)};
const auto aligned_low_addr{fmt::format("{}&{}", addr_xy[0], ssbo_align_mask)};
const auto aligned_addr{fmt::format("uvec2({},{})", aligned_low_addr, addr_xy[1])};
const auto addr_pack{fmt::format("packUint2x32({})", aligned_addr)};
const auto addr_statment{fmt::format("uint64_t {}={};", ssbo_addr, addr_pack)};
func += addr_statment;

View File

@@ -891,7 +891,9 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
const Id ssbo_size_pointer{OpAccessChain(uniform_types.U32, cbufs[ssbo.cbuf_index].U32,
zero, ssbo_size_cbuf_offset)};
const Id ssbo_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))};
const u64 ssbo_align_mask{~(profile.min_ssbo_alignment - 1U)};
const Id unaligned_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))};
const Id ssbo_addr{OpBitwiseAnd(U64, unaligned_addr, Constant(U64, ssbo_align_mask))};
const Id ssbo_size{OpUConvert(U64, OpLoad(U32[1], ssbo_size_pointer))};
const Id ssbo_end{OpIAdd(U64, ssbo_addr, ssbo_size)};
const Id cond{OpLogicalAnd(U1, OpUGreaterThanEqual(U1, addr, ssbo_addr),

View File

@@ -298,7 +298,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
Optimization::PositionPass(env, program);
Optimization::GlobalMemoryToStorageBufferPass(program);
Optimization::GlobalMemoryToStorageBufferPass(program, host_info);
Optimization::TexturePass(env, program, host_info);
if (Settings::values.resolution_info.active) {

View File

@@ -16,6 +16,7 @@ struct HostTranslateInfo {
bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers
bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS
u32 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs
bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry
///< passthrough shaders
bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional

View File

@@ -11,6 +11,7 @@
#include "shader_recompiler/frontend/ir/breadth_first_search.h"
#include "shader_recompiler/frontend/ir/ir_emitter.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/host_translate_info.h"
#include "shader_recompiler/ir_opt/passes.h"
namespace Shader::Optimization {
@@ -408,7 +409,7 @@ void CollectStorageBuffers(IR::Block& block, IR::Inst& inst, StorageInfo& info)
}
/// Returns the offset in indices (not bytes) for an equivalent storage instruction
IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer) {
IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer, u32 alignment) {
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
IR::U32 offset;
if (const std::optional<LowAddrInfo> low_addr{TrackLowAddress(&inst)}) {
@@ -421,7 +422,10 @@ IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer
}
// Subtract the least significant 32 bits from the guest offset. The result is the storage
// buffer offset in bytes.
const IR::U32 low_cbuf{ir.GetCbuf(ir.Imm32(buffer.index), ir.Imm32(buffer.offset))};
IR::U32 low_cbuf{ir.GetCbuf(ir.Imm32(buffer.index), ir.Imm32(buffer.offset))};
// Align the offset base to match the host alignment requirements
low_cbuf = ir.BitwiseAnd(low_cbuf, ir.Imm32(~(alignment - 1U)));
return ir.ISub(offset, low_cbuf);
}
@@ -516,7 +520,7 @@ void Replace(IR::Block& block, IR::Inst& inst, const IR::U32& storage_index,
}
} // Anonymous namespace
void GlobalMemoryToStorageBufferPass(IR::Program& program) {
void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info) {
StorageInfo info;
for (IR::Block* const block : program.post_order_blocks) {
for (IR::Inst& inst : block->Instructions()) {
@@ -540,7 +544,8 @@ void GlobalMemoryToStorageBufferPass(IR::Program& program) {
const IR::U32 index{IR::Value{static_cast<u32>(info.set.index_of(it))}};
IR::Block* const block{storage_inst.block};
IR::Inst* const inst{storage_inst.inst};
const IR::U32 offset{StorageOffset(*block, *inst, storage_buffer)};
const IR::U32 offset{
StorageOffset(*block, *inst, storage_buffer, host_info.min_ssbo_alignment)};
Replace(*block, *inst, index, offset);
}
}

View File

@@ -16,7 +16,7 @@ void CollectShaderInfoPass(Environment& env, IR::Program& program);
void ConditionalBarrierPass(IR::Program& program);
void ConstantPropagationPass(Environment& env, IR::Program& program);
void DeadCodeEliminationPass(IR::Program& program);
void GlobalMemoryToStorageBufferPass(IR::Program& program);
void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info);
void IdentityRemovalPass(IR::Program& program);
void LowerFp64ToFp32(IR::Program& program);
void LowerFp16ToFp32(IR::Program& program);

View File

@@ -85,6 +85,8 @@ struct Profile {
/// Maxwell and earlier nVidia architectures have broken robust support
bool has_broken_robust{};
u64 min_ssbo_alignment{};
};
} // namespace Shader

View File

@@ -15,6 +15,7 @@ add_library(video_core STATIC
buffer_cache/buffer_cache.cpp
buffer_cache/buffer_cache.h
buffer_cache/memory_tracker_base.h
buffer_cache/usage_tracker.h
buffer_cache/word_manager.h
cache_types.h
cdma_pusher.cpp

View File

@@ -67,6 +67,7 @@ void BufferCache<P>::TickFrame() {
if (!channel_state) {
return;
}
runtime.TickFrame(slot_buffers);
// Calculate hits and shots and move hit bits to the right
const u32 hits = std::reduce(channel_state->uniform_cache_hits.begin(),
@@ -230,7 +231,10 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
for (const IntervalType& add_interval : tmp_intervals) {
common_ranges.add(add_interval);
}
runtime.CopyBuffer(dest_buffer, src_buffer, copies);
const auto& copy = copies[0];
src_buffer.MarkUsage(copy.src_offset, copy.size);
dest_buffer.MarkUsage(copy.dst_offset, copy.size);
runtime.CopyBuffer(dest_buffer, src_buffer, copies, true);
if (has_new_downloads) {
memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
}
@@ -258,9 +262,10 @@ bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) {
common_ranges.subtract(subtract_interval);
const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size));
auto& dest_buffer = slot_buffers[buffer];
Buffer& dest_buffer = slot_buffers[buffer];
const u32 offset = dest_buffer.Offset(*cpu_dst_address);
runtime.ClearBuffer(dest_buffer, offset, size, value);
dest_buffer.MarkUsage(offset, size);
return true;
}
@@ -603,6 +608,7 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
VAddr orig_cpu_addr = static_cast<VAddr>(second_copy.src_offset);
const IntervalType base_interval{orig_cpu_addr, orig_cpu_addr + copy.size};
async_downloads += std::make_pair(base_interval, 1);
buffer.MarkUsage(copy.src_offset, copy.size);
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
normalized_copies.push_back(second_copy);
}
@@ -621,8 +627,9 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
// Have in mind the staging buffer offset for the copy
copy.dst_offset += download_staging.offset;
const std::array copies{copy};
runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies,
false);
Buffer& buffer = slot_buffers[buffer_id];
buffer.MarkUsage(copy.src_offset, copy.size);
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
}
runtime.PostCopyBarrier();
runtime.Finish();
@@ -742,7 +749,7 @@ void BufferCache<P>::BindHostIndexBuffer() {
{BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}};
std::memcpy(upload_staging.mapped_span.data(),
draw_state.inline_index_draw_indexes.data(), size);
runtime.CopyBuffer(buffer, upload_staging.buffer, copies);
runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true);
} else {
buffer.ImmediateUpload(0, draw_state.inline_index_draw_indexes);
}
@@ -754,6 +761,7 @@ void BufferCache<P>::BindHostIndexBuffer() {
offset + draw_state.index_buffer.first * draw_state.index_buffer.FormatSizeInBytes();
runtime.BindIndexBuffer(buffer, new_offset, size);
} else {
buffer.MarkUsage(offset, size);
runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format,
draw_state.index_buffer.first, draw_state.index_buffer.count,
buffer, offset, size);
@@ -790,6 +798,7 @@ void BufferCache<P>::BindHostVertexBuffers() {
const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
const u32 offset = buffer.Offset(binding.cpu_addr);
buffer.MarkUsage(offset, binding.size);
host_bindings.buffers.push_back(&buffer);
host_bindings.offsets.push_back(offset);
@@ -895,6 +904,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size;
}
buffer.MarkUsage(offset, size);
if constexpr (NEEDS_BIND_UNIFORM_INDEX) {
runtime.BindUniformBuffer(stage, binding_index, buffer, offset, size);
} else {
@@ -913,6 +923,7 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
SynchronizeBuffer(buffer, binding.cpu_addr, size);
const u32 offset = buffer.Offset(binding.cpu_addr);
buffer.MarkUsage(offset, size);
const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0;
if (is_written) {
@@ -943,6 +954,7 @@ void BufferCache<P>::BindHostGraphicsTextureBuffers(size_t stage) {
const u32 offset = buffer.Offset(binding.cpu_addr);
const PixelFormat format = binding.format;
buffer.MarkUsage(offset, size);
if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) {
if (((channel_state->image_texture_buffers[stage] >> index) & 1) != 0) {
runtime.BindImageBuffer(buffer, offset, size, format);
@@ -975,9 +987,10 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() {
MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size);
const u32 offset = buffer.Offset(binding.cpu_addr);
buffer.MarkUsage(offset, size);
host_bindings.buffers.push_back(&buffer);
host_bindings.offsets.push_back(offset);
host_bindings.sizes.push_back(binding.size);
host_bindings.sizes.push_back(size);
}
if (host_bindings.buffers.size() > 0) {
runtime.BindTransformFeedbackBuffers(host_bindings);
@@ -1001,6 +1014,7 @@ void BufferCache<P>::BindHostComputeUniformBuffers() {
SynchronizeBuffer(buffer, binding.cpu_addr, size);
const u32 offset = buffer.Offset(binding.cpu_addr);
buffer.MarkUsage(offset, size);
if constexpr (NEEDS_BIND_UNIFORM_INDEX) {
runtime.BindComputeUniformBuffer(binding_index, buffer, offset, size);
++binding_index;
@@ -1021,6 +1035,7 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
SynchronizeBuffer(buffer, binding.cpu_addr, size);
const u32 offset = buffer.Offset(binding.cpu_addr);
buffer.MarkUsage(offset, size);
const bool is_written =
((channel_state->written_compute_storage_buffers >> index) & 1) != 0;
@@ -1053,6 +1068,7 @@ void BufferCache<P>::BindHostComputeTextureBuffers() {
const u32 offset = buffer.Offset(binding.cpu_addr);
const PixelFormat format = binding.format;
buffer.MarkUsage(offset, size);
if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) {
if (((channel_state->image_compute_texture_buffers >> index) & 1) != 0) {
runtime.BindImageBuffer(buffer, offset, size, format);
@@ -1172,10 +1188,11 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) {
size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size));
}
const BufferId buffer_id = FindBuffer(*cpu_addr, size);
channel_state->vertex_buffers[index] = Binding{
.cpu_addr = *cpu_addr,
.size = size,
.buffer_id = FindBuffer(*cpu_addr, size),
.buffer_id = buffer_id,
};
}
@@ -1401,7 +1418,8 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id,
.dst_offset = dst_base_offset,
.size = overlap.SizeBytes(),
});
runtime.CopyBuffer(new_buffer, overlap, copies);
new_buffer.MarkUsage(copies[0].dst_offset, copies[0].size);
runtime.CopyBuffer(new_buffer, overlap, copies, true);
DeleteBuffer(overlap_id, true);
}
@@ -1414,7 +1432,9 @@ BufferId BufferCache<P>::CreateBuffer(VAddr cpu_addr, u32 wanted_size) {
const u32 size = static_cast<u32>(overlap.end - overlap.begin);
const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size);
auto& new_buffer = slot_buffers[new_buffer_id];
runtime.ClearBuffer(new_buffer, 0, new_buffer.SizeBytes(), 0);
const size_t size_bytes = new_buffer.SizeBytes();
runtime.ClearBuffer(new_buffer, 0, size_bytes, 0);
new_buffer.MarkUsage(0, size_bytes);
for (const BufferId overlap_id : overlap.ids) {
JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap);
}
@@ -1467,11 +1487,6 @@ void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept {
template <class P>
bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) {
return SynchronizeBufferImpl(buffer, cpu_addr, size);
}
template <class P>
bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) {
boost::container::small_vector<BufferCopy, 4> copies;
u64 total_size_bytes = 0;
u64 largest_copy = 0;
@@ -1493,51 +1508,6 @@ bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 s
return false;
}
template <class P>
bool BufferCache<P>::SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr, u32 size) {
boost::container::small_vector<BufferCopy, 4> copies;
u64 total_size_bytes = 0;
u64 largest_copy = 0;
IntervalSet found_sets{};
auto make_copies = [&] {
for (auto& interval : found_sets) {
const std::size_t sub_size = interval.upper() - interval.lower();
const VAddr cpu_addr_ = interval.lower();
copies.push_back(BufferCopy{
.src_offset = total_size_bytes,
.dst_offset = cpu_addr_ - buffer.CpuAddr(),
.size = sub_size,
});
total_size_bytes += sub_size;
largest_copy = std::max<u64>(largest_copy, sub_size);
}
const std::span<BufferCopy> copies_span(copies.data(), copies.size());
UploadMemory(buffer, total_size_bytes, largest_copy, copies_span);
};
memory_tracker.ForEachUploadRange(cpu_addr, size, [&](u64 cpu_addr_out, u64 range_size) {
const VAddr base_adr = cpu_addr_out;
const VAddr end_adr = base_adr + range_size;
const IntervalType add_interval{base_adr, end_adr};
found_sets.add(add_interval);
});
if (found_sets.empty()) {
return true;
}
const IntervalType search_interval{cpu_addr, cpu_addr + size};
auto it = common_ranges.lower_bound(search_interval);
auto it_end = common_ranges.upper_bound(search_interval);
if (it == common_ranges.end()) {
make_copies();
return false;
}
while (it != it_end) {
found_sets.subtract(*it);
it++;
}
make_copies();
return false;
}
template <class P>
void BufferCache<P>::UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
std::span<BufferCopy> copies) {
@@ -1586,7 +1556,8 @@ void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
// Apply the staging offset
copy.src_offset += upload_staging.offset;
}
runtime.CopyBuffer(buffer, upload_staging.buffer, copies);
const bool can_reorder = runtime.CanReorderUpload(buffer, copies);
runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true, can_reorder);
}
}
@@ -1628,7 +1599,8 @@ void BufferCache<P>::InlineMemoryImplementation(VAddr dest_address, size_t copy_
}};
u8* const src_pointer = upload_staging.mapped_span.data();
std::memcpy(src_pointer, inlined_buffer.data(), copy_size);
runtime.CopyBuffer(buffer, upload_staging.buffer, copies);
const bool can_reorder = runtime.CanReorderUpload(buffer, copies);
runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true, can_reorder);
} else {
buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size));
}
@@ -1681,8 +1653,9 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si
for (BufferCopy& copy : copies) {
// Modify copies to have the staging offset in mind
copy.dst_offset += download_staging.offset;
buffer.MarkUsage(copy.src_offset, copy.size);
}
runtime.CopyBuffer(download_staging.buffer, buffer, copies_span);
runtime.CopyBuffer(download_staging.buffer, buffer, copies_span, true);
runtime.Finish();
for (const BufferCopy& copy : copies) {
const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset;
@@ -1780,15 +1753,25 @@ Binding BufferCache<P>::StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index,
const u32 memory_layout_size = static_cast<u32>(gpu_memory->GetMemoryLayoutSize(gpu_addr));
return std::min(memory_layout_size, static_cast<u32>(8_MiB));
}();
const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
if (!cpu_addr || size == 0) {
// Alignment only applies to the offset of the buffer
const u32 alignment = runtime.GetStorageBufferAlignment();
const GPUVAddr aligned_gpu_addr = Common::AlignDown(gpu_addr, alignment);
const u32 aligned_size = static_cast<u32>(gpu_addr - aligned_gpu_addr) + size;
const std::optional<VAddr> aligned_cpu_addr = gpu_memory->GpuToCpuAddress(aligned_gpu_addr);
if (!aligned_cpu_addr || size == 0) {
LOG_WARNING(HW_GPU, "Failed to find storage buffer for cbuf index {}", cbuf_index);
return NULL_BINDING;
}
const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, YUZU_PAGESIZE);
const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
ASSERT_MSG(cpu_addr, "Unaligned storage buffer address not found for cbuf index {}",
cbuf_index);
// The end address used for size calculation does not need to be aligned
const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, Core::Memory::YUZU_PAGESIZE);
const Binding binding{
.cpu_addr = *cpu_addr,
.size = is_written ? size : static_cast<u32>(cpu_end - *cpu_addr),
.cpu_addr = *aligned_cpu_addr,
.size = is_written ? aligned_size : static_cast<u32>(cpu_end - *aligned_cpu_addr),
.buffer_id = BufferId{},
};
return binding;

View File

@@ -529,10 +529,6 @@ private:
bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size);
bool SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size);
bool SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr, u32 size);
void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
std::span<BufferCopy> copies);

View File

@@ -0,0 +1,79 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/alignment.h"
#include "common/common_types.h"
namespace VideoCommon {
class UsageTracker {
static constexpr size_t BYTES_PER_BIT_SHIFT = 6;
static constexpr size_t PAGE_SHIFT = 6 + BYTES_PER_BIT_SHIFT;
static constexpr size_t PAGE_BYTES = 1 << PAGE_SHIFT;
public:
explicit UsageTracker(size_t size) {
const size_t num_pages = (size >> PAGE_SHIFT) + 1;
pages.resize(num_pages, 0ULL);
}
void Reset() noexcept {
std::ranges::fill(pages, 0ULL);
}
void Track(u64 offset, u64 size) noexcept {
const size_t page = offset >> PAGE_SHIFT;
const size_t page_end = (offset + size) >> PAGE_SHIFT;
TrackPage(page, offset, size);
if (page == page_end) {
return;
}
for (size_t i = page + 1; i < page_end; i++) {
pages[i] = ~u64{0};
}
const size_t offset_end = offset + size;
const size_t offset_end_page_aligned = Common::AlignDown(offset_end, PAGE_BYTES);
TrackPage(page_end, offset_end_page_aligned, offset_end - offset_end_page_aligned);
}
[[nodiscard]] bool IsUsed(u64 offset, u64 size) const noexcept {
const size_t page = offset >> PAGE_SHIFT;
const size_t page_end = (offset + size) >> PAGE_SHIFT;
if (IsPageUsed(page, offset, size)) {
return true;
}
for (size_t i = page + 1; i < page_end; i++) {
if (pages[i] != 0) {
return true;
}
}
const size_t offset_end = offset + size;
const size_t offset_end_page_aligned = Common::AlignDown(offset_end, PAGE_BYTES);
return IsPageUsed(page_end, offset_end_page_aligned, offset_end - offset_end_page_aligned);
}
private:
void TrackPage(u64 page, u64 offset, u64 size) noexcept {
const size_t offset_in_page = offset % PAGE_BYTES;
const size_t first_bit = offset_in_page >> BYTES_PER_BIT_SHIFT;
const size_t num_bits = std::min<size_t>(size, PAGE_BYTES) >> BYTES_PER_BIT_SHIFT;
const size_t mask = ~u64{0} >> (64 - num_bits);
pages[page] |= (~u64{0} & mask) << first_bit;
}
bool IsPageUsed(u64 page, u64 offset, u64 size) const noexcept {
const size_t offset_in_page = offset % PAGE_BYTES;
const size_t first_bit = offset_in_page >> BYTES_PER_BIT_SHIFT;
const size_t num_bits = std::min<size_t>(size, PAGE_BYTES) >> BYTES_PER_BIT_SHIFT;
const size_t mask = ~u64{0} >> (64 - num_bits);
const size_t mask2 = (~u64{0} & mask) << first_bit;
return (pages[page] & mask2) != 0;
}
private:
std::vector<u64> pages;
};
} // namespace VideoCommon

View File

@@ -178,13 +178,14 @@ void BufferCacheRuntime::CopyBuffer(GLuint dst_buffer, Buffer& src_buffer,
}
void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, GLuint src_buffer,
std::span<const VideoCommon::BufferCopy> copies, bool barrier) {
std::span<const VideoCommon::BufferCopy> copies, bool barrier,
bool) {
CopyBuffer(dst_buffer.Handle(), src_buffer, copies, barrier);
}
void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer,
std::span<const VideoCommon::BufferCopy> copies) {
CopyBuffer(dst_buffer.Handle(), src_buffer.Handle(), copies);
std::span<const VideoCommon::BufferCopy> copies, bool) {
CopyBuffer(dst_buffer.Handle(), src_buffer.Handle(), copies, true);
}
void BufferCacheRuntime::PreCopyBarrier() {

View File

@@ -30,6 +30,8 @@ public:
void MakeResident(GLenum access) noexcept;
void MarkUsage(u64 offset, u64 size) {}
[[nodiscard]] GLuint View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format);
[[nodiscard]] GLuint64EXT HostGpuAddr() const noexcept {
@@ -66,22 +68,29 @@ public:
[[nodiscard]] StagingBufferMap DownloadStagingBuffer(size_t size);
bool CanReorderUpload(const Buffer&, std::span<const VideoCommon::BufferCopy>) {
return false;
}
void CopyBuffer(GLuint dst_buffer, GLuint src_buffer,
std::span<const VideoCommon::BufferCopy> copies, bool barrier = true);
std::span<const VideoCommon::BufferCopy> copies, bool barrier);
void CopyBuffer(GLuint dst_buffer, Buffer& src_buffer,
std::span<const VideoCommon::BufferCopy> copies, bool barrier = true);
std::span<const VideoCommon::BufferCopy> copies, bool barrier);
void CopyBuffer(Buffer& dst_buffer, GLuint src_buffer,
std::span<const VideoCommon::BufferCopy> copies, bool barrier = true);
std::span<const VideoCommon::BufferCopy> copies, bool barrier,
bool can_reorder_upload = false);
void CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer,
std::span<const VideoCommon::BufferCopy> copies);
std::span<const VideoCommon::BufferCopy> copies, bool);
void PreCopyBarrier();
void PostCopyBarrier();
void Finish();
void TickFrame(VideoCommon::SlotVector<Buffer>&) noexcept {}
void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value);
void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size);
@@ -182,6 +191,10 @@ public:
return device.CanReportMemoryUsage();
}
u32 GetStorageBufferAlignment() const {
return static_cast<u32>(device.GetShaderStorageBufferAlignment());
}
private:
static constexpr std::array PABO_LUT{
GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV,

View File

@@ -265,33 +265,33 @@ std::string Device::GetVendorName() const {
if (vendor_name == "Intel") {
// For Mesa, `Intel` is an overloaded vendor string that could mean crocus or iris.
// Simply return `INTEL` for those as well as the Windows driver.
return "INTEL";
return "Intel";
}
if (vendor_name == "Intel Open Source Technology Center") {
return "I965";
return "i965";
}
if (vendor_name == "Mesa Project") {
return "I915";
return "i915";
}
if (vendor_name == "Mesa/X.org") {
// This vendor string is overloaded between llvmpipe, softpipe, and virgl, so just return
// MESA instead of one of those driver names.
return "MESA";
return "Mesa";
}
if (vendor_name == "AMD") {
return "RADEONSI";
return "RadeonSI";
}
if (vendor_name == "nouveau") {
return "NOUVEAU";
return "Nouveau";
}
if (vendor_name == "X.Org") {
return "R600";
}
if (vendor_name == "Collabora Ltd") {
return "ZINK";
return "Zink";
}
if (vendor_name == "Intel Corporation") {
return "OPENSWR";
return "OpenSWR";
}
if (vendor_name == "Microsoft Corporation") {
return "D3D12";
@@ -300,7 +300,7 @@ std::string Device::GetVendorName() const {
// Mesa's tegra driver reports `NVIDIA`. Only present in this list because the default
// strategy would have returned `NVIDIA` here for this driver, the same result as the
// proprietary driver.
return "TEGRA";
return "Tegra";
}
return vendor_name;
}

View File

@@ -232,6 +232,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
.has_gl_bool_ref_bug = device.HasBoolRefBug(),
.ignore_nan_fp_comparisons = true,
.gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(),
.min_ssbo_alignment = device.GetShaderStorageBufferAlignment(),
},
host_info{
.support_float64 = true,
@@ -240,6 +241,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
.needs_demote_reorder = device.IsAmd(),
.support_snorm_render_buffer = false,
.support_viewport_index_layer = device.HasVertexViewportLayer(),
.min_ssbo_alignment = static_cast<u32>(device.GetShaderStorageBufferAlignment()),
.support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(),
.support_conditional_barrier = device.SupportsConditionalBarriers(),
} {

View File

@@ -79,13 +79,13 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo
} // Anonymous namespace
Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params)
: VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params) {}
: VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params), tracker{4096} {}
Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_,
VAddr cpu_addr_, u64 size_bytes_)
: VideoCommon::BufferBase<VideoCore::RasterizerInterface>(rasterizer_, cpu_addr_, size_bytes_),
device{&runtime.device}, buffer{
CreateBuffer(*device, runtime.memory_allocator, SizeBytes())} {
device{&runtime.device}, buffer{CreateBuffer(*device, runtime.memory_allocator, SizeBytes())},
tracker{SizeBytes()} {
if (runtime.device.HasDebuggingToolAttached()) {
buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str());
}
@@ -355,12 +355,35 @@ bool BufferCacheRuntime::CanReportMemoryUsage() const {
return device.CanReportMemoryUsage();
}
u32 BufferCacheRuntime::GetStorageBufferAlignment() const {
return static_cast<u32>(device.GetStorageBufferAlignment());
}
void BufferCacheRuntime::TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept {
for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) {
it->ResetUsageTracking();
}
}
void BufferCacheRuntime::Finish() {
scheduler.Finish();
}
bool BufferCacheRuntime::CanReorderUpload(const Buffer& buffer,
std::span<const VideoCommon::BufferCopy> copies) {
if (Settings::values.disable_buffer_reorder) {
return false;
}
const bool can_use_upload_cmdbuf =
std::ranges::all_of(copies, [&](const VideoCommon::BufferCopy& copy) {
return !buffer.IsRegionUsed(copy.dst_offset, copy.size);
});
return can_use_upload_cmdbuf;
}
void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,
std::span<const VideoCommon::BufferCopy> copies, bool barrier) {
std::span<const VideoCommon::BufferCopy> copies, bool barrier,
bool can_reorder_upload) {
if (dst_buffer == VK_NULL_HANDLE || src_buffer == VK_NULL_HANDLE) {
return;
}
@@ -376,9 +399,18 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
};
// Measuring a popular game, this number never exceeds the specified size once data is warmed up
boost::container::small_vector<VkBufferCopy, 8> vk_copies(copies.size());
std::ranges::transform(copies, vk_copies.begin(), MakeBufferCopy);
if (src_buffer == staging_pool.StreamBuf() && can_reorder_upload) {
scheduler.RecordWithUploadBuffer([src_buffer, dst_buffer, vk_copies](
vk::CommandBuffer, vk::CommandBuffer upload_cmdbuf) {
upload_cmdbuf.CopyBuffer(src_buffer, dst_buffer, vk_copies);
});
return;
}
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) {
if (barrier) {

View File

@@ -5,6 +5,7 @@
#include "video_core/buffer_cache/buffer_cache_base.h"
#include "video_core/buffer_cache/memory_tracker_base.h"
#include "video_core/buffer_cache/usage_tracker.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_vulkan/vk_compute_pass.h"
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
@@ -34,6 +35,18 @@ public:
return *buffer;
}
[[nodiscard]] bool IsRegionUsed(u64 offset, u64 size) const noexcept {
return tracker.IsUsed(offset, size);
}
void MarkUsage(u64 offset, u64 size) noexcept {
tracker.Track(offset, size);
}
void ResetUsageTracking() noexcept {
tracker.Reset();
}
operator VkBuffer() const noexcept {
return *buffer;
}
@@ -49,6 +62,7 @@ private:
const Device* device{};
vk::Buffer buffer;
std::vector<BufferView> views;
VideoCommon::UsageTracker tracker;
};
class QuadArrayIndexBuffer;
@@ -67,6 +81,8 @@ public:
ComputePassDescriptorQueue& compute_pass_descriptor_queue,
DescriptorPool& descriptor_pool);
void TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept;
void Finish();
u64 GetDeviceLocalMemory() const;
@@ -75,16 +91,21 @@ public:
bool CanReportMemoryUsage() const;
u32 GetStorageBufferAlignment() const;
[[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size);
[[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false);
bool CanReorderUpload(const Buffer& buffer, std::span<const VideoCommon::BufferCopy> copies);
void FreeDeferredStagingBuffer(StagingBufferRef& ref);
void PreCopyBarrier();
void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer,
std::span<const VideoCommon::BufferCopy> copies, bool barrier = true);
std::span<const VideoCommon::BufferCopy> copies, bool barrier,
bool can_reorder_upload = false);
void PostCopyBarrier();

View File

@@ -100,12 +100,14 @@ void MasterSemaphore::Wait(u64 tick) {
Refresh();
}
VkResult MasterSemaphore::SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore,
VkSemaphore wait_semaphore, u64 host_tick) {
VkResult MasterSemaphore::SubmitQueue(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf,
VkSemaphore signal_semaphore, VkSemaphore wait_semaphore,
u64 host_tick) {
if (semaphore) {
return SubmitQueueTimeline(cmdbuf, signal_semaphore, wait_semaphore, host_tick);
return SubmitQueueTimeline(cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore,
host_tick);
} else {
return SubmitQueueFence(cmdbuf, signal_semaphore, wait_semaphore, host_tick);
return SubmitQueueFence(cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore, host_tick);
}
}
@@ -115,6 +117,7 @@ static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{
};
VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
vk::CommandBuffer& upload_cmdbuf,
VkSemaphore signal_semaphore,
VkSemaphore wait_semaphore, u64 host_tick) {
const VkSemaphore timeline_semaphore = *semaphore;
@@ -123,6 +126,8 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
const std::array signal_values{host_tick, u64(0)};
const std::array signal_semaphores{timeline_semaphore, signal_semaphore};
const std::array cmdbuffers{*upload_cmdbuf, *cmdbuf};
const u32 num_wait_semaphores = wait_semaphore ? 1 : 0;
const VkTimelineSemaphoreSubmitInfo timeline_si{
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
@@ -138,8 +143,8 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
.waitSemaphoreCount = num_wait_semaphores,
.pWaitSemaphores = &wait_semaphore,
.pWaitDstStageMask = wait_stage_masks.data(),
.commandBufferCount = 1,
.pCommandBuffers = cmdbuf.address(),
.commandBufferCount = static_cast<u32>(cmdbuffers.size()),
.pCommandBuffers = cmdbuffers.data(),
.signalSemaphoreCount = num_signal_semaphores,
.pSignalSemaphores = signal_semaphores.data(),
};
@@ -147,19 +152,23 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
return device.GetGraphicsQueue().Submit(submit_info);
}
VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore,
VkSemaphore wait_semaphore, u64 host_tick) {
VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf,
vk::CommandBuffer& upload_cmdbuf,
VkSemaphore signal_semaphore, VkSemaphore wait_semaphore,
u64 host_tick) {
const u32 num_signal_semaphores = signal_semaphore ? 1 : 0;
const u32 num_wait_semaphores = wait_semaphore ? 1 : 0;
const std::array cmdbuffers{*upload_cmdbuf, *cmdbuf};
const VkSubmitInfo submit_info{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = nullptr,
.waitSemaphoreCount = num_wait_semaphores,
.pWaitSemaphores = &wait_semaphore,
.pWaitDstStageMask = wait_stage_masks.data(),
.commandBufferCount = 1,
.pCommandBuffers = cmdbuf.address(),
.commandBufferCount = static_cast<u32>(cmdbuffers.size()),
.pCommandBuffers = cmdbuffers.data(),
.signalSemaphoreCount = num_signal_semaphores,
.pSignalSemaphores = &signal_semaphore,
};

View File

@@ -52,14 +52,16 @@ public:
void Wait(u64 tick);
/// Submits the device graphics queue, updating the tick as necessary
VkResult SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore,
VkSemaphore wait_semaphore, u64 host_tick);
VkResult SubmitQueue(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf,
VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, u64 host_tick);
private:
VkResult SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore,
VkSemaphore wait_semaphore, u64 host_tick);
VkResult SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore,
VkSemaphore wait_semaphore, u64 host_tick);
VkResult SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf,
VkSemaphore signal_semaphore, VkSemaphore wait_semaphore,
u64 host_tick);
VkResult SubmitQueueFence(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf,
VkSemaphore signal_semaphore, VkSemaphore wait_semaphore,
u64 host_tick);
void WaitThread(std::stop_token token);

View File

@@ -373,6 +373,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
.has_broken_robust =
device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal,
.min_ssbo_alignment = device.GetStorageBufferAlignment(),
};
host_info = Shader::HostTranslateInfo{
@@ -383,6 +384,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE,
.support_snorm_render_buffer = true,
.support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(),
.min_ssbo_alignment = static_cast<u32>(device.GetStorageBufferAlignment()),
.support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
.support_conditional_barrier = device.SupportsConditionalBarriers(),
};

View File

@@ -22,11 +22,12 @@ namespace Vulkan {
MICROPROFILE_DECLARE(Vulkan_WaitForWorker);
void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf) {
void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf,
vk::CommandBuffer upload_cmdbuf) {
auto command = first;
while (command != nullptr) {
auto next = command->GetNext();
command->Execute(cmdbuf);
command->Execute(cmdbuf, upload_cmdbuf);
command->~Command();
command = next;
}
@@ -180,7 +181,7 @@ void Scheduler::WorkerThread(std::stop_token stop_token) {
// Perform the work, tracking whether the chunk was a submission
// before executing.
const bool has_submit = work->HasSubmit();
work->ExecuteAll(current_cmdbuf);
work->ExecuteAll(current_cmdbuf, current_upload_cmdbuf);
// If the chunk was a submission, reallocate the command buffer.
if (has_submit) {
@@ -205,6 +206,13 @@ void Scheduler::AllocateWorkerCommandBuffer() {
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
.pInheritanceInfo = nullptr,
});
current_upload_cmdbuf = vk::CommandBuffer(command_pool->Commit(), device.GetDispatchLoader());
current_upload_cmdbuf.Begin({
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.pNext = nullptr,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
.pInheritanceInfo = nullptr,
});
}
u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
@@ -212,7 +220,17 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se
InvalidateState();
const u64 signal_value = master_semaphore->NextTick();
Record([signal_semaphore, wait_semaphore, signal_value, this](vk::CommandBuffer cmdbuf) {
RecordWithUploadBuffer([signal_semaphore, wait_semaphore, signal_value,
this](vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) {
static constexpr VkMemoryBarrier WRITE_BARRIER{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
};
upload_cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, WRITE_BARRIER);
upload_cmdbuf.End();
cmdbuf.End();
if (on_submit) {
@@ -221,7 +239,7 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se
std::scoped_lock lock{submit_mutex};
switch (const VkResult result = master_semaphore->SubmitQueue(
cmdbuf, signal_semaphore, wait_semaphore, signal_value)) {
cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore, signal_value)) {
case VK_SUCCESS:
break;
case VK_ERROR_DEVICE_LOST:

View File

@@ -80,7 +80,8 @@ public:
/// Send work to a separate thread.
template <typename T>
void Record(T&& command) {
requires std::is_invocable_v<T, vk::CommandBuffer, vk::CommandBuffer>
void RecordWithUploadBuffer(T&& command) {
if (chunk->Record(command)) {
return;
}
@@ -88,6 +89,15 @@ public:
(void)chunk->Record(command);
}
template <typename T>
requires std::is_invocable_v<T, vk::CommandBuffer>
void Record(T&& c) {
this->RecordWithUploadBuffer(
[command = std::move(c)](vk::CommandBuffer cmdbuf, vk::CommandBuffer) {
command(cmdbuf);
});
}
/// Returns the current command buffer tick.
[[nodiscard]] u64 CurrentTick() const noexcept {
return master_semaphore->CurrentTick();
@@ -119,7 +129,7 @@ private:
public:
virtual ~Command() = default;
virtual void Execute(vk::CommandBuffer cmdbuf) const = 0;
virtual void Execute(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) const = 0;
Command* GetNext() const {
return next;
@@ -142,8 +152,8 @@ private:
TypedCommand(TypedCommand&&) = delete;
TypedCommand& operator=(TypedCommand&&) = delete;
void Execute(vk::CommandBuffer cmdbuf) const override {
command(cmdbuf);
void Execute(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) const override {
command(cmdbuf, upload_cmdbuf);
}
private:
@@ -152,7 +162,7 @@ private:
class CommandChunk final {
public:
void ExecuteAll(vk::CommandBuffer cmdbuf);
void ExecuteAll(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf);
template <typename T>
bool Record(T& command) {
@@ -228,6 +238,7 @@ private:
VideoCommon::QueryCacheBase<QueryCacheParams>* query_cache = nullptr;
vk::CommandBuffer current_cmdbuf;
vk::CommandBuffer current_upload_cmdbuf;
std::unique_ptr<CommandChunk> chunk;
std::function<void()> on_submit;

View File

@@ -672,7 +672,7 @@ void SMAA::UploadImages(Scheduler& scheduler) {
UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent,
VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes));
scheduler.Record([&](vk::CommandBuffer& cmdbuf) {
scheduler.Record([&](vk::CommandBuffer cmdbuf) {
for (auto& images : m_dynamic_images) {
for (size_t i = 0; i < MaxDynamicImage; i++) {
ClearColorImage(cmdbuf, *images.images[i]);
@@ -707,7 +707,7 @@ VkImageView SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage source_
UpdateDescriptorSets(source_image_view, image_index);
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([=, this](vk::CommandBuffer& cmdbuf) {
scheduler.Record([=, this](vk::CommandBuffer cmdbuf) {
TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL);
TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL);
BeginRenderPass(cmdbuf, m_renderpasses[EdgeDetection], edge_detection_framebuffer,

View File

@@ -36,6 +36,10 @@ public:
StagingBufferRef Request(size_t size, MemoryUsage usage, bool deferred = false);
void FreeDeferred(StagingBufferRef& ref);
[[nodiscard]] VkBuffer StreamBuf() const noexcept {
return *stream_buffer;
}
void TickFrame();
private:

View File

@@ -138,6 +138,10 @@ public:
return Iterator(this, SlotId{SlotId::INVALID_INDEX});
}
[[nodiscard]] size_t size() const noexcept {
return values_capacity - free_list.size();
}
private:
struct NonTrivialDummy {
NonTrivialDummy() noexcept {}

View File

@@ -847,11 +847,41 @@ std::string Device::GetDriverName() const {
case VK_DRIVER_ID_NVIDIA_PROPRIETARY:
return "NVIDIA";
case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS:
return "INTEL";
return "Intel";
case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA:
return "ANV";
case VK_DRIVER_ID_IMAGINATION_PROPRIETARY:
return "PowerVR";
case VK_DRIVER_ID_QUALCOMM_PROPRIETARY:
return "Qualcomm";
case VK_DRIVER_ID_ARM_PROPRIETARY:
return "Mali";
case VK_DRIVER_ID_GOOGLE_SWIFTSHADER:
return "SwiftShader";
case VK_DRIVER_ID_BROADCOM_PROPRIETARY:
return "Broadcom";
case VK_DRIVER_ID_MESA_LLVMPIPE:
return "LAVAPIPE";
return "Lavapipe";
case VK_DRIVER_ID_MOLTENVK:
return "MoltenVK";
case VK_DRIVER_ID_VERISILICON_PROPRIETARY:
return "Vivante";
case VK_DRIVER_ID_MESA_TURNIP:
return "Turnip";
case VK_DRIVER_ID_MESA_V3DV:
return "V3DV";
case VK_DRIVER_ID_MESA_PANVK:
return "PanVK";
case VK_DRIVER_ID_MESA_VENUS:
return "Venus";
case VK_DRIVER_ID_MESA_DOZEN:
return "Dozen";
case VK_DRIVER_ID_MESA_NVK:
return "NVK";
case VK_DRIVER_ID_IMAGINATION_OPEN_SOURCE_MESA:
return "PVR";
// case VK_DRIVER_ID_MESA_AGXV:
// return "Asahi";
default:
return properties.driver.driverName;
}

View File

@@ -1101,6 +1101,10 @@ public:
return &handle;
}
VkCommandBuffer operator*() const noexcept {
return handle;
}
void Begin(const VkCommandBufferBeginInfo& begin_info) const {
Check(dld->vkBeginCommandBuffer(handle, &begin_info));
}

View File

@@ -386,7 +386,7 @@ if (NOT WIN32)
target_include_directories(yuzu PRIVATE ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
endif()
if (UNIX AND NOT APPLE)
target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::DBus)
target_link_libraries(yuzu PRIVATE Qt${QT_MAJOR_VERSION}::DBus gamemode)
endif()
target_compile_definitions(yuzu PRIVATE

View File

@@ -30,7 +30,6 @@
#include <QSize>
#include <QStringLiteral>
#include <QSurfaceFormat>
#include <QTimer>
#include <QWindow>
#include <QtCore/qobjectdefs.h>
@@ -66,6 +65,8 @@ class QObject;
class QPaintEngine;
class QSurface;
constexpr int default_mouse_constrain_timeout = 10;
EmuThread::EmuThread(Core::System& system) : m_system{system} {}
EmuThread::~EmuThread() = default;
@@ -304,6 +305,9 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
Qt::QueuedConnection);
connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection);
connect(this, &GRenderWindow::TasPlaybackStateChanged, parent, &GMainWindow::OnTasStateChanged);
mouse_constrain_timer.setInterval(default_mouse_constrain_timeout);
connect(&mouse_constrain_timer, &QTimer::timeout, this, &GRenderWindow::ConstrainMouse);
}
void GRenderWindow::ExecuteProgram(std::size_t program_index) {
@@ -393,6 +397,22 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
QWidget::closeEvent(event);
}
void GRenderWindow::leaveEvent(QEvent* event) {
if (Settings::values.mouse_panning) {
const QRect& rect = QWidget::geometry();
QPoint position = QCursor::pos();
qint32 x = qBound(rect.left(), position.x(), rect.right());
qint32 y = qBound(rect.top(), position.y(), rect.bottom());
// Only start the timer if the mouse has left the window bound.
// The leave event is also triggered when the window looses focus.
if (x != position.x() || y != position.y()) {
mouse_constrain_timer.start();
}
event->accept();
}
}
int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) {
static constexpr std::array<std::pair<Qt::Key, Settings::NativeKeyboard::Keys>, 106> key_map = {
std::pair<Qt::Key, Settings::NativeKeyboard::Keys>{Qt::Key_A, Settings::NativeKeyboard::A},
@@ -658,10 +678,19 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
input_subsystem->GetMouse()->TouchMove(touch_x, touch_y);
input_subsystem->GetMouse()->Move(pos.x(), pos.y(), center_x, center_y);
// Center mouse for mouse panning
if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
}
// Constrain mouse for mouse emulation with mouse panning
if (Settings::values.mouse_panning && Settings::values.mouse_enabled) {
const auto [clamped_mouse_x, clamped_mouse_y] = ClipToTouchScreen(x, y);
QCursor::setPos(mapToGlobal(
QPoint{static_cast<int>(clamped_mouse_x), static_cast<int>(clamped_mouse_y)}));
}
mouse_constrain_timer.stop();
emit MouseActivity();
}
@@ -675,6 +704,31 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
input_subsystem->GetMouse()->ReleaseButton(button);
}
void GRenderWindow::ConstrainMouse() {
if (emu_thread == nullptr || !Settings::values.mouse_panning) {
mouse_constrain_timer.stop();
return;
}
if (!this->isActiveWindow()) {
mouse_constrain_timer.stop();
return;
}
if (Settings::values.mouse_enabled) {
const auto pos = mapFromGlobal(QCursor::pos());
const int new_pos_x = std::clamp(pos.x(), 0, width());
const int new_pos_y = std::clamp(pos.y(), 0, height());
QCursor::setPos(mapToGlobal(QPoint{new_pos_x, new_pos_y}));
return;
}
const int center_x = width() / 2;
const int center_y = height() / 2;
QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
}
void GRenderWindow::wheelEvent(QWheelEvent* event) {
const int x = event->angleDelta().x();
const int y = event->angleDelta().y();

View File

@@ -17,6 +17,7 @@
#include <QString>
#include <QStringList>
#include <QThread>
#include <QTimer>
#include <QWidget>
#include <qglobal.h>
#include <qnamespace.h>
@@ -38,7 +39,6 @@ class QMouseEvent;
class QObject;
class QResizeEvent;
class QShowEvent;
class QTimer;
class QTouchEvent;
class QWheelEvent;
@@ -166,6 +166,7 @@ public:
std::pair<u32, u32> ScaleTouch(const QPointF& pos) const;
void closeEvent(QCloseEvent* event) override;
void leaveEvent(QEvent* event) override;
void resizeEvent(QResizeEvent* event) override;
@@ -229,6 +230,7 @@ private:
void TouchBeginEvent(const QTouchEvent* event);
void TouchUpdateEvent(const QTouchEvent* event);
void TouchEndEvent();
void ConstrainMouse();
void RequestCameraCapture();
void OnCameraCapture(int requestId, const QImage& img);
@@ -268,6 +270,8 @@ private:
std::unique_ptr<QTimer> camera_timer;
#endif
QTimer mouse_constrain_timer;
Core::System& system;
protected:

View File

@@ -51,6 +51,8 @@ void ConfigureDebug::SetConfiguration() {
ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue());
ui->enable_renderdoc_hotkey->setEnabled(runtime_lock);
ui->enable_renderdoc_hotkey->setChecked(Settings::values.enable_renderdoc_hotkey.GetValue());
ui->disable_buffer_reorder->setEnabled(runtime_lock);
ui->disable_buffer_reorder->setChecked(Settings::values.disable_buffer_reorder.GetValue());
ui->enable_graphics_debugging->setEnabled(runtime_lock);
ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue());
ui->enable_shader_feedback->setEnabled(runtime_lock);
@@ -96,6 +98,7 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.enable_all_controllers = ui->enable_all_controllers->isChecked();
Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
Settings::values.enable_renderdoc_hotkey = ui->enable_renderdoc_hotkey->isChecked();
Settings::values.disable_buffer_reorder = ui->disable_buffer_reorder->isChecked();
Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked();

View File

@@ -271,19 +271,6 @@
</widget>
</item>
<item row="8" column="0">
<widget class="QCheckBox" name="disable_macro_hle">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, it disables the macro HLE functions. Enabling this makes games run slower</string>
</property>
<property name="text">
<string>Disable Macro HLE</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="dump_macros">
<property name="enabled">
<bool>true</bool>
@@ -306,59 +293,7 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="enable_shader_feedback">
<property name="toolTip">
<string>When checked, yuzu will log statistics about the compiled pipeline cache</string>
</property>
<property name="text">
<string>Enable Shader Feedback</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="disable_macro_jit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower</string>
</property>
<property name="text">
<string>Disable Macro JIT</string>
</property>
</widget>
</item>
<item row="9" column="0">
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="enable_graphics_debugging">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, the graphics API enters a slower debugging mode</string>
</property>
<property name="text">
<string>Enable Graphics Debugging</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="dump_shaders">
<property name="enabled">
<bool>true</bool>
@@ -378,6 +313,81 @@
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="disable_macro_jit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower</string>
</property>
<property name="text">
<string>Disable Macro JIT</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QCheckBox" name="disable_macro_hle">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, it disables the macro HLE functions. Enabling this makes games run slower</string>
</property>
<property name="text">
<string>Disable Macro HLE</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="enable_graphics_debugging">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, the graphics API enters a slower debugging mode</string>
</property>
<property name="text">
<string>Enable Graphics Debugging</string>
</property>
</widget>
</item>
<item row="10" column="0">
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="enable_shader_feedback">
<property name="toolTip">
<string>When checked, yuzu will log statistics about the compiled pipeline cache</string>
</property>
<property name="text">
<string>Enable Shader Feedback</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="disable_buffer_reorder">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When checked, disables reording of mapped memory uploads which allows to associate uploads with specific draws. May reduce performance in some cases.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Disable Buffer Reorder</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@@ -36,12 +36,29 @@ ConfigureGeneral::~ConfigureGeneral() = default;
void ConfigureGeneral::SetConfiguration() {}
void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) {
QLayout& layout = *ui->general_widget->layout();
QLayout& general_layout = *ui->general_widget->layout();
QLayout& linux_layout = *ui->linux_widget->layout();
std::map<u32, QWidget*> hold{};
std::map<u32, QWidget*> general_hold{};
std::map<u32, QWidget*> linux_hold{};
for (const auto setting :
UISettings::values.linkage.by_category[Settings::Category::UiGeneral]) {
std::vector<Settings::BasicSetting*> settings;
auto push = [&settings](auto& list) {
for (auto setting : list) {
settings.push_back(setting);
}
};
push(UISettings::values.linkage.by_category[Settings::Category::UiGeneral]);
push(Settings::values.linkage.by_category[Settings::Category::Linux]);
// Only show Linux group on Unix
#ifndef __unix__
ui->LinuxGroupBox->setVisible(false);
#endif
for (const auto setting : settings) {
auto* widget = builder.BuildWidget(setting, apply_funcs);
if (widget == nullptr) {
@@ -52,11 +69,23 @@ void ConfigureGeneral::Setup(const ConfigurationShared::Builder& builder) {
continue;
}
hold.emplace(setting->Id(), widget);
switch (setting->GetCategory()) {
case Settings::Category::UiGeneral:
general_hold.emplace(setting->Id(), widget);
break;
case Settings::Category::Linux:
linux_hold.emplace(setting->Id(), widget);
break;
default:
widget->deleteLater();
}
}
for (const auto& [id, widget] : hold) {
layout.addWidget(widget);
for (const auto& [id, widget] : general_hold) {
general_layout.addWidget(widget);
}
for (const auto& [id, widget] : linux_hold) {
linux_layout.addWidget(widget);
}
}

View File

@@ -46,6 +46,33 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="LinuxGroupBox">
<property name="title">
<string>Linux</string>
</property>
<layout class="QVBoxLayout" name="LinuxVerticalLayout_1">
<item>
<widget class="QWidget" name="linux_widget" native="true">
<layout class="QVBoxLayout" name="LinuxVerticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">

View File

@@ -57,7 +57,7 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<widget class="QGroupBox" name="coreGroup">
<property name="title">
<string>Core</string>
</property>

View File

@@ -176,6 +176,9 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"),
QStringLiteral());
// Linux
INSERT(Settings, enable_gamemode, tr("Enable Gamemode"), QStringLiteral());
// Ui Debugging
// Ui Multiplayer

View File

@@ -17,6 +17,7 @@
#ifdef __unix__
#include <csignal>
#include <sys/socket.h>
#include "common/linux/gamemode.h"
#endif
#include <boost/container/flat_set.hpp>
@@ -47,6 +48,7 @@
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/set/set_sys.h"
#include "yuzu/multiplayer/state.h"
#include "yuzu/util/controller_navigation.h"
@@ -186,7 +188,6 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
#endif
constexpr int default_mouse_hide_timeout = 2500;
constexpr int default_mouse_center_timeout = 10;
constexpr int default_input_update_timeout = 1;
constexpr size_t CopyBufferSize = 1_MiB;
@@ -319,6 +320,7 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk
provider{std::make_unique<FileSys::ManualContentProvider>()} {
#ifdef __unix__
SetupSigInterrupts();
SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue());
#endif
system->Initialize();
@@ -436,9 +438,6 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk
connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor);
connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor);
mouse_center_timer.setInterval(default_mouse_center_timeout);
connect(&mouse_center_timer, &QTimer::timeout, this, &GMainWindow::CenterMouseCursor);
update_input_timer.setInterval(default_input_update_timeout);
connect(&update_input_timer, &QTimer::timeout, this, &GMainWindow::UpdateInputDrivers);
update_input_timer.start();
@@ -1048,7 +1047,12 @@ void GMainWindow::InitializeWidgets() {
statusBar()->addPermanentWidget(label);
}
// TODO (flTobi): Add the widget when multiplayer is fully implemented
firmware_label = new QLabel();
firmware_label->setObjectName(QStringLiteral("FirmwareLabel"));
firmware_label->setVisible(false);
firmware_label->setFocusPolicy(Qt::NoFocus);
statusBar()->addPermanentWidget(firmware_label);
statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0);
statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0);
@@ -1370,14 +1374,6 @@ void GMainWindow::InitializeHotkeys() {
}
});
connect_shortcut(QStringLiteral("Toggle Mouse Panning"), [&] {
if (Settings::values.mouse_enabled) {
Settings::values.mouse_panning = false;
QMessageBox::warning(
this, tr("Emulated mouse is enabled"),
tr("Real mouse input and mouse panning are incompatible. Please disable the "
"emulated mouse in input advanced settings to allow mouse panning."));
return;
}
Settings::values.mouse_panning = !Settings::values.mouse_panning;
if (Settings::values.mouse_panning) {
render_window->installEventFilter(render_window);
@@ -2126,6 +2122,10 @@ void GMainWindow::OnEmulationStopped() {
discord_rpc->Update();
#ifdef __unix__
Common::Linux::StopGamemode();
#endif
// The emulation is stopped, so closing the window or not does not matter anymore
disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
@@ -2165,6 +2165,10 @@ void GMainWindow::OnEmulationStopped() {
emu_frametime_label->setVisible(false);
renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan);
if (!firmware_label->text().isEmpty()) {
firmware_label->setVisible(true);
}
current_game_path.clear();
// When closing the game, destroy the GLWindow to clear the context after the game is closed
@@ -3504,6 +3508,10 @@ void GMainWindow::OnStartGame() {
play_time_manager->Start();
discord_rpc->Update();
#ifdef __unix__
Common::Linux::StartGamemode();
#endif
}
void GMainWindow::OnRestartGame() {
@@ -3524,6 +3532,10 @@ void GMainWindow::OnPauseGame() {
play_time_manager->Stop();
UpdateMenuState();
AllowOSSleep();
#ifdef __unix__
Common::Linux::StopGamemode();
#endif
}
void GMainWindow::OnPauseContinueGame() {
@@ -3805,6 +3817,9 @@ void GMainWindow::OnConfigure() {
const auto old_theme = UISettings::values.theme;
const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue();
const auto old_language_index = Settings::values.language_index.GetValue();
#ifdef __unix__
const bool old_gamemode = Settings::values.enable_gamemode.GetValue();
#endif
Settings::SetConfiguringGlobal(true);
ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(),
@@ -3866,6 +3881,11 @@ void GMainWindow::OnConfigure() {
if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) {
SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue());
}
#ifdef __unix__
if (Settings::values.enable_gamemode.GetValue() != old_gamemode) {
SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue());
}
#endif
if (!multiplayer_state->IsHostingPublicRoom()) {
multiplayer_state->UpdateCredentials();
@@ -4591,6 +4611,7 @@ void GMainWindow::UpdateStatusBar() {
emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue());
game_fps_label->setVisible(true);
emu_frametime_label->setVisible(true);
firmware_label->setVisible(false);
}
void GMainWindow::UpdateGPUAccuracyButton() {
@@ -4700,26 +4721,10 @@ void GMainWindow::ShowMouseCursor() {
}
}
void GMainWindow::CenterMouseCursor() {
if (emu_thread == nullptr || !Settings::values.mouse_panning) {
mouse_center_timer.stop();
return;
}
if (!this->isActiveWindow()) {
mouse_center_timer.stop();
return;
}
const int center_x = render_window->width() / 2;
const int center_y = render_window->height() / 2;
QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
}
void GMainWindow::OnMouseActivity() {
if (!Settings::values.mouse_panning) {
ShowMouseCursor();
}
mouse_center_timer.stop();
}
void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
@@ -4810,6 +4815,8 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
"games."));
}
SetFirmwareVersion();
if (behavior == ReinitializeKeyBehavior::Warning) {
game_list->PopulateAsync(UISettings::values.game_dirs);
}
@@ -4837,7 +4844,7 @@ bool GMainWindow::CheckSystemArchiveDecryption() {
}
bool GMainWindow::CheckFirmwarePresence() {
constexpr u64 MiiEditId = 0x0100000000001009ull;
constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit);
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
@@ -4852,6 +4859,28 @@ bool GMainWindow::CheckFirmwarePresence() {
return true;
}
void GMainWindow::SetFirmwareVersion() {
Service::Set::FirmwareVersionFormat firmware_data{};
const auto result = Service::Set::GetFirmwareVersionImpl(
firmware_data, *system, Service::Set::GetFirmwareVersionType::Version2);
if (result.IsError() || !CheckFirmwarePresence()) {
LOG_INFO(Frontend, "Installed firmware: No firmware available");
firmware_label->setVisible(false);
return;
}
firmware_label->setVisible(true);
const std::string display_version(firmware_data.display_version.data());
const std::string display_title(firmware_data.display_title.data());
LOG_INFO(Frontend, "Installed firmware: {}", display_title);
firmware_label->setText(QString::fromStdString(display_version));
firmware_label->setToolTip(QString::fromStdString(display_title));
}
bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id,
u64* selected_title_id, u8* selected_content_record_type) {
using ContentInfo = std::tuple<u64, FileSys::TitleType, FileSys::ContentRecordType>;
@@ -4996,22 +5025,6 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
AcceptDropEvent(event);
}
void GMainWindow::leaveEvent(QEvent* event) {
if (Settings::values.mouse_panning) {
const QRect& rect = geometry();
QPoint position = QCursor::pos();
qint32 x = qBound(rect.left(), position.x(), rect.right());
qint32 y = qBound(rect.top(), position.y(), rect.bottom());
// Only start the timer if the mouse has left the window bound.
// The leave event is also triggered when the window looses focus.
if (x != position.x() || y != position.y()) {
mouse_center_timer.start();
}
event->accept();
}
}
bool GMainWindow::ConfirmChangeGame() {
if (emu_thread == nullptr)
return true;
@@ -5181,6 +5194,14 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
discord_rpc->Update();
}
#ifdef __unix__
void GMainWindow::SetGamemodeEnabled(bool state) {
if (emulation_running) {
Common::Linux::SetGamemodeState(state);
}
}
#endif
void GMainWindow::changeEvent(QEvent* event) {
#ifdef __unix__
// PaletteChange event appears to only reach so far into the GUI, explicitly asking to

View File

@@ -340,6 +340,7 @@ private:
void SetupSigInterrupts();
static void HandleSigInterrupt(int);
void OnSigInterruptNotifierActivated();
void SetGamemodeEnabled(bool state);
#endif
private slots:
@@ -451,13 +452,13 @@ private:
void UpdateInputDrivers();
void HideMouseCursor();
void ShowMouseCursor();
void CenterMouseCursor();
void OpenURL(const QUrl& url);
void LoadTranslation();
void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
bool CheckDarkMode();
bool CheckSystemArchiveDecryption();
bool CheckFirmwarePresence();
void SetFirmwareVersion();
void ConfigureFilesystemProvider(const std::string& filepath);
/**
* Open (or not) the right confirm dialog based on current setting and game exit lock
@@ -512,6 +513,7 @@ private:
QLabel* game_fps_label = nullptr;
QLabel* emu_frametime_label = nullptr;
QLabel* tas_label = nullptr;
QLabel* firmware_label = nullptr;
QPushButton* gpu_accuracy_button = nullptr;
QPushButton* renderer_status_button = nullptr;
QPushButton* dock_status_button = nullptr;
@@ -533,7 +535,6 @@ private:
bool auto_paused = false;
bool auto_muted = false;
QTimer mouse_hide_timer;
QTimer mouse_center_timer;
QTimer update_input_timer;
QString startup_icon_theme;
@@ -590,5 +591,4 @@ protected:
void dropEvent(QDropEvent* event) override;
void dragEnterEvent(QDragEnterEvent* event) override;
void dragMoveEvent(QDragMoveEvent* event) override;
void leaveEvent(QEvent* event) override;
};

View File

@@ -63,6 +63,10 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
}
#endif
#ifdef __unix__
#include "common/linux/gamemode.h"
#endif
static void PrintHelp(const char* argv0) {
std::cout << "Usage: " << argv0
<< " [options] <filename>\n"
@@ -425,6 +429,10 @@ int main(int argc, char** argv) {
exit(0);
});
#ifdef __unix__
Common::Linux::StartGamemode();
#endif
void(system.Run());
if (system.DebuggerEnabled()) {
system.InitializeDebugger();
@@ -436,6 +444,10 @@ int main(int argc, char** argv) {
void(system.Pause());
system.ShutdownMainProcess();
#ifdef __unix__
Common::Linux::StopGamemode();
#endif
detached_tasks.WaitForAllTasks();
return 0;
}