summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--externals/CMakeLists.txt8
m---------externals/xbyak0
-rw-r--r--src/common/CMakeLists.txt6
-rw-r--r--src/common/memory_detect.cpp60
-rw-r--r--src/common/memory_detect.h22
-rw-r--r--src/common/x64/xbyak_abi.h266
-rw-r--r--src/common/x64/xbyak_util.h47
-rw-r--r--src/core/file_sys/patch_manager.cpp34
-rw-r--r--src/core/file_sys/patch_manager.h5
-rw-r--r--src/video_core/CMakeLists.txt2
-rw-r--r--src/video_core/engines/maxwell_3d.cpp13
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp21
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp71
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h26
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_device.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp29
-rw-r--r--src/video_core/texture_cache/texture_cache.h4
-rw-r--r--src/yuzu/main.cpp5
21 files changed, 545 insertions, 86 deletions
diff --git a/.gitmodules b/.gitmodules
index bf3b80d59..2ec9dda62 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -28,3 +28,6 @@
28[submodule "libzip"] 28[submodule "libzip"]
29 path = externals/libzip/libzip 29 path = externals/libzip/libzip
30 url = https://github.com/nih-at/libzip.git 30 url = https://github.com/nih-at/libzip.git
31[submodule "xbyak"]
32 path = externals/xbyak
33 url = https://github.com/herumi/xbyak.git
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index 0b40cd1b0..df7a5e0a9 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -75,3 +75,11 @@ if (ENABLE_WEB_SERVICE)
75 target_compile_definitions(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT) 75 target_compile_definitions(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
76 target_link_libraries(httplib INTERFACE OpenSSL::SSL OpenSSL::Crypto) 76 target_link_libraries(httplib INTERFACE OpenSSL::SSL OpenSSL::Crypto)
77endif() 77endif()
78
79if (NOT TARGET xbyak)
80 if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
81 add_library(xbyak INTERFACE)
82 target_include_directories(xbyak SYSTEM INTERFACE ./xbyak/xbyak)
83 target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
84 endif()
85endif()
diff --git a/externals/xbyak b/externals/xbyak
new file mode 160000
Subproject 82b70e665918efc2ee348091742fd0237b3b68c
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index e6769a5f3..24b7a083c 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -123,6 +123,8 @@ add_library(common STATIC
123 lz4_compression.cpp 123 lz4_compression.cpp
124 lz4_compression.h 124 lz4_compression.h
125 math_util.h 125 math_util.h
126 memory_detect.cpp
127 memory_detect.h
126 memory_hook.cpp 128 memory_hook.cpp
127 memory_hook.h 129 memory_hook.h
128 microprofile.cpp 130 microprofile.cpp
@@ -169,10 +171,12 @@ if(ARCHITECTURE_x86_64)
169 PRIVATE 171 PRIVATE
170 x64/cpu_detect.cpp 172 x64/cpu_detect.cpp
171 x64/cpu_detect.h 173 x64/cpu_detect.h
174 x64/xbyak_abi.h
175 x64/xbyak_util.h
172 ) 176 )
173endif() 177endif()
174 178
175create_target_directory_groups(common) 179create_target_directory_groups(common)
176 180
177target_link_libraries(common PUBLIC Boost::boost fmt::fmt microprofile) 181target_link_libraries(common PUBLIC Boost::boost fmt::fmt microprofile)
178target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd) 182target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd xbyak)
diff --git a/src/common/memory_detect.cpp b/src/common/memory_detect.cpp
new file mode 100644
index 000000000..3fdc309a2
--- /dev/null
+++ b/src/common/memory_detect.cpp
@@ -0,0 +1,60 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#ifdef _WIN32
6// clang-format off
7#include <windows.h>
8#include <sysinfoapi.h>
9// clang-format on
10#else
11#include <sys/types.h>
12#ifdef __APPLE__
13#include <sys/sysctl.h>
14#else
15#include <sys/sysinfo.h>
16#endif
17#endif
18
19#include "common/memory_detect.h"
20
21namespace Common {
22
23// Detects the RAM and Swapfile sizes
24static MemoryInfo Detect() {
25 MemoryInfo mem_info{};
26
27#ifdef _WIN32
28 MEMORYSTATUSEX memorystatus;
29 memorystatus.dwLength = sizeof(memorystatus);
30 GlobalMemoryStatusEx(&memorystatus);
31 mem_info.TotalPhysicalMemory = memorystatus.ullTotalPhys;
32 mem_info.TotalSwapMemory = memorystatus.ullTotalPageFile - mem_info.TotalPhysicalMemory;
33#elif defined(__APPLE__)
34 u64 ramsize;
35 struct xsw_usage vmusage;
36 std::size_t sizeof_ramsize = sizeof(ramsize);
37 std::size_t sizeof_vmusage = sizeof(vmusage);
38 // hw and vm are defined in sysctl.h
39 // https://github.com/apple/darwin-xnu/blob/master/bsd/sys/sysctl.h#L471
40 // sysctlbyname(const char *, void *, size_t *, void *, size_t);
41 sysctlbyname("hw.memsize", &ramsize, &sizeof_ramsize, NULL, 0);
42 sysctlbyname("vm.swapusage", &vmusage, &sizeof_vmusage, NULL, 0);
43 mem_info.TotalPhysicalMemory = ramsize;
44 mem_info.TotalSwapMemory = vmusage.xsu_total;
45#else
46 struct sysinfo meminfo;
47 sysinfo(&meminfo);
48 mem_info.TotalPhysicalMemory = meminfo.totalram;
49 mem_info.TotalSwapMemory = meminfo.totalswap;
50#endif
51
52 return mem_info;
53}
54
55const MemoryInfo& GetMemInfo() {
56 static MemoryInfo mem_info = Detect();
57 return mem_info;
58}
59
60} // namespace Common \ No newline at end of file
diff --git a/src/common/memory_detect.h b/src/common/memory_detect.h
new file mode 100644
index 000000000..a73c0f3f4
--- /dev/null
+++ b/src/common/memory_detect.h
@@ -0,0 +1,22 @@
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 "common/common_types.h"
8
9namespace Common {
10
11struct MemoryInfo {
12 u64 TotalPhysicalMemory{};
13 u64 TotalSwapMemory{};
14};
15
16/**
17 * Gets the memory info of the host system
18 * @return Reference to a MemoryInfo struct with the physical and swap memory sizes in bytes
19 */
20const MemoryInfo& GetMemInfo();
21
22} // namespace Common \ No newline at end of file
diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h
new file mode 100644
index 000000000..794da8a52
--- /dev/null
+++ b/src/common/x64/xbyak_abi.h
@@ -0,0 +1,266 @@
1// Copyright 2016 Citra 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 <bitset>
8#include <initializer_list>
9#include <xbyak.h>
10#include "common/assert.h"
11
12namespace Common::X64 {
13
14inline int RegToIndex(const Xbyak::Reg& reg) {
15 using Kind = Xbyak::Reg::Kind;
16 ASSERT_MSG((reg.getKind() & (Kind::REG | Kind::XMM)) != 0,
17 "RegSet only support GPRs and XMM registers.");
18 ASSERT_MSG(reg.getIdx() < 16, "RegSet only supports XXM0-15.");
19 return reg.getIdx() + (reg.getKind() == Kind::REG ? 0 : 16);
20}
21
22inline Xbyak::Reg64 IndexToReg64(int reg_index) {
23 ASSERT(reg_index < 16);
24 return Xbyak::Reg64(reg_index);
25}
26
27inline Xbyak::Xmm IndexToXmm(int reg_index) {
28 ASSERT(reg_index >= 16 && reg_index < 32);
29 return Xbyak::Xmm(reg_index - 16);
30}
31
32inline Xbyak::Reg IndexToReg(int reg_index) {
33 if (reg_index < 16) {
34 return IndexToReg64(reg_index);
35 } else {
36 return IndexToXmm(reg_index);
37 }
38}
39
40inline std::bitset<32> BuildRegSet(std::initializer_list<Xbyak::Reg> regs) {
41 std::bitset<32> bits;
42 for (const Xbyak::Reg& reg : regs) {
43 bits[RegToIndex(reg)] = true;
44 }
45 return bits;
46}
47
48const std::bitset<32> ABI_ALL_GPRS(0x0000FFFF);
49const std::bitset<32> ABI_ALL_XMMS(0xFFFF0000);
50
51#ifdef _WIN32
52
53// Microsoft x64 ABI
54const Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
55const Xbyak::Reg ABI_PARAM1 = Xbyak::util::rcx;
56const Xbyak::Reg ABI_PARAM2 = Xbyak::util::rdx;
57const Xbyak::Reg ABI_PARAM3 = Xbyak::util::r8;
58const Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9;
59
60const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
61 // GPRs
62 Xbyak::util::rcx,
63 Xbyak::util::rdx,
64 Xbyak::util::r8,
65 Xbyak::util::r9,
66 Xbyak::util::r10,
67 Xbyak::util::r11,
68 // XMMs
69 Xbyak::util::xmm0,
70 Xbyak::util::xmm1,
71 Xbyak::util::xmm2,
72 Xbyak::util::xmm3,
73 Xbyak::util::xmm4,
74 Xbyak::util::xmm5,
75});
76
77const std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
78 // GPRs
79 Xbyak::util::rbx,
80 Xbyak::util::rsi,
81 Xbyak::util::rdi,
82 Xbyak::util::rbp,
83 Xbyak::util::r12,
84 Xbyak::util::r13,
85 Xbyak::util::r14,
86 Xbyak::util::r15,
87 // XMMs
88 Xbyak::util::xmm6,
89 Xbyak::util::xmm7,
90 Xbyak::util::xmm8,
91 Xbyak::util::xmm9,
92 Xbyak::util::xmm10,
93 Xbyak::util::xmm11,
94 Xbyak::util::xmm12,
95 Xbyak::util::xmm13,
96 Xbyak::util::xmm14,
97 Xbyak::util::xmm15,
98});
99
100constexpr size_t ABI_SHADOW_SPACE = 0x20;
101
102#else
103
104// System V x86-64 ABI
105const Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
106const Xbyak::Reg ABI_PARAM1 = Xbyak::util::rdi;
107const Xbyak::Reg ABI_PARAM2 = Xbyak::util::rsi;
108const Xbyak::Reg ABI_PARAM3 = Xbyak::util::rdx;
109const Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx;
110
111const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
112 // GPRs
113 Xbyak::util::rcx,
114 Xbyak::util::rdx,
115 Xbyak::util::rdi,
116 Xbyak::util::rsi,
117 Xbyak::util::r8,
118 Xbyak::util::r9,
119 Xbyak::util::r10,
120 Xbyak::util::r11,
121 // XMMs
122 Xbyak::util::xmm0,
123 Xbyak::util::xmm1,
124 Xbyak::util::xmm2,
125 Xbyak::util::xmm3,
126 Xbyak::util::xmm4,
127 Xbyak::util::xmm5,
128 Xbyak::util::xmm6,
129 Xbyak::util::xmm7,
130 Xbyak::util::xmm8,
131 Xbyak::util::xmm9,
132 Xbyak::util::xmm10,
133 Xbyak::util::xmm11,
134 Xbyak::util::xmm12,
135 Xbyak::util::xmm13,
136 Xbyak::util::xmm14,
137 Xbyak::util::xmm15,
138});
139
140const std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
141 // GPRs
142 Xbyak::util::rbx,
143 Xbyak::util::rbp,
144 Xbyak::util::r12,
145 Xbyak::util::r13,
146 Xbyak::util::r14,
147 Xbyak::util::r15,
148});
149
150constexpr size_t ABI_SHADOW_SPACE = 0;
151
152#endif
153
154inline void ABI_CalculateFrameSize(std::bitset<32> regs, size_t rsp_alignment,
155 size_t needed_frame_size, s32* out_subtraction,
156 s32* out_xmm_offset) {
157 const auto count = (regs & ABI_ALL_GPRS).count();
158 rsp_alignment -= count * 8;
159 size_t subtraction = 0;
160 const auto xmm_count = (regs & ABI_ALL_XMMS).count();
161 if (xmm_count) {
162 // If we have any XMMs to save, we must align the stack here.
163 subtraction = rsp_alignment & 0xF;
164 }
165 subtraction += 0x10 * xmm_count;
166 size_t xmm_base_subtraction = subtraction;
167 subtraction += needed_frame_size;
168 subtraction += ABI_SHADOW_SPACE;
169 // Final alignment.
170 rsp_alignment -= subtraction;
171 subtraction += rsp_alignment & 0xF;
172
173 *out_subtraction = (s32)subtraction;
174 *out_xmm_offset = (s32)(subtraction - xmm_base_subtraction);
175}
176
177inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs,
178 size_t rsp_alignment, size_t needed_frame_size = 0) {
179 s32 subtraction, xmm_offset;
180 ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
181 for (std::size_t i = 0; i < regs.size(); ++i) {
182 if (regs[i] && ABI_ALL_GPRS[i]) {
183 code.push(IndexToReg64(static_cast<int>(i)));
184 }
185 }
186 if (subtraction != 0) {
187 code.sub(code.rsp, subtraction);
188 }
189
190 for (int i = 0; i < regs.count(); i++) {
191 if (regs.test(i) & ABI_ALL_GPRS.test(i)) {
192 code.push(IndexToReg64(i));
193 }
194 }
195
196 for (std::size_t i = 0; i < regs.size(); ++i) {
197 if (regs[i] && ABI_ALL_XMMS[i]) {
198 code.movaps(code.xword[code.rsp + xmm_offset], IndexToXmm(static_cast<int>(i)));
199 xmm_offset += 0x10;
200 }
201 }
202
203 return ABI_SHADOW_SPACE;
204}
205
206inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs,
207 size_t rsp_alignment, size_t needed_frame_size = 0) {
208 s32 subtraction, xmm_offset;
209 ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
210
211 for (std::size_t i = 0; i < regs.size(); ++i) {
212 if (regs[i] && ABI_ALL_XMMS[i]) {
213 code.movaps(IndexToXmm(static_cast<int>(i)), code.xword[code.rsp + xmm_offset]);
214 xmm_offset += 0x10;
215 }
216 }
217
218 if (subtraction != 0) {
219 code.add(code.rsp, subtraction);
220 }
221
222 // GPRs need to be popped in reverse order
223 for (int i = 15; i >= 0; i--) {
224 if (regs[i]) {
225 code.pop(IndexToReg64(i));
226 }
227 }
228}
229
230inline size_t ABI_PushRegistersAndAdjustStackGPS(Xbyak::CodeGenerator& code, std::bitset<32> regs,
231 size_t rsp_alignment,
232 size_t needed_frame_size = 0) {
233 s32 subtraction, xmm_offset;
234 ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
235
236 for (std::size_t i = 0; i < regs.size(); ++i) {
237 if (regs[i] && ABI_ALL_GPRS[i]) {
238 code.push(IndexToReg64(static_cast<int>(i)));
239 }
240 }
241
242 if (subtraction != 0) {
243 code.sub(code.rsp, subtraction);
244 }
245
246 return ABI_SHADOW_SPACE;
247}
248
249inline void ABI_PopRegistersAndAdjustStackGPS(Xbyak::CodeGenerator& code, std::bitset<32> regs,
250 size_t rsp_alignment, size_t needed_frame_size = 0) {
251 s32 subtraction, xmm_offset;
252 ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
253
254 if (subtraction != 0) {
255 code.add(code.rsp, subtraction);
256 }
257
258 // GPRs need to be popped in reverse order
259 for (int i = 15; i >= 0; i--) {
260 if (regs[i]) {
261 code.pop(IndexToReg64(i));
262 }
263 }
264}
265
266} // namespace Common::X64
diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h
new file mode 100644
index 000000000..df17f8cbe
--- /dev/null
+++ b/src/common/x64/xbyak_util.h
@@ -0,0 +1,47 @@
1// Copyright 2016 Citra 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 <type_traits>
8#include <xbyak.h>
9#include "common/x64/xbyak_abi.h"
10
11namespace Common::X64 {
12
13// Constants for use with cmpps/cmpss
14enum {
15 CMP_EQ = 0,
16 CMP_LT = 1,
17 CMP_LE = 2,
18 CMP_UNORD = 3,
19 CMP_NEQ = 4,
20 CMP_NLT = 5,
21 CMP_NLE = 6,
22 CMP_ORD = 7,
23};
24
25constexpr bool IsWithin2G(uintptr_t ref, uintptr_t target) {
26 const u64 distance = target - (ref + 5);
27 return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL);
28}
29
30inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
31 return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target);
32}
33
34template <typename T>
35inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
36 static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
37 size_t addr = reinterpret_cast<size_t>(f);
38 if (IsWithin2G(code, addr)) {
39 code.call(f);
40 } else {
41 // ABI_RETURN is a safe temp register to use before a call
42 code.mov(ABI_RETURN, addr);
43 code.call(ABI_RETURN);
44 }
45}
46
47} // namespace Common::X64
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index b93aa6935..c47ff863e 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -10,6 +10,7 @@
10#include "common/file_util.h" 10#include "common/file_util.h"
11#include "common/hex_util.h" 11#include "common/hex_util.h"
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/string_util.h"
13#include "core/core.h" 14#include "core/core.h"
14#include "core/file_sys/content_archive.h" 15#include "core/file_sys/content_archive.h"
15#include "core/file_sys/control_metadata.h" 16#include "core/file_sys/control_metadata.h"
@@ -48,6 +49,23 @@ std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
48 return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]); 49 return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
49} 50}
50 51
52std::shared_ptr<VfsDirectory> FindSubdirectoryCaseless(const std::shared_ptr<VfsDirectory> dir,
53 std::string_view name) {
54#ifdef _WIN32
55 return dir->GetSubdirectory(name);
56#else
57 const auto subdirs = dir->GetSubdirectories();
58 for (const auto& subdir : subdirs) {
59 std::string dir_name = Common::ToLower(subdir->GetName());
60 if (dir_name == name) {
61 return subdir;
62 }
63 }
64
65 return nullptr;
66#endif
67}
68
51PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} 69PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
52 70
53PatchManager::~PatchManager() = default; 71PatchManager::~PatchManager() = default;
@@ -104,7 +122,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
104 if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end()) 122 if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
105 continue; 123 continue;
106 124
107 auto exefs_dir = subdir->GetSubdirectory("exefs"); 125 auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs");
108 if (exefs_dir != nullptr) 126 if (exefs_dir != nullptr)
109 layers.push_back(std::move(exefs_dir)); 127 layers.push_back(std::move(exefs_dir));
110 } 128 }
@@ -130,7 +148,7 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
130 if (std::find(disabled.cbegin(), disabled.cend(), subdir->GetName()) != disabled.cend()) 148 if (std::find(disabled.cbegin(), disabled.cend(), subdir->GetName()) != disabled.cend())
131 continue; 149 continue;
132 150
133 auto exefs_dir = subdir->GetSubdirectory("exefs"); 151 auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs");
134 if (exefs_dir != nullptr) { 152 if (exefs_dir != nullptr) {
135 for (const auto& file : exefs_dir->GetFiles()) { 153 for (const auto& file : exefs_dir->GetFiles()) {
136 if (file->GetExtension() == "ips") { 154 if (file->GetExtension() == "ips") {
@@ -295,7 +313,7 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(
295 continue; 313 continue;
296 } 314 }
297 315
298 auto cheats_dir = subdir->GetSubdirectory("cheats"); 316 auto cheats_dir = FindSubdirectoryCaseless(subdir, "cheats");
299 if (cheats_dir != nullptr) { 317 if (cheats_dir != nullptr) {
300 auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true); 318 auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true);
301 if (res.has_value()) { 319 if (res.has_value()) {
@@ -340,11 +358,11 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
340 continue; 358 continue;
341 } 359 }
342 360
343 auto romfs_dir = subdir->GetSubdirectory("romfs"); 361 auto romfs_dir = FindSubdirectoryCaseless(subdir, "romfs");
344 if (romfs_dir != nullptr) 362 if (romfs_dir != nullptr)
345 layers.push_back(std::move(romfs_dir)); 363 layers.push_back(std::move(romfs_dir));
346 364
347 auto ext_dir = subdir->GetSubdirectory("romfs_ext"); 365 auto ext_dir = FindSubdirectoryCaseless(subdir, "romfs_ext");
348 if (ext_dir != nullptr) 366 if (ext_dir != nullptr)
349 layers_ext.push_back(std::move(ext_dir)); 367 layers_ext.push_back(std::move(ext_dir));
350 } 368 }
@@ -470,7 +488,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
470 for (const auto& mod : mod_dir->GetSubdirectories()) { 488 for (const auto& mod : mod_dir->GetSubdirectories()) {
471 std::string types; 489 std::string types;
472 490
473 const auto exefs_dir = mod->GetSubdirectory("exefs"); 491 const auto exefs_dir = FindSubdirectoryCaseless(mod, "exefs");
474 if (IsDirValidAndNonEmpty(exefs_dir)) { 492 if (IsDirValidAndNonEmpty(exefs_dir)) {
475 bool ips = false; 493 bool ips = false;
476 bool ipswitch = false; 494 bool ipswitch = false;
@@ -494,9 +512,9 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
494 if (layeredfs) 512 if (layeredfs)
495 AppendCommaIfNotEmpty(types, "LayeredExeFS"); 513 AppendCommaIfNotEmpty(types, "LayeredExeFS");
496 } 514 }
497 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs"))) 515 if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(mod, "romfs")))
498 AppendCommaIfNotEmpty(types, "LayeredFS"); 516 AppendCommaIfNotEmpty(types, "LayeredFS");
499 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("cheats"))) 517 if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(mod, "cheats")))
500 AppendCommaIfNotEmpty(types, "Cheats"); 518 AppendCommaIfNotEmpty(types, "Cheats");
501 519
502 if (types.empty()) 520 if (types.empty())
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index ec6db524d..f4cb918dd 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -29,6 +29,11 @@ enum class TitleVersionFormat : u8 {
29std::string FormatTitleVersion(u32 version, 29std::string FormatTitleVersion(u32 version,
30 TitleVersionFormat format = TitleVersionFormat::ThreeElements); 30 TitleVersionFormat format = TitleVersionFormat::ThreeElements);
31 31
32// Returns a directory with name matching name case-insensitive. Returns nullptr if directory
33// doesn't have a directory with name.
34std::shared_ptr<VfsDirectory> FindSubdirectoryCaseless(const std::shared_ptr<VfsDirectory> dir,
35 std::string_view name);
36
32// A centralized class to manage patches to games. 37// A centralized class to manage patches to games.
33class PatchManager { 38class PatchManager {
34public: 39public:
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index f00c71dae..d6ee82836 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -229,7 +229,7 @@ endif()
229create_target_directory_groups(video_core) 229create_target_directory_groups(video_core)
230 230
231target_link_libraries(video_core PUBLIC common core) 231target_link_libraries(video_core PUBLIC common core)
232target_link_libraries(video_core PRIVATE glad) 232target_link_libraries(video_core PRIVATE glad xbyak)
233 233
234if (ENABLE_VULKAN) 234if (ENABLE_VULKAN)
235 target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) 235 target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 024c9e43b..004f6b261 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -457,8 +457,9 @@ void Maxwell3D::StampQueryResult(u64 payload, bool long_query) {
457 457
458void Maxwell3D::ProcessQueryGet() { 458void Maxwell3D::ProcessQueryGet() {
459 // TODO(Subv): Support the other query units. 459 // TODO(Subv): Support the other query units.
460 ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop, 460 if (regs.query.query_get.unit != Regs::QueryUnit::Crop) {
461 "Units other than CROP are unimplemented"); 461 LOG_DEBUG(HW_GPU, "Units other than CROP are unimplemented");
462 }
462 463
463 switch (regs.query.query_get.operation) { 464 switch (regs.query.query_get.operation) {
464 case Regs::QueryOperation::Release: 465 case Regs::QueryOperation::Release:
@@ -534,8 +535,8 @@ void Maxwell3D::ProcessCounterReset() {
534 rasterizer.ResetCounter(QueryType::SamplesPassed); 535 rasterizer.ResetCounter(QueryType::SamplesPassed);
535 break; 536 break;
536 default: 537 default:
537 LOG_WARNING(Render_OpenGL, "Unimplemented counter reset={}", 538 LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}",
538 static_cast<int>(regs.counter_reset)); 539 static_cast<int>(regs.counter_reset));
539 break; 540 break;
540 } 541 }
541} 542}
@@ -592,8 +593,8 @@ std::optional<u64> Maxwell3D::GetQueryResult() {
592 system.GPU().GetTicks()); 593 system.GPU().GetTicks());
593 return {}; 594 return {};
594 default: 595 default:
595 UNIMPLEMENTED_MSG("Unimplemented query select type {}", 596 LOG_DEBUG(HW_GPU, "Unimplemented query select type {}",
596 static_cast<u32>(regs.query.query_get.select.Value())); 597 static_cast<u32>(regs.query.query_get.select.Value()));
597 return 1; 598 return 1;
598 } 599 }
599} 600}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 8116a5daa..716d43e65 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -977,16 +977,12 @@ void RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextu
977 glBindTextureUnit(binding, 0); 977 glBindTextureUnit(binding, 0);
978 return; 978 return;
979 } 979 }
980 glBindTextureUnit(binding, view->GetTexture()); 980 const GLuint handle = view->GetTexture(texture.tic.x_source, texture.tic.y_source,
981 981 texture.tic.z_source, texture.tic.w_source);
982 if (view->GetSurfaceParams().IsBuffer()) { 982 glBindTextureUnit(binding, handle);
983 return; 983 if (!view->GetSurfaceParams().IsBuffer()) {
984 glBindSampler(binding, sampler_cache.GetSampler(texture.tsc));
984 } 985 }
985 // Apply swizzle to textures that are not buffers.
986 view->ApplySwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source,
987 texture.tic.w_source);
988
989 glBindSampler(binding, sampler_cache.GetSampler(texture.tsc));
990} 986}
991 987
992void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, const Shader& shader) { 988void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, const Shader& shader) {
@@ -1015,14 +1011,11 @@ void RasterizerOpenGL::SetupImage(u32 binding, const Tegra::Texture::TICEntry& t
1015 glBindImageTexture(binding, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R8); 1011 glBindImageTexture(binding, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R8);
1016 return; 1012 return;
1017 } 1013 }
1018 if (!tic.IsBuffer()) {
1019 view->ApplySwizzle(tic.x_source, tic.y_source, tic.z_source, tic.w_source);
1020 }
1021 if (entry.is_written) { 1014 if (entry.is_written) {
1022 view->MarkAsModified(texture_cache.Tick()); 1015 view->MarkAsModified(texture_cache.Tick());
1023 } 1016 }
1024 glBindImageTexture(binding, view->GetTexture(), 0, GL_TRUE, 0, GL_READ_WRITE, 1017 const GLuint handle = view->GetTexture(tic.x_source, tic.y_source, tic.z_source, tic.w_source);
1025 view->GetFormat()); 1018 glBindImageTexture(binding, handle, 0, GL_TRUE, 0, GL_READ_WRITE, view->GetFormat());
1026} 1019}
1027 1020
1028void RasterizerOpenGL::SyncViewport() { 1021void RasterizerOpenGL::SyncViewport() {
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index 96605db84..8e754fa90 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -47,6 +47,10 @@ void ProgramManager::BindHostPipeline(GLuint pipeline) {
47 old_state.geometry = 0; 47 old_state.geometry = 0;
48 glDisable(GL_GEOMETRY_PROGRAM_NV); 48 glDisable(GL_GEOMETRY_PROGRAM_NV);
49 } 49 }
50 } else {
51 if (!is_graphics_bound) {
52 glUseProgram(0);
53 }
50 } 54 }
51 glBindProgramPipeline(pipeline); 55 glBindProgramPipeline(pipeline);
52} 56}
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 94fbd2a22..4faa8b90c 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -35,7 +35,7 @@ MICROPROFILE_DEFINE(OpenGL_Texture_Buffer_Copy, "OpenGL", "Texture Buffer Copy",
35namespace { 35namespace {
36 36
37struct FormatTuple { 37struct FormatTuple {
38 GLint internal_format; 38 GLenum internal_format;
39 GLenum format = GL_NONE; 39 GLenum format = GL_NONE;
40 GLenum type = GL_NONE; 40 GLenum type = GL_NONE;
41}; 41};
@@ -238,6 +238,12 @@ OGLTexture CreateTexture(const SurfaceParams& params, GLenum target, GLenum inte
238 return texture; 238 return texture;
239} 239}
240 240
241constexpr u32 EncodeSwizzle(SwizzleSource x_source, SwizzleSource y_source, SwizzleSource z_source,
242 SwizzleSource w_source) {
243 return (static_cast<u32>(x_source) << 24) | (static_cast<u32>(y_source) << 16) |
244 (static_cast<u32>(z_source) << 8) | static_cast<u32>(w_source);
245}
246
241} // Anonymous namespace 247} // Anonymous namespace
242 248
243CachedSurface::CachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& params, 249CachedSurface::CachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& params,
@@ -381,7 +387,7 @@ void CachedSurface::DecorateSurfaceName() {
381} 387}
382 388
383void CachedSurfaceView::DecorateViewName(GPUVAddr gpu_addr, std::string prefix) { 389void CachedSurfaceView::DecorateViewName(GPUVAddr gpu_addr, std::string prefix) {
384 LabelGLObject(GL_TEXTURE, texture_view.handle, gpu_addr, prefix); 390 LabelGLObject(GL_TEXTURE, main_view.handle, gpu_addr, prefix);
385} 391}
386 392
387View CachedSurface::CreateView(const ViewParams& view_key) { 393View CachedSurface::CreateView(const ViewParams& view_key) {
@@ -397,14 +403,13 @@ View CachedSurface::CreateViewInner(const ViewParams& view_key, const bool is_pr
397} 403}
398 404
399CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, const ViewParams& params, 405CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, const ViewParams& params,
400 const bool is_proxy) 406 bool is_proxy)
401 : VideoCommon::ViewBase(params), surface{surface}, is_proxy{is_proxy} { 407 : VideoCommon::ViewBase(params), surface{surface},
402 target = GetTextureTarget(params.target); 408 format{GetFormatTuple(surface.GetSurfaceParams().pixel_format).internal_format},
403 format = GetFormatTuple(surface.GetSurfaceParams().pixel_format).internal_format; 409 target{GetTextureTarget(params.target)}, is_proxy{is_proxy} {
404 if (!is_proxy) { 410 if (!is_proxy) {
405 texture_view = CreateTextureView(); 411 main_view = CreateTextureView();
406 } 412 }
407 swizzle = EncodeSwizzle(SwizzleSource::R, SwizzleSource::G, SwizzleSource::B, SwizzleSource::A);
408} 413}
409 414
410CachedSurfaceView::~CachedSurfaceView() = default; 415CachedSurfaceView::~CachedSurfaceView() = default;
@@ -447,27 +452,49 @@ void CachedSurfaceView::Attach(GLenum attachment, GLenum target) const {
447 } 452 }
448} 453}
449 454
450void CachedSurfaceView::ApplySwizzle(SwizzleSource x_source, SwizzleSource y_source, 455GLuint CachedSurfaceView::GetTexture(SwizzleSource x_source, SwizzleSource y_source,
451 SwizzleSource z_source, SwizzleSource w_source) { 456 SwizzleSource z_source, SwizzleSource w_source) {
452 u32 new_swizzle = EncodeSwizzle(x_source, y_source, z_source, w_source); 457 if (GetSurfaceParams().IsBuffer()) {
453 if (new_swizzle == swizzle) 458 return GetTexture();
454 return; 459 }
455 swizzle = new_swizzle; 460 const u32 new_swizzle = EncodeSwizzle(x_source, y_source, z_source, w_source);
456 const std::array gl_swizzle = {GetSwizzleSource(x_source), GetSwizzleSource(y_source), 461 if (current_swizzle == new_swizzle) {
457 GetSwizzleSource(z_source), GetSwizzleSource(w_source)}; 462 return current_view;
458 const GLuint handle = GetTexture(); 463 }
459 const PixelFormat format = surface.GetSurfaceParams().pixel_format; 464 current_swizzle = new_swizzle;
460 switch (format) { 465
466 const auto [entry, is_cache_miss] = view_cache.try_emplace(new_swizzle);
467 OGLTextureView& view = entry->second;
468 if (!is_cache_miss) {
469 current_view = view.handle;
470 return view.handle;
471 }
472 view = CreateTextureView();
473 current_view = view.handle;
474
475 std::array swizzle{x_source, y_source, z_source, w_source};
476
477 switch (const PixelFormat format = GetSurfaceParams().pixel_format) {
461 case PixelFormat::Z24S8: 478 case PixelFormat::Z24S8:
462 case PixelFormat::Z32FS8: 479 case PixelFormat::Z32FS8:
463 case PixelFormat::S8Z24: 480 case PixelFormat::S8Z24:
464 glTextureParameteri(handle, GL_DEPTH_STENCIL_TEXTURE_MODE, 481 UNIMPLEMENTED_IF(x_source != SwizzleSource::R && x_source != SwizzleSource::G);
482 glTextureParameteri(view.handle, GL_DEPTH_STENCIL_TEXTURE_MODE,
465 GetComponent(format, x_source == SwizzleSource::R)); 483 GetComponent(format, x_source == SwizzleSource::R));
484
485 // Make sure we sample the first component
486 std::transform(swizzle.begin(), swizzle.end(), swizzle.begin(), [](SwizzleSource value) {
487 return value == SwizzleSource::G ? SwizzleSource::R : value;
488 });
489 [[fallthrough]];
490 default: {
491 const std::array gl_swizzle = {GetSwizzleSource(swizzle[0]), GetSwizzleSource(swizzle[1]),
492 GetSwizzleSource(swizzle[2]), GetSwizzleSource(swizzle[3])};
493 glTextureParameteriv(view.handle, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle.data());
466 break; 494 break;
467 default:
468 glTextureParameteriv(handle, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle.data());
469 break;
470 } 495 }
496 }
497 return view.handle;
471} 498}
472 499
473OGLTextureView CachedSurfaceView::CreateTextureView() const { 500OGLTextureView CachedSurfaceView::CreateTextureView() const {
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 02d9981a1..8a2ac8603 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -83,7 +83,7 @@ public:
83 /// Attaches this texture view to the current bound GL_DRAW_FRAMEBUFFER 83 /// Attaches this texture view to the current bound GL_DRAW_FRAMEBUFFER
84 void Attach(GLenum attachment, GLenum target) const; 84 void Attach(GLenum attachment, GLenum target) const;
85 85
86 void ApplySwizzle(Tegra::Texture::SwizzleSource x_source, 86 GLuint GetTexture(Tegra::Texture::SwizzleSource x_source,
87 Tegra::Texture::SwizzleSource y_source, 87 Tegra::Texture::SwizzleSource y_source,
88 Tegra::Texture::SwizzleSource z_source, 88 Tegra::Texture::SwizzleSource z_source,
89 Tegra::Texture::SwizzleSource w_source); 89 Tegra::Texture::SwizzleSource w_source);
@@ -98,7 +98,7 @@ public:
98 if (is_proxy) { 98 if (is_proxy) {
99 return surface.GetTexture(); 99 return surface.GetTexture();
100 } 100 }
101 return texture_view.handle; 101 return main_view.handle;
102 } 102 }
103 103
104 GLenum GetFormat() const { 104 GLenum GetFormat() const {
@@ -110,23 +110,19 @@ public:
110 } 110 }
111 111
112private: 112private:
113 u32 EncodeSwizzle(Tegra::Texture::SwizzleSource x_source,
114 Tegra::Texture::SwizzleSource y_source,
115 Tegra::Texture::SwizzleSource z_source,
116 Tegra::Texture::SwizzleSource w_source) const {
117 return (static_cast<u32>(x_source) << 24) | (static_cast<u32>(y_source) << 16) |
118 (static_cast<u32>(z_source) << 8) | static_cast<u32>(w_source);
119 }
120
121 OGLTextureView CreateTextureView() const; 113 OGLTextureView CreateTextureView() const;
122 114
123 CachedSurface& surface; 115 CachedSurface& surface;
124 GLenum target{}; 116 const GLenum format;
125 GLenum format{}; 117 const GLenum target;
118 const bool is_proxy;
119
120 std::unordered_map<u32, OGLTextureView> view_cache;
121 OGLTextureView main_view;
126 122
127 OGLTextureView texture_view; 123 // Use an invalid default so it always fails the comparison test
128 u32 swizzle{}; 124 u32 current_swizzle = 0xffffffff;
129 bool is_proxy{}; 125 GLuint current_view = 0;
130}; 126};
131 127
132class TextureCacheOpenGL final : public TextureCacheBase { 128class TextureCacheOpenGL final : public TextureCacheBase {
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 12be691a5..2871035f5 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -142,7 +142,7 @@ struct FormatTuple {
142 {VK_FORMAT_BC6H_UFLOAT_BLOCK}, // BC6H_UF16 142 {VK_FORMAT_BC6H_UFLOAT_BLOCK}, // BC6H_UF16
143 {VK_FORMAT_BC6H_SFLOAT_BLOCK}, // BC6H_SF16 143 {VK_FORMAT_BC6H_SFLOAT_BLOCK}, // BC6H_SF16
144 {VK_FORMAT_ASTC_4x4_UNORM_BLOCK}, // ASTC_2D_4X4 144 {VK_FORMAT_ASTC_4x4_UNORM_BLOCK}, // ASTC_2D_4X4
145 {VK_FORMAT_B8G8R8A8_UNORM}, // BGRA8 145 {VK_FORMAT_B8G8R8A8_UNORM, Attachable}, // BGRA8
146 {VK_FORMAT_R32G32B32A32_SFLOAT, Attachable | Storage}, // RGBA32F 146 {VK_FORMAT_R32G32B32A32_SFLOAT, Attachable | Storage}, // RGBA32F
147 {VK_FORMAT_R32G32_SFLOAT, Attachable | Storage}, // RG32F 147 {VK_FORMAT_R32G32_SFLOAT, Attachable | Storage}, // RG32F
148 {VK_FORMAT_R32_SFLOAT, Attachable | Storage}, // R32F 148 {VK_FORMAT_R32_SFLOAT, Attachable | Storage}, // R32F
@@ -168,7 +168,7 @@ struct FormatTuple {
168 {VK_FORMAT_ASTC_8x8_UNORM_BLOCK}, // ASTC_2D_8X8 168 {VK_FORMAT_ASTC_8x8_UNORM_BLOCK}, // ASTC_2D_8X8
169 {VK_FORMAT_UNDEFINED}, // ASTC_2D_8X5 169 {VK_FORMAT_UNDEFINED}, // ASTC_2D_8X5
170 {VK_FORMAT_UNDEFINED}, // ASTC_2D_5X4 170 {VK_FORMAT_UNDEFINED}, // ASTC_2D_5X4
171 {VK_FORMAT_UNDEFINED}, // BGRA8_SRGB 171 {VK_FORMAT_B8G8R8A8_SRGB, Attachable}, // BGRA8_SRGB
172 {VK_FORMAT_BC1_RGBA_SRGB_BLOCK}, // DXT1_SRGB 172 {VK_FORMAT_BC1_RGBA_SRGB_BLOCK}, // DXT1_SRGB
173 {VK_FORMAT_BC2_SRGB_BLOCK}, // DXT23_SRGB 173 {VK_FORMAT_BC2_SRGB_BLOCK}, // DXT23_SRGB
174 {VK_FORMAT_BC3_SRGB_BLOCK}, // DXT45_SRGB 174 {VK_FORMAT_BC3_SRGB_BLOCK}, // DXT45_SRGB
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
index f0c491d00..750e5a0ca 100644
--- a/src/video_core/renderer_vulkan/vk_device.cpp
+++ b/src/video_core/renderer_vulkan/vk_device.cpp
@@ -104,6 +104,7 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(
104 VK_FORMAT_R16_SFLOAT, 104 VK_FORMAT_R16_SFLOAT,
105 VK_FORMAT_R16G16B16A16_SFLOAT, 105 VK_FORMAT_R16G16B16A16_SFLOAT,
106 VK_FORMAT_B8G8R8A8_UNORM, 106 VK_FORMAT_B8G8R8A8_UNORM,
107 VK_FORMAT_B8G8R8A8_SRGB,
107 VK_FORMAT_R4G4B4A4_UNORM_PACK16, 108 VK_FORMAT_R4G4B4A4_UNORM_PACK16,
108 VK_FORMAT_D32_SFLOAT, 109 VK_FORMAT_D32_SFLOAT,
109 VK_FORMAT_D16_UNORM, 110 VK_FORMAT_D16_UNORM,
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 55f43e61b..2f1d5021d 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -354,26 +354,23 @@ CachedSurfaceView::~CachedSurfaceView() = default;
354 354
355VkImageView CachedSurfaceView::GetHandle(SwizzleSource x_source, SwizzleSource y_source, 355VkImageView CachedSurfaceView::GetHandle(SwizzleSource x_source, SwizzleSource y_source,
356 SwizzleSource z_source, SwizzleSource w_source) { 356 SwizzleSource z_source, SwizzleSource w_source) {
357 const u32 swizzle = EncodeSwizzle(x_source, y_source, z_source, w_source); 357 const u32 new_swizzle = EncodeSwizzle(x_source, y_source, z_source, w_source);
358 if (last_image_view && last_swizzle == swizzle) { 358 if (last_image_view && last_swizzle == new_swizzle) {
359 return last_image_view; 359 return last_image_view;
360 } 360 }
361 last_swizzle = swizzle; 361 last_swizzle = new_swizzle;
362 362
363 const auto [entry, is_cache_miss] = view_cache.try_emplace(swizzle); 363 const auto [entry, is_cache_miss] = view_cache.try_emplace(new_swizzle);
364 auto& image_view = entry->second; 364 auto& image_view = entry->second;
365 if (!is_cache_miss) { 365 if (!is_cache_miss) {
366 return last_image_view = *image_view; 366 return last_image_view = *image_view;
367 } 367 }
368 368
369 auto swizzle_x = MaxwellToVK::SwizzleSource(x_source); 369 std::array swizzle{MaxwellToVK::SwizzleSource(x_source), MaxwellToVK::SwizzleSource(y_source),
370 auto swizzle_y = MaxwellToVK::SwizzleSource(y_source); 370 MaxwellToVK::SwizzleSource(z_source), MaxwellToVK::SwizzleSource(w_source)};
371 auto swizzle_z = MaxwellToVK::SwizzleSource(z_source);
372 auto swizzle_w = MaxwellToVK::SwizzleSource(w_source);
373
374 if (params.pixel_format == VideoCore::Surface::PixelFormat::A1B5G5R5U) { 371 if (params.pixel_format == VideoCore::Surface::PixelFormat::A1B5G5R5U) {
375 // A1B5G5R5 is implemented as A1R5G5B5, we have to change the swizzle here. 372 // A1B5G5R5 is implemented as A1R5G5B5, we have to change the swizzle here.
376 std::swap(swizzle_x, swizzle_z); 373 std::swap(swizzle[0], swizzle[2]);
377 } 374 }
378 375
379 // Games can sample depth or stencil values on textures. This is decided by the swizzle value on 376 // Games can sample depth or stencil values on textures. This is decided by the swizzle value on
@@ -395,11 +392,11 @@ VkImageView CachedSurfaceView::GetHandle(SwizzleSource x_source, SwizzleSource y
395 UNIMPLEMENTED(); 392 UNIMPLEMENTED();
396 } 393 }
397 394
398 // Vulkan doesn't seem to understand swizzling of a depth stencil image, use identity 395 // Make sure we sample the first component
399 swizzle_x = VK_COMPONENT_SWIZZLE_R; 396 std::transform(
400 swizzle_y = VK_COMPONENT_SWIZZLE_G; 397 swizzle.begin(), swizzle.end(), swizzle.begin(), [](VkComponentSwizzle component) {
401 swizzle_z = VK_COMPONENT_SWIZZLE_B; 398 return component == VK_COMPONENT_SWIZZLE_G ? VK_COMPONENT_SWIZZLE_R : component;
402 swizzle_w = VK_COMPONENT_SWIZZLE_A; 399 });
403 } 400 }
404 401
405 VkImageViewCreateInfo ci; 402 VkImageViewCreateInfo ci;
@@ -409,7 +406,7 @@ VkImageView CachedSurfaceView::GetHandle(SwizzleSource x_source, SwizzleSource y
409 ci.image = surface.GetImageHandle(); 406 ci.image = surface.GetImageHandle();
410 ci.viewType = image_view_type; 407 ci.viewType = image_view_type;
411 ci.format = surface.GetImage().GetFormat(); 408 ci.format = surface.GetImage().GetFormat();
412 ci.components = {swizzle_x, swizzle_y, swizzle_z, swizzle_w}; 409 ci.components = {swizzle[0], swizzle[1], swizzle[2], swizzle[3]};
413 ci.subresourceRange.aspectMask = aspect; 410 ci.subresourceRange.aspectMask = aspect;
414 ci.subresourceRange.baseMipLevel = base_level; 411 ci.subresourceRange.baseMipLevel = base_level;
415 ci.subresourceRange.levelCount = num_levels; 412 ci.subresourceRange.levelCount = num_levels;
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index d6efc34b2..8bfc541d4 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -991,7 +991,9 @@ private:
991 params.target = target; 991 params.target = target;
992 params.is_tiled = false; 992 params.is_tiled = false;
993 params.srgb_conversion = false; 993 params.srgb_conversion = false;
994 params.is_layered = false; 994 params.is_layered =
995 target == SurfaceTarget::Texture1DArray || target == SurfaceTarget::Texture2DArray ||
996 target == SurfaceTarget::TextureCubemap || target == SurfaceTarget::TextureCubeArray;
995 params.block_width = 0; 997 params.block_width = 0;
996 params.block_height = 0; 998 params.block_height = 0;
997 params.block_depth = 0; 999 params.block_depth = 0;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 0b291c7d0..270cccc77 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -65,6 +65,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
65#include "common/logging/backend.h" 65#include "common/logging/backend.h"
66#include "common/logging/filter.h" 66#include "common/logging/filter.h"
67#include "common/logging/log.h" 67#include "common/logging/log.h"
68#include "common/memory_detect.h"
68#include "common/microprofile.h" 69#include "common/microprofile.h"
69#include "common/scm_rev.h" 70#include "common/scm_rev.h"
70#include "common/scope_exit.h" 71#include "common/scope_exit.h"
@@ -219,6 +220,10 @@ GMainWindow::GMainWindow()
219 LOG_INFO(Frontend, "Host CPU: {}", Common::GetCPUCaps().cpu_string); 220 LOG_INFO(Frontend, "Host CPU: {}", Common::GetCPUCaps().cpu_string);
220#endif 221#endif
221 LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString()); 222 LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString());
223 LOG_INFO(Frontend, "Host RAM: {:.2f} GB",
224 Common::GetMemInfo().TotalPhysicalMemory / 1024.0f / 1024 / 1024);
225 LOG_INFO(Frontend, "Host Swap: {:.2f} GB",
226 Common::GetMemInfo().TotalSwapMemory / 1024.0f / 1024 / 1024);
222 UpdateWindowTitle(); 227 UpdateWindowTitle();
223 228
224 show(); 229 show();