summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.ci/scripts/linux/docker.sh3
-rw-r--r--CMakeLists.txt6
-rw-r--r--externals/libusb/CMakeLists.txt2
-rw-r--r--src/common/host_memory.cpp9
-rw-r--r--src/common/settings.h12
-rw-r--r--src/common/uuid.cpp2
-rw-r--r--src/core/hle/api_version.h14
-rw-r--r--src/core/hle/service/acc/acc.cpp22
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard.cpp4
-rw-r--r--src/core/hle/service/friend/friend.cpp6
-rw-r--r--src/core/hle/service/ns/language.cpp3
-rw-r--r--src/core/hle/service/ns/ns_language.h42
-rw-r--r--src/core/hle/service/set/set.cpp22
-rw-r--r--src/core/hle/service/set/set.h1
-rw-r--r--src/shader_recompiler/frontend/ir/value.h8
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp2
-rw-r--r--src/shader_recompiler/ir_opt/constant_propagation_pass.cpp205
-rw-r--r--src/video_core/CMakeLists.txt2
-rw-r--r--src/video_core/renderer_vulkan/pipeline_statistics.cpp100
-rw-r--r--src/video_core/renderer_vulkan/pipeline_statistics.h40
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp13
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp16
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp30
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h7
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp9
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp29
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h6
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp38
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h8
-rw-r--r--src/yuzu/applets/qt_web_browser.cpp15
-rw-r--r--src/yuzu/applets/qt_web_browser.h3
-rw-r--r--src/yuzu/applets/qt_web_browser_scripts.h6
-rw-r--r--src/yuzu/configuration/config.cpp7
-rw-r--r--src/yuzu/configuration/config.h1
-rw-r--r--src/yuzu/configuration/configuration_shared.cpp14
-rw-r--r--src/yuzu/configuration/configuration_shared.h15
-rw-r--r--src/yuzu/configuration/configure_cpu.cpp17
-rw-r--r--src/yuzu/configuration/configure_debug.cpp3
-rw-r--r--src/yuzu/configuration/configure_debug.ui23
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp8
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp20
-rw-r--r--src/yuzu/configuration/configure_system.ui5
-rw-r--r--src/yuzu/main.cpp8
-rw-r--r--src/yuzu_cmd/config.cpp1
-rw-r--r--src/yuzu_cmd/default_ini.h6
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp10
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h5
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp5
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h7
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp5
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h3
-rw-r--r--src/yuzu_cmd/yuzu.cpp4
54 files changed, 662 insertions, 198 deletions
diff --git a/.ci/scripts/linux/docker.sh b/.ci/scripts/linux/docker.sh
index 9b451d3ab..5070b92d1 100755
--- a/.ci/scripts/linux/docker.sh
+++ b/.ci/scripts/linux/docker.sh
@@ -18,7 +18,8 @@ cmake .. \
18 -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \ 18 -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
19 -DENABLE_QT_TRANSLATION=ON \ 19 -DENABLE_QT_TRANSLATION=ON \
20 -DUSE_DISCORD_PRESENCE=ON \ 20 -DUSE_DISCORD_PRESENCE=ON \
21 -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} 21 -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
22 -DYUZU_USE_BUNDLED_FFMPEG=ON
22 23
23make -j$(nproc) 24make -j$(nproc)
24 25
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 857550e71..92c9929cf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,7 +25,7 @@ option(YUZU_USE_BUNDLED_BOOST "Download bundled Boost" OFF)
25 25
26option(YUZU_USE_BUNDLED_LIBUSB "Compile bundled libusb" OFF) 26option(YUZU_USE_BUNDLED_LIBUSB "Compile bundled libusb" OFF)
27 27
28CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" ON "WIN32" OFF) 28option(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" "${WIN32}")
29 29
30option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF) 30option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
31 31
@@ -496,7 +496,7 @@ endif()
496# Ensure libusb is properly configured (based on dolphin libusb include) 496# Ensure libusb is properly configured (based on dolphin libusb include)
497if(NOT APPLE AND NOT YUZU_USE_BUNDLED_LIBUSB) 497if(NOT APPLE AND NOT YUZU_USE_BUNDLED_LIBUSB)
498 include(FindPkgConfig) 498 include(FindPkgConfig)
499 if (PKG_CONFIG_FOUND) 499 if (PKG_CONFIG_FOUND AND NOT CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD")
500 pkg_check_modules(LIBUSB QUIET libusb-1.0>=1.0.24) 500 pkg_check_modules(LIBUSB QUIET libusb-1.0>=1.0.24)
501 else() 501 else()
502 find_package(LibUSB) 502 find_package(LibUSB)
@@ -604,6 +604,8 @@ if (YUZU_USE_BUNDLED_FFMPEG)
604 --disable-vdpau 604 --disable-vdpau
605 --enable-decoder=h264 605 --enable-decoder=h264
606 --enable-decoder=vp9 606 --enable-decoder=vp9
607 --cc="${CMAKE_C_COMPILER}"
608 --cxx="${CMAKE_CXX_COMPILER}"
607 WORKING_DIRECTORY 609 WORKING_DIRECTORY
608 ${FFmpeg_BUILD_DIR} 610 ${FFmpeg_BUILD_DIR}
609 ) 611 )
diff --git a/externals/libusb/CMakeLists.txt b/externals/libusb/CMakeLists.txt
index 151ddc462..12bdc097a 100644
--- a/externals/libusb/CMakeLists.txt
+++ b/externals/libusb/CMakeLists.txt
@@ -67,6 +67,8 @@ if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") OR APPLE)
67 "${LIBUSB_MAKEFILE}" 67 "${LIBUSB_MAKEFILE}"
68 COMMAND 68 COMMAND
69 env 69 env
70 CC="${CMAKE_C_COMPILER}"
71 CXX="${CMAKE_CXX_COMPILER}"
70 CFLAGS="${LIBUSB_CFLAGS}" 72 CFLAGS="${LIBUSB_CFLAGS}"
71 sh "${LIBUSB_CONFIGURE}" 73 sh "${LIBUSB_CONFIGURE}"
72 ${LIBUSB_CONFIGURE_ARGS} 74 ${LIBUSB_CONFIGURE_ARGS}
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index 2a5a7596c..6661244cf 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -6,7 +6,7 @@
6#include <windows.h> 6#include <windows.h>
7#include "common/dynamic_library.h" 7#include "common/dynamic_library.h"
8 8
9#elif defined(__linux__) // ^^^ Windows ^^^ vvv Linux vvv 9#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
10 10
11#ifndef _GNU_SOURCE 11#ifndef _GNU_SOURCE
12#define _GNU_SOURCE 12#define _GNU_SOURCE
@@ -343,7 +343,7 @@ private:
343 std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset 343 std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
344}; 344};
345 345
346#elif defined(__linux__) // ^^^ Windows ^^^ vvv Linux vvv 346#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
347 347
348class HostMemory::Impl { 348class HostMemory::Impl {
349public: 349public:
@@ -357,7 +357,12 @@ public:
357 }); 357 });
358 358
359 // Backing memory initialization 359 // Backing memory initialization
360#if defined(__FreeBSD__) && __FreeBSD__ < 13
361 // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
362 fd = shm_open(SHM_ANON, O_RDWR, 0600);
363#else
360 fd = memfd_create("HostMemory", 0); 364 fd = memfd_create("HostMemory", 0);
365#endif
361 if (fd == -1) { 366 if (fd == -1) {
362 LOG_CRITICAL(HW_Memory, "memfd_create failed: {}", strerror(errno)); 367 LOG_CRITICAL(HW_Memory, "memfd_create failed: {}", strerror(errno));
363 throw std::bad_alloc{}; 368 throw std::bad_alloc{};
diff --git a/src/common/settings.h b/src/common/settings.h
index d8730f515..a88ee045d 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -42,6 +42,11 @@ enum class CPUAccuracy : u32 {
42 Unsafe = 2, 42 Unsafe = 2,
43}; 43};
44 44
45enum class FullscreenMode : u32 {
46 Borderless = 0,
47 Exclusive = 1,
48};
49
45/** The BasicSetting class is a simple resource manager. It defines a label and default value 50/** The BasicSetting class is a simple resource manager. It defines a label and default value
46 * alongside the actual value of the setting for simpler and less-error prone use with frontend 51 * alongside the actual value of the setting for simpler and less-error prone use with frontend
47 * configurations. Setting a default value and label is required, though subclasses may deviate from 52 * configurations. Setting a default value and label is required, though subclasses may deviate from
@@ -314,6 +319,7 @@ struct Values {
314 // Renderer 319 // Renderer
315 Setting<RendererBackend> renderer_backend{RendererBackend::OpenGL, "backend"}; 320 Setting<RendererBackend> renderer_backend{RendererBackend::OpenGL, "backend"};
316 BasicSetting<bool> renderer_debug{false, "debug"}; 321 BasicSetting<bool> renderer_debug{false, "debug"};
322 BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"};
317 BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; 323 BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
318 BasicSetting<bool> disable_shader_loop_safety_checks{false, 324 BasicSetting<bool> disable_shader_loop_safety_checks{false,
319 "disable_shader_loop_safety_checks"}; 325 "disable_shader_loop_safety_checks"};
@@ -322,11 +328,11 @@ struct Values {
322 Setting<u16> resolution_factor{1, "resolution_factor"}; 328 Setting<u16> resolution_factor{1, "resolution_factor"};
323 // *nix platforms may have issues with the borderless windowed fullscreen mode. 329 // *nix platforms may have issues with the borderless windowed fullscreen mode.
324 // Default to exclusive fullscreen on these platforms for now. 330 // Default to exclusive fullscreen on these platforms for now.
325 Setting<int> fullscreen_mode{ 331 Setting<FullscreenMode> fullscreen_mode{
326#ifdef _WIN32 332#ifdef _WIN32
327 0, 333 FullscreenMode::Borderless,
328#else 334#else
329 1, 335 FullscreenMode::Exclusive,
330#endif 336#endif
331 "fullscreen_mode"}; 337 "fullscreen_mode"};
332 Setting<int> aspect_ratio{0, "aspect_ratio"}; 338 Setting<int> aspect_ratio{0, "aspect_ratio"};
diff --git a/src/common/uuid.cpp b/src/common/uuid.cpp
index 26db03fba..18303a1e3 100644
--- a/src/common/uuid.cpp
+++ b/src/common/uuid.cpp
@@ -18,7 +18,7 @@ UUID UUID::Generate() {
18} 18}
19 19
20std::string UUID::Format() const { 20std::string UUID::Format() const {
21 return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]); 21 return fmt::format("{:016x}{:016x}", uuid[1], uuid[0]);
22} 22}
23 23
24std::string UUID::FormatSwitch() const { 24std::string UUID::FormatSwitch() const {
diff --git a/src/core/hle/api_version.h b/src/core/hle/api_version.h
index 5e10a7ad9..43d5670a9 100644
--- a/src/core/hle/api_version.h
+++ b/src/core/hle/api_version.h
@@ -12,9 +12,9 @@ namespace HLE::ApiVersion {
12 12
13// Horizon OS version constants. 13// Horizon OS version constants.
14 14
15constexpr u8 HOS_VERSION_MAJOR = 11; 15constexpr u8 HOS_VERSION_MAJOR = 12;
16constexpr u8 HOS_VERSION_MINOR = 0; 16constexpr u8 HOS_VERSION_MINOR = 1;
17constexpr u8 HOS_VERSION_MICRO = 1; 17constexpr u8 HOS_VERSION_MICRO = 0;
18 18
19// NintendoSDK version constants. 19// NintendoSDK version constants.
20 20
@@ -22,15 +22,15 @@ constexpr u8 SDK_REVISION_MAJOR = 1;
22constexpr u8 SDK_REVISION_MINOR = 0; 22constexpr u8 SDK_REVISION_MINOR = 0;
23 23
24constexpr char PLATFORM_STRING[] = "NX"; 24constexpr char PLATFORM_STRING[] = "NX";
25constexpr char VERSION_HASH[] = "69103fcb2004dace877094c2f8c29e6113be5dbf"; 25constexpr char VERSION_HASH[] = "76b10c2dab7d3aa73fc162f8dff1655e6a21caf4";
26constexpr char DISPLAY_VERSION[] = "11.0.1"; 26constexpr char DISPLAY_VERSION[] = "12.1.0";
27constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 11.0.1-1.0"; 27constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 12.1.0-1.0";
28 28
29// Atmosphere version constants. 29// Atmosphere version constants.
30 30
31constexpr u8 ATMOSPHERE_RELEASE_VERSION_MAJOR = 0; 31constexpr u8 ATMOSPHERE_RELEASE_VERSION_MAJOR = 0;
32constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 19; 32constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 19;
33constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 4; 33constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 5;
34 34
35constexpr u32 GetTargetFirmware() { 35constexpr u32 GetTargetFirmware() {
36 return u32{HOS_VERSION_MAJOR} << 24 | u32{HOS_VERSION_MINOR} << 16 | 36 return u32{HOS_VERSION_MAJOR} << 24 | u32{HOS_VERSION_MINOR} << 16 |
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 2e969f2a8..882fc1492 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -292,7 +292,7 @@ public:
292 292
293protected: 293protected:
294 void Get(Kernel::HLERequestContext& ctx) { 294 void Get(Kernel::HLERequestContext& ctx) {
295 LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format()); 295 LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
296 ProfileBase profile_base{}; 296 ProfileBase profile_base{};
297 ProfileData data{}; 297 ProfileData data{};
298 if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { 298 if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
@@ -301,7 +301,7 @@ protected:
301 rb.Push(ResultSuccess); 301 rb.Push(ResultSuccess);
302 rb.PushRaw(profile_base); 302 rb.PushRaw(profile_base);
303 } else { 303 } else {
304 LOG_ERROR(Service_ACC, "Failed to get profile base and data for user={}", 304 LOG_ERROR(Service_ACC, "Failed to get profile base and data for user=0x{}",
305 user_id.Format()); 305 user_id.Format());
306 IPC::ResponseBuilder rb{ctx, 2}; 306 IPC::ResponseBuilder rb{ctx, 2};
307 rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code 307 rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code
@@ -309,14 +309,14 @@ protected:
309 } 309 }
310 310
311 void GetBase(Kernel::HLERequestContext& ctx) { 311 void GetBase(Kernel::HLERequestContext& ctx) {
312 LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format()); 312 LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
313 ProfileBase profile_base{}; 313 ProfileBase profile_base{};
314 if (profile_manager.GetProfileBase(user_id, profile_base)) { 314 if (profile_manager.GetProfileBase(user_id, profile_base)) {
315 IPC::ResponseBuilder rb{ctx, 16}; 315 IPC::ResponseBuilder rb{ctx, 16};
316 rb.Push(ResultSuccess); 316 rb.Push(ResultSuccess);
317 rb.PushRaw(profile_base); 317 rb.PushRaw(profile_base);
318 } else { 318 } else {
319 LOG_ERROR(Service_ACC, "Failed to get profile base for user={}", user_id.Format()); 319 LOG_ERROR(Service_ACC, "Failed to get profile base for user=0x{}", user_id.Format());
320 IPC::ResponseBuilder rb{ctx, 2}; 320 IPC::ResponseBuilder rb{ctx, 2};
321 rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code 321 rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code
322 } 322 }
@@ -372,7 +372,7 @@ protected:
372 372
373 const auto user_data = ctx.ReadBuffer(); 373 const auto user_data = ctx.ReadBuffer();
374 374
375 LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid={}", 375 LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
376 Common::StringFromFixedZeroTerminatedBuffer( 376 Common::StringFromFixedZeroTerminatedBuffer(
377 reinterpret_cast<const char*>(base.username.data()), base.username.size()), 377 reinterpret_cast<const char*>(base.username.data()), base.username.size()),
378 base.timestamp, base.user_uuid.Format()); 378 base.timestamp, base.user_uuid.Format());
@@ -405,7 +405,7 @@ protected:
405 const auto user_data = ctx.ReadBuffer(); 405 const auto user_data = ctx.ReadBuffer();
406 const auto image_data = ctx.ReadBuffer(1); 406 const auto image_data = ctx.ReadBuffer(1);
407 407
408 LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid={}", 408 LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
409 Common::StringFromFixedZeroTerminatedBuffer( 409 Common::StringFromFixedZeroTerminatedBuffer(
410 reinterpret_cast<const char*>(base.username.data()), base.username.size()), 410 reinterpret_cast<const char*>(base.username.data()), base.username.size()),
411 base.timestamp, base.user_uuid.Format()); 411 base.timestamp, base.user_uuid.Format());
@@ -662,7 +662,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
662void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) { 662void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
663 IPC::RequestParser rp{ctx}; 663 IPC::RequestParser rp{ctx};
664 Common::UUID user_id = rp.PopRaw<Common::UUID>(); 664 Common::UUID user_id = rp.PopRaw<Common::UUID>();
665 LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format()); 665 LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
666 666
667 IPC::ResponseBuilder rb{ctx, 3}; 667 IPC::ResponseBuilder rb{ctx, 3};
668 rb.Push(ResultSuccess); 668 rb.Push(ResultSuccess);
@@ -693,7 +693,7 @@ void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
693void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) { 693void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
694 IPC::RequestParser rp{ctx}; 694 IPC::RequestParser rp{ctx};
695 Common::UUID user_id = rp.PopRaw<Common::UUID>(); 695 Common::UUID user_id = rp.PopRaw<Common::UUID>();
696 LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format()); 696 LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
697 697
698 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 698 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
699 rb.Push(ResultSuccess); 699 rb.Push(ResultSuccess);
@@ -802,7 +802,7 @@ void Module::Interface::GetProfileEditor(Kernel::HLERequestContext& ctx) {
802 IPC::RequestParser rp{ctx}; 802 IPC::RequestParser rp{ctx};
803 Common::UUID user_id = rp.PopRaw<Common::UUID>(); 803 Common::UUID user_id = rp.PopRaw<Common::UUID>();
804 804
805 LOG_DEBUG(Service_ACC, "called, user_id={}", user_id.Format()); 805 LOG_DEBUG(Service_ACC, "called, user_id=0x{}", user_id.Format());
806 806
807 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 807 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
808 rb.Push(ResultSuccess); 808 rb.Push(ResultSuccess);
@@ -844,7 +844,7 @@ void Module::Interface::StoreSaveDataThumbnailApplication(Kernel::HLERequestCont
844 IPC::RequestParser rp{ctx}; 844 IPC::RequestParser rp{ctx};
845 const auto uuid = rp.PopRaw<Common::UUID>(); 845 const auto uuid = rp.PopRaw<Common::UUID>();
846 846
847 LOG_WARNING(Service_ACC, "(STUBBED) called, uuid={}", uuid.Format()); 847 LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}", uuid.Format());
848 848
849 // TODO(ogniK): Check if application ID is zero on acc initialize. As we don't have a reliable 849 // TODO(ogniK): Check if application ID is zero on acc initialize. As we don't have a reliable
850 // way of confirming things like the TID, we're going to assume a non zero value for the time 850 // way of confirming things like the TID, we're going to assume a non zero value for the time
@@ -858,7 +858,7 @@ void Module::Interface::StoreSaveDataThumbnailSystem(Kernel::HLERequestContext&
858 const auto uuid = rp.PopRaw<Common::UUID>(); 858 const auto uuid = rp.PopRaw<Common::UUID>();
859 const auto tid = rp.Pop<u64_le>(); 859 const auto tid = rp.Pop<u64_le>();
860 860
861 LOG_WARNING(Service_ACC, "(STUBBED) called, uuid={}, tid={:016X}", uuid.Format(), tid); 861 LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}, tid={:016X}", uuid.Format(), tid);
862 StoreSaveDataThumbnail(ctx, uuid, tid); 862 StoreSaveDataThumbnail(ctx, uuid, tid);
863} 863}
864 864
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.cpp b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
index 7cae90609..673abb755 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
@@ -377,7 +377,7 @@ void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) {
377 377
378 if (swkbd_config_common.use_utf8) { 378 if (swkbd_config_common.use_utf8) {
379 std::string utf8_submitted_text = Common::UTF16ToUTF8(current_text); 379 std::string utf8_submitted_text = Common::UTF16ToUTF8(current_text);
380 const u64 buffer_size = sizeof(u64) + utf8_submitted_text.size(); 380 const u64 buffer_size = utf8_submitted_text.size();
381 381
382 LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-8 Submitted Text: {}", buffer_size, 382 LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-8 Submitted Text: {}", buffer_size,
383 utf8_submitted_text); 383 utf8_submitted_text);
@@ -386,7 +386,7 @@ void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) {
386 std::memcpy(out_data.data() + sizeof(u64), utf8_submitted_text.data(), 386 std::memcpy(out_data.data() + sizeof(u64), utf8_submitted_text.data(),
387 utf8_submitted_text.size()); 387 utf8_submitted_text.size());
388 } else { 388 } else {
389 const u64 buffer_size = sizeof(u64) + current_text.size() * sizeof(char16_t); 389 const u64 buffer_size = current_text.size() * sizeof(char16_t);
390 390
391 LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-16 Submitted Text: {}", buffer_size, 391 LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-16 Submitted Text: {}", buffer_size,
392 Common::UTF16ToUTF8(current_text)); 392 Common::UTF16ToUTF8(current_text));
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index a3c939c0c..b58c152ce 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -158,7 +158,7 @@ private:
158 const auto local_play = rp.Pop<bool>(); 158 const auto local_play = rp.Pop<bool>();
159 const auto uuid = rp.PopRaw<Common::UUID>(); 159 const auto uuid = rp.PopRaw<Common::UUID>();
160 160
161 LOG_WARNING(Service_Friend, "(STUBBED) called local_play={} uuid={}", local_play, 161 LOG_WARNING(Service_Friend, "(STUBBED) called, local_play={}, uuid=0x{}", local_play,
162 uuid.Format()); 162 uuid.Format());
163 163
164 IPC::ResponseBuilder rb{ctx, 2}; 164 IPC::ResponseBuilder rb{ctx, 2};
@@ -171,7 +171,7 @@ private:
171 const auto uuid = rp.PopRaw<Common::UUID>(); 171 const auto uuid = rp.PopRaw<Common::UUID>();
172 [[maybe_unused]] const auto filter = rp.PopRaw<SizedFriendFilter>(); 172 [[maybe_unused]] const auto filter = rp.PopRaw<SizedFriendFilter>();
173 const auto pid = rp.Pop<u64>(); 173 const auto pid = rp.Pop<u64>();
174 LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid={}, pid={}", friend_offset, 174 LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid=0x{}, pid={}", friend_offset,
175 uuid.Format(), pid); 175 uuid.Format(), pid);
176 176
177 IPC::ResponseBuilder rb{ctx, 3}; 177 IPC::ResponseBuilder rb{ctx, 3};
@@ -289,7 +289,7 @@ void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx
289 IPC::RequestParser rp{ctx}; 289 IPC::RequestParser rp{ctx};
290 auto uuid = rp.PopRaw<Common::UUID>(); 290 auto uuid = rp.PopRaw<Common::UUID>();
291 291
292 LOG_DEBUG(Service_Friend, "called, uuid={}", uuid.Format()); 292 LOG_DEBUG(Service_Friend, "called, uuid=0x{}", uuid.Format());
293 293
294 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 294 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
295 rb.Push(ResultSuccess); 295 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/ns/language.cpp b/src/core/hle/service/ns/language.cpp
index 29c4a820c..7d9e4a20b 100644
--- a/src/core/hle/service/ns/language.cpp
+++ b/src/core/hle/service/ns/language.cpp
@@ -339,13 +339,16 @@ std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
339 case Set::LanguageCode::FR_CA: 339 case Set::LanguageCode::FR_CA:
340 return ApplicationLanguage::CanadianFrench; 340 return ApplicationLanguage::CanadianFrench;
341 case Set::LanguageCode::PT: 341 case Set::LanguageCode::PT:
342 case Set::LanguageCode::PT_BR:
342 return ApplicationLanguage::Portuguese; 343 return ApplicationLanguage::Portuguese;
343 case Set::LanguageCode::RU: 344 case Set::LanguageCode::RU:
344 return ApplicationLanguage::Russian; 345 return ApplicationLanguage::Russian;
345 case Set::LanguageCode::KO: 346 case Set::LanguageCode::KO:
346 return ApplicationLanguage::Korean; 347 return ApplicationLanguage::Korean;
348 case Set::LanguageCode::ZH_TW:
347 case Set::LanguageCode::ZH_HANT: 349 case Set::LanguageCode::ZH_HANT:
348 return ApplicationLanguage::TraditionalChinese; 350 return ApplicationLanguage::TraditionalChinese;
351 case Set::LanguageCode::ZH_CN:
349 case Set::LanguageCode::ZH_HANS: 352 case Set::LanguageCode::ZH_HANS:
350 return ApplicationLanguage::SimplifiedChinese; 353 return ApplicationLanguage::SimplifiedChinese;
351 default: 354 default:
diff --git a/src/core/hle/service/ns/ns_language.h b/src/core/hle/service/ns/ns_language.h
deleted file mode 100644
index 59ac85a19..000000000
--- a/src/core/hle/service/ns/ns_language.h
+++ /dev/null
@@ -1,42 +0,0 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6#include <optional>
7#include <string>
8#include "common/common_types.h"
9#include "core/hle/service/set/set.h"
10
11namespace Service::NS {
12/// This is nn::ns::detail::ApplicationLanguage
13enum class ApplicationLanguage : u8 {
14 AmericanEnglish = 0,
15 BritishEnglish,
16 Japanese,
17 French,
18 German,
19 LatinAmericanSpanish,
20 Spanish,
21 Italian,
22 Dutch,
23 CanadianFrench,
24 Portuguese,
25 Russian,
26 Korean,
27 TraditionalChinese,
28 SimplifiedChinese,
29 Count
30};
31using ApplicationLanguagePriorityList =
32 const std::array<ApplicationLanguage, static_cast<std::size_t>(ApplicationLanguage::Count)>;
33
34constexpr u32 GetSupportedLanguageFlag(const ApplicationLanguage lang) {
35 return 1U << static_cast<u32>(lang);
36}
37
38const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(ApplicationLanguage lang);
39std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
40 Service::Set::LanguageCode language_code);
41std::optional<Service::Set::LanguageCode> ConvertToLanguageCode(ApplicationLanguage lang);
42} // namespace Service::NS \ No newline at end of file
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index 522a604a5..f2e2e8306 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -12,7 +12,7 @@
12 12
13namespace Service::Set { 13namespace Service::Set {
14namespace { 14namespace {
15constexpr std::array<LanguageCode, 17> available_language_codes = {{ 15constexpr std::array<LanguageCode, 18> available_language_codes = {{
16 LanguageCode::JA, 16 LanguageCode::JA,
17 LanguageCode::EN_US, 17 LanguageCode::EN_US,
18 LanguageCode::FR, 18 LanguageCode::FR,
@@ -30,6 +30,7 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{
30 LanguageCode::ES_419, 30 LanguageCode::ES_419,
31 LanguageCode::ZH_HANS, 31 LanguageCode::ZH_HANS,
32 LanguageCode::ZH_HANT, 32 LanguageCode::ZH_HANT,
33 LanguageCode::PT_BR,
33}}; 34}};
34 35
35enum class KeyboardLayout : u64 { 36enum class KeyboardLayout : u64 {
@@ -50,7 +51,7 @@ enum class KeyboardLayout : u64 {
50 ChineseTraditional = 14, 51 ChineseTraditional = 14,
51}; 52};
52 53
53constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 17> language_to_layout{{ 54constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 18> language_to_layout{{
54 {LanguageCode::JA, KeyboardLayout::Japanese}, 55 {LanguageCode::JA, KeyboardLayout::Japanese},
55 {LanguageCode::EN_US, KeyboardLayout::EnglishUs}, 56 {LanguageCode::EN_US, KeyboardLayout::EnglishUs},
56 {LanguageCode::FR, KeyboardLayout::French}, 57 {LanguageCode::FR, KeyboardLayout::French},
@@ -68,10 +69,11 @@ constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 17> language_to_la
68 {LanguageCode::ES_419, KeyboardLayout::SpanishLatin}, 69 {LanguageCode::ES_419, KeyboardLayout::SpanishLatin},
69 {LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified}, 70 {LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified},
70 {LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional}, 71 {LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional},
72 {LanguageCode::PT_BR, KeyboardLayout::Portuguese},
71}}; 73}};
72 74
73constexpr std::size_t pre4_0_0_max_entries = 15; 75constexpr std::size_t PRE_4_0_0_MAX_ENTRIES = 0xF;
74constexpr std::size_t post4_0_0_max_entries = 17; 76constexpr std::size_t POST_4_0_0_MAX_ENTRIES = 0x40;
75 77
76constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625}; 78constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625};
77 79
@@ -81,9 +83,9 @@ void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t num_la
81 rb.Push(static_cast<u32>(num_language_codes)); 83 rb.Push(static_cast<u32>(num_language_codes));
82} 84}
83 85
84void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_size) { 86void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_entries) {
85 const std::size_t requested_amount = ctx.GetWriteBufferSize() / sizeof(LanguageCode); 87 const std::size_t requested_amount = ctx.GetWriteBufferSize() / sizeof(LanguageCode);
86 const std::size_t copy_amount = std::min(requested_amount, max_size); 88 const std::size_t copy_amount = std::min(requested_amount, max_entries);
87 const std::size_t copy_size = copy_amount * sizeof(LanguageCode); 89 const std::size_t copy_size = copy_amount * sizeof(LanguageCode);
88 90
89 ctx.WriteBuffer(available_language_codes.data(), copy_size); 91 ctx.WriteBuffer(available_language_codes.data(), copy_size);
@@ -118,7 +120,7 @@ LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
118void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) { 120void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
119 LOG_DEBUG(Service_SET, "called"); 121 LOG_DEBUG(Service_SET, "called");
120 122
121 GetAvailableLanguageCodesImpl(ctx, pre4_0_0_max_entries); 123 GetAvailableLanguageCodesImpl(ctx, PRE_4_0_0_MAX_ENTRIES);
122} 124}
123 125
124void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) { 126void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
@@ -140,19 +142,19 @@ void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
140void SET::GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx) { 142void SET::GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx) {
141 LOG_DEBUG(Service_SET, "called"); 143 LOG_DEBUG(Service_SET, "called");
142 144
143 GetAvailableLanguageCodesImpl(ctx, post4_0_0_max_entries); 145 GetAvailableLanguageCodesImpl(ctx, POST_4_0_0_MAX_ENTRIES);
144} 146}
145 147
146void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) { 148void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) {
147 LOG_DEBUG(Service_SET, "called"); 149 LOG_DEBUG(Service_SET, "called");
148 150
149 PushResponseLanguageCode(ctx, pre4_0_0_max_entries); 151 PushResponseLanguageCode(ctx, PRE_4_0_0_MAX_ENTRIES);
150} 152}
151 153
152void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) { 154void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) {
153 LOG_DEBUG(Service_SET, "called"); 155 LOG_DEBUG(Service_SET, "called");
154 156
155 PushResponseLanguageCode(ctx, post4_0_0_max_entries); 157 PushResponseLanguageCode(ctx, POST_4_0_0_MAX_ENTRIES);
156} 158}
157 159
158void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) { 160void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h
index d5bd7828d..acabebeaa 100644
--- a/src/core/hle/service/set/set.h
+++ b/src/core/hle/service/set/set.h
@@ -31,6 +31,7 @@ enum class LanguageCode : u64 {
31 ES_419 = 0x00003931342D7365, 31 ES_419 = 0x00003931342D7365,
32 ZH_HANS = 0x00736E61482D687A, 32 ZH_HANS = 0x00736E61482D687A,
33 ZH_HANT = 0x00746E61482D687A, 33 ZH_HANT = 0x00746E61482D687A,
34 PT_BR = 0x00000052422D7470,
34}; 35};
35LanguageCode GetLanguageCodeFromIndex(std::size_t idx); 36LanguageCode GetLanguageCodeFromIndex(std::size_t idx);
36 37
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h
index 795194d41..334bb47aa 100644
--- a/src/shader_recompiler/frontend/ir/value.h
+++ b/src/shader_recompiler/frontend/ir/value.h
@@ -57,6 +57,7 @@ public:
57 57
58 [[nodiscard]] IR::Inst* Inst() const; 58 [[nodiscard]] IR::Inst* Inst() const;
59 [[nodiscard]] IR::Inst* InstRecursive() const; 59 [[nodiscard]] IR::Inst* InstRecursive() const;
60 [[nodiscard]] IR::Inst* TryInstRecursive() const;
60 [[nodiscard]] IR::Value Resolve() const; 61 [[nodiscard]] IR::Value Resolve() const;
61 [[nodiscard]] IR::Reg Reg() const; 62 [[nodiscard]] IR::Reg Reg() const;
62 [[nodiscard]] IR::Pred Pred() const; 63 [[nodiscard]] IR::Pred Pred() const;
@@ -308,6 +309,13 @@ inline IR::Inst* Value::InstRecursive() const {
308 return inst; 309 return inst;
309} 310}
310 311
312inline IR::Inst* Value::TryInstRecursive() const {
313 if (IsIdentity()) {
314 return inst->Arg(0).TryInstRecursive();
315 }
316 return type == Type::Opaque ? inst : nullptr;
317}
318
311inline IR::Value Value::Resolve() const { 319inline IR::Value Value::Resolve() const {
312 if (IsIdentity()) { 320 if (IsIdentity()) {
313 return inst->Arg(0).Resolve(); 321 return inst->Arg(0).Resolve();
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
index 5ead930f1..f69e1c9cc 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -111,6 +111,8 @@ void VisitUsages(Info& info, IR::Inst& inst) {
111 case IR::Opcode::ConvertF16U16: 111 case IR::Opcode::ConvertF16U16:
112 case IR::Opcode::ConvertF16U32: 112 case IR::Opcode::ConvertF16U32:
113 case IR::Opcode::ConvertF16U64: 113 case IR::Opcode::ConvertF16U64:
114 case IR::Opcode::ConvertF16F32:
115 case IR::Opcode::ConvertF32F16:
114 case IR::Opcode::FPAbs16: 116 case IR::Opcode::FPAbs16:
115 case IR::Opcode::FPAdd16: 117 case IR::Opcode::FPAdd16:
116 case IR::Opcode::FPCeil16: 118 case IR::Opcode::FPCeil16:
diff --git a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
index 8dd6d6c2c..d089fdd12 100644
--- a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
+++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <functional>
6#include <tuple> 7#include <tuple>
7#include <type_traits> 8#include <type_traits>
8 9
@@ -88,6 +89,26 @@ bool FoldWhenAllImmediates(IR::Inst& inst, Func&& func) {
88 return true; 89 return true;
89} 90}
90 91
92/// Return true when all values in a range are equal
93template <typename Range>
94bool AreEqual(const Range& range) {
95 auto resolver{[](const auto& value) { return value.Resolve(); }};
96 auto equal{[](const IR::Value& lhs, const IR::Value& rhs) {
97 if (lhs == rhs) {
98 return true;
99 }
100 // Not equal, but try to match if they read the same constant buffer
101 if (!lhs.IsImmediate() && !rhs.IsImmediate() &&
102 lhs.Inst()->GetOpcode() == IR::Opcode::GetCbufU32 &&
103 rhs.Inst()->GetOpcode() == IR::Opcode::GetCbufU32 &&
104 lhs.Inst()->Arg(0) == rhs.Inst()->Arg(0) && lhs.Inst()->Arg(1) == rhs.Inst()->Arg(1)) {
105 return true;
106 }
107 return false;
108 }};
109 return std::ranges::adjacent_find(range, std::not_fn(equal), resolver) == std::end(range);
110}
111
91void FoldGetRegister(IR::Inst& inst) { 112void FoldGetRegister(IR::Inst& inst) {
92 if (inst.Arg(0).Reg() == IR::Reg::RZ) { 113 if (inst.Arg(0).Reg() == IR::Reg::RZ) {
93 inst.ReplaceUsesWith(IR::Value{u32{0}}); 114 inst.ReplaceUsesWith(IR::Value{u32{0}});
@@ -100,6 +121,157 @@ void FoldGetPred(IR::Inst& inst) {
100 } 121 }
101} 122}
102 123
124/// Replaces the XMAD pattern generated by an integer FMA
125bool FoldXmadMultiplyAdd(IR::Block& block, IR::Inst& inst) {
126 /*
127 * We are looking for this specific pattern:
128 * %6 = BitFieldUExtract %op_b, #0, #16
129 * %7 = BitFieldUExtract %op_a', #16, #16
130 * %8 = IMul32 %6, %7
131 * %10 = BitFieldUExtract %op_a', #0, #16
132 * %11 = BitFieldInsert %8, %10, #16, #16
133 * %15 = BitFieldUExtract %op_b, #0, #16
134 * %16 = BitFieldUExtract %op_a, #0, #16
135 * %17 = IMul32 %15, %16
136 * %18 = IAdd32 %17, %op_c
137 * %22 = BitFieldUExtract %op_b, #16, #16
138 * %23 = BitFieldUExtract %11, #16, #16
139 * %24 = IMul32 %22, %23
140 * %25 = ShiftLeftLogical32 %24, #16
141 * %26 = ShiftLeftLogical32 %11, #16
142 * %27 = IAdd32 %26, %18
143 * %result = IAdd32 %25, %27
144 *
145 * And replace it with:
146 * %temp = IMul32 %op_a, %op_b
147 * %result = IAdd32 %temp, %op_c
148 *
149 * This optimization has been proven safe by Nvidia's compiler logic being reversed.
150 * (If Nvidia generates this code from 'fma(a, b, c)', we can do the same in the reverse order.)
151 */
152 const IR::Value zero{0u};
153 const IR::Value sixteen{16u};
154 IR::Inst* const _25{inst.Arg(0).TryInstRecursive()};
155 IR::Inst* const _27{inst.Arg(1).TryInstRecursive()};
156 if (!_25 || !_27) {
157 return false;
158 }
159 if (_27->GetOpcode() != IR::Opcode::IAdd32) {
160 return false;
161 }
162 if (_25->GetOpcode() != IR::Opcode::ShiftLeftLogical32 || _25->Arg(1) != sixteen) {
163 return false;
164 }
165 IR::Inst* const _24{_25->Arg(0).TryInstRecursive()};
166 if (!_24 || _24->GetOpcode() != IR::Opcode::IMul32) {
167 return false;
168 }
169 IR::Inst* const _22{_24->Arg(0).TryInstRecursive()};
170 IR::Inst* const _23{_24->Arg(1).TryInstRecursive()};
171 if (!_22 || !_23) {
172 return false;
173 }
174 if (_22->GetOpcode() != IR::Opcode::BitFieldUExtract) {
175 return false;
176 }
177 if (_23->GetOpcode() != IR::Opcode::BitFieldUExtract) {
178 return false;
179 }
180 if (_22->Arg(1) != sixteen || _22->Arg(2) != sixteen) {
181 return false;
182 }
183 if (_23->Arg(1) != sixteen || _23->Arg(2) != sixteen) {
184 return false;
185 }
186 IR::Inst* const _11{_23->Arg(0).TryInstRecursive()};
187 if (!_11 || _11->GetOpcode() != IR::Opcode::BitFieldInsert) {
188 return false;
189 }
190 if (_11->Arg(2) != sixteen || _11->Arg(3) != sixteen) {
191 return false;
192 }
193 IR::Inst* const _8{_11->Arg(0).TryInstRecursive()};
194 IR::Inst* const _10{_11->Arg(1).TryInstRecursive()};
195 if (!_8 || !_10) {
196 return false;
197 }
198 if (_8->GetOpcode() != IR::Opcode::IMul32) {
199 return false;
200 }
201 if (_10->GetOpcode() != IR::Opcode::BitFieldUExtract) {
202 return false;
203 }
204 IR::Inst* const _6{_8->Arg(0).TryInstRecursive()};
205 IR::Inst* const _7{_8->Arg(1).TryInstRecursive()};
206 if (!_6 || !_7) {
207 return false;
208 }
209 if (_6->GetOpcode() != IR::Opcode::BitFieldUExtract) {
210 return false;
211 }
212 if (_7->GetOpcode() != IR::Opcode::BitFieldUExtract) {
213 return false;
214 }
215 if (_6->Arg(1) != zero || _6->Arg(2) != sixteen) {
216 return false;
217 }
218 if (_7->Arg(1) != sixteen || _7->Arg(2) != sixteen) {
219 return false;
220 }
221 IR::Inst* const _26{_27->Arg(0).TryInstRecursive()};
222 IR::Inst* const _18{_27->Arg(1).TryInstRecursive()};
223 if (!_26 || !_18) {
224 return false;
225 }
226 if (_26->GetOpcode() != IR::Opcode::ShiftLeftLogical32 || _26->Arg(1) != sixteen) {
227 return false;
228 }
229 if (_26->Arg(0).InstRecursive() != _11) {
230 return false;
231 }
232 if (_18->GetOpcode() != IR::Opcode::IAdd32) {
233 return false;
234 }
235 IR::Inst* const _17{_18->Arg(0).TryInstRecursive()};
236 if (!_17 || _17->GetOpcode() != IR::Opcode::IMul32) {
237 return false;
238 }
239 IR::Inst* const _15{_17->Arg(0).TryInstRecursive()};
240 IR::Inst* const _16{_17->Arg(1).TryInstRecursive()};
241 if (!_15 || !_16) {
242 return false;
243 }
244 if (_15->GetOpcode() != IR::Opcode::BitFieldUExtract) {
245 return false;
246 }
247 if (_16->GetOpcode() != IR::Opcode::BitFieldUExtract) {
248 return false;
249 }
250 if (_15->Arg(1) != zero || _16->Arg(1) != zero || _10->Arg(1) != zero) {
251 return false;
252 }
253 if (_15->Arg(2) != sixteen || _16->Arg(2) != sixteen || _10->Arg(2) != sixteen) {
254 return false;
255 }
256 const std::array<IR::Value, 3> op_as{
257 _7->Arg(0).Resolve(),
258 _16->Arg(0).Resolve(),
259 _10->Arg(0).Resolve(),
260 };
261 const std::array<IR::Value, 3> op_bs{
262 _22->Arg(0).Resolve(),
263 _6->Arg(0).Resolve(),
264 _15->Arg(0).Resolve(),
265 };
266 const IR::U32 op_c{_18->Arg(1)};
267 if (!AreEqual(op_as) || !AreEqual(op_bs)) {
268 return false;
269 }
270 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
271 inst.ReplaceUsesWith(ir.IAdd(ir.IMul(IR::U32{op_as[0]}, IR::U32{op_bs[1]}), op_c));
272 return true;
273}
274
103/// Replaces the pattern generated by two XMAD multiplications 275/// Replaces the pattern generated by two XMAD multiplications
104bool FoldXmadMultiply(IR::Block& block, IR::Inst& inst) { 276bool FoldXmadMultiply(IR::Block& block, IR::Inst& inst) {
105 /* 277 /*
@@ -116,33 +288,31 @@ bool FoldXmadMultiply(IR::Block& block, IR::Inst& inst) {
116 * 288 *
117 * This optimization has been proven safe by LLVM and MSVC. 289 * This optimization has been proven safe by LLVM and MSVC.
118 */ 290 */
119 const IR::Value lhs_arg{inst.Arg(0)}; 291 IR::Inst* const lhs_shl{inst.Arg(0).TryInstRecursive()};
120 const IR::Value rhs_arg{inst.Arg(1)}; 292 IR::Inst* const rhs_mul{inst.Arg(1).TryInstRecursive()};
121 if (lhs_arg.IsImmediate() || rhs_arg.IsImmediate()) { 293 if (!lhs_shl || !rhs_mul) {
122 return false; 294 return false;
123 } 295 }
124 IR::Inst* const lhs_shl{lhs_arg.InstRecursive()};
125 if (lhs_shl->GetOpcode() != IR::Opcode::ShiftLeftLogical32 || 296 if (lhs_shl->GetOpcode() != IR::Opcode::ShiftLeftLogical32 ||
126 lhs_shl->Arg(1) != IR::Value{16U}) { 297 lhs_shl->Arg(1) != IR::Value{16U}) {
127 return false; 298 return false;
128 } 299 }
129 if (lhs_shl->Arg(0).IsImmediate()) { 300 IR::Inst* const lhs_mul{lhs_shl->Arg(0).TryInstRecursive()};
301 if (!lhs_mul) {
130 return false; 302 return false;
131 } 303 }
132 IR::Inst* const lhs_mul{lhs_shl->Arg(0).InstRecursive()};
133 IR::Inst* const rhs_mul{rhs_arg.InstRecursive()};
134 if (lhs_mul->GetOpcode() != IR::Opcode::IMul32 || rhs_mul->GetOpcode() != IR::Opcode::IMul32) { 304 if (lhs_mul->GetOpcode() != IR::Opcode::IMul32 || rhs_mul->GetOpcode() != IR::Opcode::IMul32) {
135 return false; 305 return false;
136 } 306 }
137 if (lhs_mul->Arg(1).Resolve() != rhs_mul->Arg(1).Resolve()) { 307 const IR::U32 factor_b{lhs_mul->Arg(1)};
308 if (factor_b.Resolve() != rhs_mul->Arg(1).Resolve()) {
138 return false; 309 return false;
139 } 310 }
140 const IR::U32 factor_b{lhs_mul->Arg(1)}; 311 IR::Inst* const lhs_bfe{lhs_mul->Arg(0).TryInstRecursive()};
141 if (lhs_mul->Arg(0).IsImmediate() || rhs_mul->Arg(0).IsImmediate()) { 312 IR::Inst* const rhs_bfe{rhs_mul->Arg(0).TryInstRecursive()};
313 if (!lhs_bfe || !rhs_bfe) {
142 return false; 314 return false;
143 } 315 }
144 IR::Inst* const lhs_bfe{lhs_mul->Arg(0).InstRecursive()};
145 IR::Inst* const rhs_bfe{rhs_mul->Arg(0).InstRecursive()};
146 if (lhs_bfe->GetOpcode() != IR::Opcode::BitFieldUExtract) { 316 if (lhs_bfe->GetOpcode() != IR::Opcode::BitFieldUExtract) {
147 return false; 317 return false;
148 } 318 }
@@ -155,10 +325,10 @@ bool FoldXmadMultiply(IR::Block& block, IR::Inst& inst) {
155 if (rhs_bfe->Arg(1) != IR::Value{0U} || rhs_bfe->Arg(2) != IR::Value{16U}) { 325 if (rhs_bfe->Arg(1) != IR::Value{0U} || rhs_bfe->Arg(2) != IR::Value{16U}) {
156 return false; 326 return false;
157 } 327 }
158 if (lhs_bfe->Arg(0).Resolve() != rhs_bfe->Arg(0).Resolve()) { 328 const IR::U32 factor_a{lhs_bfe->Arg(0)};
329 if (factor_a.Resolve() != rhs_bfe->Arg(0).Resolve()) {
159 return false; 330 return false;
160 } 331 }
161 const IR::U32 factor_a{lhs_bfe->Arg(0)};
162 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; 332 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
163 inst.ReplaceUsesWith(ir.IMul(factor_a, factor_b)); 333 inst.ReplaceUsesWith(ir.IMul(factor_a, factor_b));
164 return true; 334 return true;
@@ -181,6 +351,9 @@ void FoldAdd(IR::Block& block, IR::Inst& inst) {
181 if (FoldXmadMultiply(block, inst)) { 351 if (FoldXmadMultiply(block, inst)) {
182 return; 352 return;
183 } 353 }
354 if (FoldXmadMultiplyAdd(block, inst)) {
355 return;
356 }
184 } 357 }
185} 358}
186 359
@@ -476,6 +649,10 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) {
476 return FoldInverseFunc(inst, IR::Opcode::UnpackHalf2x16); 649 return FoldInverseFunc(inst, IR::Opcode::UnpackHalf2x16);
477 case IR::Opcode::UnpackHalf2x16: 650 case IR::Opcode::UnpackHalf2x16:
478 return FoldInverseFunc(inst, IR::Opcode::PackHalf2x16); 651 return FoldInverseFunc(inst, IR::Opcode::PackHalf2x16);
652 case IR::Opcode::PackFloat2x16:
653 return FoldInverseFunc(inst, IR::Opcode::UnpackFloat2x16);
654 case IR::Opcode::UnpackFloat2x16:
655 return FoldInverseFunc(inst, IR::Opcode::PackFloat2x16);
479 case IR::Opcode::SelectU1: 656 case IR::Opcode::SelectU1:
480 case IR::Opcode::SelectU8: 657 case IR::Opcode::SelectU8:
481 case IR::Opcode::SelectU16: 658 case IR::Opcode::SelectU16:
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 007ecc13e..333f6f35f 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -106,6 +106,8 @@ add_library(video_core STATIC
106 renderer_vulkan/maxwell_to_vk.cpp 106 renderer_vulkan/maxwell_to_vk.cpp
107 renderer_vulkan/maxwell_to_vk.h 107 renderer_vulkan/maxwell_to_vk.h
108 renderer_vulkan/pipeline_helper.h 108 renderer_vulkan/pipeline_helper.h
109 renderer_vulkan/pipeline_statistics.cpp
110 renderer_vulkan/pipeline_statistics.h
109 renderer_vulkan/renderer_vulkan.h 111 renderer_vulkan/renderer_vulkan.h
110 renderer_vulkan/renderer_vulkan.cpp 112 renderer_vulkan/renderer_vulkan.cpp
111 renderer_vulkan/vk_blit_screen.cpp 113 renderer_vulkan/vk_blit_screen.cpp
diff --git a/src/video_core/renderer_vulkan/pipeline_statistics.cpp b/src/video_core/renderer_vulkan/pipeline_statistics.cpp
new file mode 100644
index 000000000..bfec931a6
--- /dev/null
+++ b/src/video_core/renderer_vulkan/pipeline_statistics.cpp
@@ -0,0 +1,100 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6
7#include <fmt/format.h>
8
9#include "common/common_types.h"
10#include "common/logging/log.h"
11#include "video_core/renderer_vulkan/pipeline_statistics.h"
12#include "video_core/vulkan_common/vulkan_device.h"
13#include "video_core/vulkan_common/vulkan_wrapper.h"
14
15namespace Vulkan {
16
17using namespace std::string_view_literals;
18
19static u64 GetUint64(const VkPipelineExecutableStatisticKHR& statistic) {
20 switch (statistic.format) {
21 case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_INT64_KHR:
22 return static_cast<u64>(statistic.value.i64);
23 case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR:
24 return statistic.value.u64;
25 case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_FLOAT64_KHR:
26 return static_cast<u64>(statistic.value.f64);
27 default:
28 return 0;
29 }
30}
31
32PipelineStatistics::PipelineStatistics(const Device& device_) : device{device_} {}
33
34void PipelineStatistics::Collect(VkPipeline pipeline) {
35 const auto& dev{device.GetLogical()};
36 const std::vector properties{dev.GetPipelineExecutablePropertiesKHR(pipeline)};
37 const u32 num_executables{static_cast<u32>(properties.size())};
38 for (u32 executable = 0; executable < num_executables; ++executable) {
39 const auto statistics{dev.GetPipelineExecutableStatisticsKHR(pipeline, executable)};
40 if (statistics.empty()) {
41 continue;
42 }
43 Stats stage_stats;
44 for (const auto& statistic : statistics) {
45 const char* const name{statistic.name};
46 if (name == "Binary Size"sv || name == "Code size"sv || name == "Instruction Count"sv) {
47 stage_stats.code_size = GetUint64(statistic);
48 } else if (name == "Register Count"sv) {
49 stage_stats.register_count = GetUint64(statistic);
50 } else if (name == "SGPRs"sv || name == "numUsedSgprs"sv) {
51 stage_stats.sgpr_count = GetUint64(statistic);
52 } else if (name == "VGPRs"sv || name == "numUsedVgprs"sv) {
53 stage_stats.vgpr_count = GetUint64(statistic);
54 } else if (name == "Branches"sv) {
55 stage_stats.branches_count = GetUint64(statistic);
56 } else if (name == "Basic Block Count"sv) {
57 stage_stats.basic_block_count = GetUint64(statistic);
58 }
59 }
60 std::lock_guard lock{mutex};
61 collected_stats.push_back(stage_stats);
62 }
63}
64
65void PipelineStatistics::Report() const {
66 double num{};
67 Stats total;
68 {
69 std::lock_guard lock{mutex};
70 for (const Stats& stats : collected_stats) {
71 total.code_size += stats.code_size;
72 total.register_count += stats.register_count;
73 total.sgpr_count += stats.sgpr_count;
74 total.vgpr_count += stats.vgpr_count;
75 total.branches_count += stats.branches_count;
76 total.basic_block_count += stats.basic_block_count;
77 }
78 num = static_cast<double>(collected_stats.size());
79 }
80 std::string report;
81 const auto add = [&](const char* fmt, u64 value) {
82 if (value > 0) {
83 report += fmt::format(fmt::runtime(fmt), static_cast<double>(value) / num);
84 }
85 };
86 add("Code size: {:9.03f}\n", total.code_size);
87 add("Register count: {:9.03f}\n", total.register_count);
88 add("SGPRs: {:9.03f}\n", total.sgpr_count);
89 add("VGPRs: {:9.03f}\n", total.vgpr_count);
90 add("Branches count: {:9.03f}\n", total.branches_count);
91 add("Basic blocks: {:9.03f}\n", total.basic_block_count);
92
93 LOG_INFO(Render_Vulkan,
94 "\nAverage pipeline statistics\n"
95 "==========================================\n"
96 "{}\n",
97 report);
98}
99
100} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/pipeline_statistics.h b/src/video_core/renderer_vulkan/pipeline_statistics.h
new file mode 100644
index 000000000..b61840107
--- /dev/null
+++ b/src/video_core/renderer_vulkan/pipeline_statistics.h
@@ -0,0 +1,40 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <mutex>
8#include <vector>
9
10#include "common/common_types.h"
11#include "video_core/vulkan_common/vulkan_wrapper.h"
12
13namespace Vulkan {
14
15class Device;
16
17class PipelineStatistics {
18public:
19 explicit PipelineStatistics(const Device& device_);
20
21 void Collect(VkPipeline pipeline);
22
23 void Report() const;
24
25private:
26 struct Stats {
27 u64 code_size{};
28 u64 register_count{};
29 u64 sgpr_count{};
30 u64 vgpr_count{};
31 u64 branches_count{};
32 u64 basic_block_count{};
33 };
34
35 const Device& device;
36 mutable std::mutex mutex;
37 std::vector<Stats> collected_stats;
38};
39
40} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 70b84c7a6..44faf626a 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -8,6 +8,7 @@
8#include <boost/container/small_vector.hpp> 8#include <boost/container/small_vector.hpp>
9 9
10#include "video_core/renderer_vulkan/pipeline_helper.h" 10#include "video_core/renderer_vulkan/pipeline_helper.h"
11#include "video_core/renderer_vulkan/pipeline_statistics.h"
11#include "video_core/renderer_vulkan/vk_buffer_cache.h" 12#include "video_core/renderer_vulkan/vk_buffer_cache.h"
12#include "video_core/renderer_vulkan/vk_compute_pipeline.h" 13#include "video_core/renderer_vulkan/vk_compute_pipeline.h"
13#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 14#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
@@ -26,6 +27,7 @@ using Tegra::Texture::TexturePair;
26ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descriptor_pool, 27ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descriptor_pool,
27 VKUpdateDescriptorQueue& update_descriptor_queue_, 28 VKUpdateDescriptorQueue& update_descriptor_queue_,
28 Common::ThreadWorker* thread_worker, 29 Common::ThreadWorker* thread_worker,
30 PipelineStatistics* pipeline_statistics,
29 VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_, 31 VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_,
30 vk::ShaderModule spv_module_) 32 vk::ShaderModule spv_module_)
31 : device{device_}, update_descriptor_queue{update_descriptor_queue_}, info{info_}, 33 : device{device_}, update_descriptor_queue{update_descriptor_queue_}, info{info_},
@@ -36,7 +38,7 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
36 std::copy_n(info.constant_buffer_used_sizes.begin(), uniform_buffer_sizes.size(), 38 std::copy_n(info.constant_buffer_used_sizes.begin(), uniform_buffer_sizes.size(),
37 uniform_buffer_sizes.begin()); 39 uniform_buffer_sizes.begin());
38 40
39 auto func{[this, &descriptor_pool, shader_notify] { 41 auto func{[this, &descriptor_pool, shader_notify, pipeline_statistics] {
40 DescriptorLayoutBuilder builder{device}; 42 DescriptorLayoutBuilder builder{device};
41 builder.Add(info, VK_SHADER_STAGE_COMPUTE_BIT); 43 builder.Add(info, VK_SHADER_STAGE_COMPUTE_BIT);
42 44
@@ -50,10 +52,14 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
50 .pNext = nullptr, 52 .pNext = nullptr,
51 .requiredSubgroupSize = GuestWarpSize, 53 .requiredSubgroupSize = GuestWarpSize,
52 }; 54 };
55 VkPipelineCreateFlags flags{};
56 if (device.IsKhrPipelineEexecutablePropertiesEnabled()) {
57 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
58 }
53 pipeline = device.GetLogical().CreateComputePipeline({ 59 pipeline = device.GetLogical().CreateComputePipeline({
54 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, 60 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
55 .pNext = nullptr, 61 .pNext = nullptr,
56 .flags = 0, 62 .flags = flags,
57 .stage{ 63 .stage{
58 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 64 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
59 .pNext = device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr, 65 .pNext = device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr,
@@ -67,6 +73,9 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
67 .basePipelineHandle = 0, 73 .basePipelineHandle = 0,
68 .basePipelineIndex = 0, 74 .basePipelineIndex = 0,
69 }); 75 });
76 if (pipeline_statistics) {
77 pipeline_statistics->Collect(*pipeline);
78 }
70 std::lock_guard lock{build_mutex}; 79 std::lock_guard lock{build_mutex};
71 is_built = true; 80 is_built = true;
72 build_condvar.notify_one(); 81 build_condvar.notify_one();
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h
index 52fec04d3..8c4b0a301 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h
@@ -25,6 +25,7 @@ class ShaderNotify;
25namespace Vulkan { 25namespace Vulkan {
26 26
27class Device; 27class Device;
28class PipelineStatistics;
28class VKScheduler; 29class VKScheduler;
29 30
30class ComputePipeline { 31class ComputePipeline {
@@ -32,6 +33,7 @@ public:
32 explicit ComputePipeline(const Device& device, DescriptorPool& descriptor_pool, 33 explicit ComputePipeline(const Device& device, DescriptorPool& descriptor_pool,
33 VKUpdateDescriptorQueue& update_descriptor_queue, 34 VKUpdateDescriptorQueue& update_descriptor_queue,
34 Common::ThreadWorker* thread_worker, 35 Common::ThreadWorker* thread_worker,
36 PipelineStatistics* pipeline_statistics,
35 VideoCore::ShaderNotify* shader_notify, const Shader::Info& info, 37 VideoCore::ShaderNotify* shader_notify, const Shader::Info& info,
36 vk::ShaderModule spv_module); 38 vk::ShaderModule spv_module);
37 39
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 18482e1d0..7c0f91007 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -11,6 +11,7 @@
11#include "common/bit_field.h" 11#include "common/bit_field.h"
12#include "video_core/renderer_vulkan/maxwell_to_vk.h" 12#include "video_core/renderer_vulkan/maxwell_to_vk.h"
13#include "video_core/renderer_vulkan/pipeline_helper.h" 13#include "video_core/renderer_vulkan/pipeline_helper.h"
14#include "video_core/renderer_vulkan/pipeline_statistics.h"
14#include "video_core/renderer_vulkan/vk_buffer_cache.h" 15#include "video_core/renderer_vulkan/vk_buffer_cache.h"
15#include "video_core/renderer_vulkan/vk_graphics_pipeline.h" 16#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
16#include "video_core/renderer_vulkan/vk_render_pass_cache.h" 17#include "video_core/renderer_vulkan/vk_render_pass_cache.h"
@@ -217,8 +218,8 @@ GraphicsPipeline::GraphicsPipeline(
217 VKScheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_, 218 VKScheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_,
218 VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool, 219 VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool,
219 VKUpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread, 220 VKUpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread,
220 RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key_, 221 PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache,
221 std::array<vk::ShaderModule, NUM_STAGES> stages, 222 const GraphicsPipelineCacheKey& key_, std::array<vk::ShaderModule, NUM_STAGES> stages,
222 const std::array<const Shader::Info*, NUM_STAGES>& infos) 223 const std::array<const Shader::Info*, NUM_STAGES>& infos)
223 : key{key_}, maxwell3d{maxwell3d_}, gpu_memory{gpu_memory_}, device{device_}, 224 : key{key_}, maxwell3d{maxwell3d_}, gpu_memory{gpu_memory_}, device{device_},
224 texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, scheduler{scheduler_}, 225 texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, scheduler{scheduler_},
@@ -235,7 +236,7 @@ GraphicsPipeline::GraphicsPipeline(
235 enabled_uniform_buffer_masks[stage] = info->constant_buffer_mask; 236 enabled_uniform_buffer_masks[stage] = info->constant_buffer_mask;
236 std::ranges::copy(info->constant_buffer_used_sizes, uniform_buffer_sizes[stage].begin()); 237 std::ranges::copy(info->constant_buffer_used_sizes, uniform_buffer_sizes[stage].begin());
237 } 238 }
238 auto func{[this, shader_notify, &render_pass_cache, &descriptor_pool] { 239 auto func{[this, shader_notify, &render_pass_cache, &descriptor_pool, pipeline_statistics] {
239 DescriptorLayoutBuilder builder{MakeBuilder(device, stage_infos)}; 240 DescriptorLayoutBuilder builder{MakeBuilder(device, stage_infos)};
240 uses_push_descriptor = builder.CanUsePushDescriptor(); 241 uses_push_descriptor = builder.CanUsePushDescriptor();
241 descriptor_set_layout = builder.CreateDescriptorSetLayout(uses_push_descriptor); 242 descriptor_set_layout = builder.CreateDescriptorSetLayout(uses_push_descriptor);
@@ -250,6 +251,9 @@ GraphicsPipeline::GraphicsPipeline(
250 const VkRenderPass render_pass{render_pass_cache.Get(MakeRenderPassKey(key.state))}; 251 const VkRenderPass render_pass{render_pass_cache.Get(MakeRenderPassKey(key.state))};
251 Validate(); 252 Validate();
252 MakePipeline(render_pass); 253 MakePipeline(render_pass);
254 if (pipeline_statistics) {
255 pipeline_statistics->Collect(*pipeline);
256 }
253 257
254 std::lock_guard lock{build_mutex}; 258 std::lock_guard lock{build_mutex};
255 is_built = true; 259 is_built = true;
@@ -782,10 +786,14 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
782 } 786 }
783 */ 787 */
784 } 788 }
789 VkPipelineCreateFlags flags{};
790 if (device.IsKhrPipelineEexecutablePropertiesEnabled()) {
791 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
792 }
785 pipeline = device.GetLogical().CreateGraphicsPipeline({ 793 pipeline = device.GetLogical().CreateGraphicsPipeline({
786 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, 794 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
787 .pNext = nullptr, 795 .pNext = nullptr,
788 .flags = 0, 796 .flags = flags,
789 .stageCount = static_cast<u32>(shader_stages.size()), 797 .stageCount = static_cast<u32>(shader_stages.size()),
790 .pStages = shader_stages.data(), 798 .pStages = shader_stages.data(),
791 .pVertexInputState = &vertex_input_ci, 799 .pVertexInputState = &vertex_input_ci,
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
index 2bd48d697..1c780e944 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
@@ -60,6 +60,7 @@ struct hash<Vulkan::GraphicsPipelineCacheKey> {
60namespace Vulkan { 60namespace Vulkan {
61 61
62class Device; 62class Device;
63class PipelineStatistics;
63class RenderPassCache; 64class RenderPassCache;
64class VKScheduler; 65class VKScheduler;
65class VKUpdateDescriptorQueue; 66class VKUpdateDescriptorQueue;
@@ -73,8 +74,9 @@ public:
73 VKScheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache, 74 VKScheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache,
74 VideoCore::ShaderNotify* shader_notify, const Device& device, 75 VideoCore::ShaderNotify* shader_notify, const Device& device,
75 DescriptorPool& descriptor_pool, VKUpdateDescriptorQueue& update_descriptor_queue, 76 DescriptorPool& descriptor_pool, VKUpdateDescriptorQueue& update_descriptor_queue,
76 Common::ThreadWorker* worker_thread, RenderPassCache& render_pass_cache, 77 Common::ThreadWorker* worker_thread, PipelineStatistics* pipeline_statistics,
77 const GraphicsPipelineCacheKey& key, std::array<vk::ShaderModule, NUM_STAGES> stages, 78 RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key,
79 std::array<vk::ShaderModule, NUM_STAGES> stages,
78 const std::array<const Shader::Info*, NUM_STAGES>& infos); 80 const std::array<const Shader::Info*, NUM_STAGES>& infos);
79 81
80 GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete; 82 GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete;
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 57b163247..a37ca1fdf 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -29,6 +29,7 @@
29#include "video_core/renderer_vulkan/fixed_pipeline_state.h" 29#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
30#include "video_core/renderer_vulkan/maxwell_to_vk.h" 30#include "video_core/renderer_vulkan/maxwell_to_vk.h"
31#include "video_core/renderer_vulkan/pipeline_helper.h" 31#include "video_core/renderer_vulkan/pipeline_helper.h"
32#include "video_core/renderer_vulkan/pipeline_statistics.h"
32#include "video_core/renderer_vulkan/vk_compute_pipeline.h" 33#include "video_core/renderer_vulkan/vk_compute_pipeline.h"
33#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 34#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
34#include "video_core/renderer_vulkan/vk_pipeline_cache.h" 35#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
@@ -389,15 +390,19 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
389 size_t total{}; 390 size_t total{};
390 size_t built{}; 391 size_t built{};
391 bool has_loaded{}; 392 bool has_loaded{};
393 std::unique_ptr<PipelineStatistics> statistics;
392 } state; 394 } state;
393 395
396 if (device.IsKhrPipelineEexecutablePropertiesEnabled()) {
397 state.statistics = std::make_unique<PipelineStatistics>(device);
398 }
394 const auto load_compute{[&](std::ifstream& file, FileEnvironment env) { 399 const auto load_compute{[&](std::ifstream& file, FileEnvironment env) {
395 ComputePipelineCacheKey key; 400 ComputePipelineCacheKey key;
396 file.read(reinterpret_cast<char*>(&key), sizeof(key)); 401 file.read(reinterpret_cast<char*>(&key), sizeof(key));
397 402
398 workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable { 403 workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable {
399 ShaderPools pools; 404 ShaderPools pools;
400 auto pipeline{CreateComputePipeline(pools, key, env, false)}; 405 auto pipeline{CreateComputePipeline(pools, key, env, state.statistics.get(), false)};
401 std::lock_guard lock{state.mutex}; 406 std::lock_guard lock{state.mutex};
402 if (pipeline) { 407 if (pipeline) {
403 compute_cache.emplace(key, std::move(pipeline)); 408 compute_cache.emplace(key, std::move(pipeline));
@@ -425,7 +430,8 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
425 for (auto& env : envs) { 430 for (auto& env : envs) {
426 env_ptrs.push_back(&env); 431 env_ptrs.push_back(&env);
427 } 432 }
428 auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs), false)}; 433 auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs),
434 state.statistics.get(), false)};
429 435
430 std::lock_guard lock{state.mutex}; 436 std::lock_guard lock{state.mutex};
431 graphics_cache.emplace(key, std::move(pipeline)); 437 graphics_cache.emplace(key, std::move(pipeline));
@@ -445,6 +451,10 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
445 lock.unlock(); 451 lock.unlock();
446 452
447 workers.WaitForRequests(); 453 workers.WaitForRequests();
454
455 if (state.statistics) {
456 state.statistics->Report();
457 }
448} 458}
449 459
450GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() { 460GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() {
@@ -486,7 +496,8 @@ GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const
486 496
487std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( 497std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
488 ShaderPools& pools, const GraphicsPipelineCacheKey& key, 498 ShaderPools& pools, const GraphicsPipelineCacheKey& key,
489 std::span<Shader::Environment* const> envs, bool build_in_parallel) try { 499 std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
500 bool build_in_parallel) try {
490 LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash()); 501 LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
491 size_t env_index{0}; 502 size_t env_index{0};
492 std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs; 503 std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
@@ -540,7 +551,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
540 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; 551 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
541 return std::make_unique<GraphicsPipeline>( 552 return std::make_unique<GraphicsPipeline>(
542 maxwell3d, gpu_memory, scheduler, buffer_cache, texture_cache, &shader_notify, device, 553 maxwell3d, gpu_memory, scheduler, buffer_cache, texture_cache, &shader_notify, device,
543 descriptor_pool, update_descriptor_queue, thread_worker, render_pass_cache, key, 554 descriptor_pool, update_descriptor_queue, thread_worker, statistics, render_pass_cache, key,
544 std::move(modules), infos); 555 std::move(modules), infos);
545 556
546} catch (const Shader::Exception& exception) { 557} catch (const Shader::Exception& exception) {
@@ -553,7 +564,8 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
553 GetGraphicsEnvironments(environments, graphics_key.unique_hashes); 564 GetGraphicsEnvironments(environments, graphics_key.unique_hashes);
554 565
555 main_pools.ReleaseContents(); 566 main_pools.ReleaseContents();
556 auto pipeline{CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), true)}; 567 auto pipeline{
568 CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), nullptr, true)};
557 if (!pipeline || pipeline_cache_filename.empty()) { 569 if (!pipeline || pipeline_cache_filename.empty()) {
558 return pipeline; 570 return pipeline;
559 } 571 }
@@ -578,7 +590,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
578 env.SetCachedSize(shader->size_bytes); 590 env.SetCachedSize(shader->size_bytes);
579 591
580 main_pools.ReleaseContents(); 592 main_pools.ReleaseContents();
581 auto pipeline{CreateComputePipeline(main_pools, key, env, true)}; 593 auto pipeline{CreateComputePipeline(main_pools, key, env, nullptr, true)};
582 if (!pipeline || pipeline_cache_filename.empty()) { 594 if (!pipeline || pipeline_cache_filename.empty()) {
583 return pipeline; 595 return pipeline;
584 } 596 }
@@ -591,7 +603,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
591 603
592std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline( 604std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
593 ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env, 605 ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env,
594 bool build_in_parallel) try { 606 PipelineStatistics* statistics, bool build_in_parallel) try {
595 LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash()); 607 LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
596 608
597 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; 609 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
@@ -605,8 +617,8 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
605 } 617 }
606 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; 618 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
607 return std::make_unique<ComputePipeline>(device, descriptor_pool, update_descriptor_queue, 619 return std::make_unique<ComputePipeline>(device, descriptor_pool, update_descriptor_queue,
608 thread_worker, &shader_notify, program.info, 620 thread_worker, statistics, &shader_notify,
609 std::move(spv_module)); 621 program.info, std::move(spv_module));
610 622
611} catch (const Shader::Exception& exception) { 623} catch (const Shader::Exception& exception) {
612 LOG_ERROR(Render_Vulkan, "{}", exception.what()); 624 LOG_ERROR(Render_Vulkan, "{}", exception.what());
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index efe5a7ed8..4c135b5dd 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -80,8 +80,9 @@ struct hash<Vulkan::ComputePipelineCacheKey> {
80namespace Vulkan { 80namespace Vulkan {
81 81
82class ComputePipeline; 82class ComputePipeline;
83class Device;
84class DescriptorPool; 83class DescriptorPool;
84class Device;
85class PipelineStatistics;
85class RasterizerVulkan; 86class RasterizerVulkan;
86class RenderPassCache; 87class RenderPassCache;
87class VKScheduler; 88class VKScheduler;
@@ -128,7 +129,8 @@ private:
128 129
129 std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline( 130 std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline(
130 ShaderPools& pools, const GraphicsPipelineCacheKey& key, 131 ShaderPools& pools, const GraphicsPipelineCacheKey& key,
131 std::span<Shader::Environment* const> envs, bool build_in_parallel); 132 std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
133 bool build_in_parallel);
132 134
133 std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineCacheKey& key, 135 std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineCacheKey& key,
134 const ShaderInfo* shader); 136 const ShaderInfo* shader);
@@ -136,6 +138,7 @@ private:
136 std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderPools& pools, 138 std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderPools& pools,
137 const ComputePipelineCacheKey& key, 139 const ComputePipelineCacheKey& key,
138 Shader::Environment& env, 140 Shader::Environment& env,
141 PipelineStatistics* statistics,
139 bool build_in_parallel); 142 bool build_in_parallel);
140 143
141 const Device& device; 144 const Device& device;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index c7a07fdd8..23cef2996 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -61,11 +61,16 @@ struct DrawParams {
61VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t index) { 61VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t index) {
62 const auto& src = regs.viewport_transform[index]; 62 const auto& src = regs.viewport_transform[index];
63 const float width = src.scale_x * 2.0f; 63 const float width = src.scale_x * 2.0f;
64 const float height = src.scale_y * 2.0f; 64 float y = src.translate_y - src.scale_y;
65 float height = src.scale_y * 2.0f;
66 if (regs.screen_y_control.y_negate) {
67 y += height;
68 height = -height;
69 }
65 const float reduce_z = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1.0f : 0.0f; 70 const float reduce_z = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1.0f : 0.0f;
66 VkViewport viewport{ 71 VkViewport viewport{
67 .x = src.translate_x - src.scale_x, 72 .x = src.translate_x - src.scale_x,
68 .y = src.translate_y - src.scale_y, 73 .y = y,
69 .width = width != 0.0f ? width : 1.0f, 74 .width = width != 0.0f ? width : 1.0f,
70 .height = height != 0.0f ? height : 1.0f, 75 .height = height != 0.0f ? height : 1.0f,
71 .minDepth = src.translate_z - src.scale_z * reduce_z, 76 .minDepth = src.translate_z - src.scale_z * reduce_z,
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 44afdc1cd..8e56a89e1 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -526,6 +526,17 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
526 SetNext(next, workgroup_layout); 526 SetNext(next, workgroup_layout);
527 } 527 }
528 528
529 VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties;
530 if (khr_pipeline_executable_properties) {
531 LOG_INFO(Render_Vulkan, "Enabling shader feedback, expect slower shader build times");
532 executable_properties = {
533 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR,
534 .pNext = nullptr,
535 .pipelineExecutableInfo = VK_TRUE,
536 };
537 SetNext(next, executable_properties);
538 }
539
529 if (!ext_depth_range_unrestricted) { 540 if (!ext_depth_range_unrestricted) {
530 LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); 541 LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
531 } 542 }
@@ -824,6 +835,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
824 835
825 bool has_khr_shader_float16_int8{}; 836 bool has_khr_shader_float16_int8{};
826 bool has_khr_workgroup_memory_explicit_layout{}; 837 bool has_khr_workgroup_memory_explicit_layout{};
838 bool has_khr_pipeline_executable_properties{};
827 bool has_ext_subgroup_size_control{}; 839 bool has_ext_subgroup_size_control{};
828 bool has_ext_transform_feedback{}; 840 bool has_ext_transform_feedback{};
829 bool has_ext_custom_border_color{}; 841 bool has_ext_custom_border_color{};
@@ -878,6 +890,10 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
878 test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, 890 test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME,
879 true); 891 true);
880 } 892 }
893 if (Settings::values.renderer_shader_feedback) {
894 test(has_khr_pipeline_executable_properties,
895 VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME, false);
896 }
881 } 897 }
882 VkPhysicalDeviceFeatures2KHR features{}; 898 VkPhysicalDeviceFeatures2KHR features{};
883 features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; 899 features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
@@ -1033,6 +1049,19 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1033 khr_workgroup_memory_explicit_layout = true; 1049 khr_workgroup_memory_explicit_layout = true;
1034 } 1050 }
1035 } 1051 }
1052 if (has_khr_pipeline_executable_properties) {
1053 VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties;
1054 executable_properties.sType =
1055 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR;
1056 executable_properties.pNext = nullptr;
1057 features.pNext = &executable_properties;
1058 physical.GetFeatures2KHR(features);
1059
1060 if (executable_properties.pipelineExecutableInfo) {
1061 extensions.push_back(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
1062 khr_pipeline_executable_properties = true;
1063 }
1064 }
1036 if (khr_push_descriptor) { 1065 if (khr_push_descriptor) {
1037 VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor; 1066 VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor;
1038 push_descriptor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR; 1067 push_descriptor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR;
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index df394e384..c19f40746 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -214,6 +214,11 @@ public:
214 return khr_push_descriptor; 214 return khr_push_descriptor;
215 } 215 }
216 216
217 /// Returns true if VK_KHR_pipeline_executable_properties is enabled.
218 bool IsKhrPipelineEexecutablePropertiesEnabled() const {
219 return khr_pipeline_executable_properties;
220 }
221
217 /// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout. 222 /// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout.
218 bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const { 223 bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const {
219 return khr_workgroup_memory_explicit_layout; 224 return khr_workgroup_memory_explicit_layout;
@@ -378,6 +383,7 @@ private:
378 bool khr_spirv_1_4{}; ///< Support for VK_KHR_spirv_1_4. 383 bool khr_spirv_1_4{}; ///< Support for VK_KHR_spirv_1_4.
379 bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts. 384 bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts.
380 bool khr_push_descriptor{}; ///< Support for VK_KHR_push_descritor. 385 bool khr_push_descriptor{}; ///< Support for VK_KHR_push_descritor.
386 bool khr_pipeline_executable_properties{}; ///< Support for executable properties.
381 bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8. 387 bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
382 bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax. 388 bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax.
383 bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. 389 bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 70898004a..a9faa4807 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -181,6 +181,8 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
181 X(vkGetMemoryWin32HandleKHR); 181 X(vkGetMemoryWin32HandleKHR);
182#endif 182#endif
183 X(vkGetQueryPoolResults); 183 X(vkGetQueryPoolResults);
184 X(vkGetPipelineExecutablePropertiesKHR);
185 X(vkGetPipelineExecutableStatisticsKHR);
184 X(vkGetSemaphoreCounterValueKHR); 186 X(vkGetSemaphoreCounterValueKHR);
185 X(vkMapMemory); 187 X(vkMapMemory);
186 X(vkQueueSubmit); 188 X(vkQueueSubmit);
@@ -809,6 +811,42 @@ VkMemoryRequirements Device::GetImageMemoryRequirements(VkImage image) const noe
809 return requirements; 811 return requirements;
810} 812}
811 813
814std::vector<VkPipelineExecutablePropertiesKHR> Device::GetPipelineExecutablePropertiesKHR(
815 VkPipeline pipeline) const {
816 const VkPipelineInfoKHR info{
817 .sType = VK_STRUCTURE_TYPE_PIPELINE_INFO_KHR,
818 .pNext = nullptr,
819 .pipeline = pipeline,
820 };
821 u32 num{};
822 dld->vkGetPipelineExecutablePropertiesKHR(handle, &info, &num, nullptr);
823 std::vector<VkPipelineExecutablePropertiesKHR> properties(num);
824 for (auto& property : properties) {
825 property.sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_PROPERTIES_KHR;
826 }
827 Check(dld->vkGetPipelineExecutablePropertiesKHR(handle, &info, &num, properties.data()));
828 return properties;
829}
830
831std::vector<VkPipelineExecutableStatisticKHR> Device::GetPipelineExecutableStatisticsKHR(
832 VkPipeline pipeline, u32 executable_index) const {
833 const VkPipelineExecutableInfoKHR executable_info{
834 .sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_INFO_KHR,
835 .pNext = nullptr,
836 .pipeline = pipeline,
837 .executableIndex = executable_index,
838 };
839 u32 num{};
840 dld->vkGetPipelineExecutableStatisticsKHR(handle, &executable_info, &num, nullptr);
841 std::vector<VkPipelineExecutableStatisticKHR> statistics(num);
842 for (auto& statistic : statistics) {
843 statistic.sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_STATISTIC_KHR;
844 }
845 Check(dld->vkGetPipelineExecutableStatisticsKHR(handle, &executable_info, &num,
846 statistics.data()));
847 return statistics;
848}
849
812void Device::UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes, 850void Device::UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes,
813 Span<VkCopyDescriptorSet> copies) const noexcept { 851 Span<VkCopyDescriptorSet> copies) const noexcept {
814 dld->vkUpdateDescriptorSets(handle, writes.size(), writes.data(), copies.size(), copies.data()); 852 dld->vkUpdateDescriptorSets(handle, writes.size(), writes.data(), copies.size(), copies.data());
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index d76bb4324..b7ae01c6c 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -295,6 +295,8 @@ struct DeviceDispatch : InstanceDispatch {
295#ifdef _WIN32 295#ifdef _WIN32
296 PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{}; 296 PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{};
297#endif 297#endif
298 PFN_vkGetPipelineExecutablePropertiesKHR vkGetPipelineExecutablePropertiesKHR{};
299 PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR{};
298 PFN_vkGetQueryPoolResults vkGetQueryPoolResults{}; 300 PFN_vkGetQueryPoolResults vkGetQueryPoolResults{};
299 PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR{}; 301 PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR{};
300 PFN_vkMapMemory vkMapMemory{}; 302 PFN_vkMapMemory vkMapMemory{};
@@ -879,6 +881,12 @@ public:
879 881
880 VkMemoryRequirements GetImageMemoryRequirements(VkImage image) const noexcept; 882 VkMemoryRequirements GetImageMemoryRequirements(VkImage image) const noexcept;
881 883
884 std::vector<VkPipelineExecutablePropertiesKHR> GetPipelineExecutablePropertiesKHR(
885 VkPipeline pipeline) const;
886
887 std::vector<VkPipelineExecutableStatisticKHR> GetPipelineExecutableStatisticsKHR(
888 VkPipeline pipeline, u32 executable_index) const;
889
882 void UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes, 890 void UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes,
883 Span<VkCopyDescriptorSet> copies) const noexcept; 891 Span<VkCopyDescriptorSet> copies) const noexcept;
884 892
diff --git a/src/yuzu/applets/qt_web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp
index b112dd7b0..652d99570 100644
--- a/src/yuzu/applets/qt_web_browser.cpp
+++ b/src/yuzu/applets/qt_web_browser.cpp
@@ -107,6 +107,7 @@ void QtNXWebEngineView::LoadLocalWebPage(const std::string& main_url,
107 is_local = true; 107 is_local = true;
108 108
109 LoadExtractedFonts(); 109 LoadExtractedFonts();
110 FocusFirstLinkElement();
110 SetUserAgent(UserAgent::WebApplet); 111 SetUserAgent(UserAgent::WebApplet);
111 SetFinished(false); 112 SetFinished(false);
112 SetExitReason(Service::AM::Applets::WebExitReason::EndButtonPressed); 113 SetExitReason(Service::AM::Applets::WebExitReason::EndButtonPressed);
@@ -121,6 +122,7 @@ void QtNXWebEngineView::LoadExternalWebPage(const std::string& main_url,
121 const std::string& additional_args) { 122 const std::string& additional_args) {
122 is_local = false; 123 is_local = false;
123 124
125 FocusFirstLinkElement();
124 SetUserAgent(UserAgent::WebApplet); 126 SetUserAgent(UserAgent::WebApplet);
125 SetFinished(false); 127 SetFinished(false);
126 SetExitReason(Service::AM::Applets::WebExitReason::EndButtonPressed); 128 SetExitReason(Service::AM::Applets::WebExitReason::EndButtonPressed);
@@ -208,7 +210,7 @@ void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() {
208 if (input_interpreter->IsButtonPressedOnce(button)) { 210 if (input_interpreter->IsButtonPressedOnce(button)) {
209 page()->runJavaScript( 211 page()->runJavaScript(
210 QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(static_cast<u8>(button)), 212 QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(static_cast<u8>(button)),
211 [&](const QVariant& variant) { 213 [this, button](const QVariant& variant) {
212 if (variant.toBool()) { 214 if (variant.toBool()) {
213 switch (button) { 215 switch (button) {
214 case HIDButton::A: 216 case HIDButton::A:
@@ -364,6 +366,17 @@ void QtNXWebEngineView::LoadExtractedFonts() {
364 Qt::QueuedConnection); 366 Qt::QueuedConnection);
365} 367}
366 368
369void QtNXWebEngineView::FocusFirstLinkElement() {
370 QWebEngineScript focus_link_element;
371
372 focus_link_element.setName(QStringLiteral("focus_link_element.js"));
373 focus_link_element.setSourceCode(QString::fromStdString(FOCUS_LINK_ELEMENT_SCRIPT));
374 focus_link_element.setWorldId(QWebEngineScript::MainWorld);
375 focus_link_element.setInjectionPoint(QWebEngineScript::Deferred);
376 focus_link_element.setRunsOnSubFrames(true);
377 default_profile->scripts()->insert(focus_link_element);
378}
379
367#endif 380#endif
368 381
369QtWebBrowser::QtWebBrowser(GMainWindow& main_window) { 382QtWebBrowser::QtWebBrowser(GMainWindow& main_window) {
diff --git a/src/yuzu/applets/qt_web_browser.h b/src/yuzu/applets/qt_web_browser.h
index 7ad07409f..7e9f703fc 100644
--- a/src/yuzu/applets/qt_web_browser.h
+++ b/src/yuzu/applets/qt_web_browser.h
@@ -161,6 +161,9 @@ private:
161 /// Loads the extracted fonts using JavaScript. 161 /// Loads the extracted fonts using JavaScript.
162 void LoadExtractedFonts(); 162 void LoadExtractedFonts();
163 163
164 /// Brings focus to the first available link element.
165 void FocusFirstLinkElement();
166
164 InputCommon::InputSubsystem* input_subsystem; 167 InputCommon::InputSubsystem* input_subsystem;
165 168
166 std::unique_ptr<UrlRequestInterceptor> url_interceptor; 169 std::unique_ptr<UrlRequestInterceptor> url_interceptor;
diff --git a/src/yuzu/applets/qt_web_browser_scripts.h b/src/yuzu/applets/qt_web_browser_scripts.h
index 992837a85..c4ba8d40f 100644
--- a/src/yuzu/applets/qt_web_browser_scripts.h
+++ b/src/yuzu/applets/qt_web_browser_scripts.h
@@ -73,6 +73,12 @@ constexpr char LOAD_NX_FONT[] = R"(
73})(); 73})();
74)"; 74)";
75 75
76constexpr char FOCUS_LINK_ELEMENT_SCRIPT[] = R"(
77if (document.getElementsByTagName("a").length > 0) {
78 document.getElementsByTagName("a")[0].focus();
79}
80)";
81
76constexpr char GAMEPAD_SCRIPT[] = R"( 82constexpr char GAMEPAD_SCRIPT[] = R"(
77window.addEventListener("gamepadconnected", function(e) { 83window.addEventListener("gamepadconnected", function(e) {
78 console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.", 84 console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.",
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 72027e773..f3b8787f5 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -825,6 +825,7 @@ void Config::ReadRendererValues() {
825 if (global) { 825 if (global) {
826 ReadBasicSetting(Settings::values.fps_cap); 826 ReadBasicSetting(Settings::values.fps_cap);
827 ReadBasicSetting(Settings::values.renderer_debug); 827 ReadBasicSetting(Settings::values.renderer_debug);
828 ReadBasicSetting(Settings::values.renderer_shader_feedback);
828 ReadBasicSetting(Settings::values.enable_nsight_aftermath); 829 ReadBasicSetting(Settings::values.enable_nsight_aftermath);
829 ReadBasicSetting(Settings::values.disable_shader_loop_safety_checks); 830 ReadBasicSetting(Settings::values.disable_shader_loop_safety_checks);
830 } 831 }
@@ -1332,7 +1333,10 @@ void Config::SaveRendererValues() {
1332 static_cast<u32>(Settings::values.renderer_backend.GetDefault()), 1333 static_cast<u32>(Settings::values.renderer_backend.GetDefault()),
1333 Settings::values.renderer_backend.UsingGlobal()); 1334 Settings::values.renderer_backend.UsingGlobal());
1334 WriteGlobalSetting(Settings::values.vulkan_device); 1335 WriteGlobalSetting(Settings::values.vulkan_device);
1335 WriteGlobalSetting(Settings::values.fullscreen_mode); 1336 WriteSetting(QString::fromStdString(Settings::values.fullscreen_mode.GetLabel()),
1337 static_cast<u32>(Settings::values.fullscreen_mode.GetValue(global)),
1338 static_cast<u32>(Settings::values.fullscreen_mode.GetDefault()),
1339 Settings::values.fullscreen_mode.UsingGlobal());
1336 WriteGlobalSetting(Settings::values.aspect_ratio); 1340 WriteGlobalSetting(Settings::values.aspect_ratio);
1337 WriteGlobalSetting(Settings::values.max_anisotropy); 1341 WriteGlobalSetting(Settings::values.max_anisotropy);
1338 WriteGlobalSetting(Settings::values.use_speed_limit); 1342 WriteGlobalSetting(Settings::values.use_speed_limit);
@@ -1360,6 +1364,7 @@ void Config::SaveRendererValues() {
1360 if (global) { 1364 if (global) {
1361 WriteBasicSetting(Settings::values.fps_cap); 1365 WriteBasicSetting(Settings::values.fps_cap);
1362 WriteBasicSetting(Settings::values.renderer_debug); 1366 WriteBasicSetting(Settings::values.renderer_debug);
1367 WriteBasicSetting(Settings::values.renderer_shader_feedback);
1363 WriteBasicSetting(Settings::values.enable_nsight_aftermath); 1368 WriteBasicSetting(Settings::values.enable_nsight_aftermath);
1364 WriteBasicSetting(Settings::values.disable_shader_loop_safety_checks); 1369 WriteBasicSetting(Settings::values.disable_shader_loop_safety_checks);
1365 } 1370 }
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 4bbb9f1cd..c1d7feb9f 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -181,5 +181,6 @@ private:
181// These metatype declarations cannot be in common/settings.h because core is devoid of QT 181// These metatype declarations cannot be in common/settings.h because core is devoid of QT
182Q_DECLARE_METATYPE(Settings::CPUAccuracy); 182Q_DECLARE_METATYPE(Settings::CPUAccuracy);
183Q_DECLARE_METATYPE(Settings::GPUAccuracy); 183Q_DECLARE_METATYPE(Settings::GPUAccuracy);
184Q_DECLARE_METATYPE(Settings::FullscreenMode);
184Q_DECLARE_METATYPE(Settings::RendererBackend); 185Q_DECLARE_METATYPE(Settings::RendererBackend);
185Q_DECLARE_METATYPE(Settings::ShaderBackend); 186Q_DECLARE_METATYPE(Settings::ShaderBackend);
diff --git a/src/yuzu/configuration/configuration_shared.cpp b/src/yuzu/configuration/configuration_shared.cpp
index 096e42e94..251aab912 100644
--- a/src/yuzu/configuration/configuration_shared.cpp
+++ b/src/yuzu/configuration/configuration_shared.cpp
@@ -25,20 +25,6 @@ void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<bool>* setting,
25 } 25 }
26} 26}
27 27
28void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<int>* setting,
29 const QComboBox* combobox) {
30 if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
31 setting->SetValue(combobox->currentIndex());
32 } else if (!Settings::IsConfiguringGlobal()) {
33 if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
34 setting->SetGlobal(true);
35 } else {
36 setting->SetGlobal(false);
37 setting->SetValue(combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET);
38 }
39 }
40}
41
42void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox, 28void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox,
43 const Settings::Setting<bool>* setting) { 29 const Settings::Setting<bool>* setting) {
44 if (setting->UsingGlobal()) { 30 if (setting->UsingGlobal()) {
diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h
index 1e0ef01ca..5423dbc92 100644
--- a/src/yuzu/configuration/configuration_shared.h
+++ b/src/yuzu/configuration/configuration_shared.h
@@ -28,7 +28,20 @@ enum class CheckState {
28// ApplyPerGameSetting, given a Settings::Setting and a Qt UI element, properly applies a Setting 28// ApplyPerGameSetting, given a Settings::Setting and a Qt UI element, properly applies a Setting
29void ApplyPerGameSetting(Settings::Setting<bool>* setting, const QCheckBox* checkbox, 29void ApplyPerGameSetting(Settings::Setting<bool>* setting, const QCheckBox* checkbox,
30 const CheckState& tracker); 30 const CheckState& tracker);
31void ApplyPerGameSetting(Settings::Setting<int>* setting, const QComboBox* combobox); 31template <typename Type>
32void ApplyPerGameSetting(Settings::Setting<Type>* setting, const QComboBox* combobox) {
33 if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
34 setting->SetValue(static_cast<Type>(combobox->currentIndex()));
35 } else if (!Settings::IsConfiguringGlobal()) {
36 if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
37 setting->SetGlobal(true);
38 } else {
39 setting->SetGlobal(false);
40 setting->SetValue(static_cast<Type>(combobox->currentIndex() -
41 ConfigurationShared::USE_GLOBAL_OFFSET));
42 }
43 }
44}
32 45
33// Sets a Qt UI element given a Settings::Setting 46// Sets a Qt UI element given a Settings::Setting
34void SetPerGameSetting(QCheckBox* checkbox, const Settings::Setting<bool>* setting); 47void SetPerGameSetting(QCheckBox* checkbox, const Settings::Setting<bool>* setting);
diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp
index 8d7171487..784b6484e 100644
--- a/src/yuzu/configuration/configure_cpu.cpp
+++ b/src/yuzu/configuration/configure_cpu.cpp
@@ -65,6 +65,7 @@ void ConfigureCpu::UpdateGroup(int index) {
65} 65}
66 66
67void ConfigureCpu::ApplyConfiguration() { 67void ConfigureCpu::ApplyConfiguration() {
68 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpu_accuracy, ui->accuracy);
68 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_unfuse_fma, 69 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_unfuse_fma,
69 ui->cpuopt_unsafe_unfuse_fma, 70 ui->cpuopt_unsafe_unfuse_fma,
70 cpuopt_unsafe_unfuse_fma); 71 cpuopt_unsafe_unfuse_fma);
@@ -80,22 +81,6 @@ void ConfigureCpu::ApplyConfiguration() {
80 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_fastmem_check, 81 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_fastmem_check,
81 ui->cpuopt_unsafe_fastmem_check, 82 ui->cpuopt_unsafe_fastmem_check,
82 cpuopt_unsafe_fastmem_check); 83 cpuopt_unsafe_fastmem_check);
83
84 if (Settings::IsConfiguringGlobal()) {
85 // Guard if during game and set to game-specific value
86 if (Settings::values.cpu_accuracy.UsingGlobal()) {
87 Settings::values.cpu_accuracy.SetValue(
88 static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex()));
89 }
90 } else {
91 if (ui->accuracy->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
92 Settings::values.cpu_accuracy.SetGlobal(true);
93 } else {
94 Settings::values.cpu_accuracy.SetGlobal(false);
95 Settings::values.cpu_accuracy.SetValue(static_cast<Settings::CPUAccuracy>(
96 ui->accuracy->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET));
97 }
98 }
99} 84}
100 85
101void ConfigureCpu::changeEvent(QEvent* event) { 86void ConfigureCpu::changeEvent(QEvent* event) {
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index f7e29dbd7..c0b240c1e 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -43,6 +43,8 @@ void ConfigureDebug::SetConfiguration() {
43 ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue()); 43 ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue());
44 ui->enable_graphics_debugging->setEnabled(runtime_lock); 44 ui->enable_graphics_debugging->setEnabled(runtime_lock);
45 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue()); 45 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue());
46 ui->enable_shader_feedback->setEnabled(runtime_lock);
47 ui->enable_shader_feedback->setChecked(Settings::values.renderer_shader_feedback.GetValue());
46 ui->enable_cpu_debugging->setEnabled(runtime_lock); 48 ui->enable_cpu_debugging->setEnabled(runtime_lock);
47 ui->enable_cpu_debugging->setChecked(Settings::values.cpu_debug_mode.GetValue()); 49 ui->enable_cpu_debugging->setChecked(Settings::values.cpu_debug_mode.GetValue());
48 ui->enable_nsight_aftermath->setEnabled(runtime_lock); 50 ui->enable_nsight_aftermath->setEnabled(runtime_lock);
@@ -65,6 +67,7 @@ void ConfigureDebug::ApplyConfiguration() {
65 Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked(); 67 Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked();
66 Settings::values.use_auto_stub = ui->use_auto_stub->isChecked(); 68 Settings::values.use_auto_stub = ui->use_auto_stub->isChecked();
67 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); 69 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
70 Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
68 Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked(); 71 Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
69 Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked(); 72 Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked();
70 Settings::values.disable_shader_loop_safety_checks = 73 Settings::values.disable_shader_loop_safety_checks =
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index c8baf2921..3fe9ff7de 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -111,8 +111,8 @@
111 <property name="title"> 111 <property name="title">
112 <string>Graphics</string> 112 <string>Graphics</string>
113 </property> 113 </property>
114 <layout class="QVBoxLayout" name="verticalLayout_6"> 114 <layout class="QGridLayout" name="gridLayout_3">
115 <item> 115 <item row="0" column="0">
116 <widget class="QCheckBox" name="enable_graphics_debugging"> 116 <widget class="QCheckBox" name="enable_graphics_debugging">
117 <property name="enabled"> 117 <property name="enabled">
118 <bool>true</bool> 118 <bool>true</bool>
@@ -125,7 +125,7 @@
125 </property> 125 </property>
126 </widget> 126 </widget>
127 </item> 127 </item>
128 <item> 128 <item row="2" column="0">
129 <widget class="QCheckBox" name="enable_nsight_aftermath"> 129 <widget class="QCheckBox" name="enable_nsight_aftermath">
130 <property name="toolTip"> 130 <property name="toolTip">
131 <string>When checked, it enables Nsight Aftermath crash dumps</string> 131 <string>When checked, it enables Nsight Aftermath crash dumps</string>
@@ -135,7 +135,7 @@
135 </property> 135 </property>
136 </widget> 136 </widget>
137 </item> 137 </item>
138 <item> 138 <item row="0" column="1">
139 <widget class="QCheckBox" name="disable_macro_jit"> 139 <widget class="QCheckBox" name="disable_macro_jit">
140 <property name="enabled"> 140 <property name="enabled">
141 <bool>true</bool> 141 <bool>true</bool>
@@ -148,7 +148,17 @@
148 </property> 148 </property>
149 </widget> 149 </widget>
150 </item> 150 </item>
151 <item> 151 <item row="1" column="0">
152 <widget class="QCheckBox" name="enable_shader_feedback">
153 <property name="toolTip">
154 <string>When checked, yuzu will log statistics about the compiled pipeline cache</string>
155 </property>
156 <property name="text">
157 <string>Enable Shader Feedback</string>
158 </property>
159 </widget>
160 </item>
161 <item row="1" column="1">
152 <widget class="QCheckBox" name="disable_loop_safety_checks"> 162 <widget class="QCheckBox" name="disable_loop_safety_checks">
153 <property name="toolTip"> 163 <property name="toolTip">
154 <string>When checked, it executes shaders without loop logic changes</string> 164 <string>When checked, it executes shaders without loop logic changes</string>
@@ -276,11 +286,14 @@
276 <tabstop>open_log_button</tabstop> 286 <tabstop>open_log_button</tabstop>
277 <tabstop>homebrew_args_edit</tabstop> 287 <tabstop>homebrew_args_edit</tabstop>
278 <tabstop>enable_graphics_debugging</tabstop> 288 <tabstop>enable_graphics_debugging</tabstop>
289 <tabstop>enable_shader_feedback</tabstop>
279 <tabstop>enable_nsight_aftermath</tabstop> 290 <tabstop>enable_nsight_aftermath</tabstop>
280 <tabstop>disable_macro_jit</tabstop> 291 <tabstop>disable_macro_jit</tabstop>
281 <tabstop>disable_loop_safety_checks</tabstop> 292 <tabstop>disable_loop_safety_checks</tabstop>
293 <tabstop>fs_access_log</tabstop>
282 <tabstop>reporting_services</tabstop> 294 <tabstop>reporting_services</tabstop>
283 <tabstop>quest_flag</tabstop> 295 <tabstop>quest_flag</tabstop>
296 <tabstop>enable_cpu_debugging</tabstop>
284 <tabstop>use_debug_asserts</tabstop> 297 <tabstop>use_debug_asserts</tabstop>
285 <tabstop>use_auto_stub</tabstop> 298 <tabstop>use_auto_stub</tabstop>
286 </tabstops> 299 </tabstops>
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 1bc477c96..37e896258 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -98,7 +98,8 @@ void ConfigureGraphics::SetConfiguration() {
98 98
99 if (Settings::IsConfiguringGlobal()) { 99 if (Settings::IsConfiguringGlobal()) {
100 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue())); 100 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue()));
101 ui->fullscreen_mode_combobox->setCurrentIndex(Settings::values.fullscreen_mode.GetValue()); 101 ui->fullscreen_mode_combobox->setCurrentIndex(
102 static_cast<int>(Settings::values.fullscreen_mode.GetValue()));
102 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue()); 103 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
103 } else { 104 } else {
104 ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend); 105 ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend);
@@ -310,8 +311,9 @@ void ConfigureGraphics::SetupPerGameUI() {
310 311
311 ConfigurationShared::SetColoredComboBox(ui->aspect_ratio_combobox, ui->ar_label, 312 ConfigurationShared::SetColoredComboBox(ui->aspect_ratio_combobox, ui->ar_label,
312 Settings::values.aspect_ratio.GetValue(true)); 313 Settings::values.aspect_ratio.GetValue(true));
313 ConfigurationShared::SetColoredComboBox(ui->fullscreen_mode_combobox, ui->fullscreen_mode_label, 314 ConfigurationShared::SetColoredComboBox(
314 Settings::values.fullscreen_mode.GetValue(true)); 315 ui->fullscreen_mode_combobox, ui->fullscreen_mode_label,
316 static_cast<int>(Settings::values.fullscreen_mode.GetValue(true)));
315 ConfigurationShared::InsertGlobalItem( 317 ConfigurationShared::InsertGlobalItem(
316 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); 318 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
317} 319}
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index 38276feb1..a31b8e192 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -48,11 +48,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
48} 48}
49 49
50void ConfigureGraphicsAdvanced::ApplyConfiguration() { 50void ConfigureGraphicsAdvanced::ApplyConfiguration() {
51 // Subtract 2 if configuring per-game (separator and "use global configuration" take 2 slots) 51 ConfigurationShared::ApplyPerGameSetting(&Settings::values.gpu_accuracy, ui->gpu_accuracy);
52 const auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(
53 ui->gpu_accuracy->currentIndex() -
54 ((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET));
55
56 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, 52 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
57 ui->anisotropic_filtering_combobox); 53 ui->anisotropic_filtering_combobox);
58 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync); 54 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync);
@@ -63,20 +59,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
63 use_caches_gc); 59 use_caches_gc);
64 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time, 60 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
65 ui->use_fast_gpu_time, use_fast_gpu_time); 61 ui->use_fast_gpu_time, use_fast_gpu_time);
66
67 if (Settings::IsConfiguringGlobal()) {
68 // Must guard in case of a during-game configuration when set to be game-specific.
69 if (Settings::values.gpu_accuracy.UsingGlobal()) {
70 Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
71 }
72 } else {
73 if (ui->gpu_accuracy->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
74 Settings::values.gpu_accuracy.SetGlobal(true);
75 } else {
76 Settings::values.gpu_accuracy.SetGlobal(false);
77 Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
78 }
79 }
80} 62}
81 63
82void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { 64void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui
index 53b95658b..27f552f59 100644
--- a/src/yuzu/configuration/configure_system.ui
+++ b/src/yuzu/configuration/configure_system.ui
@@ -401,6 +401,11 @@
401 <string>Traditional Chinese (正體中文)</string> 401 <string>Traditional Chinese (正體中文)</string>
402 </property> 402 </property>
403 </item> 403 </item>
404 <item>
405 <property name="text">
406 <string>Brazilian Portuguese (português do Brasil)</string>
407 </property>
408 </item>
404 </widget> 409 </widget>
405 </item> 410 </item>
406 <item row="5" column="0"> 411 <item row="5" column="0">
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index e172d2ff4..9544f0fb0 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -2513,7 +2513,7 @@ void GMainWindow::ShowFullscreen() {
2513 ui.menubar->hide(); 2513 ui.menubar->hide();
2514 statusBar()->hide(); 2514 statusBar()->hide();
2515 2515
2516 if (Settings::values.fullscreen_mode.GetValue() == 1) { 2516 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
2517 showFullScreen(); 2517 showFullScreen();
2518 return; 2518 return;
2519 } 2519 }
@@ -2528,7 +2528,7 @@ void GMainWindow::ShowFullscreen() {
2528 } else { 2528 } else {
2529 UISettings::values.renderwindow_geometry = render_window->saveGeometry(); 2529 UISettings::values.renderwindow_geometry = render_window->saveGeometry();
2530 2530
2531 if (Settings::values.fullscreen_mode.GetValue() == 1) { 2531 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
2532 render_window->showFullScreen(); 2532 render_window->showFullScreen();
2533 return; 2533 return;
2534 } 2534 }
@@ -2545,7 +2545,7 @@ void GMainWindow::ShowFullscreen() {
2545 2545
2546void GMainWindow::HideFullscreen() { 2546void GMainWindow::HideFullscreen() {
2547 if (ui.action_Single_Window_Mode->isChecked()) { 2547 if (ui.action_Single_Window_Mode->isChecked()) {
2548 if (Settings::values.fullscreen_mode.GetValue() == 1) { 2548 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
2549 showNormal(); 2549 showNormal();
2550 restoreGeometry(UISettings::values.geometry); 2550 restoreGeometry(UISettings::values.geometry);
2551 } else { 2551 } else {
@@ -2559,7 +2559,7 @@ void GMainWindow::HideFullscreen() {
2559 statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked()); 2559 statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked());
2560 ui.menubar->show(); 2560 ui.menubar->show();
2561 } else { 2561 } else {
2562 if (Settings::values.fullscreen_mode.GetValue() == 1) { 2562 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
2563 render_window->showNormal(); 2563 render_window->showNormal();
2564 render_window->restoreGeometry(UISettings::values.renderwindow_geometry); 2564 render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
2565 } else { 2565 } else {
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 5af1ee6a8..064ecaafa 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -444,6 +444,7 @@ void Config::ReadValues() {
444 // Renderer 444 // Renderer
445 ReadSetting("Renderer", Settings::values.renderer_backend); 445 ReadSetting("Renderer", Settings::values.renderer_backend);
446 ReadSetting("Renderer", Settings::values.renderer_debug); 446 ReadSetting("Renderer", Settings::values.renderer_debug);
447 ReadSetting("Renderer", Settings::values.renderer_shader_feedback);
447 ReadSetting("Renderer", Settings::values.enable_nsight_aftermath); 448 ReadSetting("Renderer", Settings::values.enable_nsight_aftermath);
448 ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks); 449 ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks);
449 ReadSetting("Renderer", Settings::values.vulkan_device); 450 ReadSetting("Renderer", Settings::values.vulkan_device);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index e646e2d2f..e02eceb99 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -221,6 +221,10 @@ backend =
221# 0 (default): Disabled, 1: Enabled 221# 0 (default): Disabled, 1: Enabled
222debug = 222debug =
223 223
224# Enable shader feedback.
225# 0 (default): Disabled, 1: Enabled
226renderer_shader_feedback =
227
224# Enable Nsight Aftermath crash dumps 228# Enable Nsight Aftermath crash dumps
225# 0 (default): Disabled, 1: Enabled 229# 0 (default): Disabled, 1: Enabled
226nsight_aftermath = 230nsight_aftermath =
@@ -363,7 +367,7 @@ custom_rtc =
363# Sets the systems language index 367# Sets the systems language index
364# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese, 368# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
365# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French, 369# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French,
366# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese 370# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese
367language_index = 371language_index =
368 372
369# The system region that yuzu will use during emulation 373# The system region that yuzu will use during emulation
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 353e51ea7..f643a4b0b 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -16,8 +16,8 @@
16#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 16#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
17#include "yuzu_cmd/yuzu_icon.h" 17#include "yuzu_cmd/yuzu_icon.h"
18 18
19EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_) 19EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_)
20 : input_subsystem{input_subsystem_} { 20 : input_subsystem{input_subsystem_}, system{system_} {
21 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { 21 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
22 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); 22 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
23 exit(1); 23 exit(1);
@@ -124,7 +124,7 @@ void EmuWindow_SDL2::OnResize() {
124 124
125void EmuWindow_SDL2::Fullscreen() { 125void EmuWindow_SDL2::Fullscreen() {
126 switch (Settings::values.fullscreen_mode.GetValue()) { 126 switch (Settings::values.fullscreen_mode.GetValue()) {
127 case 1: // Exclusive fullscreen 127 case Settings::FullscreenMode::Exclusive:
128 // Set window size to render size before entering fullscreen -- SDL does not resize to 128 // Set window size to render size before entering fullscreen -- SDL does not resize to
129 // display dimensions in this mode. 129 // display dimensions in this mode.
130 // TODO: Multiply the window size by resolution_factor (for both docked modes) 130 // TODO: Multiply the window size by resolution_factor (for both docked modes)
@@ -140,7 +140,7 @@ void EmuWindow_SDL2::Fullscreen() {
140 LOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError()); 140 LOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError());
141 LOG_INFO(Frontend, "Attempting to use borderless fullscreen..."); 141 LOG_INFO(Frontend, "Attempting to use borderless fullscreen...");
142 [[fallthrough]]; 142 [[fallthrough]];
143 case 0: // Borderless window 143 case Settings::FullscreenMode::Borderless:
144 if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) { 144 if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) {
145 return; 145 return;
146 } 146 }
@@ -218,7 +218,7 @@ void EmuWindow_SDL2::WaitEvent() {
218 218
219 const u32 current_time = SDL_GetTicks(); 219 const u32 current_time = SDL_GetTicks();
220 if (current_time > last_time + 2000) { 220 if (current_time > last_time + 2000) {
221 const auto results = Core::System::GetInstance().GetAndResetPerfStats(); 221 const auto results = system.GetAndResetPerfStats();
222 const auto title = 222 const auto title =
223 fmt::format("yuzu {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname, 223 fmt::format("yuzu {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname,
224 Common::g_scm_branch, Common::g_scm_desc, results.average_game_fps, 224 Common::g_scm_branch, Common::g_scm_desc, results.average_game_fps,
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index 1b9ab5b93..aa0d52ae4 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -24,7 +24,7 @@ enum class MouseButton;
24 24
25class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { 25class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
26public: 26public:
27 explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem); 27 explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem, Core::System& system_);
28 ~EmuWindow_SDL2(); 28 ~EmuWindow_SDL2();
29 29
30 /// Whether the window is still open, and a close request hasn't yet been sent 30 /// Whether the window is still open, and a close request hasn't yet been sent
@@ -87,4 +87,7 @@ protected:
87 87
88 /// Input subsystem to use with this window. 88 /// Input subsystem to use with this window.
89 InputCommon::InputSubsystem* input_subsystem; 89 InputCommon::InputSubsystem* input_subsystem;
90
91 /// yuzu core instance
92 Core::System& system;
90}; 93};
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index eadb41790..5b98c255b 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -76,8 +76,9 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
76 return unsupported_ext.empty(); 76 return unsupported_ext.empty();
77} 77}
78 78
79EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen) 79EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem,
80 : EmuWindow_SDL2{input_subsystem} { 80 Core::System& system_, bool fullscreen)
81 : EmuWindow_SDL2{input_subsystem, system_} {
81 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); 82 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
82 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6); 83 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);
83 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); 84 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
index 9e694d985..d7f2c83d8 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
@@ -8,13 +8,18 @@
8#include "core/frontend/emu_window.h" 8#include "core/frontend/emu_window.h"
9#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 9#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
10 10
11namespace Core {
12class System;
13}
14
11namespace InputCommon { 15namespace InputCommon {
12class InputSubsystem; 16class InputSubsystem;
13} 17}
14 18
15class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { 19class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {
16public: 20public:
17 explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen); 21 explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, Core::System& system_,
22 bool fullscreen);
18 ~EmuWindow_SDL2_GL(); 23 ~EmuWindow_SDL2_GL();
19 24
20 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 25 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index d1473dbab..cdda375d8 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -24,8 +24,9 @@
24#include <SDL.h> 24#include <SDL.h>
25#include <SDL_syswm.h> 25#include <SDL_syswm.h>
26 26
27EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, bool fullscreen) 27EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem,
28 : EmuWindow_SDL2{input_subsystem} { 28 Core::System& system_, bool fullscreen)
29 : EmuWindow_SDL2{input_subsystem, system_} {
29 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name, 30 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
30 Common::g_scm_branch, Common::g_scm_desc); 31 Common::g_scm_branch, Common::g_scm_desc);
31 render_window = 32 render_window =
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
index de53844f0..3ea521b2a 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
@@ -19,7 +19,8 @@ class InputSubsystem;
19 19
20class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { 20class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
21public: 21public:
22 explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, bool fullscreen); 22 explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, Core::System& system,
23 bool fullscreen);
23 ~EmuWindow_SDL2_VK() override; 24 ~EmuWindow_SDL2_VK() override;
24 25
25 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 26 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 35ce23696..c10093820 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -172,10 +172,10 @@ int main(int argc, char** argv) {
172 std::unique_ptr<EmuWindow_SDL2> emu_window; 172 std::unique_ptr<EmuWindow_SDL2> emu_window;
173 switch (Settings::values.renderer_backend.GetValue()) { 173 switch (Settings::values.renderer_backend.GetValue()) {
174 case Settings::RendererBackend::OpenGL: 174 case Settings::RendererBackend::OpenGL:
175 emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, fullscreen); 175 emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, system, fullscreen);
176 break; 176 break;
177 case Settings::RendererBackend::Vulkan: 177 case Settings::RendererBackend::Vulkan:
178 emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, fullscreen); 178 emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, system, fullscreen);
179 break; 179 break;
180 } 180 }
181 181