summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/crypto/key_manager.h10
-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/es/es.cpp2
-rw-r--r--src/core/hle/service/prepo/prepo.cpp7
-rw-r--r--src/video_core/CMakeLists.txt2
-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_device.cpp20
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp5
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h38
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp144
-rw-r--r--src/video_core/texture_cache/texture_cache.h25
-rw-r--r--src/yuzu/main.cpp61
-rw-r--r--src/yuzu/main.h6
-rw-r--r--src/yuzu/main.ui12
25 files changed, 410 insertions, 185 deletions
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 7265c4171..bf3434e1c 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -223,7 +223,13 @@ 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(KeyManager const&) = delete;
232 void operator=(KeyManager const&) = delete;
227 233
228 bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; 234 bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
229 bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; 235 bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;
@@ -257,6 +263,8 @@ public:
257 bool AddTicketPersonalized(Ticket raw); 263 bool AddTicketPersonalized(Ticket raw);
258 264
259private: 265private:
266 KeyManager();
267
260 std::map<KeyIndex<S128KeyType>, Key128> s128_keys; 268 std::map<KeyIndex<S128KeyType>, Key128> s128_keys;
261 std::map<KeyIndex<S256KeyType>, Key256> s256_keys; 269 std::map<KeyIndex<S256KeyType>, Key256> s256_keys;
262 270
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 0af44f340..464ca6503 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 Core::Crypto::KeyManager& 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..a09d504ae 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..e6c887b32 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..4b2fb08cb 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..c35a0d10b 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 Core::Crypto::KeyManager& 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..bd577f6e5 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..95da907bc 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/es/es.cpp b/src/core/hle/service/es/es.cpp
index 9365f27e1..da6b74a22 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/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/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_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..774e70a5b 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}
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/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/main.cpp b/src/yuzu/main.cpp
index 82625e67f..f586950e7 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(
@@ -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>