summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------externals/dynarmic0
-rw-r--r--src/common/file_util.cpp82
-rw-r--r--src/common/file_util.h13
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp67
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h30
-rw-r--r--src/core/arm/exclusive_monitor.cpp7
-rw-r--r--src/core/arm/exclusive_monitor.h23
-rw-r--r--src/core/core.cpp3
-rw-r--r--src/core/core.h12
-rw-r--r--src/core/core_cpu.cpp19
-rw-r--r--src/core/core_cpu.h10
-rw-r--r--src/core/file_sys/vfs.cpp111
-rw-r--r--src/core/file_sys/vfs.h57
-rw-r--r--src/core/file_sys/vfs_offset.cpp2
-rw-r--r--src/core/file_sys/vfs_offset.h5
-rw-r--r--src/core/file_sys/vfs_real.cpp57
-rw-r--r--src/core/file_sys/vfs_real.h14
-rw-r--r--src/core/hle/kernel/svc.cpp38
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp2
-rw-r--r--src/core/loader/loader.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp12
-rw-r--r--src/yuzu/main.cpp12
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp14
24 files changed, 436 insertions, 159 deletions
diff --git a/externals/dynarmic b/externals/dynarmic
Subproject 7ea12419533f193e3fa55c18ea3760a4568aa88 Subproject fc6b73bd855d0b87b6d78ba859732a4616e5a85
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index a427372c9..1bc291cf9 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -792,66 +792,80 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
792 } 792 }
793} 793}
794 794
795std::vector<std::string> SplitPathComponents(const std::string& filename) { 795std::vector<std::string> SplitPathComponents(std::string_view filename) {
796 auto copy(filename); 796 std::string copy(filename);
797 std::replace(copy.begin(), copy.end(), '\\', '/'); 797 std::replace(copy.begin(), copy.end(), '\\', '/');
798 std::vector<std::string> out; 798 std::vector<std::string> out;
799 799
800 std::stringstream stream(filename); 800 std::stringstream stream(copy);
801 std::string item; 801 std::string item;
802 while (std::getline(stream, item, '/')) 802 while (std::getline(stream, item, '/')) {
803 out.push_back(std::move(item)); 803 out.push_back(std::move(item));
804 }
804 805
805 return out; 806 return out;
806} 807}
807 808
808std::string GetParentPath(const std::string& path) { 809std::string_view GetParentPath(std::string_view path) {
809 auto out = path; 810 const auto name_bck_index = path.rfind('\\');
810 const auto name_bck_index = out.find_last_of('\\'); 811 const auto name_fwd_index = path.rfind('/');
811 const auto name_fwd_index = out.find_last_of('/');
812 size_t name_index; 812 size_t name_index;
813 if (name_bck_index == std::string::npos || name_fwd_index == std::string::npos)
814 name_index = std::min<size_t>(name_bck_index, name_fwd_index);
815 else
816 name_index = std::max<size_t>(name_bck_index, name_fwd_index);
817 813
818 return out.erase(name_index); 814 if (name_bck_index == std::string_view::npos || name_fwd_index == std::string_view::npos) {
815 name_index = std::min(name_bck_index, name_fwd_index);
816 } else {
817 name_index = std::max(name_bck_index, name_fwd_index);
818 }
819
820 return path.substr(0, name_index);
819} 821}
820 822
821std::string GetPathWithoutTop(std::string path) { 823std::string_view GetPathWithoutTop(std::string_view path) {
822 if (path.empty()) 824 if (path.empty()) {
823 return ""; 825 return path;
826 }
827
824 while (path[0] == '\\' || path[0] == '/') { 828 while (path[0] == '\\' || path[0] == '/') {
825 path = path.substr(1); 829 path.remove_suffix(1);
826 if (path.empty()) 830 if (path.empty()) {
827 return ""; 831 return path;
832 }
828 } 833 }
829 const auto name_bck_index = path.find_first_of('\\'); 834
830 const auto name_fwd_index = path.find_first_of('/'); 835 const auto name_bck_index = path.find('\\');
836 const auto name_fwd_index = path.find('/');
831 return path.substr(std::min(name_bck_index, name_fwd_index) + 1); 837 return path.substr(std::min(name_bck_index, name_fwd_index) + 1);
832} 838}
833 839
834std::string GetFilename(std::string path) { 840std::string_view GetFilename(std::string_view path) {
835 std::replace(path.begin(), path.end(), '\\', '/'); 841 const auto name_index = path.find_last_of("\\/");
836 auto name_index = path.find_last_of('/'); 842
837 if (name_index == std::string::npos) 843 if (name_index == std::string_view::npos) {
838 return ""; 844 return {};
845 }
846
839 return path.substr(name_index + 1); 847 return path.substr(name_index + 1);
840} 848}
841 849
842std::string GetExtensionFromFilename(const std::string& name) { 850std::string_view GetExtensionFromFilename(std::string_view name) {
843 size_t index = name.find_last_of('.'); 851 const size_t index = name.rfind('.');
844 if (index == std::string::npos) 852
845 return ""; 853 if (index == std::string_view::npos) {
854 return {};
855 }
846 856
847 return name.substr(index + 1); 857 return name.substr(index + 1);
848} 858}
849 859
850std::string RemoveTrailingSlash(const std::string& path) { 860std::string_view RemoveTrailingSlash(std::string_view path) {
851 if (path.empty()) 861 if (path.empty()) {
852 return path; 862 return path;
853 if (path.back() == '\\' || path.back() == '/') 863 }
854 return path.substr(0, path.size() - 1); 864
865 if (path.back() == '\\' || path.back() == '/') {
866 path.remove_suffix(1);
867 return path;
868 }
855 869
856 return path; 870 return path;
857} 871}
diff --git a/src/common/file_util.h b/src/common/file_util.h
index 090907c03..abfa79eae 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -9,6 +9,7 @@
9#include <fstream> 9#include <fstream>
10#include <functional> 10#include <functional>
11#include <string> 11#include <string>
12#include <string_view>
12#include <type_traits> 13#include <type_traits>
13#include <vector> 14#include <vector>
14#include "common/common_types.h" 15#include "common/common_types.h"
@@ -151,22 +152,22 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
151 152
152// Splits the path on '/' or '\' and put the components into a vector 153// Splits the path on '/' or '\' and put the components into a vector
153// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" } 154// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
154std::vector<std::string> SplitPathComponents(const std::string& filename); 155std::vector<std::string> SplitPathComponents(std::string_view filename);
155 156
156// Gets all of the text up to the last '/' or '\' in the path. 157// Gets all of the text up to the last '/' or '\' in the path.
157std::string GetParentPath(const std::string& path); 158std::string_view GetParentPath(std::string_view path);
158 159
159// Gets all of the text after the first '/' or '\' in the path. 160// Gets all of the text after the first '/' or '\' in the path.
160std::string GetPathWithoutTop(std::string path); 161std::string_view GetPathWithoutTop(std::string_view path);
161 162
162// Gets the filename of the path 163// Gets the filename of the path
163std::string GetFilename(std::string path); 164std::string_view GetFilename(std::string_view path);
164 165
165// Gets the extension of the filename 166// Gets the extension of the filename
166std::string GetExtensionFromFilename(const std::string& name); 167std::string_view GetExtensionFromFilename(std::string_view name);
167 168
168// Removes the final '/' or '\' if one exists 169// Removes the final '/' or '\' if one exists
169std::string RemoveTrailingSlash(const std::string& path); 170std::string_view RemoveTrailingSlash(std::string_view path);
170 171
171// Creates a new vector containing indices [first, last) from the original. 172// Creates a new vector containing indices [first, last) from the original.
172template <typename T> 173template <typename T>
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 0a5d58eea..27a5de7fd 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -1,5 +1,7 @@
1add_library(core STATIC 1add_library(core STATIC
2 arm/arm_interface.h 2 arm/arm_interface.h
3 arm/exclusive_monitor.cpp
4 arm/exclusive_monitor.h
3 arm/unicorn/arm_unicorn.cpp 5 arm/unicorn/arm_unicorn.cpp
4 arm/unicorn/arm_unicorn.h 6 arm/unicorn/arm_unicorn.h
5 core.cpp 7 core.cpp
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 5d7efc9b6..83c09db2b 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -102,18 +102,28 @@ public:
102 u64 tpidr_el0 = 0; 102 u64 tpidr_el0 = 0;
103}; 103};
104 104
105std::unique_ptr<Dynarmic::A64::Jit> MakeJit(const std::unique_ptr<ARM_Dynarmic_Callbacks>& cb) { 105std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() {
106 const auto page_table = Core::CurrentProcess()->vm_manager.page_table.pointers.data(); 106 const auto page_table = Core::CurrentProcess()->vm_manager.page_table.pointers.data();
107 107
108 Dynarmic::A64::UserConfig config; 108 Dynarmic::A64::UserConfig config;
109
110 // Callbacks
109 config.callbacks = cb.get(); 111 config.callbacks = cb.get();
112
113 // Memory
114 config.page_table = reinterpret_cast<void**>(page_table);
115 config.page_table_address_space_bits = Memory::ADDRESS_SPACE_BITS;
116 config.silently_mirror_page_table = false;
117
118 // Multi-process state
119 config.processor_id = core_index;
120 config.global_monitor = &exclusive_monitor->monitor;
121
122 // System registers
110 config.tpidrro_el0 = &cb->tpidrro_el0; 123 config.tpidrro_el0 = &cb->tpidrro_el0;
111 config.tpidr_el0 = &cb->tpidr_el0; 124 config.tpidr_el0 = &cb->tpidr_el0;
112 config.dczid_el0 = 4; 125 config.dczid_el0 = 4;
113 config.ctr_el0 = 0x8444c004; 126 config.ctr_el0 = 0x8444c004;
114 config.page_table = reinterpret_cast<void**>(page_table);
115 config.page_table_address_space_bits = Memory::ADDRESS_SPACE_BITS;
116 config.silently_mirror_page_table = false;
117 127
118 return std::make_unique<Dynarmic::A64::Jit>(config); 128 return std::make_unique<Dynarmic::A64::Jit>(config);
119} 129}
@@ -128,8 +138,11 @@ void ARM_Dynarmic::Step() {
128 cb->InterpreterFallback(jit->GetPC(), 1); 138 cb->InterpreterFallback(jit->GetPC(), 1);
129} 139}
130 140
131ARM_Dynarmic::ARM_Dynarmic() 141ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index)
132 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), jit(MakeJit(cb)) { 142 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)),
143 jit(MakeJit()), exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(
144 exclusive_monitor)},
145 core_index{core_index} {
133 ARM_Interface::ThreadContext ctx; 146 ARM_Interface::ThreadContext ctx;
134 inner_unicorn.SaveContext(ctx); 147 inner_unicorn.SaveContext(ctx);
135 LoadContext(ctx); 148 LoadContext(ctx);
@@ -237,6 +250,46 @@ void ARM_Dynarmic::ClearExclusiveState() {
237} 250}
238 251
239void ARM_Dynarmic::PageTableChanged() { 252void ARM_Dynarmic::PageTableChanged() {
240 jit = MakeJit(cb); 253 jit = MakeJit();
241 current_page_table = Memory::GetCurrentPageTable(); 254 current_page_table = Memory::GetCurrentPageTable();
242} 255}
256
257DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(size_t core_count) : monitor(core_count) {}
258DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
259
260void DynarmicExclusiveMonitor::SetExclusive(size_t core_index, u64 addr) {
261 // Size doesn't actually matter.
262 monitor.Mark(core_index, addr, 16);
263}
264
265void DynarmicExclusiveMonitor::ClearExclusive() {
266 monitor.Clear();
267}
268
269bool DynarmicExclusiveMonitor::ExclusiveWrite8(size_t core_index, u64 vaddr, u8 value) {
270 return monitor.DoExclusiveOperation(core_index, vaddr, 1,
271 [&] { Memory::Write8(vaddr, value); });
272}
273
274bool DynarmicExclusiveMonitor::ExclusiveWrite16(size_t core_index, u64 vaddr, u16 value) {
275 return monitor.DoExclusiveOperation(core_index, vaddr, 2,
276 [&] { Memory::Write16(vaddr, value); });
277}
278
279bool DynarmicExclusiveMonitor::ExclusiveWrite32(size_t core_index, u64 vaddr, u32 value) {
280 return monitor.DoExclusiveOperation(core_index, vaddr, 4,
281 [&] { Memory::Write32(vaddr, value); });
282}
283
284bool DynarmicExclusiveMonitor::ExclusiveWrite64(size_t core_index, u64 vaddr, u64 value) {
285 return monitor.DoExclusiveOperation(core_index, vaddr, 8,
286 [&] { Memory::Write64(vaddr, value); });
287}
288
289bool DynarmicExclusiveMonitor::ExclusiveWrite128(size_t core_index, u64 vaddr,
290 std::array<std::uint64_t, 2> value) {
291 return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] {
292 Memory::Write64(vaddr, value[0]);
293 Memory::Write64(vaddr, value[1]);
294 });
295}
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index a9891ac4f..0fa8b417c 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -6,15 +6,18 @@
6 6
7#include <memory> 7#include <memory>
8#include <dynarmic/A64/a64.h> 8#include <dynarmic/A64/a64.h>
9#include <dynarmic/A64/exclusive_monitor.h>
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "core/arm/arm_interface.h" 11#include "core/arm/arm_interface.h"
12#include "core/arm/exclusive_monitor.h"
11#include "core/arm/unicorn/arm_unicorn.h" 13#include "core/arm/unicorn/arm_unicorn.h"
12 14
13class ARM_Dynarmic_Callbacks; 15class ARM_Dynarmic_Callbacks;
16class DynarmicExclusiveMonitor;
14 17
15class ARM_Dynarmic final : public ARM_Interface { 18class ARM_Dynarmic final : public ARM_Interface {
16public: 19public:
17 ARM_Dynarmic(); 20 ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index);
18 ~ARM_Dynarmic(); 21 ~ARM_Dynarmic();
19 22
20 void MapBackingMemory(VAddr address, size_t size, u8* memory, 23 void MapBackingMemory(VAddr address, size_t size, u8* memory,
@@ -47,10 +50,35 @@ public:
47 void PageTableChanged() override; 50 void PageTableChanged() override;
48 51
49private: 52private:
53 std::unique_ptr<Dynarmic::A64::Jit> MakeJit();
54
50 friend class ARM_Dynarmic_Callbacks; 55 friend class ARM_Dynarmic_Callbacks;
51 std::unique_ptr<ARM_Dynarmic_Callbacks> cb; 56 std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
52 std::unique_ptr<Dynarmic::A64::Jit> jit; 57 std::unique_ptr<Dynarmic::A64::Jit> jit;
53 ARM_Unicorn inner_unicorn; 58 ARM_Unicorn inner_unicorn;
54 59
60 size_t core_index;
61 std::shared_ptr<DynarmicExclusiveMonitor> exclusive_monitor;
62
55 Memory::PageTable* current_page_table = nullptr; 63 Memory::PageTable* current_page_table = nullptr;
56}; 64};
65
66class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
67public:
68 explicit DynarmicExclusiveMonitor(size_t core_count);
69 ~DynarmicExclusiveMonitor();
70
71 void SetExclusive(size_t core_index, u64 addr) override;
72 void ClearExclusive() override;
73
74 bool ExclusiveWrite8(size_t core_index, u64 vaddr, u8 value) override;
75 bool ExclusiveWrite16(size_t core_index, u64 vaddr, u16 value) override;
76 bool ExclusiveWrite32(size_t core_index, u64 vaddr, u32 value) override;
77 bool ExclusiveWrite64(size_t core_index, u64 vaddr, u64 value) override;
78 bool ExclusiveWrite128(size_t core_index, u64 vaddr,
79 std::array<std::uint64_t, 2> value) override;
80
81private:
82 friend class ARM_Dynarmic;
83 Dynarmic::A64::ExclusiveMonitor monitor;
84};
diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp
new file mode 100644
index 000000000..cb8c81d80
--- /dev/null
+++ b/src/core/arm/exclusive_monitor.cpp
@@ -0,0 +1,7 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/arm/exclusive_monitor.h"
6
7ExclusiveMonitor::~ExclusiveMonitor() = default;
diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h
new file mode 100644
index 000000000..acfcdb94c
--- /dev/null
+++ b/src/core/arm/exclusive_monitor.h
@@ -0,0 +1,23 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_types.h"
9
10class ExclusiveMonitor {
11public:
12 virtual ~ExclusiveMonitor();
13
14 virtual void SetExclusive(size_t core_index, u64 addr) = 0;
15 virtual void ClearExclusive() = 0;
16
17 virtual bool ExclusiveWrite8(size_t core_index, u64 vaddr, u8 value) = 0;
18 virtual bool ExclusiveWrite16(size_t core_index, u64 vaddr, u16 value) = 0;
19 virtual bool ExclusiveWrite32(size_t core_index, u64 vaddr, u32 value) = 0;
20 virtual bool ExclusiveWrite64(size_t core_index, u64 vaddr, u64 value) = 0;
21 virtual bool ExclusiveWrite128(size_t core_index, u64 vaddr,
22 std::array<std::uint64_t, 2> value) = 0;
23};
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 9bd9f4bd9..b7f4b4532 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -171,8 +171,9 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
171 current_process = Kernel::Process::Create("main"); 171 current_process = Kernel::Process::Create("main");
172 172
173 cpu_barrier = std::make_shared<CpuBarrier>(); 173 cpu_barrier = std::make_shared<CpuBarrier>();
174 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
174 for (size_t index = 0; index < cpu_cores.size(); ++index) { 175 for (size_t index = 0; index < cpu_cores.size(); ++index) {
175 cpu_cores[index] = std::make_shared<Cpu>(cpu_barrier, index); 176 cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
176 } 177 }
177 178
178 gpu_core = std::make_unique<Tegra::GPU>(); 179 gpu_core = std::make_unique<Tegra::GPU>();
diff --git a/src/core/core.h b/src/core/core.h
index c6f69f001..c123fe401 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -9,6 +9,7 @@
9#include <string> 9#include <string>
10#include <thread> 10#include <thread>
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "core/arm/exclusive_monitor.h"
12#include "core/core_cpu.h" 13#include "core/core_cpu.h"
13#include "core/hle/kernel/kernel.h" 14#include "core/hle/kernel/kernel.h"
14#include "core/hle/kernel/scheduler.h" 15#include "core/hle/kernel/scheduler.h"
@@ -114,6 +115,11 @@ public:
114 return CurrentCpuCore().ArmInterface(); 115 return CurrentCpuCore().ArmInterface();
115 } 116 }
116 117
118 /// Gets the index of the currently running CPU core
119 size_t CurrentCoreIndex() {
120 return CurrentCpuCore().CoreIndex();
121 }
122
117 /// Gets an ARM interface to the CPU core with the specified index 123 /// Gets an ARM interface to the CPU core with the specified index
118 ARM_Interface& ArmInterface(size_t core_index); 124 ARM_Interface& ArmInterface(size_t core_index);
119 125
@@ -130,6 +136,11 @@ public:
130 return *CurrentCpuCore().Scheduler(); 136 return *CurrentCpuCore().Scheduler();
131 } 137 }
132 138
139 /// Gets the exclusive monitor
140 ExclusiveMonitor& Monitor() {
141 return *cpu_exclusive_monitor;
142 }
143
133 /// Gets the scheduler for the CPU core with the specified index 144 /// Gets the scheduler for the CPU core with the specified index
134 const std::shared_ptr<Kernel::Scheduler>& Scheduler(size_t core_index); 145 const std::shared_ptr<Kernel::Scheduler>& Scheduler(size_t core_index);
135 146
@@ -186,6 +197,7 @@ private:
186 std::unique_ptr<Tegra::GPU> gpu_core; 197 std::unique_ptr<Tegra::GPU> gpu_core;
187 std::shared_ptr<Tegra::DebugContext> debug_context; 198 std::shared_ptr<Tegra::DebugContext> debug_context;
188 Kernel::SharedPtr<Kernel::Process> current_process; 199 Kernel::SharedPtr<Kernel::Process> current_process;
200 std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
189 std::shared_ptr<CpuBarrier> cpu_barrier; 201 std::shared_ptr<CpuBarrier> cpu_barrier;
190 std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores; 202 std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
191 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads; 203 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index f22d6a9d0..54e15a701 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -48,14 +48,15 @@ bool CpuBarrier::Rendezvous() {
48 return false; 48 return false;
49} 49}
50 50
51Cpu::Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index) 51Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
52 std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index)
52 : cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} { 53 : cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} {
53 54
54 if (Settings::values.use_cpu_jit) { 55 if (Settings::values.use_cpu_jit) {
55#ifdef ARCHITECTURE_x86_64 56#ifdef ARCHITECTURE_x86_64
56 arm_interface = std::make_shared<ARM_Dynarmic>(); 57 arm_interface = std::make_shared<ARM_Dynarmic>(exclusive_monitor, core_index);
57#else 58#else
58 cpu_core = std::make_shared<ARM_Unicorn>(); 59 arm_interface = std::make_shared<ARM_Unicorn>();
59 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); 60 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
60#endif 61#endif
61 } else { 62 } else {
@@ -65,6 +66,18 @@ Cpu::Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index)
65 scheduler = std::make_shared<Kernel::Scheduler>(arm_interface.get()); 66 scheduler = std::make_shared<Kernel::Scheduler>(arm_interface.get());
66} 67}
67 68
69std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(size_t num_cores) {
70 if (Settings::values.use_cpu_jit) {
71#ifdef ARCHITECTURE_x86_64
72 return std::make_shared<DynarmicExclusiveMonitor>(num_cores);
73#else
74 return nullptr; // TODO(merry): Passthrough exclusive monitor
75#endif
76 } else {
77 return nullptr; // TODO(merry): Passthrough exclusive monitor
78 }
79}
80
68void Cpu::RunLoop(bool tight_loop) { 81void Cpu::RunLoop(bool tight_loop) {
69 // Wait for all other CPU cores to complete the previous slice, such that they run in lock-step 82 // Wait for all other CPU cores to complete the previous slice, such that they run in lock-step
70 if (!cpu_barrier->Rendezvous()) { 83 if (!cpu_barrier->Rendezvous()) {
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h
index 243f0b5e7..976952903 100644
--- a/src/core/core_cpu.h
+++ b/src/core/core_cpu.h
@@ -10,6 +10,7 @@
10#include <mutex> 10#include <mutex>
11#include <string> 11#include <string>
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "core/arm/exclusive_monitor.h"
13 14
14class ARM_Interface; 15class ARM_Interface;
15 16
@@ -40,7 +41,8 @@ private:
40 41
41class Cpu { 42class Cpu {
42public: 43public:
43 Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index); 44 Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
45 std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index);
44 46
45 void RunLoop(bool tight_loop = true); 47 void RunLoop(bool tight_loop = true);
46 48
@@ -64,6 +66,12 @@ public:
64 return core_index == 0; 66 return core_index == 0;
65 } 67 }
66 68
69 size_t CoreIndex() const {
70 return core_index;
71 }
72
73 static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(size_t num_cores);
74
67private: 75private:
68 void Reschedule(); 76 void Reschedule();
69 77
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index 3f690f12a..b99a4fd5b 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -13,7 +13,7 @@ namespace FileSys {
13VfsFile::~VfsFile() = default; 13VfsFile::~VfsFile() = default;
14 14
15std::string VfsFile::GetExtension() const { 15std::string VfsFile::GetExtension() const {
16 return FileUtil::GetExtensionFromFilename(GetName()); 16 return std::string(FileUtil::GetExtensionFromFilename(GetName()));
17} 17}
18 18
19VfsDirectory::~VfsDirectory() = default; 19VfsDirectory::~VfsDirectory() = default;
@@ -46,64 +46,80 @@ size_t VfsFile::WriteBytes(const std::vector<u8>& data, size_t offset) {
46 return Write(data.data(), data.size(), offset); 46 return Write(data.data(), data.size(), offset);
47} 47}
48 48
49std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(const std::string& path) const { 49std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const {
50 auto vec = FileUtil::SplitPathComponents(path); 50 auto vec = FileUtil::SplitPathComponents(path);
51 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 51 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
52 vec.end()); 52 vec.end());
53 if (vec.empty()) 53 if (vec.empty()) {
54 return nullptr; 54 return nullptr;
55 if (vec.size() == 1) 55 }
56
57 if (vec.size() == 1) {
56 return GetFile(vec[0]); 58 return GetFile(vec[0]);
59 }
60
57 auto dir = GetSubdirectory(vec[0]); 61 auto dir = GetSubdirectory(vec[0]);
58 for (size_t component = 1; component < vec.size() - 1; ++component) { 62 for (size_t component = 1; component < vec.size() - 1; ++component) {
59 if (dir == nullptr) 63 if (dir == nullptr) {
60 return nullptr; 64 return nullptr;
65 }
66
61 dir = dir->GetSubdirectory(vec[component]); 67 dir = dir->GetSubdirectory(vec[component]);
62 } 68 }
63 if (dir == nullptr) 69
70 if (dir == nullptr) {
64 return nullptr; 71 return nullptr;
72 }
73
65 return dir->GetFile(vec.back()); 74 return dir->GetFile(vec.back());
66} 75}
67 76
68std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(const std::string& path) const { 77std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(std::string_view path) const {
69 if (IsRoot()) 78 if (IsRoot()) {
70 return GetFileRelative(path); 79 return GetFileRelative(path);
80 }
71 81
72 return GetParentDirectory()->GetFileAbsolute(path); 82 return GetParentDirectory()->GetFileAbsolute(path);
73} 83}
74 84
75std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(const std::string& path) const { 85std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_view path) const {
76 auto vec = FileUtil::SplitPathComponents(path); 86 auto vec = FileUtil::SplitPathComponents(path);
77 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 87 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
78 vec.end()); 88 vec.end());
79 if (vec.empty()) 89 if (vec.empty()) {
80 // TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently 90 // TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently
81 // because of const-ness 91 // because of const-ness
82 return nullptr; 92 return nullptr;
93 }
94
83 auto dir = GetSubdirectory(vec[0]); 95 auto dir = GetSubdirectory(vec[0]);
84 for (size_t component = 1; component < vec.size(); ++component) { 96 for (size_t component = 1; component < vec.size(); ++component) {
85 if (dir == nullptr) 97 if (dir == nullptr) {
86 return nullptr; 98 return nullptr;
99 }
100
87 dir = dir->GetSubdirectory(vec[component]); 101 dir = dir->GetSubdirectory(vec[component]);
88 } 102 }
103
89 return dir; 104 return dir;
90} 105}
91 106
92std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryAbsolute(const std::string& path) const { 107std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryAbsolute(std::string_view path) const {
93 if (IsRoot()) 108 if (IsRoot()) {
94 return GetDirectoryRelative(path); 109 return GetDirectoryRelative(path);
110 }
95 111
96 return GetParentDirectory()->GetDirectoryAbsolute(path); 112 return GetParentDirectory()->GetDirectoryAbsolute(path);
97} 113}
98 114
99std::shared_ptr<VfsFile> VfsDirectory::GetFile(const std::string& name) const { 115std::shared_ptr<VfsFile> VfsDirectory::GetFile(std::string_view name) const {
100 const auto& files = GetFiles(); 116 const auto& files = GetFiles();
101 const auto iter = std::find_if(files.begin(), files.end(), 117 const auto iter = std::find_if(files.begin(), files.end(),
102 [&name](const auto& file1) { return name == file1->GetName(); }); 118 [&name](const auto& file1) { return name == file1->GetName(); });
103 return iter == files.end() ? nullptr : *iter; 119 return iter == files.end() ? nullptr : *iter;
104} 120}
105 121
106std::shared_ptr<VfsDirectory> VfsDirectory::GetSubdirectory(const std::string& name) const { 122std::shared_ptr<VfsDirectory> VfsDirectory::GetSubdirectory(std::string_view name) const {
107 const auto& subs = GetSubdirectories(); 123 const auto& subs = GetSubdirectories();
108 const auto iter = std::find_if(subs.begin(), subs.end(), 124 const auto iter = std::find_if(subs.begin(), subs.end(),
109 [&name](const auto& file1) { return name == file1->GetName(); }); 125 [&name](const auto& file1) { return name == file1->GetName(); });
@@ -128,77 +144,96 @@ size_t VfsDirectory::GetSize() const {
128 return file_total + subdir_total; 144 return file_total + subdir_total;
129} 145}
130 146
131std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(const std::string& path) { 147std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path) {
132 auto vec = FileUtil::SplitPathComponents(path); 148 auto vec = FileUtil::SplitPathComponents(path);
133 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 149 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
134 vec.end()); 150 vec.end());
135 if (vec.empty()) 151 if (vec.empty()) {
136 return nullptr; 152 return nullptr;
137 if (vec.size() == 1) 153 }
154
155 if (vec.size() == 1) {
138 return CreateFile(vec[0]); 156 return CreateFile(vec[0]);
157 }
158
139 auto dir = GetSubdirectory(vec[0]); 159 auto dir = GetSubdirectory(vec[0]);
140 if (dir == nullptr) { 160 if (dir == nullptr) {
141 dir = CreateSubdirectory(vec[0]); 161 dir = CreateSubdirectory(vec[0]);
142 if (dir == nullptr) 162 if (dir == nullptr) {
143 return nullptr; 163 return nullptr;
164 }
144 } 165 }
145 166
146 return dir->CreateFileRelative(FileUtil::GetPathWithoutTop(path)); 167 return dir->CreateFileRelative(FileUtil::GetPathWithoutTop(path));
147} 168}
148 169
149std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(const std::string& path) { 170std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path) {
150 if (IsRoot()) 171 if (IsRoot()) {
151 return CreateFileRelative(path); 172 return CreateFileRelative(path);
173 }
174
152 return GetParentDirectory()->CreateFileAbsolute(path); 175 return GetParentDirectory()->CreateFileAbsolute(path);
153} 176}
154 177
155std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(const std::string& path) { 178std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_view path) {
156 auto vec = FileUtil::SplitPathComponents(path); 179 auto vec = FileUtil::SplitPathComponents(path);
157 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 180 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
158 vec.end()); 181 vec.end());
159 if (vec.empty()) 182 if (vec.empty()) {
160 return nullptr; 183 return nullptr;
161 if (vec.size() == 1) 184 }
185
186 if (vec.size() == 1) {
162 return CreateSubdirectory(vec[0]); 187 return CreateSubdirectory(vec[0]);
188 }
189
163 auto dir = GetSubdirectory(vec[0]); 190 auto dir = GetSubdirectory(vec[0]);
164 if (dir == nullptr) { 191 if (dir == nullptr) {
165 dir = CreateSubdirectory(vec[0]); 192 dir = CreateSubdirectory(vec[0]);
166 if (dir == nullptr) 193 if (dir == nullptr) {
167 return nullptr; 194 return nullptr;
195 }
168 } 196 }
197
169 return dir->CreateDirectoryRelative(FileUtil::GetPathWithoutTop(path)); 198 return dir->CreateDirectoryRelative(FileUtil::GetPathWithoutTop(path));
170} 199}
171 200
172std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(const std::string& path) { 201std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(std::string_view path) {
173 if (IsRoot()) 202 if (IsRoot()) {
174 return CreateDirectoryRelative(path); 203 return CreateDirectoryRelative(path);
204 }
205
175 return GetParentDirectory()->CreateDirectoryAbsolute(path); 206 return GetParentDirectory()->CreateDirectoryAbsolute(path);
176} 207}
177 208
178bool VfsDirectory::DeleteSubdirectoryRecursive(const std::string& name) { 209bool VfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
179 auto dir = GetSubdirectory(name); 210 auto dir = GetSubdirectory(name);
180 if (dir == nullptr) 211 if (dir == nullptr) {
181 return false; 212 return false;
213 }
182 214
183 bool success = true; 215 bool success = true;
184 for (const auto& file : dir->GetFiles()) { 216 for (const auto& file : dir->GetFiles()) {
185 if (!DeleteFile(file->GetName())) 217 if (!DeleteFile(file->GetName())) {
186 success = false; 218 success = false;
219 }
187 } 220 }
188 221
189 for (const auto& sdir : dir->GetSubdirectories()) { 222 for (const auto& sdir : dir->GetSubdirectories()) {
190 if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) 223 if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) {
191 success = false; 224 success = false;
225 }
192 } 226 }
193 227
194 return success; 228 return success;
195} 229}
196 230
197bool VfsDirectory::Copy(const std::string& src, const std::string& dest) { 231bool VfsDirectory::Copy(std::string_view src, std::string_view dest) {
198 const auto f1 = GetFile(src); 232 const auto f1 = GetFile(src);
199 auto f2 = CreateFile(dest); 233 auto f2 = CreateFile(dest);
200 if (f1 == nullptr || f2 == nullptr) 234 if (f1 == nullptr || f2 == nullptr) {
201 return false; 235 return false;
236 }
202 237
203 if (!f2->Resize(f1->GetSize())) { 238 if (!f2->Resize(f1->GetSize())) {
204 DeleteFile(dest); 239 DeleteFile(dest);
@@ -216,23 +251,23 @@ bool ReadOnlyVfsDirectory::IsReadable() const {
216 return true; 251 return true;
217} 252}
218 253
219std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateSubdirectory(const std::string& name) { 254std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateSubdirectory(std::string_view name) {
220 return nullptr; 255 return nullptr;
221} 256}
222 257
223std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFile(const std::string& name) { 258std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFile(std::string_view name) {
224 return nullptr; 259 return nullptr;
225} 260}
226 261
227bool ReadOnlyVfsDirectory::DeleteSubdirectory(const std::string& name) { 262bool ReadOnlyVfsDirectory::DeleteSubdirectory(std::string_view name) {
228 return false; 263 return false;
229} 264}
230 265
231bool ReadOnlyVfsDirectory::DeleteFile(const std::string& name) { 266bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) {
232 return false; 267 return false;
233} 268}
234 269
235bool ReadOnlyVfsDirectory::Rename(const std::string& name) { 270bool ReadOnlyVfsDirectory::Rename(std::string_view name) {
236 return false; 271 return false;
237} 272}
238} // namespace FileSys 273} // namespace FileSys
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index db3c77eac..4a13b8378 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -6,11 +6,11 @@
6 6
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include <string_view>
9#include <type_traits> 10#include <type_traits>
10#include <vector> 11#include <vector>
11#include "boost/optional.hpp" 12#include "boost/optional.hpp"
12#include "common/common_types.h" 13#include "common/common_types.h"
13#include "common/file_util.h"
14 14
15namespace FileSys { 15namespace FileSys {
16struct VfsFile; 16struct VfsFile;
@@ -112,7 +112,7 @@ struct VfsFile : NonCopyable {
112 } 112 }
113 113
114 // Renames the file to name. Returns whether or not the operation was successsful. 114 // Renames the file to name. Returns whether or not the operation was successsful.
115 virtual bool Rename(const std::string& name) = 0; 115 virtual bool Rename(std::string_view name) = 0;
116}; 116};
117 117
118// A class representing a directory in an abstract filesystem. 118// A class representing a directory in an abstract filesystem.
@@ -121,27 +121,27 @@ struct VfsDirectory : NonCopyable {
121 121
122 // Retrives the file located at path as if the current directory was root. Returns nullptr if 122 // Retrives the file located at path as if the current directory was root. Returns nullptr if
123 // not found. 123 // not found.
124 virtual std::shared_ptr<VfsFile> GetFileRelative(const std::string& path) const; 124 virtual std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const;
125 // Calls GetFileRelative(path) on the root of the current directory. 125 // Calls GetFileRelative(path) on the root of the current directory.
126 virtual std::shared_ptr<VfsFile> GetFileAbsolute(const std::string& path) const; 126 virtual std::shared_ptr<VfsFile> GetFileAbsolute(std::string_view path) const;
127 127
128 // Retrives the directory located at path as if the current directory was root. Returns nullptr 128 // Retrives the directory located at path as if the current directory was root. Returns nullptr
129 // if not found. 129 // if not found.
130 virtual std::shared_ptr<VfsDirectory> GetDirectoryRelative(const std::string& path) const; 130 virtual std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const;
131 // Calls GetDirectoryRelative(path) on the root of the current directory. 131 // Calls GetDirectoryRelative(path) on the root of the current directory.
132 virtual std::shared_ptr<VfsDirectory> GetDirectoryAbsolute(const std::string& path) const; 132 virtual std::shared_ptr<VfsDirectory> GetDirectoryAbsolute(std::string_view path) const;
133 133
134 // Returns a vector containing all of the files in this directory. 134 // Returns a vector containing all of the files in this directory.
135 virtual std::vector<std::shared_ptr<VfsFile>> GetFiles() const = 0; 135 virtual std::vector<std::shared_ptr<VfsFile>> GetFiles() const = 0;
136 // Returns the file with filename matching name. Returns nullptr if directory dosen't have a 136 // Returns the file with filename matching name. Returns nullptr if directory dosen't have a
137 // file with name. 137 // file with name.
138 virtual std::shared_ptr<VfsFile> GetFile(const std::string& name) const; 138 virtual std::shared_ptr<VfsFile> GetFile(std::string_view name) const;
139 139
140 // Returns a vector containing all of the subdirectories in this directory. 140 // Returns a vector containing all of the subdirectories in this directory.
141 virtual std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const = 0; 141 virtual std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const = 0;
142 // Returns the directory with name matching name. Returns nullptr if directory dosen't have a 142 // Returns the directory with name matching name. Returns nullptr if directory dosen't have a
143 // directory with name. 143 // directory with name.
144 virtual std::shared_ptr<VfsDirectory> GetSubdirectory(const std::string& name) const; 144 virtual std::shared_ptr<VfsDirectory> GetSubdirectory(std::string_view name) const;
145 145
146 // Returns whether or not the directory can be written to. 146 // Returns whether or not the directory can be written to.
147 virtual bool IsWritable() const = 0; 147 virtual bool IsWritable() const = 0;
@@ -161,53 +161,56 @@ struct VfsDirectory : NonCopyable {
161 161
162 // Creates a new subdirectory with name name. Returns a pointer to the new directory or nullptr 162 // Creates a new subdirectory with name name. Returns a pointer to the new directory or nullptr
163 // if the operation failed. 163 // if the operation failed.
164 virtual std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) = 0; 164 virtual std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) = 0;
165 // Creates a new file with name name. Returns a pointer to the new file or nullptr if the 165 // Creates a new file with name name. Returns a pointer to the new file or nullptr if the
166 // operation failed. 166 // operation failed.
167 virtual std::shared_ptr<VfsFile> CreateFile(const std::string& name) = 0; 167 virtual std::shared_ptr<VfsFile> CreateFile(std::string_view name) = 0;
168 168
169 // Creates a new file at the path relative to this directory. Also creates directories if 169 // Creates a new file at the path relative to this directory. Also creates directories if
170 // they do not exist and is supported by this implementation. Returns nullptr on any failure. 170 // they do not exist and is supported by this implementation. Returns nullptr on any failure.
171 virtual std::shared_ptr<VfsFile> CreateFileRelative(const std::string& path); 171 virtual std::shared_ptr<VfsFile> CreateFileRelative(std::string_view path);
172 172
173 // Creates a new file at the path relative to root of this directory. Also creates directories 173 // Creates a new file at the path relative to root of this directory. Also creates directories
174 // if they do not exist and is supported by this implementation. Returns nullptr on any failure. 174 // if they do not exist and is supported by this implementation. Returns nullptr on any failure.
175 virtual std::shared_ptr<VfsFile> CreateFileAbsolute(const std::string& path); 175 virtual std::shared_ptr<VfsFile> CreateFileAbsolute(std::string_view path);
176 176
177 // Creates a new directory at the path relative to this directory. Also creates directories if 177 // Creates a new directory at the path relative to this directory. Also creates directories if
178 // they do not exist and is supported by this implementation. Returns nullptr on any failure. 178 // they do not exist and is supported by this implementation. Returns nullptr on any failure.
179 virtual std::shared_ptr<VfsDirectory> CreateDirectoryRelative(const std::string& path); 179 virtual std::shared_ptr<VfsDirectory> CreateDirectoryRelative(std::string_view path);
180 180
181 // Creates a new directory at the path relative to root of this directory. Also creates 181 // Creates a new directory at the path relative to root of this directory. Also creates
182 // directories if they do not exist and is supported by this implementation. Returns nullptr on 182 // directories if they do not exist and is supported by this implementation. Returns nullptr on
183 // any failure. 183 // any failure.
184 virtual std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(const std::string& path); 184 virtual std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(std::string_view path);
185 185
186 // Deletes the subdirectory with name and returns true on success. 186 // Deletes the subdirectory with name and returns true on success.
187 virtual bool DeleteSubdirectory(const std::string& name) = 0; 187 virtual bool DeleteSubdirectory(std::string_view name) = 0;
188 // Deletes all subdirectories and files of subdirectory with name recirsively and then deletes 188 // Deletes all subdirectories and files of subdirectory with name recirsively and then deletes
189 // the subdirectory. Returns true on success. 189 // the subdirectory. Returns true on success.
190 virtual bool DeleteSubdirectoryRecursive(const std::string& name); 190 virtual bool DeleteSubdirectoryRecursive(std::string_view name);
191 // Returnes whether or not the file with name name was deleted successfully. 191 // Returnes whether or not the file with name name was deleted successfully.
192 virtual bool DeleteFile(const std::string& name) = 0; 192 virtual bool DeleteFile(std::string_view name) = 0;
193 193
194 // Returns whether or not this directory was renamed to name. 194 // Returns whether or not this directory was renamed to name.
195 virtual bool Rename(const std::string& name) = 0; 195 virtual bool Rename(std::string_view name) = 0;
196 196
197 // Returns whether or not the file with name src was successfully copied to a new file with name 197 // Returns whether or not the file with name src was successfully copied to a new file with name
198 // dest. 198 // dest.
199 virtual bool Copy(const std::string& src, const std::string& dest); 199 virtual bool Copy(std::string_view src, std::string_view dest);
200 200
201 // Interprets the file with name file instead as a directory of type directory. 201 // Interprets the file with name file instead as a directory of type directory.
202 // The directory must have a constructor that takes a single argument of type 202 // The directory must have a constructor that takes a single argument of type
203 // std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a 203 // std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a
204 // subdirectory in one call. 204 // subdirectory in one call.
205 template <typename Directory> 205 template <typename Directory>
206 bool InterpretAsDirectory(const std::string& file) { 206 bool InterpretAsDirectory(std::string_view file) {
207 auto file_p = GetFile(file); 207 auto file_p = GetFile(file);
208 if (file_p == nullptr) 208
209 if (file_p == nullptr) {
209 return false; 210 return false;
210 return ReplaceFileWithSubdirectory(file, std::make_shared<Directory>(file_p)); 211 }
212
213 return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p));
211 } 214 }
212 215
213protected: 216protected:
@@ -221,10 +224,10 @@ protected:
221struct ReadOnlyVfsDirectory : public VfsDirectory { 224struct ReadOnlyVfsDirectory : public VfsDirectory {
222 bool IsWritable() const override; 225 bool IsWritable() const override;
223 bool IsReadable() const override; 226 bool IsReadable() const override;
224 std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override; 227 std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
225 std::shared_ptr<VfsFile> CreateFile(const std::string& name) override; 228 std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
226 bool DeleteSubdirectory(const std::string& name) override; 229 bool DeleteSubdirectory(std::string_view name) override;
227 bool DeleteFile(const std::string& name) override; 230 bool DeleteFile(std::string_view name) override;
228 bool Rename(const std::string& name) override; 231 bool Rename(std::string_view name) override;
229}; 232};
230} // namespace FileSys 233} // namespace FileSys
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp
index 217e02235..a40331cef 100644
--- a/src/core/file_sys/vfs_offset.cpp
+++ b/src/core/file_sys/vfs_offset.cpp
@@ -80,7 +80,7 @@ size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, size_t r_offset) {
80 return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset); 80 return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset);
81} 81}
82 82
83bool OffsetVfsFile::Rename(const std::string& name) { 83bool OffsetVfsFile::Rename(std::string_view name) {
84 return file->Rename(name); 84 return file->Rename(name);
85} 85}
86 86
diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h
index ded4827f5..4f471e3ba 100644
--- a/src/core/file_sys/vfs_offset.h
+++ b/src/core/file_sys/vfs_offset.h
@@ -4,6 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
8#include <string_view>
9
7#include "core/file_sys/vfs.h" 10#include "core/file_sys/vfs.h"
8 11
9namespace FileSys { 12namespace FileSys {
@@ -30,7 +33,7 @@ struct OffsetVfsFile : public VfsFile {
30 bool WriteByte(u8 data, size_t offset) override; 33 bool WriteByte(u8 data, size_t offset) override;
31 size_t WriteBytes(const std::vector<u8>& data, size_t offset) override; 34 size_t WriteBytes(const std::vector<u8>& data, size_t offset) override;
32 35
33 bool Rename(const std::string& name) override; 36 bool Rename(std::string_view name) override;
34 37
35 size_t GetOffset() const; 38 size_t GetOffset() const;
36 39
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 27fd464ae..095fec77e 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -72,12 +72,15 @@ size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) {
72 return backing.WriteBytes(data, length); 72 return backing.WriteBytes(data, length);
73} 73}
74 74
75bool RealVfsFile::Rename(const std::string& name) { 75bool RealVfsFile::Rename(std::string_view name) {
76 const auto out = FileUtil::Rename(GetName(), name); 76 std::string name_str(name.begin(), name.end());
77 path = parent_path + DIR_SEP + name; 77 const auto out = FileUtil::Rename(GetName(), name_str);
78
79 path = (parent_path + DIR_SEP).append(name);
78 path_components = parent_components; 80 path_components = parent_components;
79 path_components.push_back(name); 81 path_components.push_back(std::move(name_str));
80 backing = FileUtil::IOFile(path, PermissionsToCharArray(perms).c_str()); 82 backing = FileUtil::IOFile(path, PermissionsToCharArray(perms).c_str());
83
81 return out; 84 return out;
82} 85}
83 86
@@ -135,36 +138,54 @@ std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const {
135 return std::make_shared<RealVfsDirectory>(parent_path, perms); 138 return std::make_shared<RealVfsDirectory>(parent_path, perms);
136} 139}
137 140
138std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(const std::string& name) { 141std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(std::string_view name) {
139 if (!FileUtil::CreateDir(path + DIR_SEP + name)) 142 const std::string subdir_path = (path + DIR_SEP).append(name);
143
144 if (!FileUtil::CreateDir(subdir_path)) {
140 return nullptr; 145 return nullptr;
141 subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(path + DIR_SEP + name, perms)); 146 }
147
148 subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(subdir_path, perms));
142 return subdirectories.back(); 149 return subdirectories.back();
143} 150}
144 151
145std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(const std::string& name) { 152std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(std::string_view name) {
146 if (!FileUtil::CreateEmptyFile(path + DIR_SEP + name)) 153 const std::string file_path = (path + DIR_SEP).append(name);
154
155 if (!FileUtil::CreateEmptyFile(file_path)) {
147 return nullptr; 156 return nullptr;
148 files.emplace_back(std::make_shared<RealVfsFile>(path + DIR_SEP + name, perms)); 157 }
158
159 files.emplace_back(std::make_shared<RealVfsFile>(file_path, perms));
149 return files.back(); 160 return files.back();
150} 161}
151 162
152bool RealVfsDirectory::DeleteSubdirectory(const std::string& name) { 163bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) {
153 return FileUtil::DeleteDirRecursively(path + DIR_SEP + name); 164 const std::string subdir_path = (path + DIR_SEP).append(name);
165
166 return FileUtil::DeleteDirRecursively(subdir_path);
154} 167}
155 168
156bool RealVfsDirectory::DeleteFile(const std::string& name) { 169bool RealVfsDirectory::DeleteFile(std::string_view name) {
157 auto file = GetFile(name); 170 const auto file = GetFile(name);
158 if (file == nullptr) 171
172 if (file == nullptr) {
159 return false; 173 return false;
174 }
175
160 files.erase(std::find(files.begin(), files.end(), file)); 176 files.erase(std::find(files.begin(), files.end(), file));
177
161 auto real_file = std::static_pointer_cast<RealVfsFile>(file); 178 auto real_file = std::static_pointer_cast<RealVfsFile>(file);
162 real_file->Close(); 179 real_file->Close();
163 return FileUtil::Delete(path + DIR_SEP + name); 180
181 const std::string file_path = (path + DIR_SEP).append(name);
182 return FileUtil::Delete(file_path);
164} 183}
165 184
166bool RealVfsDirectory::Rename(const std::string& name) { 185bool RealVfsDirectory::Rename(std::string_view name) {
167 return FileUtil::Rename(path, parent_path + DIR_SEP + name); 186 const std::string new_name = (parent_path + DIR_SEP).append(name);
187
188 return FileUtil::Rename(path, new_name);
168} 189}
169 190
170bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { 191bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index 5b765a552..2151211c9 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -4,6 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <string_view>
8
7#include "common/file_util.h" 9#include "common/file_util.h"
8#include "core/file_sys/mode.h" 10#include "core/file_sys/mode.h"
9#include "core/file_sys/vfs.h" 11#include "core/file_sys/vfs.h"
@@ -24,7 +26,7 @@ struct RealVfsFile : public VfsFile {
24 bool IsReadable() const override; 26 bool IsReadable() const override;
25 size_t Read(u8* data, size_t length, size_t offset) const override; 27 size_t Read(u8* data, size_t length, size_t offset) const override;
26 size_t Write(const u8* data, size_t length, size_t offset) override; 28 size_t Write(const u8* data, size_t length, size_t offset) override;
27 bool Rename(const std::string& name) override; 29 bool Rename(std::string_view name) override;
28 30
29private: 31private:
30 bool Close(); 32 bool Close();
@@ -47,11 +49,11 @@ struct RealVfsDirectory : public VfsDirectory {
47 bool IsReadable() const override; 49 bool IsReadable() const override;
48 std::string GetName() const override; 50 std::string GetName() const override;
49 std::shared_ptr<VfsDirectory> GetParentDirectory() const override; 51 std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
50 std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override; 52 std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
51 std::shared_ptr<VfsFile> CreateFile(const std::string& name) override; 53 std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
52 bool DeleteSubdirectory(const std::string& name) override; 54 bool DeleteSubdirectory(std::string_view name) override;
53 bool DeleteFile(const std::string& name) override; 55 bool DeleteFile(std::string_view name) override;
54 bool Rename(const std::string& name) override; 56 bool Rename(std::string_view name) override;
55 57
56protected: 58protected:
57 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; 59 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 6b2995fe2..7b41c9cfd 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -650,12 +650,27 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
650 650
651 ASSERT(thread->condvar_wait_address == condition_variable_addr); 651 ASSERT(thread->condvar_wait_address == condition_variable_addr);
652 652
653 // If the mutex is not yet acquired, acquire it. 653 size_t current_core = Core::System::GetInstance().CurrentCoreIndex();
654 u32 mutex_val = Memory::Read32(thread->mutex_wait_address); 654
655 auto& monitor = Core::System::GetInstance().Monitor();
656
657 // Atomically read the value of the mutex.
658 u32 mutex_val = 0;
659 do {
660 monitor.SetExclusive(current_core, thread->mutex_wait_address);
661
662 // If the mutex is not yet acquired, acquire it.
663 mutex_val = Memory::Read32(thread->mutex_wait_address);
664
665 if (mutex_val != 0) {
666 monitor.ClearExclusive();
667 break;
668 }
669 } while (!monitor.ExclusiveWrite32(current_core, thread->mutex_wait_address,
670 thread->wait_handle));
655 671
656 if (mutex_val == 0) { 672 if (mutex_val == 0) {
657 // We were able to acquire the mutex, resume this thread. 673 // We were able to acquire the mutex, resume this thread.
658 Memory::Write32(thread->mutex_wait_address, thread->wait_handle);
659 ASSERT(thread->status == ThreadStatus::WaitMutex); 674 ASSERT(thread->status == ThreadStatus::WaitMutex);
660 thread->ResumeFromWait(); 675 thread->ResumeFromWait();
661 676
@@ -668,7 +683,19 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
668 thread->condvar_wait_address = 0; 683 thread->condvar_wait_address = 0;
669 thread->wait_handle = 0; 684 thread->wait_handle = 0;
670 } else { 685 } else {
671 // Couldn't acquire the mutex, block the thread. 686 // Atomically signal that the mutex now has a waiting thread.
687 do {
688 monitor.SetExclusive(current_core, thread->mutex_wait_address);
689
690 // Ensure that the mutex value is still what we expect.
691 u32 value = Memory::Read32(thread->mutex_wait_address);
692 // TODO(Subv): When this happens, the kernel just clears the exclusive state and
693 // retries the initial read for this thread.
694 ASSERT_MSG(mutex_val == value, "Unhandled synchronization primitive case");
695 } while (!monitor.ExclusiveWrite32(current_core, thread->mutex_wait_address,
696 mutex_val | Mutex::MutexHasWaitersFlag));
697
698 // The mutex is already owned by some other thread, make this thread wait on it.
672 Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); 699 Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
673 auto owner = g_handle_table.Get<Thread>(owner_handle); 700 auto owner = g_handle_table.Get<Thread>(owner_handle);
674 ASSERT(owner); 701 ASSERT(owner);
@@ -676,9 +703,6 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
676 thread->status = ThreadStatus::WaitMutex; 703 thread->status = ThreadStatus::WaitMutex;
677 thread->wakeup_callback = nullptr; 704 thread->wakeup_callback = nullptr;
678 705
679 // Signal that the mutex now has a waiting thread.
680 Memory::Write32(thread->mutex_wait_address, mutex_val | Mutex::MutexHasWaitersFlag);
681
682 owner->AddMutexWaiter(thread); 706 owner->AddMutexWaiter(thread);
683 707
684 Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); 708 Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 671e0b8d0..dbfe06cbc 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -24,7 +24,7 @@ namespace Service::FileSystem {
24constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000; 24constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
25 25
26static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, 26static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
27 const std::string& dir_name) { 27 std::string_view dir_name) {
28 if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\") 28 if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
29 return base; 29 return base;
30 30
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index e70f37677..4cbd9e285 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -49,7 +49,8 @@ FileType GuessFromFilename(const std::string& name) {
49 if (name == "main") 49 if (name == "main")
50 return FileType::DeconstructedRomDirectory; 50 return FileType::DeconstructedRomDirectory;
51 51
52 const std::string extension = Common::ToLower(FileUtil::GetExtensionFromFilename(name)); 52 const std::string extension =
53 Common::ToLower(std::string(FileUtil::GetExtensionFromFilename(name)));
53 54
54 if (extension == "elf") 55 if (extension == "elf")
55 return FileType::ELF; 56 return FileType::ELF;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index f47fd217d..ba827181b 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -78,14 +78,18 @@ private:
78 78
79 /// Adds and analyzes a new subroutine if it is not added yet. 79 /// Adds and analyzes a new subroutine if it is not added yet.
80 const Subroutine& AddSubroutine(u32 begin, u32 end, const std::string& suffix) { 80 const Subroutine& AddSubroutine(u32 begin, u32 end, const std::string& suffix) {
81 auto iter = subroutines.find(Subroutine{begin, end, suffix}); 81 Subroutine subroutine{begin, end, suffix, ExitMethod::Undetermined, {}};
82 if (iter != subroutines.end()) 82
83 const auto iter = subroutines.find(subroutine);
84 if (iter != subroutines.end()) {
83 return *iter; 85 return *iter;
86 }
84 87
85 Subroutine subroutine{begin, end, suffix};
86 subroutine.exit_method = Scan(begin, end, subroutine.labels); 88 subroutine.exit_method = Scan(begin, end, subroutine.labels);
87 if (subroutine.exit_method == ExitMethod::Undetermined) 89 if (subroutine.exit_method == ExitMethod::Undetermined) {
88 throw DecompileFail("Recursive function detected"); 90 throw DecompileFail("Recursive function detected");
91 }
92
89 return *subroutines.insert(std::move(subroutine)).first; 93 return *subroutines.insert(std::move(subroutine)).first;
90 } 94 }
91 95
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 16a95bb19..3c2726498 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -338,6 +338,18 @@ bool GMainWindow::SupportsRequiredGLExtensions() {
338 unsupported_ext.append("ARB_separate_shader_objects"); 338 unsupported_ext.append("ARB_separate_shader_objects");
339 if (!GLAD_GL_ARB_vertex_attrib_binding) 339 if (!GLAD_GL_ARB_vertex_attrib_binding)
340 unsupported_ext.append("ARB_vertex_attrib_binding"); 340 unsupported_ext.append("ARB_vertex_attrib_binding");
341 if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
342 unsupported_ext.append("ARB_vertex_type_10f_11f_11f_rev");
343
344 // Extensions required to support some texture formats.
345 if (!GLAD_GL_EXT_texture_compression_s3tc)
346 unsupported_ext.append("EXT_texture_compression_s3tc");
347 if (!GLAD_GL_ARB_texture_compression_rgtc)
348 unsupported_ext.append("ARB_texture_compression_rgtc");
349 if (!GLAD_GL_ARB_texture_compression_bptc)
350 unsupported_ext.append("ARB_texture_compression_bptc");
351 if (!GLAD_GL_ARB_depth_buffer_float)
352 unsupported_ext.append("ARB_depth_buffer_float");
341 353
342 for (const QString& ext : unsupported_ext) 354 for (const QString& ext : unsupported_ext)
343 LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString()); 355 LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString());
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index ec73f08bd..72ba7d49c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -85,10 +85,20 @@ bool EmuWindow_SDL2::SupportsRequiredGLExtensions() {
85 unsupported_ext.push_back("ARB_program_interface_query"); 85 unsupported_ext.push_back("ARB_program_interface_query");
86 if (!GLAD_GL_ARB_separate_shader_objects) 86 if (!GLAD_GL_ARB_separate_shader_objects)
87 unsupported_ext.push_back("ARB_separate_shader_objects"); 87 unsupported_ext.push_back("ARB_separate_shader_objects");
88 if (!GLAD_GL_ARB_shader_storage_buffer_object)
89 unsupported_ext.push_back("ARB_shader_storage_buffer_object");
90 if (!GLAD_GL_ARB_vertex_attrib_binding) 88 if (!GLAD_GL_ARB_vertex_attrib_binding)
91 unsupported_ext.push_back("ARB_vertex_attrib_binding"); 89 unsupported_ext.push_back("ARB_vertex_attrib_binding");
90 if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
91 unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev");
92
93 // Extensions required to support some texture formats.
94 if (!GLAD_GL_EXT_texture_compression_s3tc)
95 unsupported_ext.push_back("EXT_texture_compression_s3tc");
96 if (!GLAD_GL_ARB_texture_compression_rgtc)
97 unsupported_ext.push_back("ARB_texture_compression_rgtc");
98 if (!GLAD_GL_ARB_texture_compression_bptc)
99 unsupported_ext.push_back("ARB_texture_compression_bptc");
100 if (!GLAD_GL_ARB_depth_buffer_float)
101 unsupported_ext.push_back("ARB_depth_buffer_float");
92 102
93 for (const std::string& ext : unsupported_ext) 103 for (const std::string& ext : unsupported_ext)
94 LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext); 104 LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext);