summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/memory_detect.cpp23
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/crypto/key_manager.h13
-rw-r--r--src/core/file_sys/bis_factory.cpp2
-rw-r--r--src/core/file_sys/card_image.cpp4
-rw-r--r--src/core/file_sys/card_image.h2
-rw-r--r--src/core/file_sys/content_archive.cpp5
-rw-r--r--src/core/file_sys/content_archive.h5
-rw-r--r--src/core/file_sys/registered_cache.cpp6
-rw-r--r--src/core/file_sys/registered_cache.h2
-rw-r--r--src/core/file_sys/submission_package.cpp4
-rw-r--r--src/core/file_sys/submission_package.h2
-rw-r--r--src/core/file_sys/xts_archive.h2
-rw-r--r--src/core/hle/service/acc/acc.cpp9
-rw-r--r--src/core/hle/service/acc/acc.h1
-rw-r--r--src/core/hle/service/acc/acc_su.cpp2
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp4
-rw-r--r--src/core/hle/service/acc/acc_u1.cpp2
-rw-r--r--src/core/hle/service/am/am.cpp14
-rw-r--r--src/core/hle/service/es/es.cpp2
-rw-r--r--src/core/hle/service/prepo/prepo.cpp7
-rw-r--r--src/core/hle/service/set/set.cpp72
-rw-r--r--src/core/hle/service/set/set.h2
-rw-r--r--src/input_common/CMakeLists.txt6
-rw-r--r--src/input_common/gcadapter/gc_adapter.cpp379
-rw-r--r--src/input_common/gcadapter/gc_adapter.h160
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp272
-rw-r--r--src/input_common/gcadapter/gc_poller.h67
-rw-r--r--src/input_common/main.cpp24
-rw-r--r--src/input_common/main.h5
-rw-r--r--src/video_core/CMakeLists.txt2
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h6
-rw-r--r--src/video_core/compatible_formats.cpp162
-rw-r--r--src/video_core/compatible_formats.h32
-rw-r--r--src/video_core/macro/macro.cpp35
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp17
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h7
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp20
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp5
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h44
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp115
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.h207
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp144
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp28
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_device.cpp26
-rw-r--r--src/video_core/renderer_vulkan/vk_device.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp89
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp13
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h8
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp276
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h13
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp68
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.h50
-rw-r--r--src/video_core/renderer_vulkan/wrapper.cpp26
-rw-r--r--src/video_core/renderer_vulkan/wrapper.h58
-rw-r--r--src/video_core/shader_cache.h70
-rw-r--r--src/video_core/texture_cache/texture_cache.h25
-rw-r--r--src/yuzu/configuration/config.cpp12
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp60
-rw-r--r--src/yuzu/main.cpp63
-rw-r--r--src/yuzu/main.h6
-rw-r--r--src/yuzu/main.ui12
64 files changed, 2284 insertions, 533 deletions
diff --git a/src/common/memory_detect.cpp b/src/common/memory_detect.cpp
index 3fdc309a2..8cff6ec37 100644
--- a/src/common/memory_detect.cpp
+++ b/src/common/memory_detect.cpp
@@ -9,10 +9,12 @@
9// clang-format on 9// clang-format on
10#else 10#else
11#include <sys/types.h> 11#include <sys/types.h>
12#ifdef __APPLE__ 12#if defined(__APPLE__) || defined(__FreeBSD__)
13#include <sys/sysctl.h> 13#include <sys/sysctl.h>
14#else 14#elif defined(__linux__)
15#include <sys/sysinfo.h> 15#include <sys/sysinfo.h>
16#else
17#include <unistd.h>
16#endif 18#endif
17#endif 19#endif
18 20
@@ -38,15 +40,26 @@ static MemoryInfo Detect() {
38 // hw and vm are defined in sysctl.h 40 // hw and vm are defined in sysctl.h
39 // https://github.com/apple/darwin-xnu/blob/master/bsd/sys/sysctl.h#L471 41 // https://github.com/apple/darwin-xnu/blob/master/bsd/sys/sysctl.h#L471
40 // sysctlbyname(const char *, void *, size_t *, void *, size_t); 42 // sysctlbyname(const char *, void *, size_t *, void *, size_t);
41 sysctlbyname("hw.memsize", &ramsize, &sizeof_ramsize, NULL, 0); 43 sysctlbyname("hw.memsize", &ramsize, &sizeof_ramsize, nullptr, 0);
42 sysctlbyname("vm.swapusage", &vmusage, &sizeof_vmusage, NULL, 0); 44 sysctlbyname("vm.swapusage", &vmusage, &sizeof_vmusage, nullptr, 0);
43 mem_info.TotalPhysicalMemory = ramsize; 45 mem_info.TotalPhysicalMemory = ramsize;
44 mem_info.TotalSwapMemory = vmusage.xsu_total; 46 mem_info.TotalSwapMemory = vmusage.xsu_total;
45#else 47#elif defined(__FreeBSD__)
48 u_long physmem, swap_total;
49 std::size_t sizeof_u_long = sizeof(u_long);
50 // sysctlbyname(const char *, void *, size_t *, const void *, size_t);
51 sysctlbyname("hw.physmem", &physmem, &sizeof_u_long, nullptr, 0);
52 sysctlbyname("vm.swap_total", &swap_total, &sizeof_u_long, nullptr, 0);
53 mem_info.TotalPhysicalMemory = physmem;
54 mem_info.TotalSwapMemory = swap_total;
55#elif defined(__linux__)
46 struct sysinfo meminfo; 56 struct sysinfo meminfo;
47 sysinfo(&meminfo); 57 sysinfo(&meminfo);
48 mem_info.TotalPhysicalMemory = meminfo.totalram; 58 mem_info.TotalPhysicalMemory = meminfo.totalram;
49 mem_info.TotalSwapMemory = meminfo.totalswap; 59 mem_info.TotalSwapMemory = meminfo.totalswap;
60#else
61 mem_info.TotalPhysicalMemory = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE);
62 mem_info.TotalSwapMemory = 0;
50#endif 63#endif
51 64
52 return mem_info; 65 return mem_info;
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index f87d67db5..d1f173f42 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -614,7 +614,7 @@ endif()
614create_target_directory_groups(core) 614create_target_directory_groups(core)
615 615
616target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 616target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
617target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus unicorn zip) 617target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls opus unicorn zip)
618 618
619if (YUZU_ENABLE_BOXCAT) 619if (YUZU_ENABLE_BOXCAT)
620 target_compile_definitions(core PRIVATE -DYUZU_ENABLE_BOXCAT) 620 target_compile_definitions(core PRIVATE -DYUZU_ENABLE_BOXCAT)
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 7265c4171..9269a73f2 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -223,7 +223,16 @@ bool operator<(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
223 223
224class KeyManager { 224class KeyManager {
225public: 225public:
226 KeyManager(); 226 static KeyManager& Instance() {
227 static KeyManager instance;
228 return instance;
229 }
230
231 KeyManager(const KeyManager&) = delete;
232 KeyManager& operator=(const KeyManager&) = delete;
233
234 KeyManager(KeyManager&&) = delete;
235 KeyManager& operator=(KeyManager&&) = delete;
227 236
228 bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; 237 bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
229 bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; 238 bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;
@@ -257,6 +266,8 @@ public:
257 bool AddTicketPersonalized(Ticket raw); 266 bool AddTicketPersonalized(Ticket raw);
258 267
259private: 268private:
269 KeyManager();
270
260 std::map<KeyIndex<S128KeyType>, Key128> s128_keys; 271 std::map<KeyIndex<S128KeyType>, Key128> s128_keys;
261 std::map<KeyIndex<S256KeyType>, Key256> s256_keys; 272 std::map<KeyIndex<S256KeyType>, Key256> s256_keys;
262 273
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 0af44f340..8935a62c3 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -79,7 +79,7 @@ VirtualDir BISFactory::OpenPartition(BisPartitionId id) const {
79} 79}
80 80
81VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const { 81VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const {
82 Core::Crypto::KeyManager keys; 82 auto& keys = Core::Crypto::KeyManager::Instance();
83 Core::Crypto::PartitionDataManager pdm{ 83 Core::Crypto::PartitionDataManager pdm{
84 Core::System::GetInstance().GetFilesystem()->OpenDirectory( 84 Core::System::GetInstance().GetFilesystem()->OpenDirectory(
85 FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)}; 85 FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)};
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 07d0c8d5d..664a47e7f 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -178,7 +178,7 @@ u32 XCI::GetSystemUpdateVersion() {
178 return 0; 178 return 0;
179 179
180 for (const auto& file : update->GetFiles()) { 180 for (const auto& file : update->GetFiles()) {
181 NCA nca{file, nullptr, 0, keys}; 181 NCA nca{file, nullptr, 0};
182 182
183 if (nca.GetStatus() != Loader::ResultStatus::Success) 183 if (nca.GetStatus() != Loader::ResultStatus::Success)
184 continue; 184 continue;
@@ -286,7 +286,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
286 continue; 286 continue;
287 } 287 }
288 288
289 auto nca = std::make_shared<NCA>(file, nullptr, 0, keys); 289 auto nca = std::make_shared<NCA>(file, nullptr, 0);
290 if (nca->IsUpdate()) { 290 if (nca->IsUpdate()) {
291 continue; 291 continue;
292 } 292 }
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index c2ee0ea99..e1b136426 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -140,6 +140,6 @@ private:
140 140
141 u64 update_normal_partition_end; 141 u64 update_normal_partition_end;
142 142
143 Core::Crypto::KeyManager keys; 143 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
144}; 144};
145} // namespace FileSys 145} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index b8bbdd1ef..473245d5a 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -118,9 +118,8 @@ static bool IsValidNCA(const NCAHeader& header) {
118 return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); 118 return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
119} 119}
120 120
121NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset, 121NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
122 Core::Crypto::KeyManager keys_) 122 : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) {
123 : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)), keys(std::move(keys_)) {
124 if (file == nullptr) { 123 if (file == nullptr) {
125 status = Loader::ResultStatus::ErrorNullFile; 124 status = Loader::ResultStatus::ErrorNullFile;
126 return; 125 return;
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index e249079b5..d25cbcf91 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -99,8 +99,7 @@ inline bool IsDirectoryLogoPartition(const VirtualDir& pfs) {
99class NCA : public ReadOnlyVfsDirectory { 99class NCA : public ReadOnlyVfsDirectory {
100public: 100public:
101 explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr, 101 explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr,
102 u64 bktr_base_ivfc_offset = 0, 102 u64 bktr_base_ivfc_offset = 0);
103 Core::Crypto::KeyManager keys = Core::Crypto::KeyManager());
104 ~NCA() override; 103 ~NCA() override;
105 104
106 Loader::ResultStatus GetStatus() const; 105 Loader::ResultStatus GetStatus() const;
@@ -159,7 +158,7 @@ private:
159 bool encrypted = false; 158 bool encrypted = false;
160 bool is_update = false; 159 bool is_update = false;
161 160
162 Core::Crypto::KeyManager keys; 161 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
163}; 162};
164 163
165} // namespace FileSys 164} // namespace FileSys
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index ba5f76288..27c1b0233 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -408,7 +408,7 @@ void RegisteredCache::ProcessFiles(const std::vector<NcaID>& ids) {
408 408
409 if (file == nullptr) 409 if (file == nullptr)
410 continue; 410 continue;
411 const auto nca = std::make_shared<NCA>(parser(file, id), nullptr, 0, keys); 411 const auto nca = std::make_shared<NCA>(parser(file, id), nullptr, 0);
412 if (nca->GetStatus() != Loader::ResultStatus::Success || 412 if (nca->GetStatus() != Loader::ResultStatus::Success ||
413 nca->GetType() != NCAContentType::Meta) { 413 nca->GetType() != NCAContentType::Meta) {
414 continue; 414 continue;
@@ -486,7 +486,7 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t
486 const auto raw = GetEntryRaw(title_id, type); 486 const auto raw = GetEntryRaw(title_id, type);
487 if (raw == nullptr) 487 if (raw == nullptr)
488 return nullptr; 488 return nullptr;
489 return std::make_unique<NCA>(raw, nullptr, 0, keys); 489 return std::make_unique<NCA>(raw, nullptr, 0);
490} 490}
491 491
492template <typename T> 492template <typename T>
@@ -865,7 +865,7 @@ std::unique_ptr<NCA> ManualContentProvider::GetEntry(u64 title_id, ContentRecord
865 const auto res = GetEntryRaw(title_id, type); 865 const auto res = GetEntryRaw(title_id, type);
866 if (res == nullptr) 866 if (res == nullptr)
867 return nullptr; 867 return nullptr;
868 return std::make_unique<NCA>(res, nullptr, 0, keys); 868 return std::make_unique<NCA>(res, nullptr, 0);
869} 869}
870 870
871std::vector<ContentProviderEntry> ManualContentProvider::ListEntriesFilter( 871std::vector<ContentProviderEntry> ManualContentProvider::ListEntriesFilter(
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index d1eec240e..f339cd17b 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -88,7 +88,7 @@ public:
88 88
89protected: 89protected:
90 // A single instance of KeyManager to be used by GetEntry() 90 // A single instance of KeyManager to be used by GetEntry()
91 Core::Crypto::KeyManager keys; 91 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
92}; 92};
93 93
94class PlaceholderCache { 94class PlaceholderCache {
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index ef3084681..175a8266a 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -21,7 +21,7 @@
21namespace FileSys { 21namespace FileSys {
22namespace { 22namespace {
23void SetTicketKeys(const std::vector<VirtualFile>& files) { 23void SetTicketKeys(const std::vector<VirtualFile>& files) {
24 Core::Crypto::KeyManager keys; 24 auto& keys = Core::Crypto::KeyManager::Instance();
25 25
26 for (const auto& ticket_file : files) { 26 for (const auto& ticket_file : files) {
27 if (ticket_file == nullptr) { 27 if (ticket_file == nullptr) {
@@ -285,7 +285,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
285 continue; 285 continue;
286 } 286 }
287 287
288 auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0, keys); 288 auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0);
289 if (next_nca->GetType() == NCAContentType::Program) { 289 if (next_nca->GetType() == NCAContentType::Program) {
290 program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); 290 program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
291 } 291 }
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index ee9b6ce17..cf89de6a9 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -73,7 +73,7 @@ private:
73 std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas; 73 std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas;
74 std::vector<VirtualFile> ticket_files; 74 std::vector<VirtualFile> ticket_files;
75 75
76 Core::Crypto::KeyManager keys; 76 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
77 77
78 VirtualFile romfs; 78 VirtualFile romfs;
79 VirtualDir exefs; 79 VirtualDir exefs;
diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h
index 7704dee90..563531bb6 100644
--- a/src/core/file_sys/xts_archive.h
+++ b/src/core/file_sys/xts_archive.h
@@ -62,6 +62,6 @@ private:
62 62
63 VirtualFile dec_file; 63 VirtualFile dec_file;
64 64
65 Core::Crypto::KeyManager keys; 65 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
66}; 66};
67} // namespace FileSys 67} // namespace FileSys
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 94d8c1fc6..8ac856ec3 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -776,6 +776,15 @@ void Module::Interface::ListQualifiedUsers(Kernel::HLERequestContext& ctx) {
776 rb.Push(RESULT_SUCCESS); 776 rb.Push(RESULT_SUCCESS);
777} 777}
778 778
779void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) {
780 LOG_WARNING(Service_ACC, "(STUBBED) called");
781
782 // TODO(ogniK): Handle open contexts
783 ctx.WriteBuffer(profile_manager->GetOpenUsers());
784 IPC::ResponseBuilder rb{ctx, 2};
785 rb.Push(RESULT_SUCCESS);
786}
787
779void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) { 788void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) {
780 LOG_DEBUG(Service_ACC, "called"); 789 LOG_DEBUG(Service_ACC, "called");
781 // A u8 is passed into this function which we can safely ignore. It's to determine if we have 790 // A u8 is passed into this function which we can safely ignore. It's to determine if we have
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h
index 74ca39d6e..d4c6395c6 100644
--- a/src/core/hle/service/acc/acc.h
+++ b/src/core/hle/service/acc/acc.h
@@ -34,6 +34,7 @@ public:
34 void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx); 34 void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx);
35 void GetProfileEditor(Kernel::HLERequestContext& ctx); 35 void GetProfileEditor(Kernel::HLERequestContext& ctx);
36 void ListQualifiedUsers(Kernel::HLERequestContext& ctx); 36 void ListQualifiedUsers(Kernel::HLERequestContext& ctx);
37 void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx);
37 38
38 private: 39 private:
39 ResultCode InitializeApplicationInfoBase(); 40 ResultCode InitializeApplicationInfoBase();
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp
index 85620bde3..d2bb8c2c8 100644
--- a/src/core/hle/service/acc/acc_su.cpp
+++ b/src/core/hle/service/acc/acc_su.cpp
@@ -20,7 +20,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
20 {6, nullptr, "GetProfileDigest"}, // 3.0.0+ 20 {6, nullptr, "GetProfileDigest"}, // 3.0.0+
21 {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, 21 {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
22 {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, 22 {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
23 {60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 23 {60, &ACC_SU::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0
24 {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ 24 {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+
25 {100, nullptr, "GetUserRegistrationNotifier"}, 25 {100, nullptr, "GetUserRegistrationNotifier"},
26 {101, nullptr, "GetUserStateChangeNotifier"}, 26 {101, nullptr, "GetUserStateChangeNotifier"},
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index 49f6e20f1..cb44e06b7 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -20,7 +20,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
20 {6, nullptr, "GetProfileDigest"}, // 3.0.0+ 20 {6, nullptr, "GetProfileDigest"}, // 3.0.0+
21 {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, 21 {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
22 {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, 22 {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
23 {60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 23 {60, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0
24 {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ 24 {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+
25 {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, 25 {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
26 {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, 26 {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},
@@ -30,7 +30,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
30 {111, nullptr, "ClearSaveDataThumbnail"}, 30 {111, nullptr, "ClearSaveDataThumbnail"},
31 {120, nullptr, "CreateGuestLoginRequest"}, 31 {120, nullptr, "CreateGuestLoginRequest"},
32 {130, nullptr, "LoadOpenContext"}, // 5.0.0+ 32 {130, nullptr, "LoadOpenContext"}, // 5.0.0+
33 {131, nullptr, "ListOpenContextStoredUsers"}, // 6.0.0+ 33 {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+
34 {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+ 34 {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+
35 {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ 35 {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+
36 {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"}, // 6.0.0+ 36 {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"}, // 6.0.0+
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp
index f47004f84..a4aa5316a 100644
--- a/src/core/hle/service/acc/acc_u1.cpp
+++ b/src/core/hle/service/acc/acc_u1.cpp
@@ -20,7 +20,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
20 {6, nullptr, "GetProfileDigest"}, // 3.0.0+ 20 {6, nullptr, "GetProfileDigest"}, // 3.0.0+
21 {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, 21 {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
22 {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, 22 {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
23 {60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 23 {60, &ACC_U1::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0
24 {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+ 24 {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+
25 {100, nullptr, "GetUserRegistrationNotifier"}, 25 {100, nullptr, "GetUserRegistrationNotifier"},
26 {101, nullptr, "GetUserStateChangeNotifier"}, 26 {101, nullptr, "GetUserStateChangeNotifier"},
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 20f366635..1bb544dd8 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -841,7 +841,7 @@ public:
841 {110, nullptr, "NeedsToExitProcess"}, 841 {110, nullptr, "NeedsToExitProcess"},
842 {120, nullptr, "GetLibraryAppletInfo"}, 842 {120, nullptr, "GetLibraryAppletInfo"},
843 {150, nullptr, "RequestForAppletToGetForeground"}, 843 {150, nullptr, "RequestForAppletToGetForeground"},
844 {160, nullptr, "GetIndirectLayerConsumerHandle"}, 844 {160, &ILibraryAppletAccessor::GetIndirectLayerConsumerHandle, "GetIndirectLayerConsumerHandle"},
845 }; 845 };
846 // clang-format on 846 // clang-format on
847 847
@@ -960,6 +960,18 @@ private:
960 rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent()); 960 rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent());
961 } 961 }
962 962
963 void GetIndirectLayerConsumerHandle(Kernel::HLERequestContext& ctx) {
964 LOG_WARNING(Service_AM, "(STUBBED) called");
965
966 // We require a non-zero handle to be valid. Using 0xdeadbeef allows us to trace if this is
967 // actually used anywhere
968 constexpr u64 handle = 0xdeadbeef;
969
970 IPC::ResponseBuilder rb{ctx, 4};
971 rb.Push(RESULT_SUCCESS);
972 rb.Push(handle);
973 }
974
963 std::shared_ptr<Applets::Applet> applet; 975 std::shared_ptr<Applets::Applet> applet;
964}; 976};
965 977
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp
index 9365f27e1..a41c73c48 100644
--- a/src/core/hle/service/es/es.cpp
+++ b/src/core/hle/service/es/es.cpp
@@ -302,7 +302,7 @@ private:
302 rb.Push<u64>(write_size); 302 rb.Push<u64>(write_size);
303 } 303 }
304 304
305 Core::Crypto::KeyManager keys; 305 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
306}; 306};
307 307
308void InstallInterfaces(SM::ServiceManager& service_manager) { 308void InstallInterfaces(SM::ServiceManager& service_manager) {
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index 14309c679..67833d9af 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -75,8 +75,13 @@ private:
75 const auto user_id = rp.PopRaw<u128>(); 75 const auto user_id = rp.PopRaw<u128>();
76 const auto process_id = rp.PopRaw<u64>(); 76 const auto process_id = rp.PopRaw<u64>();
77 std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)}; 77 std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)};
78
78 if constexpr (Type == Core::Reporter::PlayReportType::Old2) { 79 if constexpr (Type == Core::Reporter::PlayReportType::Old2) {
79 data.emplace_back(ctx.ReadBuffer(1)); 80 const auto read_buffer_count =
81 ctx.BufferDescriptorX().size() + ctx.BufferDescriptorA().size();
82 if (read_buffer_count > 1) {
83 data.emplace_back(ctx.ReadBuffer(1));
84 }
80 } 85 }
81 86
82 LOG_DEBUG( 87 LOG_DEBUG(
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index f3b4b286c..e5cfd2101 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.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 <array>
6#include <chrono> 7#include <chrono>
7#include "common/logging/log.h" 8#include "common/logging/log.h"
8#include "core/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
@@ -31,6 +32,44 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{
31 LanguageCode::ZH_HANT, 32 LanguageCode::ZH_HANT,
32}}; 33}};
33 34
35enum class KeyboardLayout : u64 {
36 Japanese = 0,
37 EnglishUs = 1,
38 EnglishUsInternational = 2,
39 EnglishUk = 3,
40 French = 4,
41 FrenchCa = 5,
42 Spanish = 6,
43 SpanishLatin = 7,
44 German = 8,
45 Italian = 9,
46 Portuguese = 10,
47 Russian = 11,
48 Korean = 12,
49 ChineseSimplified = 13,
50 ChineseTraditional = 14,
51};
52
53constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 17> language_to_layout{{
54 {LanguageCode::JA, KeyboardLayout::Japanese},
55 {LanguageCode::EN_US, KeyboardLayout::EnglishUs},
56 {LanguageCode::FR, KeyboardLayout::French},
57 {LanguageCode::DE, KeyboardLayout::German},
58 {LanguageCode::IT, KeyboardLayout::Italian},
59 {LanguageCode::ES, KeyboardLayout::Spanish},
60 {LanguageCode::ZH_CN, KeyboardLayout::ChineseSimplified},
61 {LanguageCode::KO, KeyboardLayout::Korean},
62 {LanguageCode::NL, KeyboardLayout::EnglishUsInternational},
63 {LanguageCode::PT, KeyboardLayout::Portuguese},
64 {LanguageCode::RU, KeyboardLayout::Russian},
65 {LanguageCode::ZH_TW, KeyboardLayout::ChineseTraditional},
66 {LanguageCode::EN_GB, KeyboardLayout::EnglishUk},
67 {LanguageCode::FR_CA, KeyboardLayout::FrenchCa},
68 {LanguageCode::ES_419, KeyboardLayout::SpanishLatin},
69 {LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified},
70 {LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional},
71}};
72
34constexpr std::size_t pre4_0_0_max_entries = 15; 73constexpr std::size_t pre4_0_0_max_entries = 15;
35constexpr std::size_t post4_0_0_max_entries = 17; 74constexpr std::size_t post4_0_0_max_entries = 17;
36 75
@@ -50,6 +89,25 @@ void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t m
50 ctx.WriteBuffer(available_language_codes.data(), copy_size); 89 ctx.WriteBuffer(available_language_codes.data(), copy_size);
51 PushResponseLanguageCode(ctx, copy_amount); 90 PushResponseLanguageCode(ctx, copy_amount);
52} 91}
92
93void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) {
94 const auto language_code = available_language_codes[Settings::values.language_index];
95 const auto key_code =
96 std::find_if(language_to_layout.cbegin(), language_to_layout.cend(),
97 [=](const auto& element) { return element.first == language_code; });
98 KeyboardLayout layout = KeyboardLayout::EnglishUs;
99 if (key_code == language_to_layout.cend()) {
100 LOG_ERROR(Service_SET,
101 "Could not find keyboard layout for language index {}, defaulting to English us",
102 Settings::values.language_index);
103 } else {
104 layout = key_code->second;
105 }
106
107 IPC::ResponseBuilder rb{ctx, 2};
108 rb.Push(RESULT_SUCCESS);
109 ctx.WriteBuffer(&layout, sizeof(KeyboardLayout));
110}
53} // Anonymous namespace 111} // Anonymous namespace
54 112
55LanguageCode GetLanguageCodeFromIndex(std::size_t index) { 113LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
@@ -120,6 +178,16 @@ void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
120 rb.Push(Settings::values.region_index); 178 rb.Push(Settings::values.region_index);
121} 179}
122 180
181void SET::GetKeyCodeMap(Kernel::HLERequestContext& ctx) {
182 LOG_DEBUG(Service_SET, "Called {}", ctx.Description());
183 GetKeyCodeMapImpl(ctx);
184}
185
186void SET::GetKeyCodeMap2(Kernel::HLERequestContext& ctx) {
187 LOG_DEBUG(Service_SET, "Called {}", ctx.Description());
188 GetKeyCodeMapImpl(ctx);
189}
190
123SET::SET() : ServiceFramework("set") { 191SET::SET() : ServiceFramework("set") {
124 // clang-format off 192 // clang-format off
125 static const FunctionInfo functions[] = { 193 static const FunctionInfo functions[] = {
@@ -130,9 +198,9 @@ SET::SET() : ServiceFramework("set") {
130 {4, &SET::GetRegionCode, "GetRegionCode"}, 198 {4, &SET::GetRegionCode, "GetRegionCode"},
131 {5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"}, 199 {5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"},
132 {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"}, 200 {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"},
133 {7, nullptr, "GetKeyCodeMap"}, 201 {7, &SET::GetKeyCodeMap, "GetKeyCodeMap"},
134 {8, &SET::GetQuestFlag, "GetQuestFlag"}, 202 {8, &SET::GetQuestFlag, "GetQuestFlag"},
135 {9, nullptr, "GetKeyCodeMap2"}, 203 {9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"},
136 {10, nullptr, "GetFirmwareVersionForDebug"}, 204 {10, nullptr, "GetFirmwareVersionForDebug"},
137 }; 205 };
138 // clang-format on 206 // clang-format on
diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h
index 6084b345d..8ac9c169d 100644
--- a/src/core/hle/service/set/set.h
+++ b/src/core/hle/service/set/set.h
@@ -44,6 +44,8 @@ private:
44 void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx); 44 void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx);
45 void GetQuestFlag(Kernel::HLERequestContext& ctx); 45 void GetQuestFlag(Kernel::HLERequestContext& ctx);
46 void GetRegionCode(Kernel::HLERequestContext& ctx); 46 void GetRegionCode(Kernel::HLERequestContext& ctx);
47 void GetKeyCodeMap(Kernel::HLERequestContext& ctx);
48 void GetKeyCodeMap2(Kernel::HLERequestContext& ctx);
47}; 49};
48 50
49} // namespace Service::Set 51} // namespace Service::Set
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index a9c2392b1..3bd76dd23 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -7,6 +7,10 @@ add_library(input_common STATIC
7 main.h 7 main.h
8 motion_emu.cpp 8 motion_emu.cpp
9 motion_emu.h 9 motion_emu.h
10 gcadapter/gc_adapter.cpp
11 gcadapter/gc_adapter.h
12 gcadapter/gc_poller.cpp
13 gcadapter/gc_poller.h
10 sdl/sdl.cpp 14 sdl/sdl.cpp
11 sdl/sdl.h 15 sdl/sdl.h
12 udp/client.cpp 16 udp/client.cpp
@@ -26,5 +30,7 @@ if(SDL2_FOUND)
26 target_compile_definitions(input_common PRIVATE HAVE_SDL2) 30 target_compile_definitions(input_common PRIVATE HAVE_SDL2)
27endif() 31endif()
28 32
33target_link_libraries(input_common PUBLIC ${LIBUSB_LIBRARIES})
34
29create_target_directory_groups(input_common) 35create_target_directory_groups(input_common)
30target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost) 36target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost)
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp
new file mode 100644
index 000000000..b39d2a3fb
--- /dev/null
+++ b/src/input_common/gcadapter/gc_adapter.cpp
@@ -0,0 +1,379 @@
1// Copyright 2014 Dolphin Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <chrono>
6#include <thread>
7#include "common/logging/log.h"
8#include "input_common/gcadapter/gc_adapter.h"
9
10namespace GCAdapter {
11
12/// Used to loop through and assign button in poller
13constexpr std::array<PadButton, 12> PadButtonArray{
14 PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, PadButton::PAD_BUTTON_DOWN,
15 PadButton::PAD_BUTTON_UP, PadButton::PAD_TRIGGER_Z, PadButton::PAD_TRIGGER_R,
16 PadButton::PAD_TRIGGER_L, PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B,
17 PadButton::PAD_BUTTON_X, PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_START,
18};
19
20Adapter::Adapter() {
21 if (usb_adapter_handle != nullptr) {
22 return;
23 }
24 LOG_INFO(Input, "GC Adapter Initialization started");
25
26 current_status = NO_ADAPTER_DETECTED;
27 libusb_init(&libusb_ctx);
28
29 StartScanThread();
30}
31
32GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_payload) {
33 GCPadStatus pad = {};
34 bool get_origin = false;
35
36 ControllerTypes type = ControllerTypes(adapter_payload[1 + (9 * port)] >> 4);
37 if (type != ControllerTypes::None) {
38 get_origin = true;
39 }
40
41 adapter_controllers_status[port] = type;
42
43 static constexpr std::array<PadButton, 8> b1_buttons{
44 PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B, PadButton::PAD_BUTTON_X,
45 PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT,
46 PadButton::PAD_BUTTON_DOWN, PadButton::PAD_BUTTON_UP,
47 };
48
49 static constexpr std::array<PadButton, 4> b2_buttons{
50 PadButton::PAD_BUTTON_START,
51 PadButton::PAD_TRIGGER_Z,
52 PadButton::PAD_TRIGGER_R,
53 PadButton::PAD_TRIGGER_L,
54 };
55
56 if (adapter_controllers_status[port] != ControllerTypes::None) {
57 const u8 b1 = adapter_payload[1 + (9 * port) + 1];
58 const u8 b2 = adapter_payload[1 + (9 * port) + 2];
59
60 for (std::size_t i = 0; i < b1_buttons.size(); ++i) {
61 if ((b1 & (1U << i)) != 0) {
62 pad.button |= static_cast<u16>(b1_buttons[i]);
63 }
64 }
65
66 for (std::size_t j = 0; j < b2_buttons.size(); ++j) {
67 if ((b2 & (1U << j)) != 0) {
68 pad.button |= static_cast<u16>(b2_buttons[j]);
69 }
70 }
71
72 if (get_origin) {
73 pad.button |= PAD_GET_ORIGIN;
74 }
75
76 pad.stick_x = adapter_payload[1 + (9 * port) + 3];
77 pad.stick_y = adapter_payload[1 + (9 * port) + 4];
78 pad.substick_x = adapter_payload[1 + (9 * port) + 5];
79 pad.substick_y = adapter_payload[1 + (9 * port) + 6];
80 pad.trigger_left = adapter_payload[1 + (9 * port) + 7];
81 pad.trigger_right = adapter_payload[1 + (9 * port) + 8];
82 }
83 return pad;
84}
85
86void Adapter::PadToState(const GCPadStatus& pad, GCState& state) {
87 for (const auto& button : PadButtonArray) {
88 const u16 button_value = static_cast<u16>(button);
89 state.buttons.insert_or_assign(button_value, pad.button & button_value);
90 }
91
92 state.axes.insert_or_assign(static_cast<u8>(PadAxes::StickX), pad.stick_x);
93 state.axes.insert_or_assign(static_cast<u8>(PadAxes::StickY), pad.stick_y);
94 state.axes.insert_or_assign(static_cast<u8>(PadAxes::SubstickX), pad.substick_x);
95 state.axes.insert_or_assign(static_cast<u8>(PadAxes::SubstickY), pad.substick_y);
96 state.axes.insert_or_assign(static_cast<u8>(PadAxes::TriggerLeft), pad.trigger_left);
97 state.axes.insert_or_assign(static_cast<u8>(PadAxes::TriggerRight), pad.trigger_right);
98}
99
100void Adapter::Read() {
101 LOG_DEBUG(Input, "GC Adapter Read() thread started");
102
103 int payload_size_in, payload_size_copy;
104 std::array<u8, 37> adapter_payload;
105 std::array<u8, 37> adapter_payload_copy;
106 std::array<GCPadStatus, 4> pads;
107
108 while (adapter_thread_running) {
109 libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(),
110 sizeof(adapter_payload), &payload_size_in, 16);
111 payload_size_copy = 0;
112 // this mutex might be redundant?
113 {
114 std::lock_guard<std::mutex> lk(s_mutex);
115 std::copy(std::begin(adapter_payload), std::end(adapter_payload),
116 std::begin(adapter_payload_copy));
117 payload_size_copy = payload_size_in;
118 }
119
120 if (payload_size_copy != sizeof(adapter_payload_copy) ||
121 adapter_payload_copy[0] != LIBUSB_DT_HID) {
122 LOG_ERROR(Input, "error reading payload (size: {}, type: {:02x})", payload_size_copy,
123 adapter_payload_copy[0]);
124 adapter_thread_running = false; // error reading from adapter, stop reading.
125 break;
126 }
127 for (std::size_t port = 0; port < pads.size(); ++port) {
128 pads[port] = GetPadStatus(port, adapter_payload_copy);
129 if (DeviceConnected(port) && configuring) {
130 if (pads[port].button != PAD_GET_ORIGIN) {
131 pad_queue[port].Push(pads[port]);
132 }
133
134 // Accounting for a threshold here because of some controller variance
135 if (pads[port].stick_x > pads[port].MAIN_STICK_CENTER_X + pads[port].THRESHOLD ||
136 pads[port].stick_x < pads[port].MAIN_STICK_CENTER_X - pads[port].THRESHOLD) {
137 pads[port].axis = GCAdapter::PadAxes::StickX;
138 pads[port].axis_value = pads[port].stick_x;
139 pad_queue[port].Push(pads[port]);
140 }
141 if (pads[port].stick_y > pads[port].MAIN_STICK_CENTER_Y + pads[port].THRESHOLD ||
142 pads[port].stick_y < pads[port].MAIN_STICK_CENTER_Y - pads[port].THRESHOLD) {
143 pads[port].axis = GCAdapter::PadAxes::StickY;
144 pads[port].axis_value = pads[port].stick_y;
145 pad_queue[port].Push(pads[port]);
146 }
147 if (pads[port].substick_x > pads[port].C_STICK_CENTER_X + pads[port].THRESHOLD ||
148 pads[port].substick_x < pads[port].C_STICK_CENTER_X - pads[port].THRESHOLD) {
149 pads[port].axis = GCAdapter::PadAxes::SubstickX;
150 pads[port].axis_value = pads[port].substick_x;
151 pad_queue[port].Push(pads[port]);
152 }
153 if (pads[port].substick_y > pads[port].C_STICK_CENTER_Y + pads[port].THRESHOLD ||
154 pads[port].substick_y < pads[port].C_STICK_CENTER_Y - pads[port].THRESHOLD) {
155 pads[port].axis = GCAdapter::PadAxes::SubstickY;
156 pads[port].axis_value = pads[port].substick_y;
157 pad_queue[port].Push(pads[port]);
158 }
159 if (pads[port].trigger_left > pads[port].TRIGGER_THRESHOLD) {
160 pads[port].axis = GCAdapter::PadAxes::TriggerLeft;
161 pads[port].axis_value = pads[port].trigger_left;
162 pad_queue[port].Push(pads[port]);
163 }
164 if (pads[port].trigger_right > pads[port].TRIGGER_THRESHOLD) {
165 pads[port].axis = GCAdapter::PadAxes::TriggerRight;
166 pads[port].axis_value = pads[port].trigger_right;
167 pad_queue[port].Push(pads[port]);
168 }
169 }
170 PadToState(pads[port], state[port]);
171 }
172 std::this_thread::yield();
173 }
174}
175
176void Adapter::ScanThreadFunc() {
177 LOG_INFO(Input, "GC Adapter scanning thread started");
178
179 while (detect_thread_running) {
180 if (usb_adapter_handle == nullptr) {
181 std::lock_guard<std::mutex> lk(initialization_mutex);
182 Setup();
183 }
184 std::this_thread::sleep_for(std::chrono::milliseconds(500));
185 }
186}
187
188void Adapter::StartScanThread() {
189 if (detect_thread_running) {
190 return;
191 }
192 if (!libusb_ctx) {
193 return;
194 }
195
196 detect_thread_running = true;
197 detect_thread = std::thread([=] { ScanThreadFunc(); });
198}
199
200void Adapter::StopScanThread() {
201 detect_thread_running = false;
202 detect_thread.join();
203}
204
205void Adapter::Setup() {
206 // Reset the error status in case the adapter gets unplugged
207 if (current_status < 0) {
208 current_status = NO_ADAPTER_DETECTED;
209 }
210
211 adapter_controllers_status.fill(ControllerTypes::None);
212
213 // pointer to list of connected usb devices
214 libusb_device** devices;
215
216 // populate the list of devices, get the count
217 const std::size_t device_count = libusb_get_device_list(libusb_ctx, &devices);
218
219 for (std::size_t index = 0; index < device_count; ++index) {
220 if (CheckDeviceAccess(devices[index])) {
221 // GC Adapter found and accessible, registering it
222 GetGCEndpoint(devices[index]);
223 break;
224 }
225 }
226}
227
228bool Adapter::CheckDeviceAccess(libusb_device* device) {
229 libusb_device_descriptor desc;
230 const int get_descriptor_error = libusb_get_device_descriptor(device, &desc);
231 if (get_descriptor_error) {
232 // could not acquire the descriptor, no point in trying to use it.
233 LOG_ERROR(Input, "libusb_get_device_descriptor failed with error: {}",
234 get_descriptor_error);
235 return false;
236 }
237
238 if (desc.idVendor != 0x057e || desc.idProduct != 0x0337) {
239 // This isn't the device we are looking for.
240 return false;
241 }
242 const int open_error = libusb_open(device, &usb_adapter_handle);
243
244 if (open_error == LIBUSB_ERROR_ACCESS) {
245 LOG_ERROR(Input, "Yuzu can not gain access to this device: ID {:04X}:{:04X}.",
246 desc.idVendor, desc.idProduct);
247 return false;
248 }
249 if (open_error) {
250 LOG_ERROR(Input, "libusb_open failed to open device with error = {}", open_error);
251 return false;
252 }
253
254 int kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0);
255 if (kernel_driver_error == 1) {
256 kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0);
257 if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
258 LOG_ERROR(Input, "libusb_detach_kernel_driver failed with error = {}",
259 kernel_driver_error);
260 }
261 }
262
263 if (kernel_driver_error && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
264 libusb_close(usb_adapter_handle);
265 usb_adapter_handle = nullptr;
266 return false;
267 }
268
269 const int interface_claim_error = libusb_claim_interface(usb_adapter_handle, 0);
270 if (interface_claim_error) {
271 LOG_ERROR(Input, "libusb_claim_interface failed with error = {}", interface_claim_error);
272 libusb_close(usb_adapter_handle);
273 usb_adapter_handle = nullptr;
274 return false;
275 }
276
277 return true;
278}
279
280void Adapter::GetGCEndpoint(libusb_device* device) {
281 libusb_config_descriptor* config = nullptr;
282 libusb_get_config_descriptor(device, 0, &config);
283 for (u8 ic = 0; ic < config->bNumInterfaces; ic++) {
284 const libusb_interface* interfaceContainer = &config->interface[ic];
285 for (int i = 0; i < interfaceContainer->num_altsetting; i++) {
286 const libusb_interface_descriptor* interface = &interfaceContainer->altsetting[i];
287 for (u8 e = 0; e < interface->bNumEndpoints; e++) {
288 const libusb_endpoint_descriptor* endpoint = &interface->endpoint[e];
289 if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) {
290 input_endpoint = endpoint->bEndpointAddress;
291 } else {
292 output_endpoint = endpoint->bEndpointAddress;
293 }
294 }
295 }
296 }
297 // This transfer seems to be responsible for clearing the state of the adapter
298 // Used to clear the "busy" state of when the device is unexpectedly unplugged
299 unsigned char clear_payload = 0x13;
300 libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload,
301 sizeof(clear_payload), nullptr, 16);
302
303 adapter_thread_running = true;
304 current_status = ADAPTER_DETECTED;
305 adapter_input_thread = std::thread([=] { Read(); }); // Read input
306}
307
308Adapter::~Adapter() {
309 StopScanThread();
310 Reset();
311}
312
313void Adapter::Reset() {
314 std::unique_lock<std::mutex> lock(initialization_mutex, std::defer_lock);
315 if (!lock.try_lock()) {
316 return;
317 }
318 if (current_status != ADAPTER_DETECTED) {
319 return;
320 }
321
322 if (adapter_thread_running) {
323 adapter_thread_running = false;
324 }
325 adapter_input_thread.join();
326
327 adapter_controllers_status.fill(ControllerTypes::None);
328 current_status = NO_ADAPTER_DETECTED;
329
330 if (usb_adapter_handle) {
331 libusb_release_interface(usb_adapter_handle, 1);
332 libusb_close(usb_adapter_handle);
333 usb_adapter_handle = nullptr;
334 }
335
336 if (libusb_ctx) {
337 libusb_exit(libusb_ctx);
338 }
339}
340
341bool Adapter::DeviceConnected(int port) {
342 return adapter_controllers_status[port] != ControllerTypes::None;
343}
344
345void Adapter::ResetDeviceType(int port) {
346 adapter_controllers_status[port] = ControllerTypes::None;
347}
348
349void Adapter::BeginConfiguration() {
350 for (auto& pq : pad_queue) {
351 pq.Clear();
352 }
353 configuring = true;
354}
355
356void Adapter::EndConfiguration() {
357 for (auto& pq : pad_queue) {
358 pq.Clear();
359 }
360 configuring = false;
361}
362
363std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() {
364 return pad_queue;
365}
366
367const std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() const {
368 return pad_queue;
369}
370
371std::array<GCState, 4>& Adapter::GetPadState() {
372 return state;
373}
374
375const std::array<GCState, 4>& Adapter::GetPadState() const {
376 return state;
377}
378
379} // namespace GCAdapter
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h
new file mode 100644
index 000000000..0ea6263eb
--- /dev/null
+++ b/src/input_common/gcadapter/gc_adapter.h
@@ -0,0 +1,160 @@
1// Copyright 2014 Dolphin Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6#include <algorithm>
7#include <functional>
8#include <mutex>
9#include <thread>
10#include <libusb.h>
11#include "common/common_types.h"
12#include "common/threadsafe_queue.h"
13
14namespace GCAdapter {
15
16enum {
17 PAD_USE_ORIGIN = 0x0080,
18 PAD_GET_ORIGIN = 0x2000,
19 PAD_ERR_STATUS = 0x8000,
20};
21
22enum class PadButton {
23 PAD_BUTTON_LEFT = 0x0001,
24 PAD_BUTTON_RIGHT = 0x0002,
25 PAD_BUTTON_DOWN = 0x0004,
26 PAD_BUTTON_UP = 0x0008,
27 PAD_TRIGGER_Z = 0x0010,
28 PAD_TRIGGER_R = 0x0020,
29 PAD_TRIGGER_L = 0x0040,
30 PAD_BUTTON_A = 0x0100,
31 PAD_BUTTON_B = 0x0200,
32 PAD_BUTTON_X = 0x0400,
33 PAD_BUTTON_Y = 0x0800,
34 PAD_BUTTON_START = 0x1000,
35 // Below is for compatibility with "AxisButton" type
36 PAD_STICK = 0x2000,
37};
38
39extern const std::array<PadButton, 12> PadButtonArray;
40
41enum class PadAxes : u8 {
42 StickX,
43 StickY,
44 SubstickX,
45 SubstickY,
46 TriggerLeft,
47 TriggerRight,
48 Undefined,
49};
50
51struct GCPadStatus {
52 u16 button{}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits
53 u8 stick_x{}; // 0 <= stick_x <= 255
54 u8 stick_y{}; // 0 <= stick_y <= 255
55 u8 substick_x{}; // 0 <= substick_x <= 255
56 u8 substick_y{}; // 0 <= substick_y <= 255
57 u8 trigger_left{}; // 0 <= trigger_left <= 255
58 u8 trigger_right{}; // 0 <= trigger_right <= 255
59
60 static constexpr u8 MAIN_STICK_CENTER_X = 0x80;
61 static constexpr u8 MAIN_STICK_CENTER_Y = 0x80;
62 static constexpr u8 MAIN_STICK_RADIUS = 0x7f;
63 static constexpr u8 C_STICK_CENTER_X = 0x80;
64 static constexpr u8 C_STICK_CENTER_Y = 0x80;
65 static constexpr u8 C_STICK_RADIUS = 0x7f;
66 static constexpr u8 THRESHOLD = 10;
67
68 // 256/4, at least a quarter press to count as a press. For polling mostly
69 static constexpr u8 TRIGGER_THRESHOLD = 64;
70
71 u8 port{};
72 PadAxes axis{PadAxes::Undefined};
73 u8 axis_value{255};
74};
75
76struct GCState {
77 std::unordered_map<int, bool> buttons;
78 std::unordered_map<int, u16> axes;
79};
80
81enum class ControllerTypes { None, Wired, Wireless };
82
83enum {
84 NO_ADAPTER_DETECTED = 0,
85 ADAPTER_DETECTED = 1,
86};
87
88class Adapter {
89public:
90 /// Initialize the GC Adapter capture and read sequence
91 Adapter();
92
93 /// Close the adapter read thread and release the adapter
94 ~Adapter();
95 /// Used for polling
96 void BeginConfiguration();
97 void EndConfiguration();
98
99 std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue();
100 const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const;
101
102 std::array<GCState, 4>& GetPadState();
103 const std::array<GCState, 4>& GetPadState() const;
104
105private:
106 GCPadStatus GetPadStatus(int port, const std::array<u8, 37>& adapter_payload);
107
108 void PadToState(const GCPadStatus& pad, GCState& state);
109
110 void Read();
111 void ScanThreadFunc();
112 /// Begin scanning for the GC Adapter.
113 void StartScanThread();
114
115 /// Stop scanning for the adapter
116 void StopScanThread();
117
118 /// Returns true if there is a device connected to port
119 bool DeviceConnected(int port);
120
121 /// Resets status of device connected to port
122 void ResetDeviceType(int port);
123
124 /// Returns true if we successfully gain access to GC Adapter
125 bool CheckDeviceAccess(libusb_device* device);
126
127 /// Captures GC Adapter endpoint address,
128 void GetGCEndpoint(libusb_device* device);
129
130 /// For shutting down, clear all data, join all threads, release usb
131 void Reset();
132
133 /// For use in initialization, querying devices to find the adapter
134 void Setup();
135
136 int current_status = NO_ADAPTER_DETECTED;
137 libusb_device_handle* usb_adapter_handle = nullptr;
138 std::array<ControllerTypes, 4> adapter_controllers_status{};
139
140 std::mutex s_mutex;
141
142 std::thread adapter_input_thread;
143 bool adapter_thread_running;
144
145 std::mutex initialization_mutex;
146 std::thread detect_thread;
147 bool detect_thread_running = false;
148
149 libusb_context* libusb_ctx;
150
151 u8 input_endpoint = 0;
152 u8 output_endpoint = 0;
153
154 bool configuring = false;
155
156 std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue;
157 std::array<GCState, 4> state;
158};
159
160} // namespace GCAdapter
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
new file mode 100644
index 000000000..385ce8430
--- /dev/null
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -0,0 +1,272 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <atomic>
6#include <list>
7#include <mutex>
8#include <utility>
9#include "common/threadsafe_queue.h"
10#include "input_common/gcadapter/gc_adapter.h"
11#include "input_common/gcadapter/gc_poller.h"
12
13namespace InputCommon {
14
15class GCButton final : public Input::ButtonDevice {
16public:
17 explicit GCButton(int port_, int button_, GCAdapter::Adapter* adapter)
18 : port(port_), button(button_), gcadapter(adapter) {}
19
20 ~GCButton() override;
21
22 bool GetStatus() const override {
23 return gcadapter->GetPadState()[port].buttons.at(button);
24 }
25
26private:
27 const int port;
28 const int button;
29 GCAdapter::Adapter* gcadapter;
30};
31
32class GCAxisButton final : public Input::ButtonDevice {
33public:
34 explicit GCAxisButton(int port_, int axis_, float threshold_, bool trigger_if_greater_,
35 GCAdapter::Adapter* adapter)
36 : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_),
37 gcadapter(adapter) {
38 // L/R triggers range is only in positive direction beginning near 0
39 // 0.0 threshold equates to near half trigger press, but threshold accounts for variability.
40 if (axis > 3) {
41 threshold *= -0.5;
42 }
43 }
44
45 bool GetStatus() const override {
46 const float axis_value = (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 128.0f;
47 if (trigger_if_greater) {
48 // TODO: Might be worthwile to set a slider for the trigger threshold. It is currently
49 // always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
50 return axis_value > threshold;
51 }
52 return axis_value < -threshold;
53 }
54
55private:
56 const int port;
57 const int axis;
58 float threshold;
59 bool trigger_if_greater;
60 GCAdapter::Adapter* gcadapter;
61};
62
63GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
64 : adapter(std::move(adapter_)) {}
65
66GCButton::~GCButton() = default;
67
68std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::ParamPackage& params) {
69 const int button_id = params.Get("button", 0);
70 const int port = params.Get("port", 0);
71
72 constexpr int PAD_STICK_ID = static_cast<u16>(GCAdapter::PadButton::PAD_STICK);
73
74 // button is not an axis/stick button
75 if (button_id != PAD_STICK_ID) {
76 auto button = std::make_unique<GCButton>(port, button_id, adapter.get());
77 return std::move(button);
78 }
79
80 // For Axis buttons, used by the binary sticks.
81 if (button_id == PAD_STICK_ID) {
82 const int axis = params.Get("axis", 0);
83 const float threshold = params.Get("threshold", 0.25f);
84 const std::string direction_name = params.Get("direction", "");
85 bool trigger_if_greater;
86 if (direction_name == "+") {
87 trigger_if_greater = true;
88 } else if (direction_name == "-") {
89 trigger_if_greater = false;
90 } else {
91 trigger_if_greater = true;
92 LOG_ERROR(Input, "Unknown direction {}", direction_name);
93 }
94 return std::make_unique<GCAxisButton>(port, axis, threshold, trigger_if_greater,
95 adapter.get());
96 }
97}
98
99Common::ParamPackage GCButtonFactory::GetNextInput() {
100 Common::ParamPackage params;
101 GCAdapter::GCPadStatus pad;
102 auto& queue = adapter->GetPadQueue();
103 for (std::size_t port = 0; port < queue.size(); ++port) {
104 while (queue[port].Pop(pad)) {
105 // This while loop will break on the earliest detected button
106 params.Set("engine", "gcpad");
107 params.Set("port", static_cast<int>(port));
108 for (const auto& button : GCAdapter::PadButtonArray) {
109 const u16 button_value = static_cast<u16>(button);
110 if (pad.button & button_value) {
111 params.Set("button", button_value);
112 break;
113 }
114 }
115
116 // For Axis button implementation
117 if (pad.axis != GCAdapter::PadAxes::Undefined) {
118 params.Set("axis", static_cast<u8>(pad.axis));
119 params.Set("button", static_cast<u16>(GCAdapter::PadButton::PAD_STICK));
120 if (pad.axis_value > 128) {
121 params.Set("direction", "+");
122 params.Set("threshold", "0.25");
123 } else {
124 params.Set("direction", "-");
125 params.Set("threshold", "-0.25");
126 }
127 break;
128 }
129 }
130 }
131 return params;
132}
133
134void GCButtonFactory::BeginConfiguration() {
135 polling = true;
136 adapter->BeginConfiguration();
137}
138
139void GCButtonFactory::EndConfiguration() {
140 polling = false;
141 adapter->EndConfiguration();
142}
143
144class GCAnalog final : public Input::AnalogDevice {
145public:
146 GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter)
147 : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter) {}
148
149 float GetAxis(int axis) const {
150 std::lock_guard lock{mutex};
151 // division is not by a perfect 128 to account for some variance in center location
152 // e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range
153 // [20-230]
154 return (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 95.0f;
155 }
156
157 std::pair<float, float> GetAnalog(int axis_x, int axis_y) const {
158 float x = GetAxis(axis_x);
159 float y = GetAxis(axis_y);
160
161 // Make sure the coordinates are in the unit circle,
162 // otherwise normalize it.
163 float r = x * x + y * y;
164 if (r > 1.0f) {
165 r = std::sqrt(r);
166 x /= r;
167 y /= r;
168 }
169
170 return {x, y};
171 }
172
173 std::tuple<float, float> GetStatus() const override {
174 const auto [x, y] = GetAnalog(axis_x, axis_y);
175 const float r = std::sqrt((x * x) + (y * y));
176 if (r > deadzone) {
177 return {x / r * (r - deadzone) / (1 - deadzone),
178 y / r * (r - deadzone) / (1 - deadzone)};
179 }
180 return {0.0f, 0.0f};
181 }
182
183 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
184 const auto [x, y] = GetStatus();
185 const float directional_deadzone = 0.4f;
186 switch (direction) {
187 case Input::AnalogDirection::RIGHT:
188 return x > directional_deadzone;
189 case Input::AnalogDirection::LEFT:
190 return x < -directional_deadzone;
191 case Input::AnalogDirection::UP:
192 return y > directional_deadzone;
193 case Input::AnalogDirection::DOWN:
194 return y < -directional_deadzone;
195 }
196 return false;
197 }
198
199private:
200 const int port;
201 const int axis_x;
202 const int axis_y;
203 const float deadzone;
204 mutable std::mutex mutex;
205 GCAdapter::Adapter* gcadapter;
206};
207
208/// An analog device factory that creates analog devices from GC Adapter
209GCAnalogFactory::GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
210 : adapter(std::move(adapter_)) {}
211
212/**
213 * Creates analog device from joystick axes
214 * @param params contains parameters for creating the device:
215 * - "port": the nth gcpad on the adapter
216 * - "axis_x": the index of the axis to be bind as x-axis
217 * - "axis_y": the index of the axis to be bind as y-axis
218 */
219std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::ParamPackage& params) {
220 const int port = params.Get("port", 0);
221 const int axis_x = params.Get("axis_x", 0);
222 const int axis_y = params.Get("axis_y", 1);
223 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
224
225 return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get());
226}
227
228void GCAnalogFactory::BeginConfiguration() {
229 polling = true;
230 adapter->BeginConfiguration();
231}
232
233void GCAnalogFactory::EndConfiguration() {
234 polling = false;
235 adapter->EndConfiguration();
236}
237
238Common::ParamPackage GCAnalogFactory::GetNextInput() {
239 GCAdapter::GCPadStatus pad;
240 auto& queue = adapter->GetPadQueue();
241 for (std::size_t port = 0; port < queue.size(); ++port) {
242 while (queue[port].Pop(pad)) {
243 if (pad.axis == GCAdapter::PadAxes::Undefined ||
244 std::abs((pad.axis_value - 128.0f) / 128.0f) < 0.1) {
245 continue;
246 }
247 // An analog device needs two axes, so we need to store the axis for later and wait for
248 // a second input event. The axes also must be from the same joystick.
249 const u8 axis = static_cast<u8>(pad.axis);
250 if (analog_x_axis == -1) {
251 analog_x_axis = axis;
252 controller_number = port;
253 } else if (analog_y_axis == -1 && analog_x_axis != axis && controller_number == port) {
254 analog_y_axis = axis;
255 }
256 }
257 }
258 Common::ParamPackage params;
259 if (analog_x_axis != -1 && analog_y_axis != -1) {
260 params.Set("engine", "gcpad");
261 params.Set("port", controller_number);
262 params.Set("axis_x", analog_x_axis);
263 params.Set("axis_y", analog_y_axis);
264 analog_x_axis = -1;
265 analog_y_axis = -1;
266 controller_number = -1;
267 return params;
268 }
269 return params;
270}
271
272} // namespace InputCommon
diff --git a/src/input_common/gcadapter/gc_poller.h b/src/input_common/gcadapter/gc_poller.h
new file mode 100644
index 000000000..e96af7d51
--- /dev/null
+++ b/src/input_common/gcadapter/gc_poller.h
@@ -0,0 +1,67 @@
1// Copyright 2020 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 <memory>
8#include "core/frontend/input.h"
9#include "input_common/gcadapter/gc_adapter.h"
10
11namespace InputCommon {
12
13/**
14 * A button device factory representing a gcpad. It receives gcpad events and forward them
15 * to all button devices it created.
16 */
17class GCButtonFactory final : public Input::Factory<Input::ButtonDevice> {
18public:
19 explicit GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_);
20
21 /**
22 * Creates a button device from a button press
23 * @param params contains parameters for creating the device:
24 * - "code": the code of the key to bind with the button
25 */
26 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
27
28 Common::ParamPackage GetNextInput();
29
30 /// For device input configuration/polling
31 void BeginConfiguration();
32 void EndConfiguration();
33
34 bool IsPolling() const {
35 return polling;
36 }
37
38private:
39 std::shared_ptr<GCAdapter::Adapter> adapter;
40 bool polling = false;
41};
42
43/// An analog device factory that creates analog devices from GC Adapter
44class GCAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
45public:
46 explicit GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_);
47
48 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override;
49 Common::ParamPackage GetNextInput();
50
51 /// For device input configuration/polling
52 void BeginConfiguration();
53 void EndConfiguration();
54
55 bool IsPolling() const {
56 return polling;
57 }
58
59private:
60 std::shared_ptr<GCAdapter::Adapter> adapter;
61 int analog_x_axis = -1;
62 int analog_y_axis = -1;
63 int controller_number = -1;
64 bool polling = false;
65};
66
67} // namespace InputCommon
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 95e351e24..fd0af1019 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -4,8 +4,11 @@
4 4
5#include <memory> 5#include <memory>
6#include <thread> 6#include <thread>
7#include <libusb.h>
7#include "common/param_package.h" 8#include "common/param_package.h"
8#include "input_common/analog_from_button.h" 9#include "input_common/analog_from_button.h"
10#include "input_common/gcadapter/gc_adapter.h"
11#include "input_common/gcadapter/gc_poller.h"
9#include "input_common/keyboard.h" 12#include "input_common/keyboard.h"
10#include "input_common/main.h" 13#include "input_common/main.h"
11#include "input_common/motion_emu.h" 14#include "input_common/motion_emu.h"
@@ -22,8 +25,16 @@ static std::shared_ptr<MotionEmu> motion_emu;
22static std::unique_ptr<SDL::State> sdl; 25static std::unique_ptr<SDL::State> sdl;
23#endif 26#endif
24static std::unique_ptr<CemuhookUDP::State> udp; 27static std::unique_ptr<CemuhookUDP::State> udp;
28static std::shared_ptr<GCButtonFactory> gcbuttons;
29static std::shared_ptr<GCAnalogFactory> gcanalog;
25 30
26void Init() { 31void Init() {
32 auto gcadapter = std::make_shared<GCAdapter::Adapter>();
33 gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
34 Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
35 gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
36 Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
37
27 keyboard = std::make_shared<Keyboard>(); 38 keyboard = std::make_shared<Keyboard>();
28 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); 39 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
29 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button", 40 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
@@ -48,6 +59,11 @@ void Shutdown() {
48 sdl.reset(); 59 sdl.reset();
49#endif 60#endif
50 udp.reset(); 61 udp.reset();
62 Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
63 Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
64
65 gcbuttons.reset();
66 gcanalog.reset();
51} 67}
52 68
53Keyboard* GetKeyboard() { 69Keyboard* GetKeyboard() {
@@ -58,6 +74,14 @@ MotionEmu* GetMotionEmu() {
58 return motion_emu.get(); 74 return motion_emu.get();
59} 75}
60 76
77GCButtonFactory* GetGCButtons() {
78 return gcbuttons.get();
79}
80
81GCAnalogFactory* GetGCAnalogs() {
82 return gcanalog.get();
83}
84
61std::string GenerateKeyboardParam(int key_code) { 85std::string GenerateKeyboardParam(int key_code) {
62 Common::ParamPackage param{ 86 Common::ParamPackage param{
63 {"engine", "keyboard"}, 87 {"engine", "keyboard"},
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 77a0ce90b..0e32856f6 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -7,6 +7,7 @@
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include <vector> 9#include <vector>
10#include "input_common/gcadapter/gc_poller.h"
10 11
11namespace Common { 12namespace Common {
12class ParamPackage; 13class ParamPackage;
@@ -30,6 +31,10 @@ class MotionEmu;
30/// Gets the motion emulation factory. 31/// Gets the motion emulation factory.
31MotionEmu* GetMotionEmu(); 32MotionEmu* GetMotionEmu();
32 33
34GCButtonFactory* GetGCButtons();
35
36GCAnalogFactory* GetGCAnalogs();
37
33/// Generates a serialized param package for creating a keyboard button device 38/// Generates a serialized param package for creating a keyboard button device
34std::string GenerateKeyboardParam(int key_code); 39std::string GenerateKeyboardParam(int key_code);
35 40
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 2dc752aa9..21c46a567 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -3,6 +3,8 @@ add_library(video_core STATIC
3 buffer_cache/buffer_cache.h 3 buffer_cache/buffer_cache.h
4 buffer_cache/map_interval.cpp 4 buffer_cache/map_interval.cpp
5 buffer_cache/map_interval.h 5 buffer_cache/map_interval.h
6 compatible_formats.cpp
7 compatible_formats.h
6 dirty_flags.cpp 8 dirty_flags.cpp
7 dirty_flags.h 9 dirty_flags.h
8 dma_pusher.cpp 10 dma_pusher.cpp
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index cf8bdd021..c6479af9f 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -322,8 +322,7 @@ protected:
322 } 322 }
323 323
324private: 324private:
325 MapInterval* MapAddress(const Buffer* block, GPUVAddr gpu_addr, VAddr cpu_addr, 325 MapInterval* MapAddress(Buffer* block, GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size) {
326 std::size_t size) {
327 const VectorMapInterval overlaps = GetMapsInRange(cpu_addr, size); 326 const VectorMapInterval overlaps = GetMapsInRange(cpu_addr, size);
328 if (overlaps.empty()) { 327 if (overlaps.empty()) {
329 auto& memory_manager = system.GPU().MemoryManager(); 328 auto& memory_manager = system.GPU().MemoryManager();
@@ -377,8 +376,7 @@ private:
377 return map; 376 return map;
378 } 377 }
379 378
380 void UpdateBlock(const Buffer* block, VAddr start, VAddr end, 379 void UpdateBlock(Buffer* block, VAddr start, VAddr end, const VectorMapInterval& overlaps) {
381 const VectorMapInterval& overlaps) {
382 const IntervalType base_interval{start, end}; 380 const IntervalType base_interval{start, end};
383 IntervalSet interval_set{}; 381 IntervalSet interval_set{};
384 interval_set.add(base_interval); 382 interval_set.add(base_interval);
diff --git a/src/video_core/compatible_formats.cpp b/src/video_core/compatible_formats.cpp
new file mode 100644
index 000000000..6c426b035
--- /dev/null
+++ b/src/video_core/compatible_formats.cpp
@@ -0,0 +1,162 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <bitset>
7#include <cstddef>
8
9#include "video_core/compatible_formats.h"
10#include "video_core/surface.h"
11
12namespace VideoCore::Surface {
13
14namespace {
15
16// Compatibility table taken from Table 3.X.2 in:
17// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_view.txt
18
19constexpr std::array VIEW_CLASS_128_BITS = {
20 PixelFormat::RGBA32F,
21 PixelFormat::RGBA32UI,
22};
23// Missing formats:
24// PixelFormat::RGBA32I
25
26constexpr std::array VIEW_CLASS_96_BITS = {
27 PixelFormat::RGB32F,
28};
29// Missing formats:
30// PixelFormat::RGB32UI,
31// PixelFormat::RGB32I,
32
33constexpr std::array VIEW_CLASS_64_BITS = {
34 PixelFormat::RGBA16F, PixelFormat::RG32F, PixelFormat::RGBA16UI, PixelFormat::RG32UI,
35 PixelFormat::RGBA16U, PixelFormat::RGBA16F, PixelFormat::RGBA16S,
36};
37// Missing formats:
38// PixelFormat::RGBA16I
39// PixelFormat::RG32I
40
41// TODO: How should we handle 48 bits?
42
43constexpr std::array VIEW_CLASS_32_BITS = {
44 PixelFormat::RG16F, PixelFormat::R11FG11FB10F, PixelFormat::R32F,
45 PixelFormat::A2B10G10R10U, PixelFormat::RG16UI, PixelFormat::R32UI,
46 PixelFormat::RG16I, PixelFormat::R32I, PixelFormat::ABGR8U,
47 PixelFormat::RG16, PixelFormat::ABGR8S, PixelFormat::RG16S,
48 PixelFormat::RGBA8_SRGB, PixelFormat::E5B9G9R9F, PixelFormat::BGRA8,
49 PixelFormat::BGRA8_SRGB,
50};
51// Missing formats:
52// PixelFormat::RGBA8UI
53// PixelFormat::RGBA8I
54// PixelFormat::RGB10_A2_UI
55
56// TODO: How should we handle 24 bits?
57
58constexpr std::array VIEW_CLASS_16_BITS = {
59 PixelFormat::R16F, PixelFormat::RG8UI, PixelFormat::R16UI, PixelFormat::R16I,
60 PixelFormat::RG8U, PixelFormat::R16U, PixelFormat::RG8S, PixelFormat::R16S,
61};
62// Missing formats:
63// PixelFormat::RG8I
64
65constexpr std::array VIEW_CLASS_8_BITS = {
66 PixelFormat::R8UI,
67 PixelFormat::R8U,
68};
69// Missing formats:
70// PixelFormat::R8I
71// PixelFormat::R8S
72
73constexpr std::array VIEW_CLASS_RGTC1_RED = {
74 PixelFormat::DXN1,
75};
76// Missing formats:
77// COMPRESSED_SIGNED_RED_RGTC1
78
79constexpr std::array VIEW_CLASS_RGTC2_RG = {
80 PixelFormat::DXN2UNORM,
81 PixelFormat::DXN2SNORM,
82};
83
84constexpr std::array VIEW_CLASS_BPTC_UNORM = {
85 PixelFormat::BC7U,
86 PixelFormat::BC7U_SRGB,
87};
88
89constexpr std::array VIEW_CLASS_BPTC_FLOAT = {
90 PixelFormat::BC6H_SF16,
91 PixelFormat::BC6H_UF16,
92};
93
94// Compatibility table taken from Table 4.X.1 in:
95// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_copy_image.txt
96
97constexpr std::array COPY_CLASS_128_BITS = {
98 PixelFormat::RGBA32UI, PixelFormat::RGBA32F, PixelFormat::DXT23,
99 PixelFormat::DXT23_SRGB, PixelFormat::DXT45, PixelFormat::DXT45_SRGB,
100 PixelFormat::DXN2SNORM, PixelFormat::BC7U, PixelFormat::BC7U_SRGB,
101 PixelFormat::BC6H_SF16, PixelFormat::BC6H_UF16,
102};
103// Missing formats:
104// PixelFormat::RGBA32I
105// COMPRESSED_RG_RGTC2
106
107constexpr std::array COPY_CLASS_64_BITS = {
108 PixelFormat::RGBA16F, PixelFormat::RG32F, PixelFormat::RGBA16UI, PixelFormat::RG32UI,
109 PixelFormat::RGBA16U, PixelFormat::RGBA16S, PixelFormat::DXT1_SRGB, PixelFormat::DXT1,
110
111};
112// Missing formats:
113// PixelFormat::RGBA16I
114// PixelFormat::RG32I,
115// COMPRESSED_RGB_S3TC_DXT1_EXT
116// COMPRESSED_SRGB_S3TC_DXT1_EXT
117// COMPRESSED_RGBA_S3TC_DXT1_EXT
118// COMPRESSED_SIGNED_RED_RGTC1
119
120void Enable(FormatCompatibility::Table& compatiblity, size_t format_a, size_t format_b) {
121 compatiblity[format_a][format_b] = true;
122 compatiblity[format_b][format_a] = true;
123}
124
125void Enable(FormatCompatibility::Table& compatibility, PixelFormat format_a, PixelFormat format_b) {
126 Enable(compatibility, static_cast<size_t>(format_a), static_cast<size_t>(format_b));
127}
128
129template <typename Range>
130void EnableRange(FormatCompatibility::Table& compatibility, const Range& range) {
131 for (auto it_a = range.begin(); it_a != range.end(); ++it_a) {
132 for (auto it_b = it_a; it_b != range.end(); ++it_b) {
133 Enable(compatibility, *it_a, *it_b);
134 }
135 }
136}
137
138} // Anonymous namespace
139
140FormatCompatibility::FormatCompatibility() {
141 for (size_t i = 0; i < MaxPixelFormat; ++i) {
142 // Identity is allowed
143 Enable(view, i, i);
144 }
145
146 EnableRange(view, VIEW_CLASS_128_BITS);
147 EnableRange(view, VIEW_CLASS_96_BITS);
148 EnableRange(view, VIEW_CLASS_64_BITS);
149 EnableRange(view, VIEW_CLASS_32_BITS);
150 EnableRange(view, VIEW_CLASS_16_BITS);
151 EnableRange(view, VIEW_CLASS_8_BITS);
152 EnableRange(view, VIEW_CLASS_RGTC1_RED);
153 EnableRange(view, VIEW_CLASS_RGTC2_RG);
154 EnableRange(view, VIEW_CLASS_BPTC_UNORM);
155 EnableRange(view, VIEW_CLASS_BPTC_FLOAT);
156
157 copy = view;
158 EnableRange(copy, COPY_CLASS_128_BITS);
159 EnableRange(copy, COPY_CLASS_64_BITS);
160}
161
162} // namespace VideoCore::Surface
diff --git a/src/video_core/compatible_formats.h b/src/video_core/compatible_formats.h
new file mode 100644
index 000000000..d1082566d
--- /dev/null
+++ b/src/video_core/compatible_formats.h
@@ -0,0 +1,32 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <bitset>
7#include <cstddef>
8
9#include "video_core/surface.h"
10
11namespace VideoCore::Surface {
12
13class FormatCompatibility {
14public:
15 using Table = std::array<std::bitset<MaxPixelFormat>, MaxPixelFormat>;
16
17 explicit FormatCompatibility();
18
19 bool TestView(PixelFormat format_a, PixelFormat format_b) const noexcept {
20 return view[static_cast<size_t>(format_a)][static_cast<size_t>(format_b)];
21 }
22
23 bool TestCopy(PixelFormat format_a, PixelFormat format_b) const noexcept {
24 return copy[static_cast<size_t>(format_a)][static_cast<size_t>(format_b)];
25 }
26
27private:
28 Table view;
29 Table copy;
30};
31
32} // namespace VideoCore::Surface
diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp
index ef7dad349..a50e7b4e0 100644
--- a/src/video_core/macro/macro.cpp
+++ b/src/video_core/macro/macro.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <optional>
5#include <boost/container_hash/hash.hpp> 6#include <boost/container_hash/hash.hpp>
6#include "common/assert.h" 7#include "common/assert.h"
7#include "common/logging/log.h" 8#include "common/logging/log.h"
@@ -35,22 +36,40 @@ void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method,
35 } 36 }
36 } else { 37 } else {
37 // Macro not compiled, check if it's uploaded and if so, compile it 38 // Macro not compiled, check if it's uploaded and if so, compile it
38 auto macro_code = uploaded_macro_code.find(method); 39 std::optional<u32> mid_method = std::nullopt;
40 const auto macro_code = uploaded_macro_code.find(method);
39 if (macro_code == uploaded_macro_code.end()) { 41 if (macro_code == uploaded_macro_code.end()) {
40 UNREACHABLE_MSG("Macro 0x{0:x} was not uploaded", method); 42 for (const auto& [method_base, code] : uploaded_macro_code) {
41 return; 43 if (method >= method_base && (method - method_base) < code.size()) {
44 mid_method = method_base;
45 break;
46 }
47 }
48 if (!mid_method.has_value()) {
49 UNREACHABLE_MSG("Macro 0x{0:x} was not uploaded", method);
50 return;
51 }
42 } 52 }
43 auto& cache_info = macro_cache[method]; 53 auto& cache_info = macro_cache[method];
44 cache_info.hash = boost::hash_value(macro_code->second); 54
45 cache_info.lle_program = Compile(macro_code->second); 55 if (!mid_method.has_value()) {
56 cache_info.lle_program = Compile(macro_code->second);
57 cache_info.hash = boost::hash_value(macro_code->second);
58 } else {
59 const auto& macro_cached = uploaded_macro_code[mid_method.value()];
60 const auto rebased_method = method - mid_method.value();
61 auto& code = uploaded_macro_code[method];
62 code.resize(macro_cached.size() - rebased_method);
63 std::memcpy(code.data(), macro_cached.data() + rebased_method,
64 code.size() * sizeof(u32));
65 cache_info.hash = boost::hash_value(code);
66 cache_info.lle_program = Compile(code);
67 }
46 68
47 auto hle_program = hle_macros->GetHLEProgram(cache_info.hash); 69 auto hle_program = hle_macros->GetHLEProgram(cache_info.hash);
48 if (hle_program.has_value()) { 70 if (hle_program.has_value()) {
49 cache_info.has_hle_program = true; 71 cache_info.has_hle_program = true;
50 cache_info.hle_program = std::move(hle_program.value()); 72 cache_info.hle_program = std::move(hle_program.value());
51 }
52
53 if (cache_info.has_hle_program) {
54 cache_info.hle_program->Execute(parameters, method); 73 cache_info.hle_program->Execute(parameters, method);
55 } else { 74 } else {
56 cache_info.lle_program->Execute(parameters, method); 75 cache_info.lle_program->Execute(parameters, method);
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index d9f7b4cc6..e461e4c70 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -34,20 +34,27 @@ Buffer::Buffer(const Device& device, VAddr cpu_addr, std::size_t size)
34 34
35Buffer::~Buffer() = default; 35Buffer::~Buffer() = default;
36 36
37void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) const { 37void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) {
38 glNamedBufferSubData(Handle(), static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size), 38 glNamedBufferSubData(Handle(), static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size),
39 data); 39 data);
40} 40}
41 41
42void Buffer::Download(std::size_t offset, std::size_t size, u8* data) const { 42void Buffer::Download(std::size_t offset, std::size_t size, u8* data) {
43 MICROPROFILE_SCOPE(OpenGL_Buffer_Download); 43 MICROPROFILE_SCOPE(OpenGL_Buffer_Download);
44 const GLsizeiptr gl_size = static_cast<GLsizeiptr>(size);
45 const GLintptr gl_offset = static_cast<GLintptr>(offset);
46 if (read_buffer.handle == 0) {
47 read_buffer.Create();
48 glNamedBufferData(read_buffer.handle, static_cast<GLsizeiptr>(Size()), nullptr,
49 GL_STREAM_READ);
50 }
44 glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); 51 glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
45 glGetNamedBufferSubData(Handle(), static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size), 52 glCopyNamedBufferSubData(gl_buffer.handle, read_buffer.handle, gl_offset, gl_offset, gl_size);
46 data); 53 glGetNamedBufferSubData(read_buffer.handle, gl_offset, gl_size, data);
47} 54}
48 55
49void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, 56void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset,
50 std::size_t size) const { 57 std::size_t size) {
51 glCopyNamedBufferSubData(src.Handle(), Handle(), static_cast<GLintptr>(src_offset), 58 glCopyNamedBufferSubData(src.Handle(), Handle(), static_cast<GLintptr>(src_offset),
52 static_cast<GLintptr>(dst_offset), static_cast<GLsizeiptr>(size)); 59 static_cast<GLintptr>(dst_offset), static_cast<GLsizeiptr>(size));
53} 60}
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index 59d95adbc..88fdc0536 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -28,12 +28,12 @@ public:
28 explicit Buffer(const Device& device, VAddr cpu_addr, std::size_t size); 28 explicit Buffer(const Device& device, VAddr cpu_addr, std::size_t size);
29 ~Buffer(); 29 ~Buffer();
30 30
31 void Upload(std::size_t offset, std::size_t size, const u8* data) const; 31 void Upload(std::size_t offset, std::size_t size, const u8* data);
32 32
33 void Download(std::size_t offset, std::size_t size, u8* data) const; 33 void Download(std::size_t offset, std::size_t size, u8* data);
34 34
35 void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, 35 void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset,
36 std::size_t size) const; 36 std::size_t size);
37 37
38 GLuint Handle() const noexcept { 38 GLuint Handle() const noexcept {
39 return gl_buffer.handle; 39 return gl_buffer.handle;
@@ -45,6 +45,7 @@ public:
45 45
46private: 46private:
47 OGLBuffer gl_buffer; 47 OGLBuffer gl_buffer;
48 OGLBuffer read_buffer;
48 u64 gpu_address = 0; 49 u64 gpu_address = 0;
49}; 50};
50 51
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index b6b6659c1..208fc6167 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -188,20 +188,6 @@ bool IsASTCSupported() {
188 return true; 188 return true;
189} 189}
190 190
191/// @brief Returns true when a GL_RENDERER is a Turing GPU
192/// @param renderer GL_RENDERER string
193bool IsTuring(std::string_view renderer) {
194 static constexpr std::array<std::string_view, 12> TURING_GPUS = {
195 "GTX 1650", "GTX 1660", "RTX 2060", "RTX 2070",
196 "RTX 2080", "TITAN RTX", "Quadro RTX 3000", "Quadro RTX 4000",
197 "Quadro RTX 5000", "Quadro RTX 6000", "Quadro RTX 8000", "Tesla T4",
198 };
199 return std::any_of(TURING_GPUS.begin(), TURING_GPUS.end(),
200 [renderer](std::string_view candidate) {
201 return renderer.find(candidate) != std::string_view::npos;
202 });
203}
204
205} // Anonymous namespace 191} // Anonymous namespace
206 192
207Device::Device() 193Device::Device()
@@ -213,7 +199,6 @@ Device::Device()
213 199
214 const bool is_nvidia = vendor == "NVIDIA Corporation"; 200 const bool is_nvidia = vendor == "NVIDIA Corporation";
215 const bool is_amd = vendor == "ATI Technologies Inc."; 201 const bool is_amd = vendor == "ATI Technologies Inc.";
216 const bool is_turing = is_nvidia && IsTuring(renderer);
217 202
218 bool disable_fast_buffer_sub_data = false; 203 bool disable_fast_buffer_sub_data = false;
219 if (is_nvidia && version == "4.6.0 NVIDIA 443.24") { 204 if (is_nvidia && version == "4.6.0 NVIDIA 443.24") {
@@ -238,15 +223,12 @@ Device::Device()
238 has_component_indexing_bug = is_amd; 223 has_component_indexing_bug = is_amd;
239 has_precise_bug = TestPreciseBug(); 224 has_precise_bug = TestPreciseBug();
240 has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2; 225 has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2;
226 has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory;
241 227
242 // At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive 228 // At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive
243 // uniform buffers as "push constants" 229 // uniform buffers as "push constants"
244 has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data; 230 has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data;
245 231
246 // Nvidia's driver on Turing GPUs randomly crashes when the buffer is made resident, or on
247 // DeleteBuffers. Disable unified memory on these devices.
248 has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory && !is_turing;
249
250 use_assembly_shaders = Settings::values.use_assembly_shaders && GLAD_GL_NV_gpu_program5 && 232 use_assembly_shaders = Settings::values.use_assembly_shaders && GLAD_GL_NV_gpu_program5 &&
251 GLAD_GL_NV_compute_program5 && GLAD_GL_NV_transform_feedback && 233 GLAD_GL_NV_compute_program5 && GLAD_GL_NV_transform_feedback &&
252 GLAD_GL_NV_transform_feedback2; 234 GLAD_GL_NV_transform_feedback2;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 362457ffe..e960a0ef1 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -213,9 +213,10 @@ void RasterizerOpenGL::SetupVertexFormat() {
213 if (attrib.type == Maxwell::VertexAttribute::Type::SignedInt || 213 if (attrib.type == Maxwell::VertexAttribute::Type::SignedInt ||
214 attrib.type == Maxwell::VertexAttribute::Type::UnsignedInt) { 214 attrib.type == Maxwell::VertexAttribute::Type::UnsignedInt) {
215 glVertexAttribIFormat(gl_index, attrib.ComponentCount(), 215 glVertexAttribIFormat(gl_index, attrib.ComponentCount(),
216 MaxwellToGL::VertexType(attrib), attrib.offset); 216 MaxwellToGL::VertexFormat(attrib), attrib.offset);
217 } else { 217 } else {
218 glVertexAttribFormat(gl_index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib), 218 glVertexAttribFormat(gl_index, attrib.ComponentCount(),
219 MaxwellToGL::VertexFormat(attrib),
219 attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset); 220 attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
220 } 221 }
221 glVertexAttribBinding(gl_index, attrib.buffer); 222 glVertexAttribBinding(gl_index, attrib.buffer);
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 35e329240..fe9bd4b5a 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -24,10 +24,11 @@ namespace MaxwellToGL {
24 24
25using Maxwell = Tegra::Engines::Maxwell3D::Regs; 25using Maxwell = Tegra::Engines::Maxwell3D::Regs;
26 26
27inline GLenum VertexType(Maxwell::VertexAttribute attrib) { 27inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) {
28 switch (attrib.type) { 28 switch (attrib.type) {
29 case Maxwell::VertexAttribute::Type::UnsignedInt:
30 case Maxwell::VertexAttribute::Type::UnsignedNorm: 29 case Maxwell::VertexAttribute::Type::UnsignedNorm:
30 case Maxwell::VertexAttribute::Type::UnsignedScaled:
31 case Maxwell::VertexAttribute::Type::UnsignedInt:
31 switch (attrib.size) { 32 switch (attrib.size) {
32 case Maxwell::VertexAttribute::Size::Size_8: 33 case Maxwell::VertexAttribute::Size::Size_8:
33 case Maxwell::VertexAttribute::Size::Size_8_8: 34 case Maxwell::VertexAttribute::Size::Size_8_8:
@@ -48,8 +49,9 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
48 return GL_UNSIGNED_INT_2_10_10_10_REV; 49 return GL_UNSIGNED_INT_2_10_10_10_REV;
49 } 50 }
50 break; 51 break;
51 case Maxwell::VertexAttribute::Type::SignedInt:
52 case Maxwell::VertexAttribute::Type::SignedNorm: 52 case Maxwell::VertexAttribute::Type::SignedNorm:
53 case Maxwell::VertexAttribute::Type::SignedScaled:
54 case Maxwell::VertexAttribute::Type::SignedInt:
53 switch (attrib.size) { 55 switch (attrib.size) {
54 case Maxwell::VertexAttribute::Size::Size_8: 56 case Maxwell::VertexAttribute::Size::Size_8:
55 case Maxwell::VertexAttribute::Size::Size_8_8: 57 case Maxwell::VertexAttribute::Size::Size_8_8:
@@ -84,36 +86,8 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
84 return GL_FLOAT; 86 return GL_FLOAT;
85 } 87 }
86 break; 88 break;
87 case Maxwell::VertexAttribute::Type::UnsignedScaled:
88 switch (attrib.size) {
89 case Maxwell::VertexAttribute::Size::Size_8:
90 case Maxwell::VertexAttribute::Size::Size_8_8:
91 case Maxwell::VertexAttribute::Size::Size_8_8_8:
92 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
93 return GL_UNSIGNED_BYTE;
94 case Maxwell::VertexAttribute::Size::Size_16:
95 case Maxwell::VertexAttribute::Size::Size_16_16:
96 case Maxwell::VertexAttribute::Size::Size_16_16_16:
97 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
98 return GL_UNSIGNED_SHORT;
99 }
100 break;
101 case Maxwell::VertexAttribute::Type::SignedScaled:
102 switch (attrib.size) {
103 case Maxwell::VertexAttribute::Size::Size_8:
104 case Maxwell::VertexAttribute::Size::Size_8_8:
105 case Maxwell::VertexAttribute::Size::Size_8_8_8:
106 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
107 return GL_BYTE;
108 case Maxwell::VertexAttribute::Size::Size_16:
109 case Maxwell::VertexAttribute::Size::Size_16_16:
110 case Maxwell::VertexAttribute::Size::Size_16_16_16:
111 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
112 return GL_SHORT;
113 }
114 break;
115 } 89 }
116 UNIMPLEMENTED_MSG("Unimplemented vertex type={} and size={}", attrib.TypeString(), 90 UNIMPLEMENTED_MSG("Unimplemented vertex format of type={} and size={}", attrib.TypeString(),
117 attrib.SizeString()); 91 attrib.SizeString());
118 return {}; 92 return {};
119} 93}
@@ -217,6 +191,12 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
217 } else { 191 } else {
218 return GL_MIRROR_CLAMP_TO_EDGE; 192 return GL_MIRROR_CLAMP_TO_EDGE;
219 } 193 }
194 case Tegra::Texture::WrapMode::MirrorOnceClampOGL:
195 if (GL_EXT_texture_mirror_clamp) {
196 return GL_MIRROR_CLAMP_EXT;
197 } else {
198 return GL_MIRROR_CLAMP_TO_EDGE;
199 }
220 } 200 }
221 UNIMPLEMENTED_MSG("Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); 201 UNIMPLEMENTED_MSG("Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));
222 return GL_REPEAT; 202 return GL_REPEAT;
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index 424278816..d1f0ea932 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -39,52 +39,18 @@ constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
39 39
40} // Anonymous namespace 40} // Anonymous namespace
41 41
42void FixedPipelineState::DepthStencil::Fill(const Maxwell& regs) noexcept { 42void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_state) {
43 raw = 0;
44 front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail));
45 front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail));
46 front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass));
47 front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func));
48 if (regs.stencil_two_side_enable) {
49 back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail));
50 back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail));
51 back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass));
52 back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func));
53 } else {
54 back.action_stencil_fail.Assign(front.action_stencil_fail);
55 back.action_depth_fail.Assign(front.action_depth_fail);
56 back.action_depth_pass.Assign(front.action_depth_pass);
57 back.test_func.Assign(front.test_func);
58 }
59 depth_test_enable.Assign(regs.depth_test_enable);
60 depth_write_enable.Assign(regs.depth_write_enabled);
61 depth_bounds_enable.Assign(regs.depth_bounds_enable);
62 stencil_enable.Assign(regs.stencil_enable);
63 depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
64}
65
66void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept {
67 const auto& clip = regs.view_volume_clip_control; 43 const auto& clip = regs.view_volume_clip_control;
68 const std::array enabled_lut = {regs.polygon_offset_point_enable, 44 const std::array enabled_lut = {regs.polygon_offset_point_enable,
69 regs.polygon_offset_line_enable, 45 regs.polygon_offset_line_enable,
70 regs.polygon_offset_fill_enable}; 46 regs.polygon_offset_fill_enable};
71 const u32 topology_index = static_cast<u32>(regs.draw.topology.Value()); 47 const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
72 48
73 u32 packed_front_face = PackFrontFace(regs.front_face);
74 if (regs.screen_y_control.triangle_rast_flip != 0) {
75 // Flip front face
76 packed_front_face = 1 - packed_front_face;
77 }
78
79 raw = 0; 49 raw = 0;
80 topology.Assign(topology_index);
81 primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0); 50 primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
82 cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0);
83 depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); 51 depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
84 depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value()); 52 depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value());
85 ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); 53 ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
86 cull_face.Assign(PackCullFace(regs.cull_face));
87 front_face.Assign(packed_front_face);
88 polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front)); 54 polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front));
89 patch_control_points_minus_one.Assign(regs.patch_vertices - 1); 55 patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
90 tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value())); 56 tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value()));
@@ -93,19 +59,37 @@ void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept {
93 logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); 59 logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
94 logic_op.Assign(PackLogicOp(regs.logic_op.operation)); 60 logic_op.Assign(PackLogicOp(regs.logic_op.operation));
95 rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0); 61 rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
62
96 std::memcpy(&point_size, &regs.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast 63 std::memcpy(&point_size, &regs.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast
97}
98 64
99void FixedPipelineState::ColorBlending::Fill(const Maxwell& regs) noexcept { 65 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
66 binding_divisors[index] =
67 regs.instanced_arrays.IsInstancingEnabled(index) ? regs.vertex_array[index].divisor : 0;
68 }
69
70 for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
71 const auto& input = regs.vertex_attrib_format[index];
72 auto& attribute = attributes[index];
73 attribute.raw = 0;
74 attribute.enabled.Assign(input.IsConstant() ? 0 : 1);
75 attribute.buffer.Assign(input.buffer);
76 attribute.offset.Assign(input.offset);
77 attribute.type.Assign(static_cast<u32>(input.type.Value()));
78 attribute.size.Assign(static_cast<u32>(input.size.Value()));
79 }
80
100 for (std::size_t index = 0; index < std::size(attachments); ++index) { 81 for (std::size_t index = 0; index < std::size(attachments); ++index) {
101 attachments[index].Fill(regs, index); 82 attachments[index].Fill(regs, index);
102 } 83 }
103}
104 84
105void FixedPipelineState::ViewportSwizzles::Fill(const Maxwell& regs) noexcept {
106 const auto& transform = regs.viewport_transform; 85 const auto& transform = regs.viewport_transform;
107 std::transform(transform.begin(), transform.end(), swizzles.begin(), 86 std::transform(transform.begin(), transform.end(), viewport_swizzles.begin(),
108 [](const auto& viewport) { return static_cast<u16>(viewport.swizzle.raw); }); 87 [](const auto& viewport) { return static_cast<u16>(viewport.swizzle.raw); });
88
89 if (!has_extended_dynamic_state) {
90 no_extended_dynamic_state.Assign(1);
91 dynamic_state.Fill(regs);
92 }
109} 93}
110 94
111void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) { 95void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) {
@@ -147,20 +131,57 @@ void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size
147 enable.Assign(1); 131 enable.Assign(1);
148} 132}
149 133
150void FixedPipelineState::Fill(const Maxwell& regs) { 134void FixedPipelineState::DynamicState::Fill(const Maxwell& regs) {
151 rasterizer.Fill(regs); 135 const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
152 depth_stencil.Fill(regs); 136 u32 packed_front_face = PackFrontFace(regs.front_face);
153 color_blending.Fill(regs); 137 if (regs.screen_y_control.triangle_rast_flip != 0) {
154 viewport_swizzles.Fill(regs); 138 // Flip front face
139 packed_front_face = 1 - packed_front_face;
140 }
141
142 raw1 = 0;
143 raw2 = 0;
144 front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail));
145 front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail));
146 front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass));
147 front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func));
148 if (regs.stencil_two_side_enable) {
149 back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail));
150 back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail));
151 back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass));
152 back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func));
153 } else {
154 back.action_stencil_fail.Assign(front.action_stencil_fail);
155 back.action_depth_fail.Assign(front.action_depth_fail);
156 back.action_depth_pass.Assign(front.action_depth_pass);
157 back.test_func.Assign(front.test_func);
158 }
159 stencil_enable.Assign(regs.stencil_enable);
160 depth_write_enable.Assign(regs.depth_write_enabled);
161 depth_bounds_enable.Assign(regs.depth_bounds_enable);
162 depth_test_enable.Assign(regs.depth_test_enable);
163 front_face.Assign(packed_front_face);
164 depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
165 topology.Assign(topology_index);
166 cull_face.Assign(PackCullFace(regs.cull_face));
167 cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0);
168
169 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
170 const auto& input = regs.vertex_array[index];
171 VertexBinding& binding = vertex_bindings[index];
172 binding.raw = 0;
173 binding.enabled.Assign(input.IsEnabled() ? 1 : 0);
174 binding.stride.Assign(static_cast<u16>(input.stride.Value()));
175 }
155} 176}
156 177
157std::size_t FixedPipelineState::Hash() const noexcept { 178std::size_t FixedPipelineState::Hash() const noexcept {
158 const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this); 179 const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size());
159 return static_cast<std::size_t>(hash); 180 return static_cast<std::size_t>(hash);
160} 181}
161 182
162bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept { 183bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept {
163 return std::memcmp(this, &rhs, sizeof *this) == 0; 184 return std::memcmp(this, &rhs, Size()) == 0;
164} 185}
165 186
166u32 FixedPipelineState::PackComparisonOp(Maxwell::ComparisonOp op) noexcept { 187u32 FixedPipelineState::PackComparisonOp(Maxwell::ComparisonOp op) noexcept {
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index 31a6398f2..cdcbb65f5 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -60,14 +60,6 @@ struct FixedPipelineState {
60 60
61 void Fill(const Maxwell& regs, std::size_t index); 61 void Fill(const Maxwell& regs, std::size_t index);
62 62
63 std::size_t Hash() const noexcept;
64
65 bool operator==(const BlendingAttachment& rhs) const noexcept;
66
67 bool operator!=(const BlendingAttachment& rhs) const noexcept {
68 return !operator==(rhs);
69 }
70
71 constexpr std::array<bool, 4> Mask() const noexcept { 63 constexpr std::array<bool, 4> Mask() const noexcept {
72 return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0}; 64 return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0};
73 } 65 }
@@ -97,156 +89,116 @@ struct FixedPipelineState {
97 } 89 }
98 }; 90 };
99 91
100 struct VertexInput { 92 union VertexAttribute {
101 union Binding { 93 u32 raw;
102 u16 raw; 94 BitField<0, 1, u32> enabled;
103 BitField<0, 1, u16> enabled; 95 BitField<1, 5, u32> buffer;
104 BitField<1, 12, u16> stride; 96 BitField<6, 14, u32> offset;
105 }; 97 BitField<20, 3, u32> type;
98 BitField<23, 6, u32> size;
106 99
107 union Attribute { 100 constexpr Maxwell::VertexAttribute::Type Type() const noexcept {
108 u32 raw; 101 return static_cast<Maxwell::VertexAttribute::Type>(type.Value());
109 BitField<0, 1, u32> enabled;
110 BitField<1, 5, u32> buffer;
111 BitField<6, 14, u32> offset;
112 BitField<20, 3, u32> type;
113 BitField<23, 6, u32> size;
114
115 constexpr Maxwell::VertexAttribute::Type Type() const noexcept {
116 return static_cast<Maxwell::VertexAttribute::Type>(type.Value());
117 }
118
119 constexpr Maxwell::VertexAttribute::Size Size() const noexcept {
120 return static_cast<Maxwell::VertexAttribute::Size>(size.Value());
121 }
122 };
123
124 std::array<Binding, Maxwell::NumVertexArrays> bindings;
125 std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
126 std::array<Attribute, Maxwell::NumVertexAttributes> attributes;
127
128 void SetBinding(std::size_t index, bool enabled, u32 stride, u32 divisor) noexcept {
129 auto& binding = bindings[index];
130 binding.raw = 0;
131 binding.enabled.Assign(enabled ? 1 : 0);
132 binding.stride.Assign(static_cast<u16>(stride));
133 binding_divisors[index] = divisor;
134 } 102 }
135 103
136 void SetAttribute(std::size_t index, bool enabled, u32 buffer, u32 offset, 104 constexpr Maxwell::VertexAttribute::Size Size() const noexcept {
137 Maxwell::VertexAttribute::Type type, 105 return static_cast<Maxwell::VertexAttribute::Size>(size.Value());
138 Maxwell::VertexAttribute::Size size) noexcept {
139 auto& attribute = attributes[index];
140 attribute.raw = 0;
141 attribute.enabled.Assign(enabled ? 1 : 0);
142 attribute.buffer.Assign(buffer);
143 attribute.offset.Assign(offset);
144 attribute.type.Assign(static_cast<u32>(type));
145 attribute.size.Assign(static_cast<u32>(size));
146 } 106 }
147 }; 107 };
148 108
149 struct Rasterizer { 109 template <std::size_t Position>
150 union { 110 union StencilFace {
151 u32 raw; 111 BitField<Position + 0, 3, u32> action_stencil_fail;
152 BitField<0, 4, u32> topology; 112 BitField<Position + 3, 3, u32> action_depth_fail;
153 BitField<4, 1, u32> primitive_restart_enable; 113 BitField<Position + 6, 3, u32> action_depth_pass;
154 BitField<5, 1, u32> cull_enable; 114 BitField<Position + 9, 3, u32> test_func;
155 BitField<6, 1, u32> depth_bias_enable;
156 BitField<7, 1, u32> depth_clamp_disabled;
157 BitField<8, 1, u32> ndc_minus_one_to_one;
158 BitField<9, 2, u32> cull_face;
159 BitField<11, 1, u32> front_face;
160 BitField<12, 2, u32> polygon_mode;
161 BitField<14, 5, u32> patch_control_points_minus_one;
162 BitField<19, 2, u32> tessellation_primitive;
163 BitField<21, 2, u32> tessellation_spacing;
164 BitField<23, 1, u32> tessellation_clockwise;
165 BitField<24, 1, u32> logic_op_enable;
166 BitField<25, 4, u32> logic_op;
167 BitField<29, 1, u32> rasterize_enable;
168 };
169
170 // TODO(Rodrigo): Move this to push constants
171 u32 point_size;
172 115
173 void Fill(const Maxwell& regs) noexcept; 116 Maxwell::StencilOp ActionStencilFail() const noexcept {
117 return UnpackStencilOp(action_stencil_fail);
118 }
174 119
175 constexpr Maxwell::PrimitiveTopology Topology() const noexcept { 120 Maxwell::StencilOp ActionDepthFail() const noexcept {
176 return static_cast<Maxwell::PrimitiveTopology>(topology.Value()); 121 return UnpackStencilOp(action_depth_fail);
177 } 122 }
178 123
179 Maxwell::CullFace CullFace() const noexcept { 124 Maxwell::StencilOp ActionDepthPass() const noexcept {
180 return UnpackCullFace(cull_face.Value()); 125 return UnpackStencilOp(action_depth_pass);
181 } 126 }
182 127
183 Maxwell::FrontFace FrontFace() const noexcept { 128 Maxwell::ComparisonOp TestFunc() const noexcept {
184 return UnpackFrontFace(front_face.Value()); 129 return UnpackComparisonOp(test_func);
185 } 130 }
186 }; 131 };
187 132
188 struct DepthStencil { 133 union VertexBinding {
189 template <std::size_t Position> 134 u16 raw;
190 union StencilFace { 135 BitField<0, 12, u16> stride;
191 BitField<Position + 0, 3, u32> action_stencil_fail; 136 BitField<12, 1, u16> enabled;
192 BitField<Position + 3, 3, u32> action_depth_fail; 137 };
193 BitField<Position + 6, 3, u32> action_depth_pass;
194 BitField<Position + 9, 3, u32> test_func;
195
196 Maxwell::StencilOp ActionStencilFail() const noexcept {
197 return UnpackStencilOp(action_stencil_fail);
198 }
199
200 Maxwell::StencilOp ActionDepthFail() const noexcept {
201 return UnpackStencilOp(action_depth_fail);
202 }
203
204 Maxwell::StencilOp ActionDepthPass() const noexcept {
205 return UnpackStencilOp(action_depth_pass);
206 }
207
208 Maxwell::ComparisonOp TestFunc() const noexcept {
209 return UnpackComparisonOp(test_func);
210 }
211 };
212 138
139 struct DynamicState {
213 union { 140 union {
214 u32 raw; 141 u32 raw1;
215 StencilFace<0> front; 142 StencilFace<0> front;
216 StencilFace<12> back; 143 StencilFace<12> back;
217 BitField<24, 1, u32> depth_test_enable; 144 BitField<24, 1, u32> stencil_enable;
218 BitField<25, 1, u32> depth_write_enable; 145 BitField<25, 1, u32> depth_write_enable;
219 BitField<26, 1, u32> depth_bounds_enable; 146 BitField<26, 1, u32> depth_bounds_enable;
220 BitField<27, 1, u32> stencil_enable; 147 BitField<27, 1, u32> depth_test_enable;
221 BitField<28, 3, u32> depth_test_func; 148 BitField<28, 1, u32> front_face;
149 BitField<29, 3, u32> depth_test_func;
150 };
151 union {
152 u32 raw2;
153 BitField<0, 4, u32> topology;
154 BitField<4, 2, u32> cull_face;
155 BitField<6, 1, u32> cull_enable;
222 }; 156 };
157 std::array<VertexBinding, Maxwell::NumVertexArrays> vertex_bindings;
223 158
224 void Fill(const Maxwell& regs) noexcept; 159 void Fill(const Maxwell& regs);
225 160
226 Maxwell::ComparisonOp DepthTestFunc() const noexcept { 161 Maxwell::ComparisonOp DepthTestFunc() const noexcept {
227 return UnpackComparisonOp(depth_test_func); 162 return UnpackComparisonOp(depth_test_func);
228 } 163 }
229 };
230
231 struct ColorBlending {
232 std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
233 164
234 void Fill(const Maxwell& regs) noexcept; 165 Maxwell::CullFace CullFace() const noexcept {
235 }; 166 return UnpackCullFace(cull_face.Value());
167 }
236 168
237 struct ViewportSwizzles { 169 Maxwell::FrontFace FrontFace() const noexcept {
238 std::array<u16, Maxwell::NumViewports> swizzles; 170 return UnpackFrontFace(front_face.Value());
171 }
239 172
240 void Fill(const Maxwell& regs) noexcept; 173 constexpr Maxwell::PrimitiveTopology Topology() const noexcept {
174 return static_cast<Maxwell::PrimitiveTopology>(topology.Value());
175 }
241 }; 176 };
242 177
243 VertexInput vertex_input; 178 union {
244 Rasterizer rasterizer; 179 u32 raw;
245 DepthStencil depth_stencil; 180 BitField<0, 1, u32> no_extended_dynamic_state;
246 ColorBlending color_blending; 181 BitField<2, 1, u32> primitive_restart_enable;
247 ViewportSwizzles viewport_swizzles; 182 BitField<3, 1, u32> depth_bias_enable;
183 BitField<4, 1, u32> depth_clamp_disabled;
184 BitField<5, 1, u32> ndc_minus_one_to_one;
185 BitField<6, 2, u32> polygon_mode;
186 BitField<8, 5, u32> patch_control_points_minus_one;
187 BitField<13, 2, u32> tessellation_primitive;
188 BitField<15, 2, u32> tessellation_spacing;
189 BitField<17, 1, u32> tessellation_clockwise;
190 BitField<18, 1, u32> logic_op_enable;
191 BitField<19, 4, u32> logic_op;
192 BitField<23, 1, u32> rasterize_enable;
193 };
194 u32 point_size;
195 std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
196 std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes;
197 std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
198 std::array<u16, Maxwell::NumViewports> viewport_swizzles;
199 DynamicState dynamic_state;
248 200
249 void Fill(const Maxwell& regs); 201 void Fill(const Maxwell& regs, bool has_extended_dynamic_state);
250 202
251 std::size_t Hash() const noexcept; 203 std::size_t Hash() const noexcept;
252 204
@@ -255,6 +207,11 @@ struct FixedPipelineState {
255 bool operator!=(const FixedPipelineState& rhs) const noexcept { 207 bool operator!=(const FixedPipelineState& rhs) const noexcept {
256 return !operator==(rhs); 208 return !operator==(rhs);
257 } 209 }
210
211 std::size_t Size() const noexcept {
212 const std::size_t total_size = sizeof *this;
213 return total_size - (no_extended_dynamic_state != 0 ? 0 : sizeof(DynamicState));
214 }
258}; 215};
259static_assert(std::has_unique_object_representations_v<FixedPipelineState>); 216static_assert(std::has_unique_object_representations_v<FixedPipelineState>);
260static_assert(std::is_trivially_copyable_v<FixedPipelineState>); 217static_assert(std::is_trivially_copyable_v<FixedPipelineState>);
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 1f2b6734b..d7f1ae89f 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -294,6 +294,28 @@ VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const VKDevice& device,
294 294
295VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) { 295VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) {
296 switch (type) { 296 switch (type) {
297 case Maxwell::VertexAttribute::Type::UnsignedNorm:
298 switch (size) {
299 case Maxwell::VertexAttribute::Size::Size_8:
300 return VK_FORMAT_R8_UNORM;
301 case Maxwell::VertexAttribute::Size::Size_8_8:
302 return VK_FORMAT_R8G8_UNORM;
303 case Maxwell::VertexAttribute::Size::Size_8_8_8:
304 return VK_FORMAT_R8G8B8_UNORM;
305 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
306 return VK_FORMAT_R8G8B8A8_UNORM;
307 case Maxwell::VertexAttribute::Size::Size_16:
308 return VK_FORMAT_R16_UNORM;
309 case Maxwell::VertexAttribute::Size::Size_16_16:
310 return VK_FORMAT_R16G16_UNORM;
311 case Maxwell::VertexAttribute::Size::Size_16_16_16:
312 return VK_FORMAT_R16G16B16_UNORM;
313 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
314 return VK_FORMAT_R16G16B16A16_UNORM;
315 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
316 return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
317 }
318 break;
297 case Maxwell::VertexAttribute::Type::SignedNorm: 319 case Maxwell::VertexAttribute::Type::SignedNorm:
298 switch (size) { 320 switch (size) {
299 case Maxwell::VertexAttribute::Size::Size_8: 321 case Maxwell::VertexAttribute::Size::Size_8:
@@ -314,62 +336,50 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
314 return VK_FORMAT_R16G16B16A16_SNORM; 336 return VK_FORMAT_R16G16B16A16_SNORM;
315 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 337 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
316 return VK_FORMAT_A2B10G10R10_SNORM_PACK32; 338 return VK_FORMAT_A2B10G10R10_SNORM_PACK32;
317 default:
318 break;
319 } 339 }
320 break; 340 break;
321 case Maxwell::VertexAttribute::Type::UnsignedNorm: 341 case Maxwell::VertexAttribute::Type::UnsignedScaled:
322 switch (size) { 342 switch (size) {
323 case Maxwell::VertexAttribute::Size::Size_8: 343 case Maxwell::VertexAttribute::Size::Size_8:
324 return VK_FORMAT_R8_UNORM; 344 return VK_FORMAT_R8_USCALED;
325 case Maxwell::VertexAttribute::Size::Size_8_8: 345 case Maxwell::VertexAttribute::Size::Size_8_8:
326 return VK_FORMAT_R8G8_UNORM; 346 return VK_FORMAT_R8G8_USCALED;
327 case Maxwell::VertexAttribute::Size::Size_8_8_8: 347 case Maxwell::VertexAttribute::Size::Size_8_8_8:
328 return VK_FORMAT_R8G8B8_UNORM; 348 return VK_FORMAT_R8G8B8_USCALED;
329 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 349 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
330 return VK_FORMAT_R8G8B8A8_UNORM; 350 return VK_FORMAT_R8G8B8A8_USCALED;
331 case Maxwell::VertexAttribute::Size::Size_16: 351 case Maxwell::VertexAttribute::Size::Size_16:
332 return VK_FORMAT_R16_UNORM; 352 return VK_FORMAT_R16_USCALED;
333 case Maxwell::VertexAttribute::Size::Size_16_16: 353 case Maxwell::VertexAttribute::Size::Size_16_16:
334 return VK_FORMAT_R16G16_UNORM; 354 return VK_FORMAT_R16G16_USCALED;
335 case Maxwell::VertexAttribute::Size::Size_16_16_16: 355 case Maxwell::VertexAttribute::Size::Size_16_16_16:
336 return VK_FORMAT_R16G16B16_UNORM; 356 return VK_FORMAT_R16G16B16_USCALED;
337 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 357 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
338 return VK_FORMAT_R16G16B16A16_UNORM; 358 return VK_FORMAT_R16G16B16A16_USCALED;
339 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 359 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
340 return VK_FORMAT_A2B10G10R10_UNORM_PACK32; 360 return VK_FORMAT_A2B10G10R10_USCALED_PACK32;
341 default:
342 break;
343 } 361 }
344 break; 362 break;
345 case Maxwell::VertexAttribute::Type::SignedInt: 363 case Maxwell::VertexAttribute::Type::SignedScaled:
346 switch (size) { 364 switch (size) {
347 case Maxwell::VertexAttribute::Size::Size_8: 365 case Maxwell::VertexAttribute::Size::Size_8:
348 return VK_FORMAT_R8_SINT; 366 return VK_FORMAT_R8_SSCALED;
349 case Maxwell::VertexAttribute::Size::Size_8_8: 367 case Maxwell::VertexAttribute::Size::Size_8_8:
350 return VK_FORMAT_R8G8_SINT; 368 return VK_FORMAT_R8G8_SSCALED;
351 case Maxwell::VertexAttribute::Size::Size_8_8_8: 369 case Maxwell::VertexAttribute::Size::Size_8_8_8:
352 return VK_FORMAT_R8G8B8_SINT; 370 return VK_FORMAT_R8G8B8_SSCALED;
353 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 371 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
354 return VK_FORMAT_R8G8B8A8_SINT; 372 return VK_FORMAT_R8G8B8A8_SSCALED;
355 case Maxwell::VertexAttribute::Size::Size_16: 373 case Maxwell::VertexAttribute::Size::Size_16:
356 return VK_FORMAT_R16_SINT; 374 return VK_FORMAT_R16_SSCALED;
357 case Maxwell::VertexAttribute::Size::Size_16_16: 375 case Maxwell::VertexAttribute::Size::Size_16_16:
358 return VK_FORMAT_R16G16_SINT; 376 return VK_FORMAT_R16G16_SSCALED;
359 case Maxwell::VertexAttribute::Size::Size_16_16_16: 377 case Maxwell::VertexAttribute::Size::Size_16_16_16:
360 return VK_FORMAT_R16G16B16_SINT; 378 return VK_FORMAT_R16G16B16_SSCALED;
361 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 379 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
362 return VK_FORMAT_R16G16B16A16_SINT; 380 return VK_FORMAT_R16G16B16A16_SSCALED;
363 case Maxwell::VertexAttribute::Size::Size_32: 381 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
364 return VK_FORMAT_R32_SINT; 382 return VK_FORMAT_A2B10G10R10_SSCALED_PACK32;
365 case Maxwell::VertexAttribute::Size::Size_32_32:
366 return VK_FORMAT_R32G32_SINT;
367 case Maxwell::VertexAttribute::Size::Size_32_32_32:
368 return VK_FORMAT_R32G32B32_SINT;
369 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
370 return VK_FORMAT_R32G32B32A32_SINT;
371 default:
372 break;
373 } 383 }
374 break; 384 break;
375 case Maxwell::VertexAttribute::Type::UnsignedInt: 385 case Maxwell::VertexAttribute::Type::UnsignedInt:
@@ -398,56 +408,50 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
398 return VK_FORMAT_R32G32B32_UINT; 408 return VK_FORMAT_R32G32B32_UINT;
399 case Maxwell::VertexAttribute::Size::Size_32_32_32_32: 409 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
400 return VK_FORMAT_R32G32B32A32_UINT; 410 return VK_FORMAT_R32G32B32A32_UINT;
401 default: 411 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
402 break; 412 return VK_FORMAT_A2B10G10R10_UINT_PACK32;
403 } 413 }
404 break; 414 break;
405 case Maxwell::VertexAttribute::Type::UnsignedScaled: 415 case Maxwell::VertexAttribute::Type::SignedInt:
406 switch (size) { 416 switch (size) {
407 case Maxwell::VertexAttribute::Size::Size_8: 417 case Maxwell::VertexAttribute::Size::Size_8:
408 return VK_FORMAT_R8_USCALED; 418 return VK_FORMAT_R8_SINT;
409 case Maxwell::VertexAttribute::Size::Size_8_8: 419 case Maxwell::VertexAttribute::Size::Size_8_8:
410 return VK_FORMAT_R8G8_USCALED; 420 return VK_FORMAT_R8G8_SINT;
411 case Maxwell::VertexAttribute::Size::Size_8_8_8: 421 case Maxwell::VertexAttribute::Size::Size_8_8_8:
412 return VK_FORMAT_R8G8B8_USCALED; 422 return VK_FORMAT_R8G8B8_SINT;
413 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 423 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
414 return VK_FORMAT_R8G8B8A8_USCALED; 424 return VK_FORMAT_R8G8B8A8_SINT;
415 case Maxwell::VertexAttribute::Size::Size_16: 425 case Maxwell::VertexAttribute::Size::Size_16:
416 return VK_FORMAT_R16_USCALED; 426 return VK_FORMAT_R16_SINT;
417 case Maxwell::VertexAttribute::Size::Size_16_16: 427 case Maxwell::VertexAttribute::Size::Size_16_16:
418 return VK_FORMAT_R16G16_USCALED; 428 return VK_FORMAT_R16G16_SINT;
419 case Maxwell::VertexAttribute::Size::Size_16_16_16: 429 case Maxwell::VertexAttribute::Size::Size_16_16_16:
420 return VK_FORMAT_R16G16B16_USCALED; 430 return VK_FORMAT_R16G16B16_SINT;
421 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 431 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
422 return VK_FORMAT_R16G16B16A16_USCALED; 432 return VK_FORMAT_R16G16B16A16_SINT;
423 default: 433 case Maxwell::VertexAttribute::Size::Size_32:
424 break; 434 return VK_FORMAT_R32_SINT;
435 case Maxwell::VertexAttribute::Size::Size_32_32:
436 return VK_FORMAT_R32G32_SINT;
437 case Maxwell::VertexAttribute::Size::Size_32_32_32:
438 return VK_FORMAT_R32G32B32_SINT;
439 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
440 return VK_FORMAT_R32G32B32A32_SINT;
441 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
442 return VK_FORMAT_A2B10G10R10_SINT_PACK32;
425 } 443 }
426 break; 444 break;
427 case Maxwell::VertexAttribute::Type::SignedScaled: 445 case Maxwell::VertexAttribute::Type::Float:
428 switch (size) { 446 switch (size) {
429 case Maxwell::VertexAttribute::Size::Size_8:
430 return VK_FORMAT_R8_SSCALED;
431 case Maxwell::VertexAttribute::Size::Size_8_8:
432 return VK_FORMAT_R8G8_SSCALED;
433 case Maxwell::VertexAttribute::Size::Size_8_8_8:
434 return VK_FORMAT_R8G8B8_SSCALED;
435 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
436 return VK_FORMAT_R8G8B8A8_SSCALED;
437 case Maxwell::VertexAttribute::Size::Size_16: 447 case Maxwell::VertexAttribute::Size::Size_16:
438 return VK_FORMAT_R16_SSCALED; 448 return VK_FORMAT_R16_SFLOAT;
439 case Maxwell::VertexAttribute::Size::Size_16_16: 449 case Maxwell::VertexAttribute::Size::Size_16_16:
440 return VK_FORMAT_R16G16_SSCALED; 450 return VK_FORMAT_R16G16_SFLOAT;
441 case Maxwell::VertexAttribute::Size::Size_16_16_16: 451 case Maxwell::VertexAttribute::Size::Size_16_16_16:
442 return VK_FORMAT_R16G16B16_SSCALED; 452 return VK_FORMAT_R16G16B16_SFLOAT;
443 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 453 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
444 return VK_FORMAT_R16G16B16A16_SSCALED; 454 return VK_FORMAT_R16G16B16A16_SFLOAT;
445 default:
446 break;
447 }
448 break;
449 case Maxwell::VertexAttribute::Type::Float:
450 switch (size) {
451 case Maxwell::VertexAttribute::Size::Size_32: 455 case Maxwell::VertexAttribute::Size::Size_32:
452 return VK_FORMAT_R32_SFLOAT; 456 return VK_FORMAT_R32_SFLOAT;
453 case Maxwell::VertexAttribute::Size::Size_32_32: 457 case Maxwell::VertexAttribute::Size::Size_32_32:
@@ -456,16 +460,6 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
456 return VK_FORMAT_R32G32B32_SFLOAT; 460 return VK_FORMAT_R32G32B32_SFLOAT;
457 case Maxwell::VertexAttribute::Size::Size_32_32_32_32: 461 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
458 return VK_FORMAT_R32G32B32A32_SFLOAT; 462 return VK_FORMAT_R32G32B32A32_SFLOAT;
459 case Maxwell::VertexAttribute::Size::Size_16:
460 return VK_FORMAT_R16_SFLOAT;
461 case Maxwell::VertexAttribute::Size::Size_16_16:
462 return VK_FORMAT_R16G16_SFLOAT;
463 case Maxwell::VertexAttribute::Size::Size_16_16_16:
464 return VK_FORMAT_R16G16B16_SFLOAT;
465 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
466 return VK_FORMAT_R16G16B16A16_SFLOAT;
467 default:
468 break;
469 } 463 }
470 break; 464 break;
471 } 465 }
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index cd9673d1f..2d9b18ed9 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -155,11 +155,31 @@ vk::Instance CreateInstance(Common::DynamicLibrary& library, vk::InstanceDispatc
155 } 155 }
156 } 156 }
157 157
158 static constexpr std::array layers_data{"VK_LAYER_LUNARG_standard_validation"}; 158 std::vector<const char*> layers;
159 vk::Span<const char*> layers = layers_data; 159 layers.reserve(1);
160 if (!enable_layers) { 160 if (enable_layers) {
161 layers = {}; 161 layers.push_back("VK_LAYER_KHRONOS_validation");
162 }
163
164 const std::optional layer_properties = vk::EnumerateInstanceLayerProperties(dld);
165 if (!layer_properties) {
166 LOG_ERROR(Render_Vulkan, "Failed to query layer properties, disabling layers");
167 layers.clear();
168 }
169
170 for (auto layer_it = layers.begin(); layer_it != layers.end();) {
171 const char* const layer = *layer_it;
172 const auto it = std::find_if(
173 layer_properties->begin(), layer_properties->end(),
174 [layer](const VkLayerProperties& prop) { return !std::strcmp(layer, prop.layerName); });
175 if (it == layer_properties->end()) {
176 LOG_ERROR(Render_Vulkan, "Layer {} not available, removing it", layer);
177 layer_it = layers.erase(layer_it);
178 } else {
179 ++layer_it;
180 }
162 } 181 }
182
163 vk::Instance instance = vk::Instance::Create(layers, extensions, dld); 183 vk::Instance instance = vk::Instance::Create(layers, extensions, dld);
164 if (!instance) { 184 if (!instance) {
165 LOG_ERROR(Render_Vulkan, "Failed to create Vulkan instance"); 185 LOG_ERROR(Render_Vulkan, "Failed to create Vulkan instance");
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index f10f96cd8..2be38d419 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -56,7 +56,7 @@ Buffer::Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VKSchedu
56 56
57Buffer::~Buffer() = default; 57Buffer::~Buffer() = default;
58 58
59void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) const { 59void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) {
60 const auto& staging = staging_pool.GetUnusedBuffer(size, true); 60 const auto& staging = staging_pool.GetUnusedBuffer(size, true);
61 std::memcpy(staging.commit->Map(size), data, size); 61 std::memcpy(staging.commit->Map(size), data, size);
62 62
@@ -81,7 +81,7 @@ void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) const
81 }); 81 });
82} 82}
83 83
84void Buffer::Download(std::size_t offset, std::size_t size, u8* data) const { 84void Buffer::Download(std::size_t offset, std::size_t size, u8* data) {
85 const auto& staging = staging_pool.GetUnusedBuffer(size, true); 85 const auto& staging = staging_pool.GetUnusedBuffer(size, true);
86 scheduler.RequestOutsideRenderPassOperationContext(); 86 scheduler.RequestOutsideRenderPassOperationContext();
87 87
@@ -110,7 +110,7 @@ void Buffer::Download(std::size_t offset, std::size_t size, u8* data) const {
110} 110}
111 111
112void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, 112void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset,
113 std::size_t size) const { 113 std::size_t size) {
114 scheduler.RequestOutsideRenderPassOperationContext(); 114 scheduler.RequestOutsideRenderPassOperationContext();
115 115
116 const VkBuffer dst_buffer = Handle(); 116 const VkBuffer dst_buffer = Handle();
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 3630aca77..991ee451c 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -29,12 +29,12 @@ public:
29 VKStagingBufferPool& staging_pool, VAddr cpu_addr, std::size_t size); 29 VKStagingBufferPool& staging_pool, VAddr cpu_addr, std::size_t size);
30 ~Buffer(); 30 ~Buffer();
31 31
32 void Upload(std::size_t offset, std::size_t size, const u8* data) const; 32 void Upload(std::size_t offset, std::size_t size, const u8* data);
33 33
34 void Download(std::size_t offset, std::size_t size, u8* data) const; 34 void Download(std::size_t offset, std::size_t size, u8* data);
35 35
36 void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, 36 void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset,
37 std::size_t size) const; 37 std::size_t size);
38 38
39 VkBuffer Handle() const { 39 VkBuffer Handle() const {
40 return *buffer.handle; 40 return *buffer.handle;
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
index 9fd8ac3f6..fdaea4210 100644
--- a/src/video_core/renderer_vulkan/vk_device.cpp
+++ b/src/video_core/renderer_vulkan/vk_device.cpp
@@ -313,6 +313,16 @@ bool VKDevice::Create() {
313 LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors"); 313 LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors");
314 } 314 }
315 315
316 VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state;
317 if (ext_extended_dynamic_state) {
318 dynamic_state.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
319 dynamic_state.pNext = nullptr;
320 dynamic_state.extendedDynamicState = VK_TRUE;
321 SetNext(next, dynamic_state);
322 } else {
323 LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state");
324 }
325
316 if (!ext_depth_range_unrestricted) { 326 if (!ext_depth_range_unrestricted) {
317 LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); 327 LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
318 } 328 }
@@ -541,6 +551,7 @@ std::vector<const char*> VKDevice::LoadExtensions() {
541 bool has_ext_subgroup_size_control{}; 551 bool has_ext_subgroup_size_control{};
542 bool has_ext_transform_feedback{}; 552 bool has_ext_transform_feedback{};
543 bool has_ext_custom_border_color{}; 553 bool has_ext_custom_border_color{};
554 bool has_ext_extended_dynamic_state{};
544 for (const auto& extension : physical.EnumerateDeviceExtensionProperties()) { 555 for (const auto& extension : physical.EnumerateDeviceExtensionProperties()) {
545 Test(extension, nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true); 556 Test(extension, nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true);
546 Test(extension, khr_uniform_buffer_standard_layout, 557 Test(extension, khr_uniform_buffer_standard_layout,
@@ -558,6 +569,8 @@ std::vector<const char*> VKDevice::LoadExtensions() {
558 false); 569 false);
559 Test(extension, has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, 570 Test(extension, has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME,
560 false); 571 false);
572 Test(extension, has_ext_extended_dynamic_state,
573 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false);
561 if (Settings::values.renderer_debug) { 574 if (Settings::values.renderer_debug) {
562 Test(extension, nv_device_diagnostics_config, 575 Test(extension, nv_device_diagnostics_config,
563 VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true); 576 VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true);
@@ -643,6 +656,19 @@ std::vector<const char*> VKDevice::LoadExtensions() {
643 } 656 }
644 } 657 }
645 658
659 if (has_ext_extended_dynamic_state) {
660 VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state;
661 dynamic_state.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
662 dynamic_state.pNext = nullptr;
663 features.pNext = &dynamic_state;
664 physical.GetFeatures2KHR(features);
665
666 if (dynamic_state.extendedDynamicState) {
667 extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
668 ext_extended_dynamic_state = true;
669 }
670 }
671
646 return extensions; 672 return extensions;
647} 673}
648 674
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h
index 6b9227b09..ae5c21baa 100644
--- a/src/video_core/renderer_vulkan/vk_device.h
+++ b/src/video_core/renderer_vulkan/vk_device.h
@@ -182,6 +182,11 @@ public:
182 return ext_custom_border_color; 182 return ext_custom_border_color;
183 } 183 }
184 184
185 /// Returns true if the device supports VK_EXT_extended_dynamic_state.
186 bool IsExtExtendedDynamicStateSupported() const {
187 return ext_extended_dynamic_state;
188 }
189
185 /// Returns the vendor name reported from Vulkan. 190 /// Returns the vendor name reported from Vulkan.
186 std::string_view GetVendorName() const { 191 std::string_view GetVendorName() const {
187 return vendor_name; 192 return vendor_name;
@@ -239,6 +244,7 @@ private:
239 bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. 244 bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
240 bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. 245 bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback.
241 bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color. 246 bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color.
247 bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state.
242 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. 248 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
243 249
244 // Telemetry parameters 250 // Telemetry parameters
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 69b6bba00..844445105 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -176,20 +176,32 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules(
176 176
177vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params, 177vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params,
178 const SPIRVProgram& program) const { 178 const SPIRVProgram& program) const {
179 const auto& vi = fixed_state.vertex_input; 179 const auto& state = fixed_state;
180 const auto& ds = fixed_state.depth_stencil; 180 const auto& viewport_swizzles = state.viewport_swizzles;
181 const auto& cd = fixed_state.color_blending; 181
182 const auto& rs = fixed_state.rasterizer; 182 FixedPipelineState::DynamicState dynamic;
183 const auto& viewport_swizzles = fixed_state.viewport_swizzles.swizzles; 183 if (device.IsExtExtendedDynamicStateSupported()) {
184 // Insert dummy values, as long as they are valid they don't matter as extended dynamic
185 // state is ignored
186 dynamic.raw1 = 0;
187 dynamic.raw2 = 0;
188 for (FixedPipelineState::VertexBinding& binding : dynamic.vertex_bindings) {
189 // Enable all vertex bindings
190 binding.raw = 0;
191 binding.enabled.Assign(1);
192 }
193 } else {
194 dynamic = state.dynamic_state;
195 }
184 196
185 std::vector<VkVertexInputBindingDescription> vertex_bindings; 197 std::vector<VkVertexInputBindingDescription> vertex_bindings;
186 std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors; 198 std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors;
187 for (std::size_t index = 0; index < std::size(vi.bindings); ++index) { 199 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
188 const auto& binding = vi.bindings[index]; 200 const auto& binding = dynamic.vertex_bindings[index];
189 if (!binding.enabled) { 201 if (!binding.enabled) {
190 continue; 202 continue;
191 } 203 }
192 const bool instanced = vi.binding_divisors[index] != 0; 204 const bool instanced = state.binding_divisors[index] != 0;
193 const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; 205 const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
194 206
195 auto& vertex_binding = vertex_bindings.emplace_back(); 207 auto& vertex_binding = vertex_bindings.emplace_back();
@@ -200,14 +212,14 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
200 if (instanced) { 212 if (instanced) {
201 auto& binding_divisor = vertex_binding_divisors.emplace_back(); 213 auto& binding_divisor = vertex_binding_divisors.emplace_back();
202 binding_divisor.binding = static_cast<u32>(index); 214 binding_divisor.binding = static_cast<u32>(index);
203 binding_divisor.divisor = vi.binding_divisors[index]; 215 binding_divisor.divisor = state.binding_divisors[index];
204 } 216 }
205 } 217 }
206 218
207 std::vector<VkVertexInputAttributeDescription> vertex_attributes; 219 std::vector<VkVertexInputAttributeDescription> vertex_attributes;
208 const auto& input_attributes = program[0]->entries.attributes; 220 const auto& input_attributes = program[0]->entries.attributes;
209 for (std::size_t index = 0; index < std::size(vi.attributes); ++index) { 221 for (std::size_t index = 0; index < state.attributes.size(); ++index) {
210 const auto& attribute = vi.attributes[index]; 222 const auto& attribute = state.attributes[index];
211 if (!attribute.enabled) { 223 if (!attribute.enabled) {
212 continue; 224 continue;
213 } 225 }
@@ -244,15 +256,15 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
244 input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; 256 input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
245 input_assembly_ci.pNext = nullptr; 257 input_assembly_ci.pNext = nullptr;
246 input_assembly_ci.flags = 0; 258 input_assembly_ci.flags = 0;
247 input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, rs.Topology()); 259 input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, dynamic.Topology());
248 input_assembly_ci.primitiveRestartEnable = 260 input_assembly_ci.primitiveRestartEnable =
249 rs.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology); 261 state.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology);
250 262
251 VkPipelineTessellationStateCreateInfo tessellation_ci; 263 VkPipelineTessellationStateCreateInfo tessellation_ci;
252 tessellation_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; 264 tessellation_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
253 tessellation_ci.pNext = nullptr; 265 tessellation_ci.pNext = nullptr;
254 tessellation_ci.flags = 0; 266 tessellation_ci.flags = 0;
255 tessellation_ci.patchControlPoints = rs.patch_control_points_minus_one.Value() + 1; 267 tessellation_ci.patchControlPoints = state.patch_control_points_minus_one.Value() + 1;
256 268
257 VkPipelineViewportStateCreateInfo viewport_ci; 269 VkPipelineViewportStateCreateInfo viewport_ci;
258 viewport_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; 270 viewport_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
@@ -280,13 +292,13 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
280 rasterization_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; 292 rasterization_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
281 rasterization_ci.pNext = nullptr; 293 rasterization_ci.pNext = nullptr;
282 rasterization_ci.flags = 0; 294 rasterization_ci.flags = 0;
283 rasterization_ci.depthClampEnable = rs.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE; 295 rasterization_ci.depthClampEnable = state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE;
284 rasterization_ci.rasterizerDiscardEnable = rs.rasterize_enable == 0 ? VK_TRUE : VK_FALSE; 296 rasterization_ci.rasterizerDiscardEnable = state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE;
285 rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL; 297 rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL;
286 rasterization_ci.cullMode = 298 rasterization_ci.cullMode =
287 rs.cull_enable ? MaxwellToVK::CullFace(rs.CullFace()) : VK_CULL_MODE_NONE; 299 dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE;
288 rasterization_ci.frontFace = MaxwellToVK::FrontFace(rs.FrontFace()); 300 rasterization_ci.frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace());
289 rasterization_ci.depthBiasEnable = rs.depth_bias_enable; 301 rasterization_ci.depthBiasEnable = state.depth_bias_enable;
290 rasterization_ci.depthBiasConstantFactor = 0.0f; 302 rasterization_ci.depthBiasConstantFactor = 0.0f;
291 rasterization_ci.depthBiasClamp = 0.0f; 303 rasterization_ci.depthBiasClamp = 0.0f;
292 rasterization_ci.depthBiasSlopeFactor = 0.0f; 304 rasterization_ci.depthBiasSlopeFactor = 0.0f;
@@ -307,14 +319,15 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
307 depth_stencil_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; 319 depth_stencil_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
308 depth_stencil_ci.pNext = nullptr; 320 depth_stencil_ci.pNext = nullptr;
309 depth_stencil_ci.flags = 0; 321 depth_stencil_ci.flags = 0;
310 depth_stencil_ci.depthTestEnable = ds.depth_test_enable; 322 depth_stencil_ci.depthTestEnable = dynamic.depth_test_enable;
311 depth_stencil_ci.depthWriteEnable = ds.depth_write_enable; 323 depth_stencil_ci.depthWriteEnable = dynamic.depth_write_enable;
312 depth_stencil_ci.depthCompareOp = 324 depth_stencil_ci.depthCompareOp = dynamic.depth_test_enable
313 ds.depth_test_enable ? MaxwellToVK::ComparisonOp(ds.DepthTestFunc()) : VK_COMPARE_OP_ALWAYS; 325 ? MaxwellToVK::ComparisonOp(dynamic.DepthTestFunc())
314 depth_stencil_ci.depthBoundsTestEnable = ds.depth_bounds_enable; 326 : VK_COMPARE_OP_ALWAYS;
315 depth_stencil_ci.stencilTestEnable = ds.stencil_enable; 327 depth_stencil_ci.depthBoundsTestEnable = dynamic.depth_bounds_enable;
316 depth_stencil_ci.front = GetStencilFaceState(ds.front); 328 depth_stencil_ci.stencilTestEnable = dynamic.stencil_enable;
317 depth_stencil_ci.back = GetStencilFaceState(ds.back); 329 depth_stencil_ci.front = GetStencilFaceState(dynamic.front);
330 depth_stencil_ci.back = GetStencilFaceState(dynamic.back);
318 depth_stencil_ci.minDepthBounds = 0.0f; 331 depth_stencil_ci.minDepthBounds = 0.0f;
319 depth_stencil_ci.maxDepthBounds = 0.0f; 332 depth_stencil_ci.maxDepthBounds = 0.0f;
320 333
@@ -324,7 +337,7 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
324 static constexpr std::array COMPONENT_TABLE = { 337 static constexpr std::array COMPONENT_TABLE = {
325 VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT, 338 VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT,
326 VK_COLOR_COMPONENT_A_BIT}; 339 VK_COLOR_COMPONENT_A_BIT};
327 const auto& blend = cd.attachments[index]; 340 const auto& blend = state.attachments[index];
328 341
329 VkColorComponentFlags color_components = 0; 342 VkColorComponentFlags color_components = 0;
330 for (std::size_t i = 0; i < COMPONENT_TABLE.size(); ++i) { 343 for (std::size_t i = 0; i < COMPONENT_TABLE.size(); ++i) {
@@ -354,11 +367,27 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
354 color_blend_ci.pAttachments = cb_attachments.data(); 367 color_blend_ci.pAttachments = cb_attachments.data();
355 std::memset(color_blend_ci.blendConstants, 0, sizeof(color_blend_ci.blendConstants)); 368 std::memset(color_blend_ci.blendConstants, 0, sizeof(color_blend_ci.blendConstants));
356 369
357 static constexpr std::array dynamic_states = { 370 std::vector dynamic_states = {
358 VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, 371 VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
359 VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, 372 VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS,
360 VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, 373 VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
361 VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE}; 374 VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE,
375 };
376 if (device.IsExtExtendedDynamicStateSupported()) {
377 static constexpr std::array extended = {
378 VK_DYNAMIC_STATE_CULL_MODE_EXT,
379 VK_DYNAMIC_STATE_FRONT_FACE_EXT,
380 VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT,
381 VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT,
382 VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT,
383 VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT,
384 VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT,
385 VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT,
386 VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT,
387 VK_DYNAMIC_STATE_STENCIL_OP_EXT,
388 };
389 dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end());
390 }
362 391
363 VkPipelineDynamicStateCreateInfo dynamic_state_ci; 392 VkPipelineDynamicStateCreateInfo dynamic_state_ci;
364 dynamic_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; 393 dynamic_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index ea66e621e..3da835324 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -116,12 +116,12 @@ u32 FillDescriptorLayout(const ShaderEntries& entries,
116} // Anonymous namespace 116} // Anonymous namespace
117 117
118std::size_t GraphicsPipelineCacheKey::Hash() const noexcept { 118std::size_t GraphicsPipelineCacheKey::Hash() const noexcept {
119 const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this); 119 const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size());
120 return static_cast<std::size_t>(hash); 120 return static_cast<std::size_t>(hash);
121} 121}
122 122
123bool GraphicsPipelineCacheKey::operator==(const GraphicsPipelineCacheKey& rhs) const noexcept { 123bool GraphicsPipelineCacheKey::operator==(const GraphicsPipelineCacheKey& rhs) const noexcept {
124 return std::memcmp(&rhs, this, sizeof *this) == 0; 124 return std::memcmp(&rhs, this, Size()) == 0;
125} 125}
126 126
127std::size_t ComputePipelineCacheKey::Hash() const noexcept { 127std::size_t ComputePipelineCacheKey::Hash() const noexcept {
@@ -312,18 +312,19 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) {
312 const auto& gpu = system.GPU().Maxwell3D(); 312 const auto& gpu = system.GPU().Maxwell3D();
313 313
314 Specialization specialization; 314 Specialization specialization;
315 if (fixed_state.rasterizer.Topology() == Maxwell::PrimitiveTopology::Points) { 315 if (fixed_state.dynamic_state.Topology() == Maxwell::PrimitiveTopology::Points ||
316 device.IsExtExtendedDynamicStateSupported()) {
316 float point_size; 317 float point_size;
317 std::memcpy(&point_size, &fixed_state.rasterizer.point_size, sizeof(float)); 318 std::memcpy(&point_size, &fixed_state.point_size, sizeof(float));
318 specialization.point_size = point_size; 319 specialization.point_size = point_size;
319 ASSERT(point_size != 0.0f); 320 ASSERT(point_size != 0.0f);
320 } 321 }
321 for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) { 322 for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) {
322 const auto& attribute = fixed_state.vertex_input.attributes[i]; 323 const auto& attribute = fixed_state.attributes[i];
323 specialization.enabled_attributes[i] = attribute.enabled.Value() != 0; 324 specialization.enabled_attributes[i] = attribute.enabled.Value() != 0;
324 specialization.attribute_types[i] = attribute.Type(); 325 specialization.attribute_types[i] = attribute.Type();
325 } 326 }
326 specialization.ndc_minus_one_to_one = fixed_state.rasterizer.ndc_minus_one_to_one; 327 specialization.ndc_minus_one_to_one = fixed_state.ndc_minus_one_to_one;
327 328
328 SPIRVProgram program; 329 SPIRVProgram program;
329 std::vector<VkDescriptorSetLayoutBinding> bindings; 330 std::vector<VkDescriptorSetLayoutBinding> bindings;
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 0a36e5112..0a3fe65fb 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -44,10 +44,10 @@ class VKUpdateDescriptorQueue;
44using Maxwell = Tegra::Engines::Maxwell3D::Regs; 44using Maxwell = Tegra::Engines::Maxwell3D::Regs;
45 45
46struct GraphicsPipelineCacheKey { 46struct GraphicsPipelineCacheKey {
47 FixedPipelineState fixed_state;
48 RenderPassParams renderpass_params; 47 RenderPassParams renderpass_params;
48 u32 padding;
49 std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders; 49 std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders;
50 u64 padding; // This is necessary for unique object representations 50 FixedPipelineState fixed_state;
51 51
52 std::size_t Hash() const noexcept; 52 std::size_t Hash() const noexcept;
53 53
@@ -56,6 +56,10 @@ struct GraphicsPipelineCacheKey {
56 bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept { 56 bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept {
57 return !operator==(rhs); 57 return !operator==(rhs);
58 } 58 }
59
60 std::size_t Size() const noexcept {
61 return sizeof(renderpass_params) + sizeof(padding) + sizeof(shaders) + fixed_state.Size();
62 }
59}; 63};
60static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>); 64static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>);
61static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>); 65static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>);
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index a8d94eac3..380ed532b 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -186,13 +186,22 @@ bool HasToPreserveDepthContents(bool is_clear, const Maxwell& regs) {
186 scissor.max_y < regs.zeta_height; 186 scissor.max_y < regs.zeta_height;
187} 187}
188 188
189template <std::size_t N>
190std::array<VkDeviceSize, N> ExpandStrides(const std::array<u16, N>& strides) {
191 std::array<VkDeviceSize, N> expanded;
192 std::copy(strides.begin(), strides.end(), expanded.begin());
193 return expanded;
194}
195
189} // Anonymous namespace 196} // Anonymous namespace
190 197
191class BufferBindings final { 198class BufferBindings final {
192public: 199public:
193 void AddVertexBinding(VkBuffer buffer, VkDeviceSize offset) { 200 void AddVertexBinding(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, u32 stride) {
194 vertex.buffers[vertex.num_buffers] = buffer; 201 vertex.buffers[vertex.num_buffers] = buffer;
195 vertex.offsets[vertex.num_buffers] = offset; 202 vertex.offsets[vertex.num_buffers] = offset;
203 vertex.sizes[vertex.num_buffers] = size;
204 vertex.strides[vertex.num_buffers] = static_cast<u16>(stride);
196 ++vertex.num_buffers; 205 ++vertex.num_buffers;
197 } 206 }
198 207
@@ -202,76 +211,76 @@ public:
202 index.type = type; 211 index.type = type;
203 } 212 }
204 213
205 void Bind(VKScheduler& scheduler) const { 214 void Bind(const VKDevice& device, VKScheduler& scheduler) const {
206 // Use this large switch case to avoid dispatching more memory in the record lambda than 215 // Use this large switch case to avoid dispatching more memory in the record lambda than
207 // what we need. It looks horrible, but it's the best we can do on standard C++. 216 // what we need. It looks horrible, but it's the best we can do on standard C++.
208 switch (vertex.num_buffers) { 217 switch (vertex.num_buffers) {
209 case 0: 218 case 0:
210 return BindStatic<0>(scheduler); 219 return BindStatic<0>(device, scheduler);
211 case 1: 220 case 1:
212 return BindStatic<1>(scheduler); 221 return BindStatic<1>(device, scheduler);
213 case 2: 222 case 2:
214 return BindStatic<2>(scheduler); 223 return BindStatic<2>(device, scheduler);
215 case 3: 224 case 3:
216 return BindStatic<3>(scheduler); 225 return BindStatic<3>(device, scheduler);
217 case 4: 226 case 4:
218 return BindStatic<4>(scheduler); 227 return BindStatic<4>(device, scheduler);
219 case 5: 228 case 5:
220 return BindStatic<5>(scheduler); 229 return BindStatic<5>(device, scheduler);
221 case 6: 230 case 6:
222 return BindStatic<6>(scheduler); 231 return BindStatic<6>(device, scheduler);
223 case 7: 232 case 7:
224 return BindStatic<7>(scheduler); 233 return BindStatic<7>(device, scheduler);
225 case 8: 234 case 8:
226 return BindStatic<8>(scheduler); 235 return BindStatic<8>(device, scheduler);
227 case 9: 236 case 9:
228 return BindStatic<9>(scheduler); 237 return BindStatic<9>(device, scheduler);
229 case 10: 238 case 10:
230 return BindStatic<10>(scheduler); 239 return BindStatic<10>(device, scheduler);
231 case 11: 240 case 11:
232 return BindStatic<11>(scheduler); 241 return BindStatic<11>(device, scheduler);
233 case 12: 242 case 12:
234 return BindStatic<12>(scheduler); 243 return BindStatic<12>(device, scheduler);
235 case 13: 244 case 13:
236 return BindStatic<13>(scheduler); 245 return BindStatic<13>(device, scheduler);
237 case 14: 246 case 14:
238 return BindStatic<14>(scheduler); 247 return BindStatic<14>(device, scheduler);
239 case 15: 248 case 15:
240 return BindStatic<15>(scheduler); 249 return BindStatic<15>(device, scheduler);
241 case 16: 250 case 16:
242 return BindStatic<16>(scheduler); 251 return BindStatic<16>(device, scheduler);
243 case 17: 252 case 17:
244 return BindStatic<17>(scheduler); 253 return BindStatic<17>(device, scheduler);
245 case 18: 254 case 18:
246 return BindStatic<18>(scheduler); 255 return BindStatic<18>(device, scheduler);
247 case 19: 256 case 19:
248 return BindStatic<19>(scheduler); 257 return BindStatic<19>(device, scheduler);
249 case 20: 258 case 20:
250 return BindStatic<20>(scheduler); 259 return BindStatic<20>(device, scheduler);
251 case 21: 260 case 21:
252 return BindStatic<21>(scheduler); 261 return BindStatic<21>(device, scheduler);
253 case 22: 262 case 22:
254 return BindStatic<22>(scheduler); 263 return BindStatic<22>(device, scheduler);
255 case 23: 264 case 23:
256 return BindStatic<23>(scheduler); 265 return BindStatic<23>(device, scheduler);
257 case 24: 266 case 24:
258 return BindStatic<24>(scheduler); 267 return BindStatic<24>(device, scheduler);
259 case 25: 268 case 25:
260 return BindStatic<25>(scheduler); 269 return BindStatic<25>(device, scheduler);
261 case 26: 270 case 26:
262 return BindStatic<26>(scheduler); 271 return BindStatic<26>(device, scheduler);
263 case 27: 272 case 27:
264 return BindStatic<27>(scheduler); 273 return BindStatic<27>(device, scheduler);
265 case 28: 274 case 28:
266 return BindStatic<28>(scheduler); 275 return BindStatic<28>(device, scheduler);
267 case 29: 276 case 29:
268 return BindStatic<29>(scheduler); 277 return BindStatic<29>(device, scheduler);
269 case 30: 278 case 30:
270 return BindStatic<30>(scheduler); 279 return BindStatic<30>(device, scheduler);
271 case 31: 280 case 31:
272 return BindStatic<31>(scheduler); 281 return BindStatic<31>(device, scheduler);
273 case 32: 282 case 32:
274 return BindStatic<32>(scheduler); 283 return BindStatic<32>(device, scheduler);
275 } 284 }
276 UNREACHABLE(); 285 UNREACHABLE();
277 } 286 }
@@ -282,6 +291,8 @@ private:
282 std::size_t num_buffers = 0; 291 std::size_t num_buffers = 0;
283 std::array<VkBuffer, Maxwell::NumVertexArrays> buffers; 292 std::array<VkBuffer, Maxwell::NumVertexArrays> buffers;
284 std::array<VkDeviceSize, Maxwell::NumVertexArrays> offsets; 293 std::array<VkDeviceSize, Maxwell::NumVertexArrays> offsets;
294 std::array<VkDeviceSize, Maxwell::NumVertexArrays> sizes;
295 std::array<u16, Maxwell::NumVertexArrays> strides;
285 } vertex; 296 } vertex;
286 297
287 struct { 298 struct {
@@ -291,15 +302,23 @@ private:
291 } index; 302 } index;
292 303
293 template <std::size_t N> 304 template <std::size_t N>
294 void BindStatic(VKScheduler& scheduler) const { 305 void BindStatic(const VKDevice& device, VKScheduler& scheduler) const {
295 if (index.buffer) { 306 if (device.IsExtExtendedDynamicStateSupported()) {
296 BindStatic<N, true>(scheduler); 307 if (index.buffer) {
308 BindStatic<N, true, true>(scheduler);
309 } else {
310 BindStatic<N, false, true>(scheduler);
311 }
297 } else { 312 } else {
298 BindStatic<N, false>(scheduler); 313 if (index.buffer) {
314 BindStatic<N, true, false>(scheduler);
315 } else {
316 BindStatic<N, false, false>(scheduler);
317 }
299 } 318 }
300 } 319 }
301 320
302 template <std::size_t N, bool is_indexed> 321 template <std::size_t N, bool is_indexed, bool has_extended_dynamic_state>
303 void BindStatic(VKScheduler& scheduler) const { 322 void BindStatic(VKScheduler& scheduler) const {
304 static_assert(N <= Maxwell::NumVertexArrays); 323 static_assert(N <= Maxwell::NumVertexArrays);
305 if constexpr (N == 0) { 324 if constexpr (N == 0) {
@@ -311,6 +330,31 @@ private:
311 std::copy(vertex.buffers.begin(), vertex.buffers.begin() + N, buffers.begin()); 330 std::copy(vertex.buffers.begin(), vertex.buffers.begin() + N, buffers.begin());
312 std::copy(vertex.offsets.begin(), vertex.offsets.begin() + N, offsets.begin()); 331 std::copy(vertex.offsets.begin(), vertex.offsets.begin() + N, offsets.begin());
313 332
333 if constexpr (has_extended_dynamic_state) {
334 // With extended dynamic states we can specify the length and stride of a vertex buffer
335 // std::array<VkDeviceSize, N> sizes;
336 std::array<u16, N> strides;
337 // std::copy(vertex.sizes.begin(), vertex.sizes.begin() + N, sizes.begin());
338 std::copy(vertex.strides.begin(), vertex.strides.begin() + N, strides.begin());
339
340 if constexpr (is_indexed) {
341 scheduler.Record(
342 [buffers, offsets, strides, index = index](vk::CommandBuffer cmdbuf) {
343 cmdbuf.BindIndexBuffer(index.buffer, index.offset, index.type);
344 cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(),
345 offsets.data(), nullptr,
346 ExpandStrides(strides).data());
347 });
348 } else {
349 scheduler.Record([buffers, offsets, strides](vk::CommandBuffer cmdbuf) {
350 cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(),
351 offsets.data(), nullptr,
352 ExpandStrides(strides).data());
353 });
354 }
355 return;
356 }
357
314 if constexpr (is_indexed) { 358 if constexpr (is_indexed) {
315 // Indexed draw 359 // Indexed draw
316 scheduler.Record([buffers, offsets, index = index](vk::CommandBuffer cmdbuf) { 360 scheduler.Record([buffers, offsets, index = index](vk::CommandBuffer cmdbuf) {
@@ -369,7 +413,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
369 413
370 const auto& gpu = system.GPU().Maxwell3D(); 414 const auto& gpu = system.GPU().Maxwell3D();
371 GraphicsPipelineCacheKey key; 415 GraphicsPipelineCacheKey key;
372 key.fixed_state.Fill(gpu.regs); 416 key.fixed_state.Fill(gpu.regs, device.IsExtExtendedDynamicStateSupported());
373 417
374 buffer_cache.Map(CalculateGraphicsStreamBufferSize(is_indexed)); 418 buffer_cache.Map(CalculateGraphicsStreamBufferSize(is_indexed));
375 419
@@ -402,7 +446,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
402 446
403 UpdateDynamicStates(); 447 UpdateDynamicStates();
404 448
405 buffer_bindings.Bind(scheduler); 449 buffer_bindings.Bind(device, scheduler);
406 450
407 BeginTransformFeedback(); 451 BeginTransformFeedback();
408 452
@@ -822,7 +866,7 @@ RasterizerVulkan::DrawParameters RasterizerVulkan::SetupGeometry(FixedPipelineSt
822 const auto& gpu = system.GPU().Maxwell3D(); 866 const auto& gpu = system.GPU().Maxwell3D();
823 const auto& regs = gpu.regs; 867 const auto& regs = gpu.regs;
824 868
825 SetupVertexArrays(fixed_state.vertex_input, buffer_bindings); 869 SetupVertexArrays(buffer_bindings);
826 870
827 const u32 base_instance = regs.vb_base_instance; 871 const u32 base_instance = regs.vb_base_instance;
828 const u32 num_instances = is_instanced ? gpu.mme_draw.instance_count : 1; 872 const u32 num_instances = is_instanced ? gpu.mme_draw.instance_count : 1;
@@ -893,6 +937,17 @@ void RasterizerVulkan::UpdateDynamicStates() {
893 UpdateBlendConstants(regs); 937 UpdateBlendConstants(regs);
894 UpdateDepthBounds(regs); 938 UpdateDepthBounds(regs);
895 UpdateStencilFaces(regs); 939 UpdateStencilFaces(regs);
940 if (device.IsExtExtendedDynamicStateSupported()) {
941 UpdateCullMode(regs);
942 UpdateDepthBoundsTestEnable(regs);
943 UpdateDepthTestEnable(regs);
944 UpdateDepthWriteEnable(regs);
945 UpdateDepthCompareOp(regs);
946 UpdateFrontFace(regs);
947 UpdatePrimitiveTopology(regs);
948 UpdateStencilOp(regs);
949 UpdateStencilTestEnable(regs);
950 }
896} 951}
897 952
898void RasterizerVulkan::BeginTransformFeedback() { 953void RasterizerVulkan::BeginTransformFeedback() {
@@ -940,41 +995,25 @@ void RasterizerVulkan::EndTransformFeedback() {
940 [](vk::CommandBuffer cmdbuf) { cmdbuf.EndTransformFeedbackEXT(0, 0, nullptr, nullptr); }); 995 [](vk::CommandBuffer cmdbuf) { cmdbuf.EndTransformFeedbackEXT(0, 0, nullptr, nullptr); });
941} 996}
942 997
943void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input, 998void RasterizerVulkan::SetupVertexArrays(BufferBindings& buffer_bindings) {
944 BufferBindings& buffer_bindings) {
945 const auto& regs = system.GPU().Maxwell3D().regs; 999 const auto& regs = system.GPU().Maxwell3D().regs;
946 1000
947 for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
948 const auto& attrib = regs.vertex_attrib_format[index];
949 if (attrib.IsConstant()) {
950 vertex_input.SetAttribute(index, false, 0, 0, {}, {});
951 continue;
952 }
953 vertex_input.SetAttribute(index, true, attrib.buffer, attrib.offset, attrib.type.Value(),
954 attrib.size.Value());
955 }
956
957 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { 1001 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
958 const auto& vertex_array = regs.vertex_array[index]; 1002 const auto& vertex_array = regs.vertex_array[index];
959 if (!vertex_array.IsEnabled()) { 1003 if (!vertex_array.IsEnabled()) {
960 vertex_input.SetBinding(index, false, 0, 0);
961 continue; 1004 continue;
962 } 1005 }
963 vertex_input.SetBinding(
964 index, true, vertex_array.stride,
965 regs.instanced_arrays.IsInstancingEnabled(index) ? vertex_array.divisor : 0);
966
967 const GPUVAddr start{vertex_array.StartAddress()}; 1006 const GPUVAddr start{vertex_array.StartAddress()};
968 const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()}; 1007 const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()};
969 1008
970 ASSERT(end >= start); 1009 ASSERT(end >= start);
971 const std::size_t size{end - start}; 1010 const std::size_t size = end - start;
972 if (size == 0) { 1011 if (size == 0) {
973 buffer_bindings.AddVertexBinding(DefaultBuffer(), 0); 1012 buffer_bindings.AddVertexBinding(DefaultBuffer(), 0, DEFAULT_BUFFER_SIZE, 0);
974 continue; 1013 continue;
975 } 1014 }
976 const auto info = buffer_cache.UploadMemory(start, size); 1015 const auto info = buffer_cache.UploadMemory(start, size);
977 buffer_bindings.AddVertexBinding(info.handle, info.offset); 1016 buffer_bindings.AddVertexBinding(info.handle, info.offset, size, vertex_array.stride);
978 } 1017 }
979} 1018}
980 1019
@@ -1326,6 +1365,117 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
1326 } 1365 }
1327} 1366}
1328 1367
1368void RasterizerVulkan::UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs) {
1369 if (!state_tracker.TouchCullMode()) {
1370 return;
1371 }
1372 scheduler.Record(
1373 [enabled = regs.cull_test_enabled, cull_face = regs.cull_face](vk::CommandBuffer cmdbuf) {
1374 cmdbuf.SetCullModeEXT(enabled ? MaxwellToVK::CullFace(cull_face) : VK_CULL_MODE_NONE);
1375 });
1376}
1377
1378void RasterizerVulkan::UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1379 if (!state_tracker.TouchDepthBoundsTestEnable()) {
1380 return;
1381 }
1382 scheduler.Record([enable = regs.depth_bounds_enable](vk::CommandBuffer cmdbuf) {
1383 cmdbuf.SetDepthBoundsTestEnableEXT(enable);
1384 });
1385}
1386
1387void RasterizerVulkan::UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1388 if (!state_tracker.TouchDepthTestEnable()) {
1389 return;
1390 }
1391 scheduler.Record([enable = regs.depth_test_enable](vk::CommandBuffer cmdbuf) {
1392 cmdbuf.SetDepthTestEnableEXT(enable);
1393 });
1394}
1395
1396void RasterizerVulkan::UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1397 if (!state_tracker.TouchDepthWriteEnable()) {
1398 return;
1399 }
1400 scheduler.Record([enable = regs.depth_write_enabled](vk::CommandBuffer cmdbuf) {
1401 cmdbuf.SetDepthWriteEnableEXT(enable);
1402 });
1403}
1404
1405void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) {
1406 if (!state_tracker.TouchDepthCompareOp()) {
1407 return;
1408 }
1409 scheduler.Record([func = regs.depth_test_func](vk::CommandBuffer cmdbuf) {
1410 cmdbuf.SetDepthCompareOpEXT(MaxwellToVK::ComparisonOp(func));
1411 });
1412}
1413
1414void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) {
1415 if (!state_tracker.TouchFrontFace()) {
1416 return;
1417 }
1418
1419 VkFrontFace front_face = MaxwellToVK::FrontFace(regs.front_face);
1420 if (regs.screen_y_control.triangle_rast_flip != 0) {
1421 front_face = front_face == VK_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_COUNTER_CLOCKWISE
1422 : VK_FRONT_FACE_CLOCKWISE;
1423 }
1424 scheduler.Record(
1425 [front_face](vk::CommandBuffer cmdbuf) { cmdbuf.SetFrontFaceEXT(front_face); });
1426}
1427
1428void RasterizerVulkan::UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs) {
1429 if (!state_tracker.TouchPrimitiveTopology()) {
1430 return;
1431 }
1432 const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value();
1433 scheduler.Record([this, primitive_topology](vk::CommandBuffer cmdbuf) {
1434 cmdbuf.SetPrimitiveTopologyEXT(MaxwellToVK::PrimitiveTopology(device, primitive_topology));
1435 });
1436}
1437
1438void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) {
1439 if (!state_tracker.TouchStencilOp()) {
1440 return;
1441 }
1442 const Maxwell::StencilOp fail = regs.stencil_front_op_fail;
1443 const Maxwell::StencilOp zfail = regs.stencil_front_op_zfail;
1444 const Maxwell::StencilOp zpass = regs.stencil_front_op_zpass;
1445 const Maxwell::ComparisonOp compare = regs.stencil_front_func_func;
1446 if (regs.stencil_two_side_enable) {
1447 scheduler.Record([fail, zfail, zpass, compare](vk::CommandBuffer cmdbuf) {
1448 cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_AND_BACK, MaxwellToVK::StencilOp(fail),
1449 MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail),
1450 MaxwellToVK::ComparisonOp(compare));
1451 });
1452 } else {
1453 const Maxwell::StencilOp back_fail = regs.stencil_back_op_fail;
1454 const Maxwell::StencilOp back_zfail = regs.stencil_back_op_zfail;
1455 const Maxwell::StencilOp back_zpass = regs.stencil_back_op_zpass;
1456 const Maxwell::ComparisonOp back_compare = regs.stencil_back_func_func;
1457 scheduler.Record([fail, zfail, zpass, compare, back_fail, back_zfail, back_zpass,
1458 back_compare](vk::CommandBuffer cmdbuf) {
1459 cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_BIT, MaxwellToVK::StencilOp(fail),
1460 MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail),
1461 MaxwellToVK::ComparisonOp(compare));
1462 cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_BACK_BIT, MaxwellToVK::StencilOp(back_fail),
1463 MaxwellToVK::StencilOp(back_zpass),
1464 MaxwellToVK::StencilOp(back_zfail),
1465 MaxwellToVK::ComparisonOp(back_compare));
1466 });
1467 }
1468}
1469
1470void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1471 if (!state_tracker.TouchStencilTestEnable()) {
1472 return;
1473 }
1474 scheduler.Record([enable = regs.stencil_enable](vk::CommandBuffer cmdbuf) {
1475 cmdbuf.SetStencilTestEnableEXT(enable);
1476 });
1477}
1478
1329std::size_t RasterizerVulkan::CalculateGraphicsStreamBufferSize(bool is_indexed) const { 1479std::size_t RasterizerVulkan::CalculateGraphicsStreamBufferSize(bool is_indexed) const {
1330 std::size_t size = CalculateVertexArraysSize(); 1480 std::size_t size = CalculateVertexArraysSize();
1331 if (is_indexed) { 1481 if (is_indexed) {
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 83e00e7e9..923178b0b 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -185,8 +185,7 @@ private:
185 185
186 bool WalkAttachmentOverlaps(const CachedSurfaceView& attachment); 186 bool WalkAttachmentOverlaps(const CachedSurfaceView& attachment);
187 187
188 void SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input, 188 void SetupVertexArrays(BufferBindings& buffer_bindings);
189 BufferBindings& buffer_bindings);
190 189
191 void SetupIndexBuffer(BufferBindings& buffer_bindings, DrawParameters& params, bool is_indexed); 190 void SetupIndexBuffer(BufferBindings& buffer_bindings, DrawParameters& params, bool is_indexed);
192 191
@@ -246,6 +245,16 @@ private:
246 void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs); 245 void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs);
247 void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs); 246 void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs);
248 247
248 void UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs);
249 void UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
250 void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
251 void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs);
252 void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs);
253 void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs);
254 void UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs);
255 void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs);
256 void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
257
249 std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const; 258 std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const;
250 259
251 std::size_t CalculateComputeStreamBufferSize() const; 260 std::size_t CalculateComputeStreamBufferSize() const;
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index 94a89e388..e5a583dd5 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -36,6 +36,15 @@ Flags MakeInvalidationFlags() {
36 flags[BlendConstants] = true; 36 flags[BlendConstants] = true;
37 flags[DepthBounds] = true; 37 flags[DepthBounds] = true;
38 flags[StencilProperties] = true; 38 flags[StencilProperties] = true;
39 flags[CullMode] = true;
40 flags[DepthBoundsEnable] = true;
41 flags[DepthTestEnable] = true;
42 flags[DepthWriteEnable] = true;
43 flags[DepthCompareOp] = true;
44 flags[FrontFace] = true;
45 flags[PrimitiveTopology] = true;
46 flags[StencilOp] = true;
47 flags[StencilTestEnable] = true;
39 return flags; 48 return flags;
40} 49}
41 50
@@ -75,6 +84,57 @@ void SetupDirtyStencilProperties(Tables& tables) {
75 table[OFF(stencil_back_func_mask)] = StencilProperties; 84 table[OFF(stencil_back_func_mask)] = StencilProperties;
76} 85}
77 86
87void SetupDirtyCullMode(Tables& tables) {
88 auto& table = tables[0];
89 table[OFF(cull_face)] = CullMode;
90 table[OFF(cull_test_enabled)] = CullMode;
91}
92
93void SetupDirtyDepthBoundsEnable(Tables& tables) {
94 tables[0][OFF(depth_bounds_enable)] = DepthBoundsEnable;
95}
96
97void SetupDirtyDepthTestEnable(Tables& tables) {
98 tables[0][OFF(depth_test_enable)] = DepthTestEnable;
99}
100
101void SetupDirtyDepthWriteEnable(Tables& tables) {
102 tables[0][OFF(depth_write_enabled)] = DepthWriteEnable;
103}
104
105void SetupDirtyDepthCompareOp(Tables& tables) {
106 tables[0][OFF(depth_test_func)] = DepthCompareOp;
107}
108
109void SetupDirtyFrontFace(Tables& tables) {
110 auto& table = tables[0];
111 table[OFF(front_face)] = FrontFace;
112 table[OFF(screen_y_control)] = FrontFace;
113}
114
115void SetupDirtyPrimitiveTopology(Tables& tables) {
116 tables[0][OFF(draw.topology)] = PrimitiveTopology;
117}
118
119void SetupDirtyStencilOp(Tables& tables) {
120 auto& table = tables[0];
121 table[OFF(stencil_front_op_fail)] = StencilOp;
122 table[OFF(stencil_front_op_zfail)] = StencilOp;
123 table[OFF(stencil_front_op_zpass)] = StencilOp;
124 table[OFF(stencil_front_func_func)] = StencilOp;
125 table[OFF(stencil_back_op_fail)] = StencilOp;
126 table[OFF(stencil_back_op_zfail)] = StencilOp;
127 table[OFF(stencil_back_op_zpass)] = StencilOp;
128 table[OFF(stencil_back_func_func)] = StencilOp;
129
130 // Table 0 is used by StencilProperties
131 tables[1][OFF(stencil_two_side_enable)] = StencilOp;
132}
133
134void SetupDirtyStencilTestEnable(Tables& tables) {
135 tables[0][OFF(stencil_enable)] = StencilTestEnable;
136}
137
78} // Anonymous namespace 138} // Anonymous namespace
79 139
80StateTracker::StateTracker(Core::System& system) 140StateTracker::StateTracker(Core::System& system)
@@ -90,6 +150,14 @@ void StateTracker::Initialize() {
90 SetupDirtyBlendConstants(tables); 150 SetupDirtyBlendConstants(tables);
91 SetupDirtyDepthBounds(tables); 151 SetupDirtyDepthBounds(tables);
92 SetupDirtyStencilProperties(tables); 152 SetupDirtyStencilProperties(tables);
153 SetupDirtyCullMode(tables);
154 SetupDirtyDepthBoundsEnable(tables);
155 SetupDirtyDepthTestEnable(tables);
156 SetupDirtyDepthWriteEnable(tables);
157 SetupDirtyDepthCompareOp(tables);
158 SetupDirtyFrontFace(tables);
159 SetupDirtyPrimitiveTopology(tables);
160 SetupDirtyStencilOp(tables);
93} 161}
94 162
95void StateTracker::InvalidateCommandBufferState() { 163void StateTracker::InvalidateCommandBufferState() {
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 03bc415b2..54ca0d6c6 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -26,6 +26,16 @@ enum : u8 {
26 DepthBounds, 26 DepthBounds,
27 StencilProperties, 27 StencilProperties,
28 28
29 CullMode,
30 DepthBoundsEnable,
31 DepthTestEnable,
32 DepthWriteEnable,
33 DepthCompareOp,
34 FrontFace,
35 PrimitiveTopology,
36 StencilOp,
37 StencilTestEnable,
38
29 Last 39 Last
30}; 40};
31static_assert(Last <= std::numeric_limits<u8>::max()); 41static_assert(Last <= std::numeric_limits<u8>::max());
@@ -64,6 +74,46 @@ public:
64 return Exchange(Dirty::StencilProperties, false); 74 return Exchange(Dirty::StencilProperties, false);
65 } 75 }
66 76
77 bool TouchCullMode() {
78 return Exchange(Dirty::CullMode, false);
79 }
80
81 bool TouchDepthBoundsTestEnable() {
82 return Exchange(Dirty::DepthBoundsEnable, false);
83 }
84
85 bool TouchDepthTestEnable() {
86 return Exchange(Dirty::DepthTestEnable, false);
87 }
88
89 bool TouchDepthBoundsEnable() {
90 return Exchange(Dirty::DepthBoundsEnable, false);
91 }
92
93 bool TouchDepthWriteEnable() {
94 return Exchange(Dirty::DepthWriteEnable, false);
95 }
96
97 bool TouchDepthCompareOp() {
98 return Exchange(Dirty::DepthCompareOp, false);
99 }
100
101 bool TouchFrontFace() {
102 return Exchange(Dirty::FrontFace, false);
103 }
104
105 bool TouchPrimitiveTopology() {
106 return Exchange(Dirty::PrimitiveTopology, false);
107 }
108
109 bool TouchStencilOp() {
110 return Exchange(Dirty::StencilOp, false);
111 }
112
113 bool TouchStencilTestEnable() {
114 return Exchange(Dirty::StencilTestEnable, false);
115 }
116
67private: 117private:
68 bool Exchange(std::size_t id, bool new_value) const noexcept { 118 bool Exchange(std::size_t id, bool new_value) const noexcept {
69 auto& flags = system.GPU().Maxwell3D().dirty.flags; 119 auto& flags = system.GPU().Maxwell3D().dirty.flags;
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp
index 42eff85d3..051298cc8 100644
--- a/src/video_core/renderer_vulkan/wrapper.cpp
+++ b/src/video_core/renderer_vulkan/wrapper.cpp
@@ -88,6 +88,16 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
88 X(vkCmdSetStencilWriteMask); 88 X(vkCmdSetStencilWriteMask);
89 X(vkCmdSetViewport); 89 X(vkCmdSetViewport);
90 X(vkCmdWaitEvents); 90 X(vkCmdWaitEvents);
91 X(vkCmdBindVertexBuffers2EXT);
92 X(vkCmdSetCullModeEXT);
93 X(vkCmdSetDepthBoundsTestEnableEXT);
94 X(vkCmdSetDepthCompareOpEXT);
95 X(vkCmdSetDepthTestEnableEXT);
96 X(vkCmdSetDepthWriteEnableEXT);
97 X(vkCmdSetFrontFaceEXT);
98 X(vkCmdSetPrimitiveTopologyEXT);
99 X(vkCmdSetStencilOpEXT);
100 X(vkCmdSetStencilTestEnableEXT);
91 X(vkCreateBuffer); 101 X(vkCreateBuffer);
92 X(vkCreateBufferView); 102 X(vkCreateBufferView);
93 X(vkCreateCommandPool); 103 X(vkCreateCommandPool);
@@ -153,7 +163,8 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
153 163
154bool Load(InstanceDispatch& dld) noexcept { 164bool Load(InstanceDispatch& dld) noexcept {
155#define X(name) Proc(dld.name, dld, #name) 165#define X(name) Proc(dld.name, dld, #name)
156 return X(vkCreateInstance) && X(vkEnumerateInstanceExtensionProperties); 166 return X(vkCreateInstance) && X(vkEnumerateInstanceExtensionProperties) &&
167 X(vkEnumerateInstanceLayerProperties);
157#undef X 168#undef X
158} 169}
159 170
@@ -770,4 +781,17 @@ std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProp
770 return properties; 781 return properties;
771} 782}
772 783
784std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties(
785 const InstanceDispatch& dld) {
786 u32 num;
787 if (dld.vkEnumerateInstanceLayerProperties(&num, nullptr) != VK_SUCCESS) {
788 return std::nullopt;
789 }
790 std::vector<VkLayerProperties> properties(num);
791 if (dld.vkEnumerateInstanceLayerProperties(&num, properties.data()) != VK_SUCCESS) {
792 return std::nullopt;
793 }
794 return properties;
795}
796
773} // namespace Vulkan::vk 797} // namespace Vulkan::vk
diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/renderer_vulkan/wrapper.h
index da42ca88e..71daac9d7 100644
--- a/src/video_core/renderer_vulkan/wrapper.h
+++ b/src/video_core/renderer_vulkan/wrapper.h
@@ -141,6 +141,7 @@ struct InstanceDispatch {
141 PFN_vkCreateInstance vkCreateInstance; 141 PFN_vkCreateInstance vkCreateInstance;
142 PFN_vkDestroyInstance vkDestroyInstance; 142 PFN_vkDestroyInstance vkDestroyInstance;
143 PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties; 143 PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties;
144 PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;
144 145
145 PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT; 146 PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT;
146 PFN_vkCreateDevice vkCreateDevice; 147 PFN_vkCreateDevice vkCreateDevice;
@@ -206,6 +207,16 @@ struct DeviceDispatch : public InstanceDispatch {
206 PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask; 207 PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask;
207 PFN_vkCmdSetViewport vkCmdSetViewport; 208 PFN_vkCmdSetViewport vkCmdSetViewport;
208 PFN_vkCmdWaitEvents vkCmdWaitEvents; 209 PFN_vkCmdWaitEvents vkCmdWaitEvents;
210 PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT;
211 PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT;
212 PFN_vkCmdSetDepthBoundsTestEnableEXT vkCmdSetDepthBoundsTestEnableEXT;
213 PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT;
214 PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT;
215 PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT;
216 PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT;
217 PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT;
218 PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT;
219 PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT;
209 PFN_vkCreateBuffer vkCreateBuffer; 220 PFN_vkCreateBuffer vkCreateBuffer;
210 PFN_vkCreateBufferView vkCreateBufferView; 221 PFN_vkCreateBufferView vkCreateBufferView;
211 PFN_vkCreateCommandPool vkCreateCommandPool; 222 PFN_vkCreateCommandPool vkCreateCommandPool;
@@ -968,6 +979,50 @@ public:
968 buffer_barriers.data(), image_barriers.size(), image_barriers.data()); 979 buffer_barriers.data(), image_barriers.size(), image_barriers.data());
969 } 980 }
970 981
982 void BindVertexBuffers2EXT(u32 first_binding, u32 binding_count, const VkBuffer* buffers,
983 const VkDeviceSize* offsets, const VkDeviceSize* sizes,
984 const VkDeviceSize* strides) const noexcept {
985 dld->vkCmdBindVertexBuffers2EXT(handle, first_binding, binding_count, buffers, offsets,
986 sizes, strides);
987 }
988
989 void SetCullModeEXT(VkCullModeFlags cull_mode) const noexcept {
990 dld->vkCmdSetCullModeEXT(handle, cull_mode);
991 }
992
993 void SetDepthBoundsTestEnableEXT(bool enable) const noexcept {
994 dld->vkCmdSetDepthBoundsTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
995 }
996
997 void SetDepthCompareOpEXT(VkCompareOp compare_op) const noexcept {
998 dld->vkCmdSetDepthCompareOpEXT(handle, compare_op);
999 }
1000
1001 void SetDepthTestEnableEXT(bool enable) const noexcept {
1002 dld->vkCmdSetDepthTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1003 }
1004
1005 void SetDepthWriteEnableEXT(bool enable) const noexcept {
1006 dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1007 }
1008
1009 void SetFrontFaceEXT(VkFrontFace front_face) const noexcept {
1010 dld->vkCmdSetFrontFaceEXT(handle, front_face);
1011 }
1012
1013 void SetPrimitiveTopologyEXT(VkPrimitiveTopology primitive_topology) const noexcept {
1014 dld->vkCmdSetPrimitiveTopologyEXT(handle, primitive_topology);
1015 }
1016
1017 void SetStencilOpEXT(VkStencilFaceFlags face_mask, VkStencilOp fail_op, VkStencilOp pass_op,
1018 VkStencilOp depth_fail_op, VkCompareOp compare_op) const noexcept {
1019 dld->vkCmdSetStencilOpEXT(handle, face_mask, fail_op, pass_op, depth_fail_op, compare_op);
1020 }
1021
1022 void SetStencilTestEnableEXT(bool enable) const noexcept {
1023 dld->vkCmdSetStencilTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1024 }
1025
971 void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers, 1026 void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers,
972 const VkDeviceSize* offsets, 1027 const VkDeviceSize* offsets,
973 const VkDeviceSize* sizes) const noexcept { 1028 const VkDeviceSize* sizes) const noexcept {
@@ -996,4 +1051,7 @@ private:
996std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProperties( 1051std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProperties(
997 const InstanceDispatch& dld); 1052 const InstanceDispatch& dld);
998 1053
1054std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties(
1055 const InstanceDispatch& dld);
1056
999} // namespace Vulkan::vk 1057} // namespace Vulkan::vk
diff --git a/src/video_core/shader_cache.h b/src/video_core/shader_cache.h
index 2dd270e99..b7608fc7b 100644
--- a/src/video_core/shader_cache.h
+++ b/src/video_core/shader_cache.h
@@ -20,6 +20,7 @@ namespace VideoCommon {
20template <class T> 20template <class T>
21class ShaderCache { 21class ShaderCache {
22 static constexpr u64 PAGE_BITS = 14; 22 static constexpr u64 PAGE_BITS = 14;
23 static constexpr u64 PAGE_SIZE = u64(1) << PAGE_BITS;
23 24
24 struct Entry { 25 struct Entry {
25 VAddr addr_start; 26 VAddr addr_start;
@@ -87,8 +88,8 @@ protected:
87 const VAddr addr_end = addr + size; 88 const VAddr addr_end = addr + size;
88 Entry* const entry = NewEntry(addr, addr_end, data.get()); 89 Entry* const entry = NewEntry(addr, addr_end, data.get());
89 90
90 const u64 page_end = addr_end >> PAGE_BITS; 91 const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS;
91 for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) { 92 for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) {
92 invalidation_cache[page].push_back(entry); 93 invalidation_cache[page].push_back(entry);
93 } 94 }
94 95
@@ -108,20 +109,13 @@ private:
108 /// @pre invalidation_mutex is locked 109 /// @pre invalidation_mutex is locked
109 void InvalidatePagesInRegion(VAddr addr, std::size_t size) { 110 void InvalidatePagesInRegion(VAddr addr, std::size_t size) {
110 const VAddr addr_end = addr + size; 111 const VAddr addr_end = addr + size;
111 const u64 page_end = addr_end >> PAGE_BITS; 112 const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS;
112 for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) { 113 for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) {
113 const auto it = invalidation_cache.find(page); 114 auto it = invalidation_cache.find(page);
114 if (it == invalidation_cache.end()) { 115 if (it == invalidation_cache.end()) {
115 continue; 116 continue;
116 } 117 }
117 118 InvalidatePageEntries(it->second, addr, addr_end);
118 std::vector<Entry*>& entries = it->second;
119 InvalidatePageEntries(entries, addr, addr_end);
120
121 // If there's nothing else in this page, remove it to avoid overpopulating the hash map.
122 if (entries.empty()) {
123 invalidation_cache.erase(it);
124 }
125 } 119 }
126 } 120 }
127 121
@@ -131,15 +125,22 @@ private:
131 if (marked_for_removal.empty()) { 125 if (marked_for_removal.empty()) {
132 return; 126 return;
133 } 127 }
134 std::scoped_lock lock{lookup_mutex}; 128 // Remove duplicates
129 std::sort(marked_for_removal.begin(), marked_for_removal.end());
130 marked_for_removal.erase(std::unique(marked_for_removal.begin(), marked_for_removal.end()),
131 marked_for_removal.end());
135 132
136 std::vector<T*> removed_shaders; 133 std::vector<T*> removed_shaders;
137 removed_shaders.reserve(marked_for_removal.size()); 134 removed_shaders.reserve(marked_for_removal.size());
138 135
136 std::scoped_lock lock{lookup_mutex};
137
139 for (Entry* const entry : marked_for_removal) { 138 for (Entry* const entry : marked_for_removal) {
140 if (lookup_cache.erase(entry->addr_start) > 0) { 139 removed_shaders.push_back(entry->data);
141 removed_shaders.push_back(entry->data); 140
142 } 141 const auto it = lookup_cache.find(entry->addr_start);
142 ASSERT(it != lookup_cache.end());
143 lookup_cache.erase(it);
143 } 144 }
144 marked_for_removal.clear(); 145 marked_for_removal.clear();
145 146
@@ -154,17 +155,33 @@ private:
154 /// @param addr_end Non-inclusive end address of the invalidation 155 /// @param addr_end Non-inclusive end address of the invalidation
155 /// @pre invalidation_mutex is locked 156 /// @pre invalidation_mutex is locked
156 void InvalidatePageEntries(std::vector<Entry*>& entries, VAddr addr, VAddr addr_end) { 157 void InvalidatePageEntries(std::vector<Entry*>& entries, VAddr addr, VAddr addr_end) {
157 auto it = entries.begin(); 158 std::size_t index = 0;
158 while (it != entries.end()) { 159 while (index < entries.size()) {
159 Entry* const entry = *it; 160 Entry* const entry = entries[index];
160 if (!entry->Overlaps(addr, addr_end)) { 161 if (!entry->Overlaps(addr, addr_end)) {
161 ++it; 162 ++index;
162 continue; 163 continue;
163 } 164 }
165
164 UnmarkMemory(entry); 166 UnmarkMemory(entry);
167 RemoveEntryFromInvalidationCache(entry);
165 marked_for_removal.push_back(entry); 168 marked_for_removal.push_back(entry);
169 }
170 }
166 171
167 it = entries.erase(it); 172 /// @brief Removes all references to an entry in the invalidation cache
173 /// @param entry Entry to remove from the invalidation cache
174 /// @pre invalidation_mutex is locked
175 void RemoveEntryFromInvalidationCache(const Entry* entry) {
176 const u64 page_end = (entry->addr_end + PAGE_SIZE - 1) >> PAGE_BITS;
177 for (u64 page = entry->addr_start >> PAGE_BITS; page < page_end; ++page) {
178 const auto entries_it = invalidation_cache.find(page);
179 ASSERT(entries_it != invalidation_cache.end());
180 std::vector<Entry*>& entries = entries_it->second;
181
182 const auto entry_it = std::find(entries.begin(), entries.end(), entry);
183 ASSERT(entry_it != entries.end());
184 entries.erase(entry_it);
168 } 185 }
169 } 186 }
170 187
@@ -182,16 +199,11 @@ private:
182 } 199 }
183 200
184 /// @brief Removes a vector of shaders from a list 201 /// @brief Removes a vector of shaders from a list
185 /// @param removed_shaders Shaders to be removed from the storage, it can contain duplicates 202 /// @param removed_shaders Shaders to be removed from the storage
186 /// @pre invalidation_mutex is locked 203 /// @pre invalidation_mutex is locked
187 /// @pre lookup_mutex is locked 204 /// @pre lookup_mutex is locked
188 void RemoveShadersFromStorage(std::vector<T*> removed_shaders) { 205 void RemoveShadersFromStorage(std::vector<T*> removed_shaders) {
189 // Remove duplicates 206 // Notify removals
190 std::sort(removed_shaders.begin(), removed_shaders.end());
191 removed_shaders.erase(std::unique(removed_shaders.begin(), removed_shaders.end()),
192 removed_shaders.end());
193
194 // Now that there are no duplicates, we can notify removals
195 for (T* const shader : removed_shaders) { 207 for (T* const shader : removed_shaders) {
196 OnShaderRemoval(shader); 208 OnShaderRemoval(shader);
197 } 209 }
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 85075e868..6207d8dfe 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -24,6 +24,7 @@
24#include "core/core.h" 24#include "core/core.h"
25#include "core/memory.h" 25#include "core/memory.h"
26#include "core/settings.h" 26#include "core/settings.h"
27#include "video_core/compatible_formats.h"
27#include "video_core/dirty_flags.h" 28#include "video_core/dirty_flags.h"
28#include "video_core/engines/fermi_2d.h" 29#include "video_core/engines/fermi_2d.h"
29#include "video_core/engines/maxwell_3d.h" 30#include "video_core/engines/maxwell_3d.h"
@@ -47,8 +48,8 @@ class RasterizerInterface;
47 48
48namespace VideoCommon { 49namespace VideoCommon {
49 50
51using VideoCore::Surface::FormatCompatibility;
50using VideoCore::Surface::PixelFormat; 52using VideoCore::Surface::PixelFormat;
51
52using VideoCore::Surface::SurfaceTarget; 53using VideoCore::Surface::SurfaceTarget;
53using RenderTargetConfig = Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig; 54using RenderTargetConfig = Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig;
54 55
@@ -595,7 +596,7 @@ private:
595 } else { 596 } else {
596 new_surface = GetUncachedSurface(gpu_addr, params); 597 new_surface = GetUncachedSurface(gpu_addr, params);
597 } 598 }
598 const auto& final_params = new_surface->GetSurfaceParams(); 599 const SurfaceParams& final_params = new_surface->GetSurfaceParams();
599 if (cr_params.type != final_params.type) { 600 if (cr_params.type != final_params.type) {
600 if (Settings::IsGPULevelExtreme()) { 601 if (Settings::IsGPULevelExtreme()) {
601 BufferCopy(current_surface, new_surface); 602 BufferCopy(current_surface, new_surface);
@@ -603,7 +604,7 @@ private:
603 } else { 604 } else {
604 std::vector<CopyParams> bricks = current_surface->BreakDown(final_params); 605 std::vector<CopyParams> bricks = current_surface->BreakDown(final_params);
605 for (auto& brick : bricks) { 606 for (auto& brick : bricks) {
606 ImageCopy(current_surface, new_surface, brick); 607 TryCopyImage(current_surface, new_surface, brick);
607 } 608 }
608 } 609 }
609 Unregister(current_surface); 610 Unregister(current_surface);
@@ -694,7 +695,7 @@ private:
694 } 695 }
695 const CopyParams copy_params(0, 0, 0, 0, 0, base_layer, 0, mipmap, width, height, 696 const CopyParams copy_params(0, 0, 0, 0, 0, base_layer, 0, mipmap, width, height,
696 src_params.depth); 697 src_params.depth);
697 ImageCopy(surface, new_surface, copy_params); 698 TryCopyImage(surface, new_surface, copy_params);
698 } 699 }
699 } 700 }
700 if (passed_tests == 0) { 701 if (passed_tests == 0) {
@@ -791,7 +792,7 @@ private:
791 const u32 width = params.width; 792 const u32 width = params.width;
792 const u32 height = params.height; 793 const u32 height = params.height;
793 const CopyParams copy_params(0, 0, 0, 0, 0, slice, 0, 0, width, height, 1); 794 const CopyParams copy_params(0, 0, 0, 0, 0, slice, 0, 0, width, height, 1);
794 ImageCopy(surface, new_surface, copy_params); 795 TryCopyImage(surface, new_surface, copy_params);
795 } 796 }
796 for (const auto& surface : overlaps) { 797 for (const auto& surface : overlaps) {
797 Unregister(surface); 798 Unregister(surface);
@@ -1192,6 +1193,19 @@ private:
1192 return {}; 1193 return {};
1193 } 1194 }
1194 1195
1196 /// Try to do an image copy logging when formats are incompatible.
1197 void TryCopyImage(TSurface& src, TSurface& dst, const CopyParams& copy) {
1198 const SurfaceParams& src_params = src->GetSurfaceParams();
1199 const SurfaceParams& dst_params = dst->GetSurfaceParams();
1200 if (!format_compatibility.TestCopy(src_params.pixel_format, dst_params.pixel_format)) {
1201 LOG_ERROR(HW_GPU, "Illegal copy between formats={{{}, {}}}",
1202 static_cast<int>(dst_params.pixel_format),
1203 static_cast<int>(src_params.pixel_format));
1204 return;
1205 }
1206 ImageCopy(src, dst, copy);
1207 }
1208
1195 constexpr PixelFormat GetSiblingFormat(PixelFormat format) const { 1209 constexpr PixelFormat GetSiblingFormat(PixelFormat format) const {
1196 return siblings_table[static_cast<std::size_t>(format)]; 1210 return siblings_table[static_cast<std::size_t>(format)];
1197 } 1211 }
@@ -1241,6 +1255,7 @@ private:
1241 VideoCore::RasterizerInterface& rasterizer; 1255 VideoCore::RasterizerInterface& rasterizer;
1242 1256
1243 FormatLookupTable format_lookup_table; 1257 FormatLookupTable format_lookup_table;
1258 FormatCompatibility format_compatibility;
1244 1259
1245 u64 ticks{}; 1260 u64 ticks{};
1246 1261
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index bbbd96113..5e0d0e7af 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -212,7 +212,7 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default
212// UISetting::values.shortcuts, which is alphabetically ordered. 212// UISetting::values.shortcuts, which is alphabetically ordered.
213// clang-format off 213// clang-format off
214const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{ 214const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{
215 {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}}, 215 {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}},
216 {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}}, 216 {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}},
217 {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, 217 {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
218 {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}}, 218 {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}},
@@ -220,8 +220,8 @@ const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{
220 {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}}, 220 {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}},
221 {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}}, 221 {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}},
222 {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}}, 222 {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}},
223 {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}}, 223 {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}},
224 {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WindowShortcut}}, 224 {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}},
225 {QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}}, 225 {QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}},
226 {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, 226 {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
227 {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, 227 {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
@@ -665,11 +665,13 @@ void Config::ReadShortcutValues() {
665 const auto& [keyseq, context] = shortcut; 665 const auto& [keyseq, context] = shortcut;
666 qt_config->beginGroup(group); 666 qt_config->beginGroup(group);
667 qt_config->beginGroup(name); 667 qt_config->beginGroup(name);
668 // No longer using ReadSetting for shortcut.second as it innacurately returns a value of 1
669 // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open
670 // a file dialog in windowed mode
668 UISettings::values.shortcuts.push_back( 671 UISettings::values.shortcuts.push_back(
669 {name, 672 {name,
670 group, 673 group,
671 {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), 674 {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), shortcut.second}});
672 ReadSetting(QStringLiteral("Context"), context).toInt()}});
673 qt_config->endGroup(); 675 qt_config->endGroup();
674 qt_config->endGroup(); 676 qt_config->endGroup();
675 } 677 }
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index a05fa64ba..00433926d 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -70,6 +70,20 @@ static QString ButtonToText(const Common::ParamPackage& param) {
70 return GetKeyName(param.Get("code", 0)); 70 return GetKeyName(param.Get("code", 0));
71 } 71 }
72 72
73 if (param.Get("engine", "") == "gcpad") {
74 if (param.Has("axis")) {
75 const QString axis_str = QString::fromStdString(param.Get("axis", ""));
76 const QString direction_str = QString::fromStdString(param.Get("direction", ""));
77
78 return QObject::tr("GC Axis %1%2").arg(axis_str, direction_str);
79 }
80 if (param.Has("button")) {
81 const QString button_str = QString::number(int(std::log2(param.Get("button", 0))));
82 return QObject::tr("GC Button %1").arg(button_str);
83 }
84 return GetKeyName(param.Get("code", 0));
85 }
86
73 if (param.Get("engine", "") == "sdl") { 87 if (param.Get("engine", "") == "sdl") {
74 if (param.Has("hat")) { 88 if (param.Has("hat")) {
75 const QString hat_str = QString::fromStdString(param.Get("hat", "")); 89 const QString hat_str = QString::fromStdString(param.Get("hat", ""));
@@ -126,6 +140,25 @@ static QString AnalogToText(const Common::ParamPackage& param, const std::string
126 return {}; 140 return {};
127 } 141 }
128 142
143 if (param.Get("engine", "") == "gcpad") {
144 if (dir == "modifier") {
145 return QObject::tr("[unused]");
146 }
147
148 if (dir == "left" || dir == "right") {
149 const QString axis_x_str = QString::fromStdString(param.Get("axis_x", ""));
150
151 return QObject::tr("GC Axis %1").arg(axis_x_str);
152 }
153
154 if (dir == "up" || dir == "down") {
155 const QString axis_y_str = QString::fromStdString(param.Get("axis_y", ""));
156
157 return QObject::tr("GC Axis %1").arg(axis_y_str);
158 }
159
160 return {};
161 }
129 return QObject::tr("[unknown]"); 162 return QObject::tr("[unknown]");
130} 163}
131 164
@@ -332,7 +365,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
332 365
333 connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, [=] { 366 connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, [=] {
334 const float slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value(); 367 const float slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value();
335 if (analogs_param[analog_id].Get("engine", "") == "sdl") { 368 if (analogs_param[analog_id].Get("engine", "") == "sdl" ||
369 analogs_param[analog_id].Get("engine", "") == "gcpad") {
336 analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( 370 analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
337 tr("Deadzone: %1%").arg(slider_value)); 371 tr("Deadzone: %1%").arg(slider_value));
338 analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); 372 analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
@@ -352,6 +386,20 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
352 386
353 connect(poll_timer.get(), &QTimer::timeout, [this] { 387 connect(poll_timer.get(), &QTimer::timeout, [this] {
354 Common::ParamPackage params; 388 Common::ParamPackage params;
389 if (InputCommon::GetGCButtons()->IsPolling()) {
390 params = InputCommon::GetGCButtons()->GetNextInput();
391 if (params.Has("engine")) {
392 SetPollingResult(params, false);
393 return;
394 }
395 }
396 if (InputCommon::GetGCAnalogs()->IsPolling()) {
397 params = InputCommon::GetGCAnalogs()->GetNextInput();
398 if (params.Has("engine")) {
399 SetPollingResult(params, false);
400 return;
401 }
402 }
355 for (auto& poller : device_pollers) { 403 for (auto& poller : device_pollers) {
356 params = poller->GetNextInput(); 404 params = poller->GetNextInput();
357 if (params.Has("engine")) { 405 if (params.Has("engine")) {
@@ -534,7 +582,7 @@ void ConfigureInputPlayer::UpdateButtonLabels() {
534 analog_map_deadzone_and_modifier_slider_label[analog_id]; 582 analog_map_deadzone_and_modifier_slider_label[analog_id];
535 583
536 if (param.Has("engine")) { 584 if (param.Has("engine")) {
537 if (param.Get("engine", "") == "sdl") { 585 if (param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad") {
538 if (!param.Has("deadzone")) { 586 if (!param.Has("deadzone")) {
539 param.Set("deadzone", 0.1f); 587 param.Set("deadzone", 0.1f);
540 } 588 }
@@ -583,6 +631,11 @@ void ConfigureInputPlayer::HandleClick(
583 631
584 grabKeyboard(); 632 grabKeyboard();
585 grabMouse(); 633 grabMouse();
634 if (type == InputCommon::Polling::DeviceType::Button) {
635 InputCommon::GetGCButtons()->BeginConfiguration();
636 } else {
637 InputCommon::GetGCAnalogs()->BeginConfiguration();
638 }
586 timeout_timer->start(5000); // Cancel after 5 seconds 639 timeout_timer->start(5000); // Cancel after 5 seconds
587 poll_timer->start(200); // Check for new inputs every 200ms 640 poll_timer->start(200); // Check for new inputs every 200ms
588} 641}
@@ -596,6 +649,9 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params,
596 poller->Stop(); 649 poller->Stop();
597 } 650 }
598 651
652 InputCommon::GetGCButtons()->EndConfiguration();
653 InputCommon::GetGCAnalogs()->EndConfiguration();
654
599 if (!abort) { 655 if (!abort) {
600 (*input_setter)(params); 656 (*input_setter)(params);
601 } 657 }
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 82625e67f..9844e4764 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -689,6 +689,11 @@ void GMainWindow::InitializeHotkeys() {
689 ui.action_Capture_Screenshot->setShortcutContext( 689 ui.action_Capture_Screenshot->setShortcutContext(
690 hotkey_registry.GetShortcutContext(main_window, capture_screenshot)); 690 hotkey_registry.GetShortcutContext(main_window, capture_screenshot));
691 691
692 ui.action_Fullscreen->setShortcut(
693 hotkey_registry.GetHotkey(main_window, fullscreen, this)->key());
694 ui.action_Fullscreen->setShortcutContext(
695 hotkey_registry.GetShortcutContext(main_window, fullscreen));
696
692 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load File"), this), 697 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load File"), this),
693 &QShortcut::activated, this, &GMainWindow::OnMenuLoadFile); 698 &QShortcut::activated, this, &GMainWindow::OnMenuLoadFile);
694 connect( 699 connect(
@@ -748,7 +753,7 @@ void GMainWindow::InitializeHotkeys() {
748 }); 753 });
749 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Capture Screenshot"), this), 754 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Capture Screenshot"), this),
750 &QShortcut::activated, this, [&] { 755 &QShortcut::activated, this, [&] {
751 if (emu_thread->IsRunning()) { 756 if (emu_thread != nullptr && emu_thread->IsRunning()) {
752 OnCaptureScreenshot(); 757 OnCaptureScreenshot();
753 } 758 }
754 }); 759 });
@@ -863,6 +868,9 @@ void GMainWindow::ConnectMenuEvents() {
863 connect(ui.action_Report_Compatibility, &QAction::triggered, this, 868 connect(ui.action_Report_Compatibility, &QAction::triggered, this,
864 &GMainWindow::OnMenuReportCompatibility); 869 &GMainWindow::OnMenuReportCompatibility);
865 connect(ui.action_Open_Mods_Page, &QAction::triggered, this, &GMainWindow::OnOpenModsPage); 870 connect(ui.action_Open_Mods_Page, &QAction::triggered, this, &GMainWindow::OnOpenModsPage);
871 connect(ui.action_Open_Quickstart_Guide, &QAction::triggered, this,
872 &GMainWindow::OnOpenQuickstartGuide);
873 connect(ui.action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnOpenFAQ);
866 connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); }); 874 connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); });
867 connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure); 875 connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure);
868 876
@@ -876,10 +884,6 @@ void GMainWindow::ConnectMenuEvents() {
876 connect(ui.action_Reset_Window_Size, &QAction::triggered, this, &GMainWindow::ResetWindowSize); 884 connect(ui.action_Reset_Window_Size, &QAction::triggered, this, &GMainWindow::ResetWindowSize);
877 885
878 // Fullscreen 886 // Fullscreen
879 ui.action_Fullscreen->setShortcut(
880 hotkey_registry
881 .GetHotkey(QStringLiteral("Main Window"), QStringLiteral("Fullscreen"), this)
882 ->key());
883 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); 887 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
884 888
885 // Movie 889 // Movie
@@ -1077,17 +1081,19 @@ void GMainWindow::BootGame(const QString& filename) {
1077 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 1081 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
1078 1082
1079 std::string title_name; 1083 std::string title_name;
1084 std::string title_version;
1080 const auto res = Core::System::GetInstance().GetGameName(title_name); 1085 const auto res = Core::System::GetInstance().GetGameName(title_name);
1081 if (res != Loader::ResultStatus::Success) {
1082 const auto metadata = FileSys::PatchManager(title_id).GetControlMetadata();
1083 if (metadata.first != nullptr)
1084 title_name = metadata.first->GetApplicationName();
1085 1086
1086 if (title_name.empty()) 1087 const auto metadata = FileSys::PatchManager(title_id).GetControlMetadata();
1087 title_name = FileUtil::GetFilename(filename.toStdString()); 1088 if (metadata.first != nullptr) {
1089 title_version = metadata.first->GetVersionString();
1090 title_name = metadata.first->GetApplicationName();
1088 } 1091 }
1089 LOG_INFO(Frontend, "Booting game: {:016X} | {}", title_id, title_name); 1092 if (res != Loader::ResultStatus::Success || title_name.empty()) {
1090 UpdateWindowTitle(QString::fromStdString(title_name)); 1093 title_name = FileUtil::GetFilename(filename.toStdString());
1094 }
1095 LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version);
1096 UpdateWindowTitle(title_name, title_version);
1091 1097
1092 loading_screen->Prepare(Core::System::GetInstance().GetAppLoader()); 1098 loading_screen->Prepare(Core::System::GetInstance().GetAppLoader());
1093 loading_screen->show(); 1099 loading_screen->show();
@@ -1838,16 +1844,26 @@ void GMainWindow::OnMenuReportCompatibility() {
1838 } 1844 }
1839} 1845}
1840 1846
1841void GMainWindow::OnOpenModsPage() { 1847void GMainWindow::OpenURL(const QUrl& url) {
1842 const auto mods_page_url = QStringLiteral("https://github.com/yuzu-emu/yuzu/wiki/Switch-Mods"); 1848 const bool open = QDesktopServices::openUrl(url);
1843 const QUrl mods_page(mods_page_url);
1844 const bool open = QDesktopServices::openUrl(mods_page);
1845 if (!open) { 1849 if (!open) {
1846 QMessageBox::warning(this, tr("Error opening URL"), 1850 QMessageBox::warning(this, tr("Error opening URL"),
1847 tr("Unable to open the URL \"%1\".").arg(mods_page_url)); 1851 tr("Unable to open the URL \"%1\".").arg(url.toString()));
1848 } 1852 }
1849} 1853}
1850 1854
1855void GMainWindow::OnOpenModsPage() {
1856 OpenURL(QUrl(QStringLiteral("https://github.com/yuzu-emu/yuzu/wiki/Switch-Mods")));
1857}
1858
1859void GMainWindow::OnOpenQuickstartGuide() {
1860 OpenURL(QUrl(QStringLiteral("https://yuzu-emu.org/help/quickstart/")));
1861}
1862
1863void GMainWindow::OnOpenFAQ() {
1864 OpenURL(QUrl(QStringLiteral("https://yuzu-emu.org/wiki/faq/")));
1865}
1866
1851void GMainWindow::ToggleFullscreen() { 1867void GMainWindow::ToggleFullscreen() {
1852 if (!emulation_running) { 1868 if (!emulation_running) {
1853 return; 1869 return;
@@ -2050,7 +2066,8 @@ void GMainWindow::OnCaptureScreenshot() {
2050 OnStartGame(); 2066 OnStartGame();
2051} 2067}
2052 2068
2053void GMainWindow::UpdateWindowTitle(const QString& title_name) { 2069void GMainWindow::UpdateWindowTitle(const std::string& title_name,
2070 const std::string& title_version) {
2054 const auto full_name = std::string(Common::g_build_fullname); 2071 const auto full_name = std::string(Common::g_build_fullname);
2055 const auto branch_name = std::string(Common::g_scm_branch); 2072 const auto branch_name = std::string(Common::g_scm_branch);
2056 const auto description = std::string(Common::g_scm_desc); 2073 const auto description = std::string(Common::g_scm_desc);
@@ -2059,7 +2076,7 @@ void GMainWindow::UpdateWindowTitle(const QString& title_name) {
2059 const auto date = 2076 const auto date =
2060 QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd")).toStdString(); 2077 QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd")).toStdString();
2061 2078
2062 if (title_name.isEmpty()) { 2079 if (title_name.empty()) {
2063 const auto fmt = std::string(Common::g_title_bar_format_idle); 2080 const auto fmt = std::string(Common::g_title_bar_format_idle);
2064 setWindowTitle(QString::fromStdString(fmt::format(fmt.empty() ? "yuzu {0}| {1}-{2}" : fmt, 2081 setWindowTitle(QString::fromStdString(fmt::format(fmt.empty() ? "yuzu {0}| {1}-{2}" : fmt,
2065 full_name, branch_name, description, 2082 full_name, branch_name, description,
@@ -2067,8 +2084,8 @@ void GMainWindow::UpdateWindowTitle(const QString& title_name) {
2067 } else { 2084 } else {
2068 const auto fmt = std::string(Common::g_title_bar_format_running); 2085 const auto fmt = std::string(Common::g_title_bar_format_running);
2069 setWindowTitle(QString::fromStdString( 2086 setWindowTitle(QString::fromStdString(
2070 fmt::format(fmt.empty() ? "yuzu {0}| {3} | {1}-{2}" : fmt, full_name, branch_name, 2087 fmt::format(fmt.empty() ? "yuzu {0}| {3} | {6} | {1}-{2}" : fmt, full_name, branch_name,
2071 description, title_name.toStdString(), date, build_id))); 2088 description, title_name, date, build_id, title_version)));
2072 } 2089 }
2073} 2090}
2074 2091
@@ -2209,7 +2226,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
2209 "title.keys_autogenerated"); 2226 "title.keys_autogenerated");
2210 } 2227 }
2211 2228
2212 Core::Crypto::KeyManager keys{}; 2229 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
2213 if (keys.BaseDeriveNecessary()) { 2230 if (keys.BaseDeriveNecessary()) {
2214 Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory( 2231 Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory(
2215 FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)}; 2232 FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)};
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 5581874ed..66c84e5c0 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -182,6 +182,8 @@ private slots:
182 void OnStopGame(); 182 void OnStopGame();
183 void OnMenuReportCompatibility(); 183 void OnMenuReportCompatibility();
184 void OnOpenModsPage(); 184 void OnOpenModsPage();
185 void OnOpenQuickstartGuide();
186 void OnOpenFAQ();
185 /// Called whenever a user selects a game in the game list widget. 187 /// Called whenever a user selects a game in the game list widget.
186 void OnGameListLoadFile(QString game_path); 188 void OnGameListLoadFile(QString game_path);
187 void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path); 189 void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path);
@@ -216,10 +218,12 @@ private slots:
216 218
217private: 219private:
218 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); 220 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
219 void UpdateWindowTitle(const QString& title_name = {}); 221 void UpdateWindowTitle(const std::string& title_name = {},
222 const std::string& title_version = {});
220 void UpdateStatusBar(); 223 void UpdateStatusBar();
221 void HideMouseCursor(); 224 void HideMouseCursor();
222 void ShowMouseCursor(); 225 void ShowMouseCursor();
226 void OpenURL(const QUrl& url);
223 227
224 Ui::MainWindow ui; 228 Ui::MainWindow ui;
225 229
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index b5745dfd5..bee6e107e 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -114,6 +114,8 @@
114 </property> 114 </property>
115 <addaction name="action_Report_Compatibility"/> 115 <addaction name="action_Report_Compatibility"/>
116 <addaction name="action_Open_Mods_Page"/> 116 <addaction name="action_Open_Mods_Page"/>
117 <addaction name="action_Open_Quickstart_Guide"/>
118 <addaction name="action_Open_FAQ"/>
117 <addaction name="separator"/> 119 <addaction name="separator"/>
118 <addaction name="action_About"/> 120 <addaction name="action_About"/>
119 </widget> 121 </widget>
@@ -262,6 +264,16 @@
262 <string>Open Mods Page</string> 264 <string>Open Mods Page</string>
263 </property> 265 </property>
264 </action> 266 </action>
267 <action name="action_Open_Quickstart_Guide">
268 <property name="text">
269 <string>Open Quickstart Guide</string>
270 </property>
271 </action>
272 <action name="action_Open_FAQ">
273 <property name="text">
274 <string>FAQ</string>
275 </property>
276 </action>
265 <action name="action_Open_yuzu_Folder"> 277 <action name="action_Open_yuzu_Folder">
266 <property name="text"> 278 <property name="text">
267 <string>Open yuzu Folder</string> 279 <string>Open yuzu Folder</string>