summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/settings.h1
-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/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/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/configuration/config.cpp2
-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_system.ui5
-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.cpp6
-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
38 files changed, 575 insertions, 127 deletions
diff --git a/src/common/settings.h b/src/common/settings.h
index cfc1ab46f..a88ee045d 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -319,6 +319,7 @@ struct Values {
// Renderer
Setting<RendererBackend> renderer_backend{RendererBackend::OpenGL, "backend"};
BasicSetting<bool> renderer_debug{false, "debug"};
+ BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"};
BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
BasicSetting<bool> disable_shader_loop_safety_checks{false,
"disable_shader_loop_safety_checks"};
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() {
}
std::string UUID::Format() const {
- return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
+ return fmt::format("{:016x}{:016x}", uuid[1], uuid[0]);
}
std::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 {
// Horizon OS version constants.
-constexpr u8 HOS_VERSION_MAJOR = 11;
-constexpr u8 HOS_VERSION_MINOR = 0;
-constexpr u8 HOS_VERSION_MICRO = 1;
+constexpr u8 HOS_VERSION_MAJOR = 12;
+constexpr u8 HOS_VERSION_MINOR = 1;
+constexpr u8 HOS_VERSION_MICRO = 0;
// NintendoSDK version constants.
@@ -22,15 +22,15 @@ constexpr u8 SDK_REVISION_MAJOR = 1;
constexpr u8 SDK_REVISION_MINOR = 0;
constexpr char PLATFORM_STRING[] = "NX";
-constexpr char VERSION_HASH[] = "69103fcb2004dace877094c2f8c29e6113be5dbf";
-constexpr char DISPLAY_VERSION[] = "11.0.1";
-constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 11.0.1-1.0";
+constexpr char VERSION_HASH[] = "76b10c2dab7d3aa73fc162f8dff1655e6a21caf4";
+constexpr char DISPLAY_VERSION[] = "12.1.0";
+constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 12.1.0-1.0";
// Atmosphere version constants.
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MAJOR = 0;
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 19;
-constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 4;
+constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 5;
constexpr u32 GetTargetFirmware() {
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:
protected:
void Get(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
+ LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
ProfileBase profile_base{};
ProfileData data{};
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
@@ -301,7 +301,7 @@ protected:
rb.Push(ResultSuccess);
rb.PushRaw(profile_base);
} else {
- LOG_ERROR(Service_ACC, "Failed to get profile base and data for user={}",
+ LOG_ERROR(Service_ACC, "Failed to get profile base and data for user=0x{}",
user_id.Format());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code
@@ -309,14 +309,14 @@ protected:
}
void GetBase(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
+ LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
ProfileBase profile_base{};
if (profile_manager.GetProfileBase(user_id, profile_base)) {
IPC::ResponseBuilder rb{ctx, 16};
rb.Push(ResultSuccess);
rb.PushRaw(profile_base);
} else {
- LOG_ERROR(Service_ACC, "Failed to get profile base for user={}", user_id.Format());
+ LOG_ERROR(Service_ACC, "Failed to get profile base for user=0x{}", user_id.Format());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code
}
@@ -372,7 +372,7 @@ protected:
const auto user_data = ctx.ReadBuffer();
- LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid={}",
+ LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
Common::StringFromFixedZeroTerminatedBuffer(
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
base.timestamp, base.user_uuid.Format());
@@ -405,7 +405,7 @@ protected:
const auto user_data = ctx.ReadBuffer();
const auto image_data = ctx.ReadBuffer(1);
- LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid={}",
+ LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
Common::StringFromFixedZeroTerminatedBuffer(
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
base.timestamp, base.user_uuid.Format());
@@ -662,7 +662,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
Common::UUID user_id = rp.PopRaw<Common::UUID>();
- LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
+ LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@@ -693,7 +693,7 @@ void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
Common::UUID user_id = rp.PopRaw<Common::UUID>();
- LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
+ LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
@@ -802,7 +802,7 @@ void Module::Interface::GetProfileEditor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
Common::UUID user_id = rp.PopRaw<Common::UUID>();
- LOG_DEBUG(Service_ACC, "called, user_id={}", user_id.Format());
+ LOG_DEBUG(Service_ACC, "called, user_id=0x{}", user_id.Format());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
@@ -844,7 +844,7 @@ void Module::Interface::StoreSaveDataThumbnailApplication(Kernel::HLERequestCont
IPC::RequestParser rp{ctx};
const auto uuid = rp.PopRaw<Common::UUID>();
- LOG_WARNING(Service_ACC, "(STUBBED) called, uuid={}", uuid.Format());
+ LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}", uuid.Format());
// TODO(ogniK): Check if application ID is zero on acc initialize. As we don't have a reliable
// 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&
const auto uuid = rp.PopRaw<Common::UUID>();
const auto tid = rp.Pop<u64_le>();
- LOG_WARNING(Service_ACC, "(STUBBED) called, uuid={}, tid={:016X}", uuid.Format(), tid);
+ LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}, tid={:016X}", uuid.Format(), tid);
StoreSaveDataThumbnail(ctx, uuid, tid);
}
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) {
if (swkbd_config_common.use_utf8) {
std::string utf8_submitted_text = Common::UTF16ToUTF8(current_text);
- const u64 buffer_size = sizeof(u64) + utf8_submitted_text.size();
+ const u64 buffer_size = utf8_submitted_text.size();
LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-8 Submitted Text: {}", buffer_size,
utf8_submitted_text);
@@ -386,7 +386,7 @@ void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) {
std::memcpy(out_data.data() + sizeof(u64), utf8_submitted_text.data(),
utf8_submitted_text.size());
} else {
- const u64 buffer_size = sizeof(u64) + current_text.size() * sizeof(char16_t);
+ const u64 buffer_size = current_text.size() * sizeof(char16_t);
LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-16 Submitted Text: {}", buffer_size,
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:
const auto local_play = rp.Pop<bool>();
const auto uuid = rp.PopRaw<Common::UUID>();
- LOG_WARNING(Service_Friend, "(STUBBED) called local_play={} uuid={}", local_play,
+ LOG_WARNING(Service_Friend, "(STUBBED) called, local_play={}, uuid=0x{}", local_play,
uuid.Format());
IPC::ResponseBuilder rb{ctx, 2};
@@ -171,7 +171,7 @@ private:
const auto uuid = rp.PopRaw<Common::UUID>();
[[maybe_unused]] const auto filter = rp.PopRaw<SizedFriendFilter>();
const auto pid = rp.Pop<u64>();
- LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid={}, pid={}", friend_offset,
+ LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid=0x{}, pid={}", friend_offset,
uuid.Format(), pid);
IPC::ResponseBuilder rb{ctx, 3};
@@ -289,7 +289,7 @@ void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx
IPC::RequestParser rp{ctx};
auto uuid = rp.PopRaw<Common::UUID>();
- LOG_DEBUG(Service_Friend, "called, uuid={}", uuid.Format());
+ LOG_DEBUG(Service_Friend, "called, uuid=0x{}", uuid.Format());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
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(
case Set::LanguageCode::FR_CA:
return ApplicationLanguage::CanadianFrench;
case Set::LanguageCode::PT:
+ case Set::LanguageCode::PT_BR:
return ApplicationLanguage::Portuguese;
case Set::LanguageCode::RU:
return ApplicationLanguage::Russian;
case Set::LanguageCode::KO:
return ApplicationLanguage::Korean;
+ case Set::LanguageCode::ZH_TW:
case Set::LanguageCode::ZH_HANT:
return ApplicationLanguage::TraditionalChinese;
+ case Set::LanguageCode::ZH_CN:
case Set::LanguageCode::ZH_HANS:
return ApplicationLanguage::SimplifiedChinese;
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 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-#include <optional>
-#include <string>
-#include "common/common_types.h"
-#include "core/hle/service/set/set.h"
-
-namespace Service::NS {
-/// This is nn::ns::detail::ApplicationLanguage
-enum class ApplicationLanguage : u8 {
- AmericanEnglish = 0,
- BritishEnglish,
- Japanese,
- French,
- German,
- LatinAmericanSpanish,
- Spanish,
- Italian,
- Dutch,
- CanadianFrench,
- Portuguese,
- Russian,
- Korean,
- TraditionalChinese,
- SimplifiedChinese,
- Count
-};
-using ApplicationLanguagePriorityList =
- const std::array<ApplicationLanguage, static_cast<std::size_t>(ApplicationLanguage::Count)>;
-
-constexpr u32 GetSupportedLanguageFlag(const ApplicationLanguage lang) {
- return 1U << static_cast<u32>(lang);
-}
-
-const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(ApplicationLanguage lang);
-std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
- Service::Set::LanguageCode language_code);
-std::optional<Service::Set::LanguageCode> ConvertToLanguageCode(ApplicationLanguage lang);
-} // 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 @@
namespace Service::Set {
namespace {
-constexpr std::array<LanguageCode, 17> available_language_codes = {{
+constexpr std::array<LanguageCode, 18> available_language_codes = {{
LanguageCode::JA,
LanguageCode::EN_US,
LanguageCode::FR,
@@ -30,6 +30,7 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{
LanguageCode::ES_419,
LanguageCode::ZH_HANS,
LanguageCode::ZH_HANT,
+ LanguageCode::PT_BR,
}};
enum class KeyboardLayout : u64 {
@@ -50,7 +51,7 @@ enum class KeyboardLayout : u64 {
ChineseTraditional = 14,
};
-constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 17> language_to_layout{{
+constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 18> language_to_layout{{
{LanguageCode::JA, KeyboardLayout::Japanese},
{LanguageCode::EN_US, KeyboardLayout::EnglishUs},
{LanguageCode::FR, KeyboardLayout::French},
@@ -68,10 +69,11 @@ constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 17> language_to_la
{LanguageCode::ES_419, KeyboardLayout::SpanishLatin},
{LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified},
{LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional},
+ {LanguageCode::PT_BR, KeyboardLayout::Portuguese},
}};
-constexpr std::size_t pre4_0_0_max_entries = 15;
-constexpr std::size_t post4_0_0_max_entries = 17;
+constexpr std::size_t PRE_4_0_0_MAX_ENTRIES = 0xF;
+constexpr std::size_t POST_4_0_0_MAX_ENTRIES = 0x40;
constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625};
@@ -81,9 +83,9 @@ void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t num_la
rb.Push(static_cast<u32>(num_language_codes));
}
-void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_size) {
+void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_entries) {
const std::size_t requested_amount = ctx.GetWriteBufferSize() / sizeof(LanguageCode);
- const std::size_t copy_amount = std::min(requested_amount, max_size);
+ const std::size_t copy_amount = std::min(requested_amount, max_entries);
const std::size_t copy_size = copy_amount * sizeof(LanguageCode);
ctx.WriteBuffer(available_language_codes.data(), copy_size);
@@ -118,7 +120,7 @@ LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
- GetAvailableLanguageCodesImpl(ctx, pre4_0_0_max_entries);
+ GetAvailableLanguageCodesImpl(ctx, PRE_4_0_0_MAX_ENTRIES);
}
void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
@@ -140,19 +142,19 @@ void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
void SET::GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
- GetAvailableLanguageCodesImpl(ctx, post4_0_0_max_entries);
+ GetAvailableLanguageCodesImpl(ctx, POST_4_0_0_MAX_ENTRIES);
}
void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
- PushResponseLanguageCode(ctx, pre4_0_0_max_entries);
+ PushResponseLanguageCode(ctx, PRE_4_0_0_MAX_ENTRIES);
}
void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
- PushResponseLanguageCode(ctx, post4_0_0_max_entries);
+ PushResponseLanguageCode(ctx, POST_4_0_0_MAX_ENTRIES);
}
void 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 {
ES_419 = 0x00003931342D7365,
ZH_HANS = 0x00736E61482D687A,
ZH_HANT = 0x00746E61482D687A,
+ PT_BR = 0x00000052422D7470,
};
LanguageCode GetLanguageCodeFromIndex(std::size_t idx);
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:
[[nodiscard]] IR::Inst* Inst() const;
[[nodiscard]] IR::Inst* InstRecursive() const;
+ [[nodiscard]] IR::Inst* TryInstRecursive() const;
[[nodiscard]] IR::Value Resolve() const;
[[nodiscard]] IR::Reg Reg() const;
[[nodiscard]] IR::Pred Pred() const;
@@ -308,6 +309,13 @@ inline IR::Inst* Value::InstRecursive() const {
return inst;
}
+inline IR::Inst* Value::TryInstRecursive() const {
+ if (IsIdentity()) {
+ return inst->Arg(0).TryInstRecursive();
+ }
+ return type == Type::Opaque ? inst : nullptr;
+}
+
inline IR::Value Value::Resolve() const {
if (IsIdentity()) {
return inst->Arg(0).Resolve();
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 @@
// Refer to the license.txt file included.
#include <algorithm>
+#include <functional>
#include <tuple>
#include <type_traits>
@@ -88,6 +89,26 @@ bool FoldWhenAllImmediates(IR::Inst& inst, Func&& func) {
return true;
}
+/// Return true when all values in a range are equal
+template <typename Range>
+bool AreEqual(const Range& range) {
+ auto resolver{[](const auto& value) { return value.Resolve(); }};
+ auto equal{[](const IR::Value& lhs, const IR::Value& rhs) {
+ if (lhs == rhs) {
+ return true;
+ }
+ // Not equal, but try to match if they read the same constant buffer
+ if (!lhs.IsImmediate() && !rhs.IsImmediate() &&
+ lhs.Inst()->GetOpcode() == IR::Opcode::GetCbufU32 &&
+ rhs.Inst()->GetOpcode() == IR::Opcode::GetCbufU32 &&
+ lhs.Inst()->Arg(0) == rhs.Inst()->Arg(0) && lhs.Inst()->Arg(1) == rhs.Inst()->Arg(1)) {
+ return true;
+ }
+ return false;
+ }};
+ return std::ranges::adjacent_find(range, std::not_fn(equal), resolver) == std::end(range);
+}
+
void FoldGetRegister(IR::Inst& inst) {
if (inst.Arg(0).Reg() == IR::Reg::RZ) {
inst.ReplaceUsesWith(IR::Value{u32{0}});
@@ -100,6 +121,157 @@ void FoldGetPred(IR::Inst& inst) {
}
}
+/// Replaces the XMAD pattern generated by an integer FMA
+bool FoldXmadMultiplyAdd(IR::Block& block, IR::Inst& inst) {
+ /*
+ * We are looking for this specific pattern:
+ * %6 = BitFieldUExtract %op_b, #0, #16
+ * %7 = BitFieldUExtract %op_a', #16, #16
+ * %8 = IMul32 %6, %7
+ * %10 = BitFieldUExtract %op_a', #0, #16
+ * %11 = BitFieldInsert %8, %10, #16, #16
+ * %15 = BitFieldUExtract %op_b, #0, #16
+ * %16 = BitFieldUExtract %op_a, #0, #16
+ * %17 = IMul32 %15, %16
+ * %18 = IAdd32 %17, %op_c
+ * %22 = BitFieldUExtract %op_b, #16, #16
+ * %23 = BitFieldUExtract %11, #16, #16
+ * %24 = IMul32 %22, %23
+ * %25 = ShiftLeftLogical32 %24, #16
+ * %26 = ShiftLeftLogical32 %11, #16
+ * %27 = IAdd32 %26, %18
+ * %result = IAdd32 %25, %27
+ *
+ * And replace it with:
+ * %temp = IMul32 %op_a, %op_b
+ * %result = IAdd32 %temp, %op_c
+ *
+ * This optimization has been proven safe by Nvidia's compiler logic being reversed.
+ * (If Nvidia generates this code from 'fma(a, b, c)', we can do the same in the reverse order.)
+ */
+ const IR::Value zero{0u};
+ const IR::Value sixteen{16u};
+ IR::Inst* const _25{inst.Arg(0).TryInstRecursive()};
+ IR::Inst* const _27{inst.Arg(1).TryInstRecursive()};
+ if (!_25 || !_27) {
+ return false;
+ }
+ if (_27->GetOpcode() != IR::Opcode::IAdd32) {
+ return false;
+ }
+ if (_25->GetOpcode() != IR::Opcode::ShiftLeftLogical32 || _25->Arg(1) != sixteen) {
+ return false;
+ }
+ IR::Inst* const _24{_25->Arg(0).TryInstRecursive()};
+ if (!_24 || _24->GetOpcode() != IR::Opcode::IMul32) {
+ return false;
+ }
+ IR::Inst* const _22{_24->Arg(0).TryInstRecursive()};
+ IR::Inst* const _23{_24->Arg(1).TryInstRecursive()};
+ if (!_22 || !_23) {
+ return false;
+ }
+ if (_22->GetOpcode() != IR::Opcode::BitFieldUExtract) {
+ return false;
+ }
+ if (_23->GetOpcode() != IR::Opcode::BitFieldUExtract) {
+ return false;
+ }
+ if (_22->Arg(1) != sixteen || _22->Arg(2) != sixteen) {
+ return false;
+ }
+ if (_23->Arg(1) != sixteen || _23->Arg(2) != sixteen) {
+ return false;
+ }
+ IR::Inst* const _11{_23->Arg(0).TryInstRecursive()};
+ if (!_11 || _11->GetOpcode() != IR::Opcode::BitFieldInsert) {
+ return false;
+ }
+ if (_11->Arg(2) != sixteen || _11->Arg(3) != sixteen) {
+ return false;
+ }
+ IR::Inst* const _8{_11->Arg(0).TryInstRecursive()};
+ IR::Inst* const _10{_11->Arg(1).TryInstRecursive()};
+ if (!_8 || !_10) {
+ return false;
+ }
+ if (_8->GetOpcode() != IR::Opcode::IMul32) {
+ return false;
+ }
+ if (_10->GetOpcode() != IR::Opcode::BitFieldUExtract) {
+ return false;
+ }
+ IR::Inst* const _6{_8->Arg(0).TryInstRecursive()};
+ IR::Inst* const _7{_8->Arg(1).TryInstRecursive()};
+ if (!_6 || !_7) {
+ return false;
+ }
+ if (_6->GetOpcode() != IR::Opcode::BitFieldUExtract) {
+ return false;
+ }
+ if (_7->GetOpcode() != IR::Opcode::BitFieldUExtract) {
+ return false;
+ }
+ if (_6->Arg(1) != zero || _6->Arg(2) != sixteen) {
+ return false;
+ }
+ if (_7->Arg(1) != sixteen || _7->Arg(2) != sixteen) {
+ return false;
+ }
+ IR::Inst* const _26{_27->Arg(0).TryInstRecursive()};
+ IR::Inst* const _18{_27->Arg(1).TryInstRecursive()};
+ if (!_26 || !_18) {
+ return false;
+ }
+ if (_26->GetOpcode() != IR::Opcode::ShiftLeftLogical32 || _26->Arg(1) != sixteen) {
+ return false;
+ }
+ if (_26->Arg(0).InstRecursive() != _11) {
+ return false;
+ }
+ if (_18->GetOpcode() != IR::Opcode::IAdd32) {
+ return false;
+ }
+ IR::Inst* const _17{_18->Arg(0).TryInstRecursive()};
+ if (!_17 || _17->GetOpcode() != IR::Opcode::IMul32) {
+ return false;
+ }
+ IR::Inst* const _15{_17->Arg(0).TryInstRecursive()};
+ IR::Inst* const _16{_17->Arg(1).TryInstRecursive()};
+ if (!_15 || !_16) {
+ return false;
+ }
+ if (_15->GetOpcode() != IR::Opcode::BitFieldUExtract) {
+ return false;
+ }
+ if (_16->GetOpcode() != IR::Opcode::BitFieldUExtract) {
+ return false;
+ }
+ if (_15->Arg(1) != zero || _16->Arg(1) != zero || _10->Arg(1) != zero) {
+ return false;
+ }
+ if (_15->Arg(2) != sixteen || _16->Arg(2) != sixteen || _10->Arg(2) != sixteen) {
+ return false;
+ }
+ const std::array<IR::Value, 3> op_as{
+ _7->Arg(0).Resolve(),
+ _16->Arg(0).Resolve(),
+ _10->Arg(0).Resolve(),
+ };
+ const std::array<IR::Value, 3> op_bs{
+ _22->Arg(0).Resolve(),
+ _6->Arg(0).Resolve(),
+ _15->Arg(0).Resolve(),
+ };
+ const IR::U32 op_c{_18->Arg(1)};
+ if (!AreEqual(op_as) || !AreEqual(op_bs)) {
+ return false;
+ }
+ IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
+ inst.ReplaceUsesWith(ir.IAdd(ir.IMul(IR::U32{op_as[0]}, IR::U32{op_bs[1]}), op_c));
+ return true;
+}
+
/// Replaces the pattern generated by two XMAD multiplications
bool FoldXmadMultiply(IR::Block& block, IR::Inst& inst) {
/*
@@ -116,33 +288,31 @@ bool FoldXmadMultiply(IR::Block& block, IR::Inst& inst) {
*
* This optimization has been proven safe by LLVM and MSVC.
*/
- const IR::Value lhs_arg{inst.Arg(0)};
- const IR::Value rhs_arg{inst.Arg(1)};
- if (lhs_arg.IsImmediate() || rhs_arg.IsImmediate()) {
+ IR::Inst* const lhs_shl{inst.Arg(0).TryInstRecursive()};
+ IR::Inst* const rhs_mul{inst.Arg(1).TryInstRecursive()};
+ if (!lhs_shl || !rhs_mul) {
return false;
}
- IR::Inst* const lhs_shl{lhs_arg.InstRecursive()};
if (lhs_shl->GetOpcode() != IR::Opcode::ShiftLeftLogical32 ||
lhs_shl->Arg(1) != IR::Value{16U}) {
return false;
}
- if (lhs_shl->Arg(0).IsImmediate()) {
+ IR::Inst* const lhs_mul{lhs_shl->Arg(0).TryInstRecursive()};
+ if (!lhs_mul) {
return false;
}
- IR::Inst* const lhs_mul{lhs_shl->Arg(0).InstRecursive()};
- IR::Inst* const rhs_mul{rhs_arg.InstRecursive()};
if (lhs_mul->GetOpcode() != IR::Opcode::IMul32 || rhs_mul->GetOpcode() != IR::Opcode::IMul32) {
return false;
}
- if (lhs_mul->Arg(1).Resolve() != rhs_mul->Arg(1).Resolve()) {
+ const IR::U32 factor_b{lhs_mul->Arg(1)};
+ if (factor_b.Resolve() != rhs_mul->Arg(1).Resolve()) {
return false;
}
- const IR::U32 factor_b{lhs_mul->Arg(1)};
- if (lhs_mul->Arg(0).IsImmediate() || rhs_mul->Arg(0).IsImmediate()) {
+ IR::Inst* const lhs_bfe{lhs_mul->Arg(0).TryInstRecursive()};
+ IR::Inst* const rhs_bfe{rhs_mul->Arg(0).TryInstRecursive()};
+ if (!lhs_bfe || !rhs_bfe) {
return false;
}
- IR::Inst* const lhs_bfe{lhs_mul->Arg(0).InstRecursive()};
- IR::Inst* const rhs_bfe{rhs_mul->Arg(0).InstRecursive()};
if (lhs_bfe->GetOpcode() != IR::Opcode::BitFieldUExtract) {
return false;
}
@@ -155,10 +325,10 @@ bool FoldXmadMultiply(IR::Block& block, IR::Inst& inst) {
if (rhs_bfe->Arg(1) != IR::Value{0U} || rhs_bfe->Arg(2) != IR::Value{16U}) {
return false;
}
- if (lhs_bfe->Arg(0).Resolve() != rhs_bfe->Arg(0).Resolve()) {
+ const IR::U32 factor_a{lhs_bfe->Arg(0)};
+ if (factor_a.Resolve() != rhs_bfe->Arg(0).Resolve()) {
return false;
}
- const IR::U32 factor_a{lhs_bfe->Arg(0)};
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
inst.ReplaceUsesWith(ir.IMul(factor_a, factor_b));
return true;
@@ -181,6 +351,9 @@ void FoldAdd(IR::Block& block, IR::Inst& inst) {
if (FoldXmadMultiply(block, inst)) {
return;
}
+ if (FoldXmadMultiplyAdd(block, inst)) {
+ return;
+ }
}
}
@@ -476,6 +649,10 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) {
return FoldInverseFunc(inst, IR::Opcode::UnpackHalf2x16);
case IR::Opcode::UnpackHalf2x16:
return FoldInverseFunc(inst, IR::Opcode::PackHalf2x16);
+ case IR::Opcode::PackFloat2x16:
+ return FoldInverseFunc(inst, IR::Opcode::UnpackFloat2x16);
+ case IR::Opcode::UnpackFloat2x16:
+ return FoldInverseFunc(inst, IR::Opcode::PackFloat2x16);
case IR::Opcode::SelectU1:
case IR::Opcode::SelectU8:
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
renderer_vulkan/maxwell_to_vk.cpp
renderer_vulkan/maxwell_to_vk.h
renderer_vulkan/pipeline_helper.h
+ renderer_vulkan/pipeline_statistics.cpp
+ renderer_vulkan/pipeline_statistics.h
renderer_vulkan/renderer_vulkan.h
renderer_vulkan/renderer_vulkan.cpp
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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <string_view>
+
+#include <fmt/format.h>
+
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "video_core/renderer_vulkan/pipeline_statistics.h"
+#include "video_core/vulkan_common/vulkan_device.h"
+#include "video_core/vulkan_common/vulkan_wrapper.h"
+
+namespace Vulkan {
+
+using namespace std::string_view_literals;
+
+static u64 GetUint64(const VkPipelineExecutableStatisticKHR& statistic) {
+ switch (statistic.format) {
+ case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_INT64_KHR:
+ return static_cast<u64>(statistic.value.i64);
+ case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR:
+ return statistic.value.u64;
+ case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_FLOAT64_KHR:
+ return static_cast<u64>(statistic.value.f64);
+ default:
+ return 0;
+ }
+}
+
+PipelineStatistics::PipelineStatistics(const Device& device_) : device{device_} {}
+
+void PipelineStatistics::Collect(VkPipeline pipeline) {
+ const auto& dev{device.GetLogical()};
+ const std::vector properties{dev.GetPipelineExecutablePropertiesKHR(pipeline)};
+ const u32 num_executables{static_cast<u32>(properties.size())};
+ for (u32 executable = 0; executable < num_executables; ++executable) {
+ const auto statistics{dev.GetPipelineExecutableStatisticsKHR(pipeline, executable)};
+ if (statistics.empty()) {
+ continue;
+ }
+ Stats stage_stats;
+ for (const auto& statistic : statistics) {
+ const char* const name{statistic.name};
+ if (name == "Binary Size"sv || name == "Code size"sv || name == "Instruction Count"sv) {
+ stage_stats.code_size = GetUint64(statistic);
+ } else if (name == "Register Count"sv) {
+ stage_stats.register_count = GetUint64(statistic);
+ } else if (name == "SGPRs"sv || name == "numUsedSgprs"sv) {
+ stage_stats.sgpr_count = GetUint64(statistic);
+ } else if (name == "VGPRs"sv || name == "numUsedVgprs"sv) {
+ stage_stats.vgpr_count = GetUint64(statistic);
+ } else if (name == "Branches"sv) {
+ stage_stats.branches_count = GetUint64(statistic);
+ } else if (name == "Basic Block Count"sv) {
+ stage_stats.basic_block_count = GetUint64(statistic);
+ }
+ }
+ std::lock_guard lock{mutex};
+ collected_stats.push_back(stage_stats);
+ }
+}
+
+void PipelineStatistics::Report() const {
+ double num{};
+ Stats total;
+ {
+ std::lock_guard lock{mutex};
+ for (const Stats& stats : collected_stats) {
+ total.code_size += stats.code_size;
+ total.register_count += stats.register_count;
+ total.sgpr_count += stats.sgpr_count;
+ total.vgpr_count += stats.vgpr_count;
+ total.branches_count += stats.branches_count;
+ total.basic_block_count += stats.basic_block_count;
+ }
+ num = static_cast<double>(collected_stats.size());
+ }
+ std::string report;
+ const auto add = [&](const char* fmt, u64 value) {
+ if (value > 0) {
+ report += fmt::format(fmt::runtime(fmt), static_cast<double>(value) / num);
+ }
+ };
+ add("Code size: {:9.03f}\n", total.code_size);
+ add("Register count: {:9.03f}\n", total.register_count);
+ add("SGPRs: {:9.03f}\n", total.sgpr_count);
+ add("VGPRs: {:9.03f}\n", total.vgpr_count);
+ add("Branches count: {:9.03f}\n", total.branches_count);
+ add("Basic blocks: {:9.03f}\n", total.basic_block_count);
+
+ LOG_INFO(Render_Vulkan,
+ "\nAverage pipeline statistics\n"
+ "==========================================\n"
+ "{}\n",
+ report);
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <mutex>
+#include <vector>
+
+#include "common/common_types.h"
+#include "video_core/vulkan_common/vulkan_wrapper.h"
+
+namespace Vulkan {
+
+class Device;
+
+class PipelineStatistics {
+public:
+ explicit PipelineStatistics(const Device& device_);
+
+ void Collect(VkPipeline pipeline);
+
+ void Report() const;
+
+private:
+ struct Stats {
+ u64 code_size{};
+ u64 register_count{};
+ u64 sgpr_count{};
+ u64 vgpr_count{};
+ u64 branches_count{};
+ u64 basic_block_count{};
+ };
+
+ const Device& device;
+ mutable std::mutex mutex;
+ std::vector<Stats> collected_stats;
+};
+
+} // 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 @@
#include <boost/container/small_vector.hpp>
#include "video_core/renderer_vulkan/pipeline_helper.h"
+#include "video_core/renderer_vulkan/pipeline_statistics.h"
#include "video_core/renderer_vulkan/vk_buffer_cache.h"
#include "video_core/renderer_vulkan/vk_compute_pipeline.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
@@ -26,6 +27,7 @@ using Tegra::Texture::TexturePair;
ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descriptor_pool,
VKUpdateDescriptorQueue& update_descriptor_queue_,
Common::ThreadWorker* thread_worker,
+ PipelineStatistics* pipeline_statistics,
VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_,
vk::ShaderModule spv_module_)
: device{device_}, update_descriptor_queue{update_descriptor_queue_}, info{info_},
@@ -36,7 +38,7 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
std::copy_n(info.constant_buffer_used_sizes.begin(), uniform_buffer_sizes.size(),
uniform_buffer_sizes.begin());
- auto func{[this, &descriptor_pool, shader_notify] {
+ auto func{[this, &descriptor_pool, shader_notify, pipeline_statistics] {
DescriptorLayoutBuilder builder{device};
builder.Add(info, VK_SHADER_STAGE_COMPUTE_BIT);
@@ -50,10 +52,14 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
.pNext = nullptr,
.requiredSubgroupSize = GuestWarpSize,
};
+ VkPipelineCreateFlags flags{};
+ if (device.IsKhrPipelineEexecutablePropertiesEnabled()) {
+ flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
+ }
pipeline = device.GetLogical().CreateComputePipeline({
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
.pNext = nullptr,
- .flags = 0,
+ .flags = flags,
.stage{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.pNext = device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr,
@@ -67,6 +73,9 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
.basePipelineHandle = 0,
.basePipelineIndex = 0,
});
+ if (pipeline_statistics) {
+ pipeline_statistics->Collect(*pipeline);
+ }
std::lock_guard lock{build_mutex};
is_built = true;
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;
namespace Vulkan {
class Device;
+class PipelineStatistics;
class VKScheduler;
class ComputePipeline {
@@ -32,6 +33,7 @@ public:
explicit ComputePipeline(const Device& device, DescriptorPool& descriptor_pool,
VKUpdateDescriptorQueue& update_descriptor_queue,
Common::ThreadWorker* thread_worker,
+ PipelineStatistics* pipeline_statistics,
VideoCore::ShaderNotify* shader_notify, const Shader::Info& info,
vk::ShaderModule spv_module);
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 @@
#include "common/bit_field.h"
#include "video_core/renderer_vulkan/maxwell_to_vk.h"
#include "video_core/renderer_vulkan/pipeline_helper.h"
+#include "video_core/renderer_vulkan/pipeline_statistics.h"
#include "video_core/renderer_vulkan/vk_buffer_cache.h"
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
#include "video_core/renderer_vulkan/vk_render_pass_cache.h"
@@ -217,8 +218,8 @@ GraphicsPipeline::GraphicsPipeline(
VKScheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_,
VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool,
VKUpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread,
- RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key_,
- std::array<vk::ShaderModule, NUM_STAGES> stages,
+ PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache,
+ const GraphicsPipelineCacheKey& key_, std::array<vk::ShaderModule, NUM_STAGES> stages,
const std::array<const Shader::Info*, NUM_STAGES>& infos)
: key{key_}, maxwell3d{maxwell3d_}, gpu_memory{gpu_memory_}, device{device_},
texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, scheduler{scheduler_},
@@ -235,7 +236,7 @@ GraphicsPipeline::GraphicsPipeline(
enabled_uniform_buffer_masks[stage] = info->constant_buffer_mask;
std::ranges::copy(info->constant_buffer_used_sizes, uniform_buffer_sizes[stage].begin());
}
- auto func{[this, shader_notify, &render_pass_cache, &descriptor_pool] {
+ auto func{[this, shader_notify, &render_pass_cache, &descriptor_pool, pipeline_statistics] {
DescriptorLayoutBuilder builder{MakeBuilder(device, stage_infos)};
uses_push_descriptor = builder.CanUsePushDescriptor();
descriptor_set_layout = builder.CreateDescriptorSetLayout(uses_push_descriptor);
@@ -250,6 +251,9 @@ GraphicsPipeline::GraphicsPipeline(
const VkRenderPass render_pass{render_pass_cache.Get(MakeRenderPassKey(key.state))};
Validate();
MakePipeline(render_pass);
+ if (pipeline_statistics) {
+ pipeline_statistics->Collect(*pipeline);
+ }
std::lock_guard lock{build_mutex};
is_built = true;
@@ -782,10 +786,14 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
}
*/
}
+ VkPipelineCreateFlags flags{};
+ if (device.IsKhrPipelineEexecutablePropertiesEnabled()) {
+ flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
+ }
pipeline = device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
- .flags = 0,
+ .flags = flags,
.stageCount = static_cast<u32>(shader_stages.size()),
.pStages = shader_stages.data(),
.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> {
namespace Vulkan {
class Device;
+class PipelineStatistics;
class RenderPassCache;
class VKScheduler;
class VKUpdateDescriptorQueue;
@@ -73,8 +74,9 @@ public:
VKScheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache,
VideoCore::ShaderNotify* shader_notify, const Device& device,
DescriptorPool& descriptor_pool, VKUpdateDescriptorQueue& update_descriptor_queue,
- Common::ThreadWorker* worker_thread, RenderPassCache& render_pass_cache,
- const GraphicsPipelineCacheKey& key, std::array<vk::ShaderModule, NUM_STAGES> stages,
+ Common::ThreadWorker* worker_thread, PipelineStatistics* pipeline_statistics,
+ RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key,
+ std::array<vk::ShaderModule, NUM_STAGES> stages,
const std::array<const Shader::Info*, NUM_STAGES>& infos);
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 @@
#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
#include "video_core/renderer_vulkan/maxwell_to_vk.h"
#include "video_core/renderer_vulkan/pipeline_helper.h"
+#include "video_core/renderer_vulkan/pipeline_statistics.h"
#include "video_core/renderer_vulkan/vk_compute_pipeline.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
@@ -389,15 +390,19 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
size_t total{};
size_t built{};
bool has_loaded{};
+ std::unique_ptr<PipelineStatistics> statistics;
} state;
+ if (device.IsKhrPipelineEexecutablePropertiesEnabled()) {
+ state.statistics = std::make_unique<PipelineStatistics>(device);
+ }
const auto load_compute{[&](std::ifstream& file, FileEnvironment env) {
ComputePipelineCacheKey key;
file.read(reinterpret_cast<char*>(&key), sizeof(key));
workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable {
ShaderPools pools;
- auto pipeline{CreateComputePipeline(pools, key, env, false)};
+ auto pipeline{CreateComputePipeline(pools, key, env, state.statistics.get(), false)};
std::lock_guard lock{state.mutex};
if (pipeline) {
compute_cache.emplace(key, std::move(pipeline));
@@ -425,7 +430,8 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
for (auto& env : envs) {
env_ptrs.push_back(&env);
}
- auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs), false)};
+ auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs),
+ state.statistics.get(), false)};
std::lock_guard lock{state.mutex};
graphics_cache.emplace(key, std::move(pipeline));
@@ -445,6 +451,10 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
lock.unlock();
workers.WaitForRequests();
+
+ if (state.statistics) {
+ state.statistics->Report();
+ }
}
GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() {
@@ -486,7 +496,8 @@ GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const
std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
ShaderPools& pools, const GraphicsPipelineCacheKey& key,
- std::span<Shader::Environment* const> envs, bool build_in_parallel) try {
+ std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
+ bool build_in_parallel) try {
LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
size_t env_index{0};
std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
@@ -540,7 +551,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
return std::make_unique<GraphicsPipeline>(
maxwell3d, gpu_memory, scheduler, buffer_cache, texture_cache, &shader_notify, device,
- descriptor_pool, update_descriptor_queue, thread_worker, render_pass_cache, key,
+ descriptor_pool, update_descriptor_queue, thread_worker, statistics, render_pass_cache, key,
std::move(modules), infos);
} catch (const Shader::Exception& exception) {
@@ -553,7 +564,8 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
GetGraphicsEnvironments(environments, graphics_key.unique_hashes);
main_pools.ReleaseContents();
- auto pipeline{CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), true)};
+ auto pipeline{
+ CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), nullptr, true)};
if (!pipeline || pipeline_cache_filename.empty()) {
return pipeline;
}
@@ -578,7 +590,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
env.SetCachedSize(shader->size_bytes);
main_pools.ReleaseContents();
- auto pipeline{CreateComputePipeline(main_pools, key, env, true)};
+ auto pipeline{CreateComputePipeline(main_pools, key, env, nullptr, true)};
if (!pipeline || pipeline_cache_filename.empty()) {
return pipeline;
}
@@ -591,7 +603,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env,
- bool build_in_parallel) try {
+ PipelineStatistics* statistics, bool build_in_parallel) try {
LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
@@ -605,8 +617,8 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
}
Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
return std::make_unique<ComputePipeline>(device, descriptor_pool, update_descriptor_queue,
- thread_worker, &shader_notify, program.info,
- std::move(spv_module));
+ thread_worker, statistics, &shader_notify,
+ program.info, std::move(spv_module));
} catch (const Shader::Exception& exception) {
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> {
namespace Vulkan {
class ComputePipeline;
-class Device;
class DescriptorPool;
+class Device;
+class PipelineStatistics;
class RasterizerVulkan;
class RenderPassCache;
class VKScheduler;
@@ -128,7 +129,8 @@ private:
std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline(
ShaderPools& pools, const GraphicsPipelineCacheKey& key,
- std::span<Shader::Environment* const> envs, bool build_in_parallel);
+ std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
+ bool build_in_parallel);
std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineCacheKey& key,
const ShaderInfo* shader);
@@ -136,6 +138,7 @@ private:
std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderPools& pools,
const ComputePipelineCacheKey& key,
Shader::Environment& env,
+ PipelineStatistics* statistics,
bool build_in_parallel);
const Device& device;
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
SetNext(next, workgroup_layout);
}
+ VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties;
+ if (khr_pipeline_executable_properties) {
+ LOG_INFO(Render_Vulkan, "Enabling shader feedback, expect slower shader build times");
+ executable_properties = {
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR,
+ .pNext = nullptr,
+ .pipelineExecutableInfo = VK_TRUE,
+ };
+ SetNext(next, executable_properties);
+ }
+
if (!ext_depth_range_unrestricted) {
LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
}
@@ -824,6 +835,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
bool has_khr_shader_float16_int8{};
bool has_khr_workgroup_memory_explicit_layout{};
+ bool has_khr_pipeline_executable_properties{};
bool has_ext_subgroup_size_control{};
bool has_ext_transform_feedback{};
bool has_ext_custom_border_color{};
@@ -878,6 +890,10 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME,
true);
}
+ if (Settings::values.renderer_shader_feedback) {
+ test(has_khr_pipeline_executable_properties,
+ VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME, false);
+ }
}
VkPhysicalDeviceFeatures2KHR features{};
features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
@@ -1033,6 +1049,19 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
khr_workgroup_memory_explicit_layout = true;
}
}
+ if (has_khr_pipeline_executable_properties) {
+ VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties;
+ executable_properties.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR;
+ executable_properties.pNext = nullptr;
+ features.pNext = &executable_properties;
+ physical.GetFeatures2KHR(features);
+
+ if (executable_properties.pipelineExecutableInfo) {
+ extensions.push_back(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
+ khr_pipeline_executable_properties = true;
+ }
+ }
if (khr_push_descriptor) {
VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor;
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:
return khr_push_descriptor;
}
+ /// Returns true if VK_KHR_pipeline_executable_properties is enabled.
+ bool IsKhrPipelineEexecutablePropertiesEnabled() const {
+ return khr_pipeline_executable_properties;
+ }
+
/// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout.
bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const {
return khr_workgroup_memory_explicit_layout;
@@ -378,6 +383,7 @@ private:
bool khr_spirv_1_4{}; ///< Support for VK_KHR_spirv_1_4.
bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts.
bool khr_push_descriptor{}; ///< Support for VK_KHR_push_descritor.
+ bool khr_pipeline_executable_properties{}; ///< Support for executable properties.
bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax.
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 {
X(vkGetMemoryWin32HandleKHR);
#endif
X(vkGetQueryPoolResults);
+ X(vkGetPipelineExecutablePropertiesKHR);
+ X(vkGetPipelineExecutableStatisticsKHR);
X(vkGetSemaphoreCounterValueKHR);
X(vkMapMemory);
X(vkQueueSubmit);
@@ -809,6 +811,42 @@ VkMemoryRequirements Device::GetImageMemoryRequirements(VkImage image) const noe
return requirements;
}
+std::vector<VkPipelineExecutablePropertiesKHR> Device::GetPipelineExecutablePropertiesKHR(
+ VkPipeline pipeline) const {
+ const VkPipelineInfoKHR info{
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_INFO_KHR,
+ .pNext = nullptr,
+ .pipeline = pipeline,
+ };
+ u32 num{};
+ dld->vkGetPipelineExecutablePropertiesKHR(handle, &info, &num, nullptr);
+ std::vector<VkPipelineExecutablePropertiesKHR> properties(num);
+ for (auto& property : properties) {
+ property.sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_PROPERTIES_KHR;
+ }
+ Check(dld->vkGetPipelineExecutablePropertiesKHR(handle, &info, &num, properties.data()));
+ return properties;
+}
+
+std::vector<VkPipelineExecutableStatisticKHR> Device::GetPipelineExecutableStatisticsKHR(
+ VkPipeline pipeline, u32 executable_index) const {
+ const VkPipelineExecutableInfoKHR executable_info{
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_INFO_KHR,
+ .pNext = nullptr,
+ .pipeline = pipeline,
+ .executableIndex = executable_index,
+ };
+ u32 num{};
+ dld->vkGetPipelineExecutableStatisticsKHR(handle, &executable_info, &num, nullptr);
+ std::vector<VkPipelineExecutableStatisticKHR> statistics(num);
+ for (auto& statistic : statistics) {
+ statistic.sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_STATISTIC_KHR;
+ }
+ Check(dld->vkGetPipelineExecutableStatisticsKHR(handle, &executable_info, &num,
+ statistics.data()));
+ return statistics;
+}
+
void Device::UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes,
Span<VkCopyDescriptorSet> copies) const noexcept {
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 {
#ifdef _WIN32
PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{};
#endif
+ PFN_vkGetPipelineExecutablePropertiesKHR vkGetPipelineExecutablePropertiesKHR{};
+ PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR{};
PFN_vkGetQueryPoolResults vkGetQueryPoolResults{};
PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR{};
PFN_vkMapMemory vkMapMemory{};
@@ -879,6 +881,12 @@ public:
VkMemoryRequirements GetImageMemoryRequirements(VkImage image) const noexcept;
+ std::vector<VkPipelineExecutablePropertiesKHR> GetPipelineExecutablePropertiesKHR(
+ VkPipeline pipeline) const;
+
+ std::vector<VkPipelineExecutableStatisticKHR> GetPipelineExecutableStatisticsKHR(
+ VkPipeline pipeline, u32 executable_index) const;
+
void UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes,
Span<VkCopyDescriptorSet> copies) const noexcept;
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index ecd5dfac1..f3b8787f5 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -825,6 +825,7 @@ void Config::ReadRendererValues() {
if (global) {
ReadBasicSetting(Settings::values.fps_cap);
ReadBasicSetting(Settings::values.renderer_debug);
+ ReadBasicSetting(Settings::values.renderer_shader_feedback);
ReadBasicSetting(Settings::values.enable_nsight_aftermath);
ReadBasicSetting(Settings::values.disable_shader_loop_safety_checks);
}
@@ -1363,6 +1364,7 @@ void Config::SaveRendererValues() {
if (global) {
WriteBasicSetting(Settings::values.fps_cap);
WriteBasicSetting(Settings::values.renderer_debug);
+ WriteBasicSetting(Settings::values.renderer_shader_feedback);
WriteBasicSetting(Settings::values.enable_nsight_aftermath);
WriteBasicSetting(Settings::values.disable_shader_loop_safety_checks);
}
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() {
ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue());
ui->enable_graphics_debugging->setEnabled(runtime_lock);
ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue());
+ ui->enable_shader_feedback->setEnabled(runtime_lock);
+ ui->enable_shader_feedback->setChecked(Settings::values.renderer_shader_feedback.GetValue());
ui->enable_cpu_debugging->setEnabled(runtime_lock);
ui->enable_cpu_debugging->setChecked(Settings::values.cpu_debug_mode.GetValue());
ui->enable_nsight_aftermath->setEnabled(runtime_lock);
@@ -65,6 +67,7 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked();
Settings::values.use_auto_stub = ui->use_auto_stub->isChecked();
Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
+ Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked();
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 @@
<property name="title">
<string>Graphics</string>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_6">
- <item>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="0">
<widget class="QCheckBox" name="enable_graphics_debugging">
<property name="enabled">
<bool>true</bool>
@@ -125,7 +125,7 @@
</property>
</widget>
</item>
- <item>
+ <item row="2" column="0">
<widget class="QCheckBox" name="enable_nsight_aftermath">
<property name="toolTip">
<string>When checked, it enables Nsight Aftermath crash dumps</string>
@@ -135,7 +135,7 @@
</property>
</widget>
</item>
- <item>
+ <item row="0" column="1">
<widget class="QCheckBox" name="disable_macro_jit">
<property name="enabled">
<bool>true</bool>
@@ -148,7 +148,17 @@
</property>
</widget>
</item>
- <item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="enable_shader_feedback">
+ <property name="toolTip">
+ <string>When checked, yuzu will log statistics about the compiled pipeline cache</string>
+ </property>
+ <property name="text">
+ <string>Enable Shader Feedback</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
<widget class="QCheckBox" name="disable_loop_safety_checks">
<property name="toolTip">
<string>When checked, it executes shaders without loop logic changes</string>
@@ -276,11 +286,14 @@
<tabstop>open_log_button</tabstop>
<tabstop>homebrew_args_edit</tabstop>
<tabstop>enable_graphics_debugging</tabstop>
+ <tabstop>enable_shader_feedback</tabstop>
<tabstop>enable_nsight_aftermath</tabstop>
<tabstop>disable_macro_jit</tabstop>
<tabstop>disable_loop_safety_checks</tabstop>
+ <tabstop>fs_access_log</tabstop>
<tabstop>reporting_services</tabstop>
<tabstop>quest_flag</tabstop>
+ <tabstop>enable_cpu_debugging</tabstop>
<tabstop>use_debug_asserts</tabstop>
<tabstop>use_auto_stub</tabstop>
</tabstops>
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 @@
<string>Traditional Chinese (正體中文)</string>
</property>
</item>
+ <item>
+ <property name="text">
+ <string>Brazilian Portuguese (português do Brasil)</string>
+ </property>
+ </item>
</widget>
</item>
<item row="5" column="0">
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() {
// Renderer
ReadSetting("Renderer", Settings::values.renderer_backend);
ReadSetting("Renderer", Settings::values.renderer_debug);
+ ReadSetting("Renderer", Settings::values.renderer_shader_feedback);
ReadSetting("Renderer", Settings::values.enable_nsight_aftermath);
ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks);
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 =
# 0 (default): Disabled, 1: Enabled
debug =
+# Enable shader feedback.
+# 0 (default): Disabled, 1: Enabled
+renderer_shader_feedback =
+
# Enable Nsight Aftermath crash dumps
# 0 (default): Disabled, 1: Enabled
nsight_aftermath =
@@ -363,7 +367,7 @@ custom_rtc =
# Sets the systems language index
# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French,
-# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese
+# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese
language_index =
# 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 ea3e0ada4..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 @@
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
#include "yuzu_cmd/yuzu_icon.h"
-EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_)
- : input_subsystem{input_subsystem_} {
+EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_)
+ : input_subsystem{input_subsystem_}, system{system_} {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
exit(1);
@@ -218,7 +218,7 @@ void EmuWindow_SDL2::WaitEvent() {
const u32 current_time = SDL_GetTicks();
if (current_time > last_time + 2000) {
- const auto results = Core::System::GetInstance().GetAndResetPerfStats();
+ const auto results = system.GetAndResetPerfStats();
const auto title =
fmt::format("yuzu {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname,
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;
class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
public:
- explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem);
+ explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem, Core::System& system_);
~EmuWindow_SDL2();
/// Whether the window is still open, and a close request hasn't yet been sent
@@ -87,4 +87,7 @@ protected:
/// Input subsystem to use with this window.
InputCommon::InputSubsystem* input_subsystem;
+
+ /// yuzu core instance
+ Core::System& system;
};
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() {
return unsupported_ext.empty();
}
-EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen)
- : EmuWindow_SDL2{input_subsystem} {
+EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem,
+ Core::System& system_, bool fullscreen)
+ : EmuWindow_SDL2{input_subsystem, system_} {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);
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 @@
#include "core/frontend/emu_window.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
+namespace Core {
+class System;
+}
+
namespace InputCommon {
class InputSubsystem;
}
class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {
public:
- explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen);
+ explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, Core::System& system_,
+ bool fullscreen);
~EmuWindow_SDL2_GL();
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 @@
#include <SDL.h>
#include <SDL_syswm.h>
-EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, bool fullscreen)
- : EmuWindow_SDL2{input_subsystem} {
+EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem,
+ Core::System& system_, bool fullscreen)
+ : EmuWindow_SDL2{input_subsystem, system_} {
const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
Common::g_scm_branch, Common::g_scm_desc);
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;
class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
public:
- explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, bool fullscreen);
+ explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, Core::System& system,
+ bool fullscreen);
~EmuWindow_SDL2_VK() override;
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) {
std::unique_ptr<EmuWindow_SDL2> emu_window;
switch (Settings::values.renderer_backend.GetValue()) {
case Settings::RendererBackend::OpenGL:
- emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, fullscreen);
+ emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, system, fullscreen);
break;
case Settings::RendererBackend::Vulkan:
- emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, fullscreen);
+ emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, system, fullscreen);
break;
}