summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/build.gradle.kts31
-rw-r--r--src/common/CMakeLists.txt3
-rw-r--r--src/common/range_sets.h73
-rw-r--r--src/common/range_sets.inc304
-rw-r--r--src/common/slot_vector.h (renamed from src/video_core/texture_cache/slot_vector.h)8
-rw-r--r--src/core/device_memory_manager.inc7
-rw-r--r--src/core/hle/service/nvdrv/core/heap_mapper.cpp187
-rw-r--r--src/core/memory/cheat_engine.cpp5
-rw-r--r--src/video_core/CMakeLists.txt1
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h250
-rw-r--r--src/video_core/buffer_cache/buffer_cache_base.h135
-rw-r--r--src/video_core/query_cache.h6
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h3
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h3
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h2
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h18
-rw-r--r--src/video_core/texture_cache/types.h16
-rw-r--r--src/web_service/web_backend.cpp31
-rw-r--r--src/yuzu/configuration/configure_hotkeys.cpp41
-rw-r--r--src/yuzu/configuration/configure_hotkeys.h13
-rw-r--r--src/yuzu/configuration/configure_ui.cpp78
-rw-r--r--src/yuzu/hotkeys.cpp3
24 files changed, 647 insertions, 575 deletions
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index 188ef9469..d44bb4c74 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -3,8 +3,8 @@
3 3
4import android.annotation.SuppressLint 4import android.annotation.SuppressLint
5import kotlin.collections.setOf 5import kotlin.collections.setOf
6import org.jetbrains.kotlin.konan.properties.Properties
7import org.jlleitschuh.gradle.ktlint.reporter.ReporterType 6import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
7import com.github.triplet.gradle.androidpublisher.ReleaseStatus
8 8
9plugins { 9plugins {
10 id("com.android.application") 10 id("com.android.application")
@@ -13,6 +13,7 @@ plugins {
13 kotlin("plugin.serialization") version "1.9.20" 13 kotlin("plugin.serialization") version "1.9.20"
14 id("androidx.navigation.safeargs.kotlin") 14 id("androidx.navigation.safeargs.kotlin")
15 id("org.jlleitschuh.gradle.ktlint") version "11.4.0" 15 id("org.jlleitschuh.gradle.ktlint") version "11.4.0"
16 id("com.github.triplet.play") version "3.8.6"
16} 17}
17 18
18/** 19/**
@@ -58,15 +59,7 @@ android {
58 targetSdk = 34 59 targetSdk = 34
59 versionName = getGitVersion() 60 versionName = getGitVersion()
60 61
61 // If you want to use autoVersion for the versionCode, create a property in local.properties 62 versionCode = if (System.getenv("AUTO_VERSIONED") == "true") {
62 // named "autoVersioned" and set it to "true"
63 val properties = Properties()
64 val versionProperty = try {
65 properties.load(project.rootProject.file("local.properties").inputStream())
66 properties.getProperty("autoVersioned") ?: ""
67 } catch (e: Exception) { "" }
68
69 versionCode = if (versionProperty == "true") {
70 autoVersion 63 autoVersion
71 } else { 64 } else {
72 1 65 1
@@ -221,6 +214,15 @@ ktlint {
221 } 214 }
222} 215}
223 216
217play {
218 val keyPath = System.getenv("SERVICE_ACCOUNT_KEY_PATH")
219 if (keyPath != null) {
220 serviceAccountCredentials.set(File(keyPath))
221 }
222 track.set(System.getenv("STORE_TRACK") ?: "internal")
223 releaseStatus.set(ReleaseStatus.COMPLETED)
224}
225
224dependencies { 226dependencies {
225 implementation("androidx.core:core-ktx:1.12.0") 227 implementation("androidx.core:core-ktx:1.12.0")
226 implementation("androidx.appcompat:appcompat:1.6.1") 228 implementation("androidx.appcompat:appcompat:1.6.1")
@@ -257,14 +259,13 @@ fun runGitCommand(command: List<String>): String {
257} 259}
258 260
259fun getGitVersion(): String { 261fun getGitVersion(): String {
262 val gitVersion = runGitCommand(listOf("git", "describe", "--always", "--long"))
260 val versionName = if (System.getenv("GITHUB_ACTIONS") != null) { 263 val versionName = if (System.getenv("GITHUB_ACTIONS") != null) {
261 val gitTag = System.getenv("GIT_TAG_NAME") ?: "" 264 System.getenv("GIT_TAG_NAME") ?: gitVersion
262 gitTag
263 } else { 265 } else {
264 runGitCommand(listOf("git", "describe", "--always", "--long")) 266 gitVersion
265 .replace(Regex("(-0)?-[^-]+$"), "")
266 } 267 }
267 return versionName.ifEmpty { "0.0" } 268 return versionName.replace(Regex("(-0)?-[^-]+$"), "").ifEmpty { "0.0" }
268} 269}
269 270
270fun getGitHash(): String = 271fun getGitHash(): String =
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 85926fc8f..c19af2ab8 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -107,6 +107,8 @@ add_library(common STATIC
107 quaternion.h 107 quaternion.h
108 range_map.h 108 range_map.h
109 range_mutex.h 109 range_mutex.h
110 range_sets.h
111 range_sets.inc
110 reader_writer_queue.h 112 reader_writer_queue.h
111 ring_buffer.h 113 ring_buffer.h
112 ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp 114 ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
@@ -121,6 +123,7 @@ add_library(common STATIC
121 settings_input.cpp 123 settings_input.cpp
122 settings_input.h 124 settings_input.h
123 settings_setting.h 125 settings_setting.h
126 slot_vector.h
124 socket_types.h 127 socket_types.h
125 spin_lock.cpp 128 spin_lock.cpp
126 spin_lock.h 129 spin_lock.h
diff --git a/src/common/range_sets.h b/src/common/range_sets.h
new file mode 100644
index 000000000..f8fcee483
--- /dev/null
+++ b/src/common/range_sets.h
@@ -0,0 +1,73 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7
8#include "common/common_types.h"
9
10namespace Common {
11
12template <typename AddressType>
13class RangeSet {
14public:
15 RangeSet();
16 ~RangeSet();
17
18 RangeSet(RangeSet const&) = delete;
19 RangeSet& operator=(RangeSet const&) = delete;
20
21 RangeSet(RangeSet&& other);
22 RangeSet& operator=(RangeSet&& other);
23
24 void Add(AddressType base_address, size_t size);
25 void Subtract(AddressType base_address, size_t size);
26 void Clear();
27 bool Empty() const;
28
29 template <typename Func>
30 void ForEach(Func&& func) const;
31
32 template <typename Func>
33 void ForEachInRange(AddressType device_addr, size_t size, Func&& func) const;
34
35private:
36 struct RangeSetImpl;
37 std::unique_ptr<RangeSetImpl> m_impl;
38};
39
40template <typename AddressType>
41class OverlapRangeSet {
42public:
43 OverlapRangeSet();
44 ~OverlapRangeSet();
45
46 OverlapRangeSet(OverlapRangeSet const&) = delete;
47 OverlapRangeSet& operator=(OverlapRangeSet const&) = delete;
48
49 OverlapRangeSet(OverlapRangeSet&& other);
50 OverlapRangeSet& operator=(OverlapRangeSet&& other);
51
52 void Add(AddressType base_address, size_t size);
53 void Subtract(AddressType base_address, size_t size);
54
55 template <typename Func>
56 void Subtract(AddressType base_address, size_t size, Func&& on_delete);
57
58 void DeleteAll(AddressType base_address, size_t size);
59 void Clear();
60 bool Empty() const;
61
62 template <typename Func>
63 void ForEach(Func&& func) const;
64
65 template <typename Func>
66 void ForEachInRange(AddressType device_addr, size_t size, Func&& func) const;
67
68private:
69 struct OverlapRangeSetImpl;
70 std::unique_ptr<OverlapRangeSetImpl> m_impl;
71};
72
73} // namespace Common
diff --git a/src/common/range_sets.inc b/src/common/range_sets.inc
new file mode 100644
index 000000000..b83eceb7b
--- /dev/null
+++ b/src/common/range_sets.inc
@@ -0,0 +1,304 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <limits>
7#include <utility>
8
9#include <boost/icl/interval.hpp>
10#include <boost/icl/interval_base_set.hpp>
11#include <boost/icl/interval_map.hpp>
12#include <boost/icl/interval_set.hpp>
13#include <boost/icl/split_interval_map.hpp>
14#include <boost/pool/pool.hpp>
15#include <boost/pool/pool_alloc.hpp>
16#include <boost/pool/poolfwd.hpp>
17
18#include "common/range_sets.h"
19
20namespace Common {
21
22namespace {
23template <class T>
24using RangeSetsAllocator =
25 boost::fast_pool_allocator<T, boost::default_user_allocator_new_delete,
26 boost::details::pool::default_mutex, 1024, 2048>;
27}
28
29template <typename AddressType>
30struct RangeSet<AddressType>::RangeSetImpl {
31 using IntervalSet = boost::icl::interval_set<
32 AddressType, std::less, ICL_INTERVAL_INSTANCE(ICL_INTERVAL_DEFAULT, AddressType, std::less),
33 RangeSetsAllocator>;
34 using IntervalType = typename IntervalSet::interval_type;
35
36 RangeSetImpl() = default;
37 ~RangeSetImpl() = default;
38
39 void Add(AddressType base_address, size_t size) {
40 AddressType end_address = base_address + static_cast<AddressType>(size);
41 IntervalType interval{base_address, end_address};
42 m_ranges_set.add(interval);
43 }
44
45 void Subtract(AddressType base_address, size_t size) {
46 AddressType end_address = base_address + static_cast<AddressType>(size);
47 IntervalType interval{base_address, end_address};
48 m_ranges_set.subtract(interval);
49 }
50
51 template <typename Func>
52 void ForEach(Func&& func) const {
53 if (m_ranges_set.empty()) {
54 return;
55 }
56 auto it = m_ranges_set.begin();
57 auto end_it = m_ranges_set.end();
58 for (; it != end_it; it++) {
59 const AddressType inter_addr_end = it->upper();
60 const AddressType inter_addr = it->lower();
61 func(inter_addr, inter_addr_end);
62 }
63 }
64
65 template <typename Func>
66 void ForEachInRange(AddressType base_addr, size_t size, Func&& func) const {
67 if (m_ranges_set.empty()) {
68 return;
69 }
70 const AddressType start_address = base_addr;
71 const AddressType end_address = start_address + size;
72 const RangeSetImpl::IntervalType search_interval{start_address, end_address};
73 auto it = m_ranges_set.lower_bound(search_interval);
74 if (it == m_ranges_set.end()) {
75 return;
76 }
77 auto end_it = m_ranges_set.upper_bound(search_interval);
78 for (; it != end_it; it++) {
79 AddressType inter_addr_end = it->upper();
80 AddressType inter_addr = it->lower();
81 if (inter_addr_end > end_address) {
82 inter_addr_end = end_address;
83 }
84 if (inter_addr < start_address) {
85 inter_addr = start_address;
86 }
87 func(inter_addr, inter_addr_end);
88 }
89 }
90
91 IntervalSet m_ranges_set;
92};
93
94template <typename AddressType>
95struct OverlapRangeSet<AddressType>::OverlapRangeSetImpl {
96 using IntervalSet = boost::icl::split_interval_map<
97 AddressType, s32, boost::icl::partial_enricher, std::less, boost::icl::inplace_plus,
98 boost::icl::inter_section,
99 ICL_INTERVAL_INSTANCE(ICL_INTERVAL_DEFAULT, AddressType, std::less), RangeSetsAllocator>;
100 using IntervalType = typename IntervalSet::interval_type;
101
102 OverlapRangeSetImpl() = default;
103 ~OverlapRangeSetImpl() = default;
104
105 void Add(AddressType base_address, size_t size) {
106 AddressType end_address = base_address + static_cast<AddressType>(size);
107 IntervalType interval{base_address, end_address};
108 m_split_ranges_set += std::make_pair(interval, 1);
109 }
110
111 template <bool has_on_delete, typename Func>
112 void Subtract(AddressType base_address, size_t size, s32 amount,
113 [[maybe_unused]] Func&& on_delete) {
114 if (m_split_ranges_set.empty()) {
115 return;
116 }
117 AddressType end_address = base_address + static_cast<AddressType>(size);
118 IntervalType interval{base_address, end_address};
119 bool any_removals = false;
120 m_split_ranges_set += std::make_pair(interval, -amount);
121 do {
122 any_removals = false;
123 auto it = m_split_ranges_set.lower_bound(interval);
124 if (it == m_split_ranges_set.end()) {
125 return;
126 }
127 auto end_it = m_split_ranges_set.upper_bound(interval);
128 for (; it != end_it; it++) {
129 if (it->second <= 0) {
130 if constexpr (has_on_delete) {
131 if (it->second == 0) {
132 on_delete(it->first.lower(), it->first.upper());
133 }
134 }
135 any_removals = true;
136 m_split_ranges_set.erase(it);
137 break;
138 }
139 }
140 } while (any_removals);
141 }
142
143 template <typename Func>
144 void ForEach(Func&& func) const {
145 if (m_split_ranges_set.empty()) {
146 return;
147 }
148 auto it = m_split_ranges_set.begin();
149 auto end_it = m_split_ranges_set.end();
150 for (; it != end_it; it++) {
151 const AddressType inter_addr_end = it->first.upper();
152 const AddressType inter_addr = it->first.lower();
153 func(inter_addr, inter_addr_end, it->second);
154 }
155 }
156
157 template <typename Func>
158 void ForEachInRange(AddressType base_address, size_t size, Func&& func) const {
159 if (m_split_ranges_set.empty()) {
160 return;
161 }
162 const AddressType start_address = base_address;
163 const AddressType end_address = start_address + size;
164 const OverlapRangeSetImpl::IntervalType search_interval{start_address, end_address};
165 auto it = m_split_ranges_set.lower_bound(search_interval);
166 if (it == m_split_ranges_set.end()) {
167 return;
168 }
169 auto end_it = m_split_ranges_set.upper_bound(search_interval);
170 for (; it != end_it; it++) {
171 auto& inter = it->first;
172 AddressType inter_addr_end = inter.upper();
173 AddressType inter_addr = inter.lower();
174 if (inter_addr_end > end_address) {
175 inter_addr_end = end_address;
176 }
177 if (inter_addr < start_address) {
178 inter_addr = start_address;
179 }
180 func(inter_addr, inter_addr_end, it->second);
181 }
182 }
183
184 IntervalSet m_split_ranges_set;
185};
186
187template <typename AddressType>
188RangeSet<AddressType>::RangeSet() {
189 m_impl = std::make_unique<RangeSet<AddressType>::RangeSetImpl>();
190}
191
192template <typename AddressType>
193RangeSet<AddressType>::~RangeSet() = default;
194
195template <typename AddressType>
196RangeSet<AddressType>::RangeSet(RangeSet&& other) {
197 m_impl = std::make_unique<RangeSet<AddressType>::RangeSetImpl>();
198 m_impl->m_ranges_set = std::move(other.m_impl->m_ranges_set);
199}
200
201template <typename AddressType>
202RangeSet<AddressType>& RangeSet<AddressType>::operator=(RangeSet&& other) {
203 m_impl->m_ranges_set = std::move(other.m_impl->m_ranges_set);
204}
205
206template <typename AddressType>
207void RangeSet<AddressType>::Add(AddressType base_address, size_t size) {
208 m_impl->Add(base_address, size);
209}
210
211template <typename AddressType>
212void RangeSet<AddressType>::Subtract(AddressType base_address, size_t size) {
213 m_impl->Subtract(base_address, size);
214}
215
216template <typename AddressType>
217void RangeSet<AddressType>::Clear() {
218 m_impl->m_ranges_set.clear();
219}
220
221template <typename AddressType>
222bool RangeSet<AddressType>::Empty() const {
223 return m_impl->m_ranges_set.empty();
224}
225
226template <typename AddressType>
227template <typename Func>
228void RangeSet<AddressType>::ForEach(Func&& func) const {
229 m_impl->ForEach(std::move(func));
230}
231
232template <typename AddressType>
233template <typename Func>
234void RangeSet<AddressType>::ForEachInRange(AddressType base_address, size_t size,
235 Func&& func) const {
236 m_impl->ForEachInRange(base_address, size, std::move(func));
237}
238
239template <typename AddressType>
240OverlapRangeSet<AddressType>::OverlapRangeSet() {
241 m_impl = std::make_unique<OverlapRangeSet<AddressType>::OverlapRangeSetImpl>();
242}
243
244template <typename AddressType>
245OverlapRangeSet<AddressType>::~OverlapRangeSet() = default;
246
247template <typename AddressType>
248OverlapRangeSet<AddressType>::OverlapRangeSet(OverlapRangeSet&& other) {
249 m_impl = std::make_unique<OverlapRangeSet<AddressType>::OverlapRangeSetImpl>();
250 m_impl->m_split_ranges_set = std::move(other.m_impl->m_split_ranges_set);
251}
252
253template <typename AddressType>
254OverlapRangeSet<AddressType>& OverlapRangeSet<AddressType>::operator=(OverlapRangeSet&& other) {
255 m_impl->m_split_ranges_set = std::move(other.m_impl->m_split_ranges_set);
256}
257
258template <typename AddressType>
259void OverlapRangeSet<AddressType>::Add(AddressType base_address, size_t size) {
260 m_impl->Add(base_address, size);
261}
262
263template <typename AddressType>
264void OverlapRangeSet<AddressType>::Subtract(AddressType base_address, size_t size) {
265 m_impl->template Subtract<false>(base_address, size, 1, [](AddressType, AddressType) {});
266}
267
268template <typename AddressType>
269template <typename Func>
270void OverlapRangeSet<AddressType>::Subtract(AddressType base_address, size_t size,
271 Func&& on_delete) {
272 m_impl->template Subtract<true, Func>(base_address, size, 1, std::move(on_delete));
273}
274
275template <typename AddressType>
276void OverlapRangeSet<AddressType>::DeleteAll(AddressType base_address, size_t size) {
277 m_impl->template Subtract<false>(base_address, size, std::numeric_limits<s32>::max(),
278 [](AddressType, AddressType) {});
279}
280
281template <typename AddressType>
282void OverlapRangeSet<AddressType>::Clear() {
283 m_impl->m_split_ranges_set.clear();
284}
285
286template <typename AddressType>
287bool OverlapRangeSet<AddressType>::Empty() const {
288 return m_impl->m_split_ranges_set.empty();
289}
290
291template <typename AddressType>
292template <typename Func>
293void OverlapRangeSet<AddressType>::ForEach(Func&& func) const {
294 m_impl->ForEach(func);
295}
296
297template <typename AddressType>
298template <typename Func>
299void OverlapRangeSet<AddressType>::ForEachInRange(AddressType base_address, size_t size,
300 Func&& func) const {
301 m_impl->ForEachInRange(base_address, size, std::move(func));
302}
303
304} // namespace Common
diff --git a/src/video_core/texture_cache/slot_vector.h b/src/common/slot_vector.h
index 3ffa2a661..34ff7de94 100644
--- a/src/video_core/texture_cache/slot_vector.h
+++ b/src/common/slot_vector.h
@@ -14,7 +14,7 @@
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/polyfill_ranges.h" 15#include "common/polyfill_ranges.h"
16 16
17namespace VideoCommon { 17namespace Common {
18 18
19struct SlotId { 19struct SlotId {
20 static constexpr u32 INVALID_INDEX = std::numeric_limits<u32>::max(); 20 static constexpr u32 INVALID_INDEX = std::numeric_limits<u32>::max();
@@ -217,11 +217,11 @@ private:
217 std::vector<u32> free_list; 217 std::vector<u32> free_list;
218}; 218};
219 219
220} // namespace VideoCommon 220} // namespace Common
221 221
222template <> 222template <>
223struct std::hash<VideoCommon::SlotId> { 223struct std::hash<Common::SlotId> {
224 size_t operator()(const VideoCommon::SlotId& id) const noexcept { 224 size_t operator()(const Common::SlotId& id) const noexcept {
225 return std::hash<u32>{}(id.index); 225 return std::hash<u32>{}(id.index);
226 } 226 }
227}; 227};
diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc
index b026f4220..6dfee806c 100644
--- a/src/core/device_memory_manager.inc
+++ b/src/core/device_memory_manager.inc
@@ -532,6 +532,7 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
532 cache_bytes = 0; 532 cache_bytes = 0;
533 } 533 }
534 }; 534 };
535 size_t old_vpage = (base_vaddress >> Memory::YUZU_PAGEBITS) - 1;
535 for (; page != page_end; ++page) { 536 for (; page != page_end; ++page) {
536 CounterAtomicType& count = cached_pages->at(page >> subentries_shift).Count(page); 537 CounterAtomicType& count = cached_pages->at(page >> subentries_shift).Count(page);
537 auto [asid_2, vpage] = ExtractCPUBacking(page); 538 auto [asid_2, vpage] = ExtractCPUBacking(page);
@@ -547,6 +548,12 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
547 memory_device_inter = registered_processes[asid_2.id]; 548 memory_device_inter = registered_processes[asid_2.id];
548 } 549 }
549 550
551 if (vpage != old_vpage + 1) [[unlikely]] {
552 release_pending();
553 }
554
555 old_vpage = vpage;
556
550 // Adds or subtracts 1, as count is a unsigned 8-bit value 557 // Adds or subtracts 1, as count is a unsigned 8-bit value
551 count.fetch_add(static_cast<CounterType>(delta), std::memory_order_release); 558 count.fetch_add(static_cast<CounterType>(delta), std::memory_order_release);
552 559
diff --git a/src/core/hle/service/nvdrv/core/heap_mapper.cpp b/src/core/hle/service/nvdrv/core/heap_mapper.cpp
index 096dc5deb..af17e3e85 100644
--- a/src/core/hle/service/nvdrv/core/heap_mapper.cpp
+++ b/src/core/hle/service/nvdrv/core/heap_mapper.cpp
@@ -3,110 +3,21 @@
3 3
4#include <mutex> 4#include <mutex>
5 5
6#include <boost/container/small_vector.hpp> 6#include "common/range_sets.h"
7#define BOOST_NO_MT 7#include "common/range_sets.inc"
8#include <boost/pool/detail/mutex.hpp>
9#undef BOOST_NO_MT
10#include <boost/icl/interval.hpp>
11#include <boost/icl/interval_base_set.hpp>
12#include <boost/icl/interval_set.hpp>
13#include <boost/icl/split_interval_map.hpp>
14#include <boost/pool/pool.hpp>
15#include <boost/pool/pool_alloc.hpp>
16#include <boost/pool/poolfwd.hpp>
17
18#include "core/hle/service/nvdrv/core/heap_mapper.h" 8#include "core/hle/service/nvdrv/core/heap_mapper.h"
19#include "video_core/host1x/host1x.h" 9#include "video_core/host1x/host1x.h"
20 10
21namespace boost {
22template <typename T>
23class fast_pool_allocator<T, default_user_allocator_new_delete, details::pool::null_mutex, 4096, 0>;
24}
25
26namespace Service::Nvidia::NvCore { 11namespace Service::Nvidia::NvCore {
27 12
28using IntervalCompare = std::less<DAddr>;
29using IntervalInstance = boost::icl::interval_type_default<DAddr, std::less>;
30using IntervalAllocator = boost::fast_pool_allocator<DAddr>;
31using IntervalSet = boost::icl::interval_set<DAddr>;
32using IntervalType = typename IntervalSet::interval_type;
33
34template <typename Type>
35struct counter_add_functor : public boost::icl::identity_based_inplace_combine<Type> {
36 // types
37 typedef counter_add_functor<Type> type;
38 typedef boost::icl::identity_based_inplace_combine<Type> base_type;
39
40 // public member functions
41 void operator()(Type& current, const Type& added) const {
42 current += added;
43 if (current < base_type::identity_element()) {
44 current = base_type::identity_element();
45 }
46 }
47
48 // public static functions
49 static void version(Type&){};
50};
51
52using OverlapCombine = counter_add_functor<int>;
53using OverlapSection = boost::icl::inter_section<int>;
54using OverlapCounter = boost::icl::split_interval_map<DAddr, int>;
55
56struct HeapMapper::HeapMapperInternal { 13struct HeapMapper::HeapMapperInternal {
57 HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : device_memory{host1x.MemoryManager()} {} 14 HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : m_device_memory{host1x.MemoryManager()} {}
58 ~HeapMapperInternal() = default; 15 ~HeapMapperInternal() = default;
59 16
60 template <typename Func> 17 Common::RangeSet<VAddr> m_temporary_set;
61 void ForEachInOverlapCounter(OverlapCounter& current_range, VAddr cpu_addr, u64 size, 18 Common::OverlapRangeSet<VAddr> m_mapped_ranges;
62 Func&& func) { 19 Tegra::MaxwellDeviceMemoryManager& m_device_memory;
63 const DAddr start_address = cpu_addr; 20 std::mutex m_guard;
64 const DAddr end_address = start_address + size;
65 const IntervalType search_interval{start_address, end_address};
66 auto it = current_range.lower_bound(search_interval);
67 if (it == current_range.end()) {
68 return;
69 }
70 auto end_it = current_range.upper_bound(search_interval);
71 for (; it != end_it; it++) {
72 auto& inter = it->first;
73 DAddr inter_addr_end = inter.upper();
74 DAddr inter_addr = inter.lower();
75 if (inter_addr_end > end_address) {
76 inter_addr_end = end_address;
77 }
78 if (inter_addr < start_address) {
79 inter_addr = start_address;
80 }
81 func(inter_addr, inter_addr_end, it->second);
82 }
83 }
84
85 void RemoveEachInOverlapCounter(OverlapCounter& current_range,
86 const IntervalType search_interval, int subtract_value) {
87 bool any_removals = false;
88 current_range.add(std::make_pair(search_interval, subtract_value));
89 do {
90 any_removals = false;
91 auto it = current_range.lower_bound(search_interval);
92 if (it == current_range.end()) {
93 return;
94 }
95 auto end_it = current_range.upper_bound(search_interval);
96 for (; it != end_it; it++) {
97 if (it->second <= 0) {
98 any_removals = true;
99 current_range.erase(it);
100 break;
101 }
102 }
103 } while (any_removals);
104 }
105
106 IntervalSet base_set;
107 OverlapCounter mapping_overlaps;
108 Tegra::MaxwellDeviceMemoryManager& device_memory;
109 std::mutex guard;
110}; 21};
111 22
112HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid, 23HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid,
@@ -116,60 +27,48 @@ HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size,
116} 27}
117 28
118HeapMapper::~HeapMapper() { 29HeapMapper::~HeapMapper() {
119 m_internal->device_memory.Unmap(m_daddress, m_size); 30 // Unmap whatever has been mapped.
31 m_internal->m_mapped_ranges.ForEach([this](VAddr start_addr, VAddr end_addr, s32 count) {
32 const size_t sub_size = end_addr - start_addr;
33 const size_t offset = start_addr - m_vaddress;
34 m_internal->m_device_memory.Unmap(m_daddress + offset, sub_size);
35 });
120} 36}
121 37
122DAddr HeapMapper::Map(VAddr start, size_t size) { 38DAddr HeapMapper::Map(VAddr start, size_t size) {
123 std::scoped_lock lk(m_internal->guard); 39 std::scoped_lock lk(m_internal->m_guard);
124 m_internal->base_set.clear(); 40 // Add the mapping range to a temporary range set.
125 const IntervalType interval{start, start + size}; 41 m_internal->m_temporary_set.Clear();
126 m_internal->base_set.insert(interval); 42 m_internal->m_temporary_set.Add(start, size);
127 m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size, 43
128 [this](VAddr start_addr, VAddr end_addr, int) { 44 // Remove anything that's already mapped from the temporary range set.
129 const IntervalType other{start_addr, end_addr}; 45 m_internal->m_mapped_ranges.ForEachInRange(
130 m_internal->base_set.subtract(other); 46 start, size, [this](VAddr start_addr, VAddr end_addr, s32) {
131 }); 47 m_internal->m_temporary_set.Subtract(start_addr, end_addr - start_addr);
132 if (!m_internal->base_set.empty()) { 48 });
133 auto it = m_internal->base_set.begin(); 49
134 auto end_it = m_internal->base_set.end(); 50 // Map anything that has not been mapped yet.
135 for (; it != end_it; it++) { 51 m_internal->m_temporary_set.ForEach([this](VAddr start_addr, VAddr end_addr) {
136 const VAddr inter_addr_end = it->upper(); 52 const size_t sub_size = end_addr - start_addr;
137 const VAddr inter_addr = it->lower(); 53 const size_t offset = start_addr - m_vaddress;
138 const size_t offset = inter_addr - m_vaddress; 54 m_internal->m_device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size, m_asid);
139 const size_t sub_size = inter_addr_end - inter_addr; 55 });
140 m_internal->device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size, 56
141 m_asid); 57 // Add the mapping range to the split map, to register the map and overlaps.
142 } 58 m_internal->m_mapped_ranges.Add(start, size);
143 } 59 m_internal->m_temporary_set.Clear();
144 m_internal->mapping_overlaps += std::make_pair(interval, 1); 60 return m_daddress + static_cast<DAddr>(start - m_vaddress);
145 m_internal->base_set.clear();
146 return m_daddress + (start - m_vaddress);
147} 61}
148 62
149void HeapMapper::Unmap(VAddr start, size_t size) { 63void HeapMapper::Unmap(VAddr start, size_t size) {
150 std::scoped_lock lk(m_internal->guard); 64 std::scoped_lock lk(m_internal->m_guard);
151 m_internal->base_set.clear(); 65
152 m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size, 66 // Just subtract the range and whatever is deleted, unmap it.
153 [this](VAddr start_addr, VAddr end_addr, int value) { 67 m_internal->m_mapped_ranges.Subtract(start, size, [this](VAddr start_addr, VAddr end_addr) {
154 if (value <= 1) { 68 const size_t sub_size = end_addr - start_addr;
155 const IntervalType other{start_addr, end_addr}; 69 const size_t offset = start_addr - m_vaddress;
156 m_internal->base_set.insert(other); 70 m_internal->m_device_memory.Unmap(m_daddress + offset, sub_size);
157 } 71 });
158 });
159 if (!m_internal->base_set.empty()) {
160 auto it = m_internal->base_set.begin();
161 auto end_it = m_internal->base_set.end();
162 for (; it != end_it; it++) {
163 const VAddr inter_addr_end = it->upper();
164 const VAddr inter_addr = it->lower();
165 const size_t offset = inter_addr - m_vaddress;
166 const size_t sub_size = inter_addr_end - inter_addr;
167 m_internal->device_memory.Unmap(m_daddress + offset, sub_size);
168 }
169 }
170 const IntervalType to_remove{start, start + size};
171 m_internal->RemoveEachInOverlapCounter(m_internal->mapping_overlaps, to_remove, -1);
172 m_internal->base_set.clear();
173} 72}
174 73
175} // namespace Service::Nvidia::NvCore 74} // namespace Service::Nvidia::NvCore
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index 14d1a3840..470521f44 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -5,6 +5,7 @@
5#include "common/hex_util.h" 5#include "common/hex_util.h"
6#include "common/microprofile.h" 6#include "common/microprofile.h"
7#include "common/swap.h" 7#include "common/swap.h"
8#include "core/arm/debug.h"
8#include "core/core.h" 9#include "core/core.h"
9#include "core/core_timing.h" 10#include "core/core_timing.h"
10#include "core/hle/kernel/k_page_table.h" 11#include "core/hle/kernel/k_page_table.h"
@@ -63,7 +64,9 @@ void StandardVmCallbacks::MemoryWriteUnsafe(VAddr address, const void* data, u64
63 return; 64 return;
64 } 65 }
65 66
66 system.ApplicationMemory().WriteBlock(address, data, size); 67 if (system.ApplicationMemory().WriteBlock(address, data, size)) {
68 Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), address, size);
69 }
67} 70}
68 71
69u64 StandardVmCallbacks::HidKeysDown() { 72u64 StandardVmCallbacks::HidKeysDown() {
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 16c905db9..55180f4b5 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -274,7 +274,6 @@ add_library(video_core STATIC
274 texture_cache/image_view_info.h 274 texture_cache/image_view_info.h
275 texture_cache/render_targets.h 275 texture_cache/render_targets.h
276 texture_cache/samples_helper.h 276 texture_cache/samples_helper.h
277 texture_cache/slot_vector.h
278 texture_cache/texture_cache.cpp 277 texture_cache/texture_cache.cpp
279 texture_cache/texture_cache.h 278 texture_cache/texture_cache.h
280 texture_cache/texture_cache_base.h 279 texture_cache/texture_cache_base.h
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index b4bf369d1..6d3d933c5 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -7,6 +7,7 @@
7#include <memory> 7#include <memory>
8#include <numeric> 8#include <numeric>
9 9
10#include "common/range_sets.inc"
10#include "video_core/buffer_cache/buffer_cache_base.h" 11#include "video_core/buffer_cache/buffer_cache_base.h"
11#include "video_core/guest_memory.h" 12#include "video_core/guest_memory.h"
12#include "video_core/host1x/gpu_device_memory_manager.h" 13#include "video_core/host1x/gpu_device_memory_manager.h"
@@ -20,7 +21,7 @@ BufferCache<P>::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, R
20 : runtime{runtime_}, device_memory{device_memory_}, memory_tracker{device_memory} { 21 : runtime{runtime_}, device_memory{device_memory_}, memory_tracker{device_memory} {
21 // Ensure the first slot is used for the null buffer 22 // Ensure the first slot is used for the null buffer
22 void(slot_buffers.insert(runtime, NullBufferParams{})); 23 void(slot_buffers.insert(runtime, NullBufferParams{}));
23 common_ranges.clear(); 24 gpu_modified_ranges.Clear();
24 inline_buffer_id = NULL_BUFFER_ID; 25 inline_buffer_id = NULL_BUFFER_ID;
25 26
26 if (!runtime.CanReportMemoryUsage()) { 27 if (!runtime.CanReportMemoryUsage()) {
@@ -44,6 +45,9 @@ BufferCache<P>::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, R
44} 45}
45 46
46template <class P> 47template <class P>
48BufferCache<P>::~BufferCache() = default;
49
50template <class P>
47void BufferCache<P>::RunGarbageCollector() { 51void BufferCache<P>::RunGarbageCollector() {
48 const bool aggressive_gc = total_used_memory >= critical_memory; 52 const bool aggressive_gc = total_used_memory >= critical_memory;
49 const u64 ticks_to_destroy = aggressive_gc ? 60 : 120; 53 const u64 ticks_to_destroy = aggressive_gc ? 60 : 120;
@@ -96,20 +100,17 @@ void BufferCache<P>::TickFrame() {
96 ++frame_tick; 100 ++frame_tick;
97 delayed_destruction_ring.Tick(); 101 delayed_destruction_ring.Tick();
98 102
99 if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) { 103 for (auto& buffer : async_buffers_death_ring) {
100 for (auto& buffer : async_buffers_death_ring) { 104 runtime.FreeDeferredStagingBuffer(buffer);
101 runtime.FreeDeferredStagingBuffer(buffer);
102 }
103 async_buffers_death_ring.clear();
104 } 105 }
106 async_buffers_death_ring.clear();
105} 107}
106 108
107template <class P> 109template <class P>
108void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) { 110void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
109 if (memory_tracker.IsRegionGpuModified(device_addr, size)) { 111 if (memory_tracker.IsRegionGpuModified(device_addr, size)) {
110 const IntervalType subtract_interval{device_addr, device_addr + size}; 112 ClearDownload(device_addr, size);
111 ClearDownload(subtract_interval); 113 gpu_modified_ranges.Subtract(device_addr, size);
112 common_ranges.subtract(subtract_interval);
113 } 114 }
114 memory_tracker.MarkRegionAsCpuModified(device_addr, size); 115 memory_tracker.MarkRegionAsCpuModified(device_addr, size);
115} 116}
@@ -174,11 +175,11 @@ void BufferCache<P>::DownloadMemory(DAddr device_addr, u64 size) {
174} 175}
175 176
176template <class P> 177template <class P>
177void BufferCache<P>::ClearDownload(IntervalType subtract_interval) { 178void BufferCache<P>::ClearDownload(DAddr device_addr, u64 size) {
178 RemoveEachInOverlapCounter(async_downloads, subtract_interval, -1024); 179 async_downloads.DeleteAll(device_addr, size);
179 uncommitted_ranges.subtract(subtract_interval); 180 uncommitted_gpu_modified_ranges.Subtract(device_addr, size);
180 for (auto& interval_set : committed_ranges) { 181 for (auto& interval_set : committed_gpu_modified_ranges) {
181 interval_set.subtract(subtract_interval); 182 interval_set.Subtract(device_addr, size);
182 } 183 }
183} 184}
184 185
@@ -195,8 +196,7 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
195 return false; 196 return false;
196 } 197 }
197 198
198 const IntervalType subtract_interval{*cpu_dest_address, *cpu_dest_address + amount}; 199 ClearDownload(*cpu_dest_address, amount);
199 ClearDownload(subtract_interval);
200 200
201 BufferId buffer_a; 201 BufferId buffer_a;
202 BufferId buffer_b; 202 BufferId buffer_b;
@@ -215,21 +215,20 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
215 .size = amount, 215 .size = amount,
216 }}; 216 }};
217 217
218 boost::container::small_vector<IntervalType, 4> tmp_intervals; 218 boost::container::small_vector<std::pair<DAddr, size_t>, 4> tmp_intervals;
219 auto mirror = [&](DAddr base_address, DAddr base_address_end) { 219 auto mirror = [&](DAddr base_address, DAddr base_address_end) {
220 const u64 size = base_address_end - base_address; 220 const u64 size = base_address_end - base_address;
221 const DAddr diff = base_address - *cpu_src_address; 221 const DAddr diff = base_address - *cpu_src_address;
222 const DAddr new_base_address = *cpu_dest_address + diff; 222 const DAddr new_base_address = *cpu_dest_address + diff;
223 const IntervalType add_interval{new_base_address, new_base_address + size}; 223 tmp_intervals.push_back({new_base_address, size});
224 tmp_intervals.push_back(add_interval); 224 uncommitted_gpu_modified_ranges.Add(new_base_address, size);
225 uncommitted_ranges.add(add_interval);
226 }; 225 };
227 ForEachInRangeSet(common_ranges, *cpu_src_address, amount, mirror); 226 gpu_modified_ranges.ForEachInRange(*cpu_src_address, amount, mirror);
228 // This subtraction in this order is important for overlapping copies. 227 // This subtraction in this order is important for overlapping copies.
229 common_ranges.subtract(subtract_interval); 228 gpu_modified_ranges.Subtract(*cpu_dest_address, amount);
230 const bool has_new_downloads = tmp_intervals.size() != 0; 229 const bool has_new_downloads = tmp_intervals.size() != 0;
231 for (const IntervalType& add_interval : tmp_intervals) { 230 for (const auto& pair : tmp_intervals) {
232 common_ranges.add(add_interval); 231 gpu_modified_ranges.Add(pair.first, pair.second);
233 } 232 }
234 const auto& copy = copies[0]; 233 const auto& copy = copies[0];
235 src_buffer.MarkUsage(copy.src_offset, copy.size); 234 src_buffer.MarkUsage(copy.src_offset, copy.size);
@@ -257,9 +256,8 @@ bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) {
257 } 256 }
258 257
259 const size_t size = amount * sizeof(u32); 258 const size_t size = amount * sizeof(u32);
260 const IntervalType subtract_interval{*cpu_dst_address, *cpu_dst_address + size}; 259 ClearDownload(*cpu_dst_address, size);
261 ClearDownload(subtract_interval); 260 gpu_modified_ranges.Subtract(*cpu_dst_address, size);
262 common_ranges.subtract(subtract_interval);
263 261
264 const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size)); 262 const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size));
265 Buffer& dest_buffer = slot_buffers[buffer]; 263 Buffer& dest_buffer = slot_buffers[buffer];
@@ -300,11 +298,11 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainCPUBuffer(
300 MarkWrittenBuffer(buffer_id, device_addr, size); 298 MarkWrittenBuffer(buffer_id, device_addr, size);
301 break; 299 break;
302 case ObtainBufferOperation::DiscardWrite: { 300 case ObtainBufferOperation::DiscardWrite: {
303 DAddr device_addr_start = Common::AlignDown(device_addr, 64); 301 const DAddr device_addr_start = Common::AlignDown(device_addr, 64);
304 DAddr device_addr_end = Common::AlignUp(device_addr + size, 64); 302 const DAddr device_addr_end = Common::AlignUp(device_addr + size, 64);
305 IntervalType interval{device_addr_start, device_addr_end}; 303 const size_t new_size = device_addr_end - device_addr_start;
306 ClearDownload(interval); 304 ClearDownload(device_addr_start, new_size);
307 common_ranges.subtract(interval); 305 gpu_modified_ranges.Subtract(device_addr_start, new_size);
308 break; 306 break;
309 } 307 }
310 default: 308 default:
@@ -504,46 +502,40 @@ void BufferCache<P>::FlushCachedWrites() {
504 502
505template <class P> 503template <class P>
506bool BufferCache<P>::HasUncommittedFlushes() const noexcept { 504bool BufferCache<P>::HasUncommittedFlushes() const noexcept {
507 return !uncommitted_ranges.empty() || !committed_ranges.empty(); 505 return !uncommitted_gpu_modified_ranges.Empty() || !committed_gpu_modified_ranges.empty();
508} 506}
509 507
510template <class P> 508template <class P>
511void BufferCache<P>::AccumulateFlushes() { 509void BufferCache<P>::AccumulateFlushes() {
512 if (uncommitted_ranges.empty()) { 510 if (uncommitted_gpu_modified_ranges.Empty()) {
513 return; 511 return;
514 } 512 }
515 committed_ranges.emplace_back(std::move(uncommitted_ranges)); 513 committed_gpu_modified_ranges.emplace_back(std::move(uncommitted_gpu_modified_ranges));
516} 514}
517 515
518template <class P> 516template <class P>
519bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept { 517bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
520 if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) { 518 return (!async_buffers.empty() && async_buffers.front().has_value());
521 return (!async_buffers.empty() && async_buffers.front().has_value());
522 } else {
523 return false;
524 }
525} 519}
526 520
527template <class P> 521template <class P>
528void BufferCache<P>::CommitAsyncFlushesHigh() { 522void BufferCache<P>::CommitAsyncFlushesHigh() {
529 AccumulateFlushes(); 523 AccumulateFlushes();
530 524
531 if (committed_ranges.empty()) { 525 if (committed_gpu_modified_ranges.empty()) {
532 if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) { 526 async_buffers.emplace_back(std::optional<Async_Buffer>{});
533 async_buffers.emplace_back(std::optional<Async_Buffer>{});
534 }
535 return; 527 return;
536 } 528 }
537 MICROPROFILE_SCOPE(GPU_DownloadMemory); 529 MICROPROFILE_SCOPE(GPU_DownloadMemory);
538 530
539 auto it = committed_ranges.begin(); 531 auto it = committed_gpu_modified_ranges.begin();
540 while (it != committed_ranges.end()) { 532 while (it != committed_gpu_modified_ranges.end()) {
541 auto& current_intervals = *it; 533 auto& current_intervals = *it;
542 auto next_it = std::next(it); 534 auto next_it = std::next(it);
543 while (next_it != committed_ranges.end()) { 535 while (next_it != committed_gpu_modified_ranges.end()) {
544 for (auto& interval : *next_it) { 536 next_it->ForEach([&current_intervals](DAddr start, DAddr end) {
545 current_intervals.subtract(interval); 537 current_intervals.Subtract(start, end - start);
546 } 538 });
547 next_it++; 539 next_it++;
548 } 540 }
549 it++; 541 it++;
@@ -552,10 +544,10 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
552 boost::container::small_vector<std::pair<BufferCopy, BufferId>, 16> downloads; 544 boost::container::small_vector<std::pair<BufferCopy, BufferId>, 16> downloads;
553 u64 total_size_bytes = 0; 545 u64 total_size_bytes = 0;
554 u64 largest_copy = 0; 546 u64 largest_copy = 0;
555 for (const IntervalSet& intervals : committed_ranges) { 547 for (const Common::RangeSet<DAddr>& range_set : committed_gpu_modified_ranges) {
556 for (auto& interval : intervals) { 548 range_set.ForEach([&](DAddr interval_lower, DAddr interval_upper) {
557 const std::size_t size = interval.upper() - interval.lower(); 549 const std::size_t size = interval_upper - interval_lower;
558 const DAddr device_addr = interval.lower(); 550 const DAddr device_addr = interval_lower;
559 ForEachBufferInRange(device_addr, size, [&](BufferId buffer_id, Buffer& buffer) { 551 ForEachBufferInRange(device_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
560 const DAddr buffer_start = buffer.CpuAddr(); 552 const DAddr buffer_start = buffer.CpuAddr();
561 const DAddr buffer_end = buffer_start + buffer.SizeBytes(); 553 const DAddr buffer_end = buffer_start + buffer.SizeBytes();
@@ -583,77 +575,35 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
583 largest_copy = std::max(largest_copy, new_size); 575 largest_copy = std::max(largest_copy, new_size);
584 }; 576 };
585 577
586 ForEachInRangeSet(common_ranges, device_addr_out, range_size, add_download); 578 gpu_modified_ranges.ForEachInRange(device_addr_out, range_size,
579 add_download);
587 }); 580 });
588 }); 581 });
589 } 582 });
590 } 583 }
591 committed_ranges.clear(); 584 committed_gpu_modified_ranges.clear();
592 if (downloads.empty()) { 585 if (downloads.empty()) {
593 if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) { 586 async_buffers.emplace_back(std::optional<Async_Buffer>{});
594 async_buffers.emplace_back(std::optional<Async_Buffer>{});
595 }
596 return; 587 return;
597 } 588 }
598 if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) { 589 auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true);
599 auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true); 590 boost::container::small_vector<BufferCopy, 4> normalized_copies;
600 boost::container::small_vector<BufferCopy, 4> normalized_copies; 591 runtime.PreCopyBarrier();
601 IntervalSet new_async_range{}; 592 for (auto& [copy, buffer_id] : downloads) {
602 runtime.PreCopyBarrier(); 593 copy.dst_offset += download_staging.offset;
603 for (auto& [copy, buffer_id] : downloads) { 594 const std::array copies{copy};
604 copy.dst_offset += download_staging.offset; 595 BufferCopy second_copy{copy};
605 const std::array copies{copy}; 596 Buffer& buffer = slot_buffers[buffer_id];
606 BufferCopy second_copy{copy}; 597 second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset;
607 Buffer& buffer = slot_buffers[buffer_id]; 598 const DAddr orig_device_addr = static_cast<DAddr>(second_copy.src_offset);
608 second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset; 599 async_downloads.Add(orig_device_addr, copy.size);
609 DAddr orig_device_addr = static_cast<DAddr>(second_copy.src_offset); 600 buffer.MarkUsage(copy.src_offset, copy.size);
610 const IntervalType base_interval{orig_device_addr, orig_device_addr + copy.size}; 601 runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
611 async_downloads += std::make_pair(base_interval, 1); 602 normalized_copies.push_back(second_copy);
612 buffer.MarkUsage(copy.src_offset, copy.size);
613 runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
614 normalized_copies.push_back(second_copy);
615 }
616 runtime.PostCopyBarrier();
617 pending_downloads.emplace_back(std::move(normalized_copies));
618 async_buffers.emplace_back(download_staging);
619 } else {
620 if (!Settings::IsGPULevelHigh()) {
621 committed_ranges.clear();
622 uncommitted_ranges.clear();
623 } else {
624 if constexpr (USE_MEMORY_MAPS) {
625 auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
626 runtime.PreCopyBarrier();
627 for (auto& [copy, buffer_id] : downloads) {
628 // Have in mind the staging buffer offset for the copy
629 copy.dst_offset += download_staging.offset;
630 const std::array copies{copy};
631 Buffer& buffer = slot_buffers[buffer_id];
632 buffer.MarkUsage(copy.src_offset, copy.size);
633 runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
634 }
635 runtime.PostCopyBarrier();
636 runtime.Finish();
637 for (const auto& [copy, buffer_id] : downloads) {
638 const Buffer& buffer = slot_buffers[buffer_id];
639 const DAddr device_addr = buffer.CpuAddr() + copy.src_offset;
640 // Undo the modified offset
641 const u64 dst_offset = copy.dst_offset - download_staging.offset;
642 const u8* read_mapped_memory = download_staging.mapped_span.data() + dst_offset;
643 device_memory.WriteBlockUnsafe(device_addr, read_mapped_memory, copy.size);
644 }
645 } else {
646 const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
647 for (const auto& [copy, buffer_id] : downloads) {
648 Buffer& buffer = slot_buffers[buffer_id];
649 buffer.ImmediateDownload(copy.src_offset,
650 immediate_buffer.subspan(0, copy.size));
651 const DAddr device_addr = buffer.CpuAddr() + copy.src_offset;
652 device_memory.WriteBlockUnsafe(device_addr, immediate_buffer.data(), copy.size);
653 }
654 }
655 }
656 } 603 }
604 runtime.PostCopyBarrier();
605 pending_downloads.emplace_back(std::move(normalized_copies));
606 async_buffers.emplace_back(download_staging);
657} 607}
658 608
659template <class P> 609template <class P>
@@ -676,37 +626,31 @@ void BufferCache<P>::PopAsyncBuffers() {
676 async_buffers.pop_front(); 626 async_buffers.pop_front();
677 return; 627 return;
678 } 628 }
679 if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) { 629 auto& downloads = pending_downloads.front();
680 auto& downloads = pending_downloads.front(); 630 auto& async_buffer = async_buffers.front();
681 auto& async_buffer = async_buffers.front(); 631 u8* base = async_buffer->mapped_span.data();
682 u8* base = async_buffer->mapped_span.data(); 632 const size_t base_offset = async_buffer->offset;
683 const size_t base_offset = async_buffer->offset; 633 for (const auto& copy : downloads) {
684 for (const auto& copy : downloads) { 634 const DAddr device_addr = static_cast<DAddr>(copy.src_offset);
685 const DAddr device_addr = static_cast<DAddr>(copy.src_offset); 635 const u64 dst_offset = copy.dst_offset - base_offset;
686 const u64 dst_offset = copy.dst_offset - base_offset; 636 const u8* read_mapped_memory = base + dst_offset;
687 const u8* read_mapped_memory = base + dst_offset; 637 async_downloads.ForEachInRange(device_addr, copy.size, [&](DAddr start, DAddr end, s32) {
688 ForEachInOverlapCounter( 638 device_memory.WriteBlockUnsafe(start, &read_mapped_memory[start - device_addr],
689 async_downloads, device_addr, copy.size, [&](DAddr start, DAddr end, int count) { 639 end - start);
690 device_memory.WriteBlockUnsafe(start, &read_mapped_memory[start - device_addr], 640 });
691 end - start); 641 async_downloads.Subtract(device_addr, copy.size, [&](DAddr start, DAddr end) {
692 if (count == 1) { 642 gpu_modified_ranges.Subtract(start, end - start);
693 const IntervalType base_interval{start, end}; 643 });
694 common_ranges.subtract(base_interval);
695 }
696 });
697 const IntervalType subtract_interval{device_addr, device_addr + copy.size};
698 RemoveEachInOverlapCounter(async_downloads, subtract_interval, -1);
699 }
700 async_buffers_death_ring.emplace_back(*async_buffer);
701 async_buffers.pop_front();
702 pending_downloads.pop_front();
703 } 644 }
645 async_buffers_death_ring.emplace_back(*async_buffer);
646 async_buffers.pop_front();
647 pending_downloads.pop_front();
704} 648}
705 649
706template <class P> 650template <class P>
707bool BufferCache<P>::IsRegionGpuModified(DAddr addr, size_t size) { 651bool BufferCache<P>::IsRegionGpuModified(DAddr addr, size_t size) {
708 bool is_dirty = false; 652 bool is_dirty = false;
709 ForEachInRangeSet(common_ranges, addr, size, [&](DAddr, DAddr) { is_dirty = true; }); 653 gpu_modified_ranges.ForEachInRange(addr, size, [&](DAddr, DAddr) { is_dirty = true; });
710 return is_dirty; 654 return is_dirty;
711} 655}
712 656
@@ -1320,10 +1264,8 @@ void BufferCache<P>::UpdateComputeTextureBuffers() {
1320template <class P> 1264template <class P>
1321void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size) { 1265void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size) {
1322 memory_tracker.MarkRegionAsGpuModified(device_addr, size); 1266 memory_tracker.MarkRegionAsGpuModified(device_addr, size);
1323 1267 gpu_modified_ranges.Add(device_addr, size);
1324 const IntervalType base_interval{device_addr, device_addr + size}; 1268 uncommitted_gpu_modified_ranges.Add(device_addr, size);
1325 common_ranges.add(base_interval);
1326 uncommitted_ranges.add(base_interval);
1327} 1269}
1328 1270
1329template <class P> 1271template <class P>
@@ -1600,9 +1542,8 @@ bool BufferCache<P>::InlineMemory(DAddr dest_address, size_t copy_size,
1600template <class P> 1542template <class P>
1601void BufferCache<P>::InlineMemoryImplementation(DAddr dest_address, size_t copy_size, 1543void BufferCache<P>::InlineMemoryImplementation(DAddr dest_address, size_t copy_size,
1602 std::span<const u8> inlined_buffer) { 1544 std::span<const u8> inlined_buffer) {
1603 const IntervalType subtract_interval{dest_address, dest_address + copy_size}; 1545 ClearDownload(dest_address, copy_size);
1604 ClearDownload(subtract_interval); 1546 gpu_modified_ranges.Subtract(dest_address, copy_size);
1605 common_ranges.subtract(subtract_interval);
1606 1547
1607 BufferId buffer_id = FindBuffer(dest_address, static_cast<u32>(copy_size)); 1548 BufferId buffer_id = FindBuffer(dest_address, static_cast<u32>(copy_size));
1608 auto& buffer = slot_buffers[buffer_id]; 1549 auto& buffer = slot_buffers[buffer_id];
@@ -1652,12 +1593,9 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, DAddr device_addr, u64
1652 largest_copy = std::max(largest_copy, new_size); 1593 largest_copy = std::max(largest_copy, new_size);
1653 }; 1594 };
1654 1595
1655 const DAddr start_address = device_addr_out; 1596 gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, add_download);
1656 const DAddr end_address = start_address + range_size; 1597 ClearDownload(device_addr_out, range_size);
1657 ForEachInRangeSet(common_ranges, start_address, range_size, add_download); 1598 gpu_modified_ranges.Subtract(device_addr_out, range_size);
1658 const IntervalType subtract_interval{start_address, end_address};
1659 ClearDownload(subtract_interval);
1660 common_ranges.subtract(subtract_interval);
1661 }); 1599 });
1662 if (total_size_bytes == 0) { 1600 if (total_size_bytes == 0) {
1663 return; 1601 return;
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h
index 80dbb81e7..240e9f015 100644
--- a/src/video_core/buffer_cache/buffer_cache_base.h
+++ b/src/video_core/buffer_cache/buffer_cache_base.h
@@ -13,25 +13,15 @@
13#include <unordered_map> 13#include <unordered_map>
14#include <vector> 14#include <vector>
15 15
16#include <boost/container/small_vector.hpp>
17#define BOOST_NO_MT
18#include <boost/pool/detail/mutex.hpp>
19#undef BOOST_NO_MT
20#include <boost/icl/interval.hpp>
21#include <boost/icl/interval_base_set.hpp>
22#include <boost/icl/interval_set.hpp>
23#include <boost/icl/split_interval_map.hpp>
24#include <boost/pool/pool.hpp>
25#include <boost/pool/pool_alloc.hpp>
26#include <boost/pool/poolfwd.hpp>
27
28#include "common/common_types.h" 16#include "common/common_types.h"
29#include "common/div_ceil.h" 17#include "common/div_ceil.h"
30#include "common/literals.h" 18#include "common/literals.h"
31#include "common/lru_cache.h" 19#include "common/lru_cache.h"
32#include "common/microprofile.h" 20#include "common/microprofile.h"
21#include "common/range_sets.h"
33#include "common/scope_exit.h" 22#include "common/scope_exit.h"
34#include "common/settings.h" 23#include "common/settings.h"
24#include "common/slot_vector.h"
35#include "video_core/buffer_cache/buffer_base.h" 25#include "video_core/buffer_cache/buffer_base.h"
36#include "video_core/control/channel_state_cache.h" 26#include "video_core/control/channel_state_cache.h"
37#include "video_core/delayed_destruction_ring.h" 27#include "video_core/delayed_destruction_ring.h"
@@ -41,21 +31,15 @@
41#include "video_core/engines/maxwell_3d.h" 31#include "video_core/engines/maxwell_3d.h"
42#include "video_core/memory_manager.h" 32#include "video_core/memory_manager.h"
43#include "video_core/surface.h" 33#include "video_core/surface.h"
44#include "video_core/texture_cache/slot_vector.h"
45#include "video_core/texture_cache/types.h" 34#include "video_core/texture_cache/types.h"
46 35
47namespace boost {
48template <typename T>
49class fast_pool_allocator<T, default_user_allocator_new_delete, details::pool::null_mutex, 4096, 0>;
50}
51
52namespace VideoCommon { 36namespace VideoCommon {
53 37
54MICROPROFILE_DECLARE(GPU_PrepareBuffers); 38MICROPROFILE_DECLARE(GPU_PrepareBuffers);
55MICROPROFILE_DECLARE(GPU_BindUploadBuffers); 39MICROPROFILE_DECLARE(GPU_BindUploadBuffers);
56MICROPROFILE_DECLARE(GPU_DownloadMemory); 40MICROPROFILE_DECLARE(GPU_DownloadMemory);
57 41
58using BufferId = SlotId; 42using BufferId = Common::SlotId;
59 43
60using VideoCore::Surface::PixelFormat; 44using VideoCore::Surface::PixelFormat;
61using namespace Common::Literals; 45using namespace Common::Literals;
@@ -184,7 +168,6 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
184 static constexpr bool NEEDS_BIND_STORAGE_INDEX = P::NEEDS_BIND_STORAGE_INDEX; 168 static constexpr bool NEEDS_BIND_STORAGE_INDEX = P::NEEDS_BIND_STORAGE_INDEX;
185 static constexpr bool USE_MEMORY_MAPS = P::USE_MEMORY_MAPS; 169 static constexpr bool USE_MEMORY_MAPS = P::USE_MEMORY_MAPS;
186 static constexpr bool SEPARATE_IMAGE_BUFFERS_BINDINGS = P::SEPARATE_IMAGE_BUFFER_BINDINGS; 170 static constexpr bool SEPARATE_IMAGE_BUFFERS_BINDINGS = P::SEPARATE_IMAGE_BUFFER_BINDINGS;
187 static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = P::IMPLEMENTS_ASYNC_DOWNLOADS;
188 static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = P::USE_MEMORY_MAPS_FOR_UPLOADS; 171 static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = P::USE_MEMORY_MAPS_FOR_UPLOADS;
189 172
190 static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB; 173 static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB;
@@ -202,34 +185,6 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
202 using Async_Buffer = typename P::Async_Buffer; 185 using Async_Buffer = typename P::Async_Buffer;
203 using MemoryTracker = typename P::MemoryTracker; 186 using MemoryTracker = typename P::MemoryTracker;
204 187
205 using IntervalCompare = std::less<DAddr>;
206 using IntervalInstance = boost::icl::interval_type_default<DAddr, std::less>;
207 using IntervalAllocator = boost::fast_pool_allocator<DAddr>;
208 using IntervalSet = boost::icl::interval_set<DAddr>;
209 using IntervalType = typename IntervalSet::interval_type;
210
211 template <typename Type>
212 struct counter_add_functor : public boost::icl::identity_based_inplace_combine<Type> {
213 // types
214 typedef counter_add_functor<Type> type;
215 typedef boost::icl::identity_based_inplace_combine<Type> base_type;
216
217 // public member functions
218 void operator()(Type& current, const Type& added) const {
219 current += added;
220 if (current < base_type::identity_element()) {
221 current = base_type::identity_element();
222 }
223 }
224
225 // public static functions
226 static void version(Type&){};
227 };
228
229 using OverlapCombine = counter_add_functor<int>;
230 using OverlapSection = boost::icl::inter_section<int>;
231 using OverlapCounter = boost::icl::split_interval_map<DAddr, int>;
232
233 struct OverlapResult { 188 struct OverlapResult {
234 boost::container::small_vector<BufferId, 16> ids; 189 boost::container::small_vector<BufferId, 16> ids;
235 DAddr begin; 190 DAddr begin;
@@ -240,6 +195,8 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
240public: 195public:
241 explicit BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, Runtime& runtime_); 196 explicit BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, Runtime& runtime_);
242 197
198 ~BufferCache();
199
243 void TickFrame(); 200 void TickFrame();
244 201
245 void WriteMemory(DAddr device_addr, u64 size); 202 void WriteMemory(DAddr device_addr, u64 size);
@@ -379,75 +336,6 @@ private:
379 } 336 }
380 } 337 }
381 338
382 template <typename Func>
383 void ForEachInRangeSet(IntervalSet& current_range, DAddr device_addr, u64 size, Func&& func) {
384 const DAddr start_address = device_addr;
385 const DAddr end_address = start_address + size;
386 const IntervalType search_interval{start_address, end_address};
387 auto it = current_range.lower_bound(search_interval);
388 if (it == current_range.end()) {
389 return;
390 }
391 auto end_it = current_range.upper_bound(search_interval);
392 for (; it != end_it; it++) {
393 DAddr inter_addr_end = it->upper();
394 DAddr inter_addr = it->lower();
395 if (inter_addr_end > end_address) {
396 inter_addr_end = end_address;
397 }
398 if (inter_addr < start_address) {
399 inter_addr = start_address;
400 }
401 func(inter_addr, inter_addr_end);
402 }
403 }
404
405 template <typename Func>
406 void ForEachInOverlapCounter(OverlapCounter& current_range, DAddr device_addr, u64 size,
407 Func&& func) {
408 const DAddr start_address = device_addr;
409 const DAddr end_address = start_address + size;
410 const IntervalType search_interval{start_address, end_address};
411 auto it = current_range.lower_bound(search_interval);
412 if (it == current_range.end()) {
413 return;
414 }
415 auto end_it = current_range.upper_bound(search_interval);
416 for (; it != end_it; it++) {
417 auto& inter = it->first;
418 DAddr inter_addr_end = inter.upper();
419 DAddr inter_addr = inter.lower();
420 if (inter_addr_end > end_address) {
421 inter_addr_end = end_address;
422 }
423 if (inter_addr < start_address) {
424 inter_addr = start_address;
425 }
426 func(inter_addr, inter_addr_end, it->second);
427 }
428 }
429
430 void RemoveEachInOverlapCounter(OverlapCounter& current_range,
431 const IntervalType search_interval, int subtract_value) {
432 bool any_removals = false;
433 current_range.add(std::make_pair(search_interval, subtract_value));
434 do {
435 any_removals = false;
436 auto it = current_range.lower_bound(search_interval);
437 if (it == current_range.end()) {
438 return;
439 }
440 auto end_it = current_range.upper_bound(search_interval);
441 for (; it != end_it; it++) {
442 if (it->second <= 0) {
443 any_removals = true;
444 current_range.erase(it);
445 break;
446 }
447 }
448 } while (any_removals);
449 }
450
451 static bool IsRangeGranular(DAddr device_addr, size_t size) { 339 static bool IsRangeGranular(DAddr device_addr, size_t size) {
452 return (device_addr & ~Core::DEVICE_PAGEMASK) == 340 return (device_addr & ~Core::DEVICE_PAGEMASK) ==
453 ((device_addr + size) & ~Core::DEVICE_PAGEMASK); 341 ((device_addr + size) & ~Core::DEVICE_PAGEMASK);
@@ -552,14 +440,14 @@ private:
552 440
553 [[nodiscard]] bool HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept; 441 [[nodiscard]] bool HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept;
554 442
555 void ClearDownload(IntervalType subtract_interval); 443 void ClearDownload(DAddr base_addr, u64 size);
556 444
557 void InlineMemoryImplementation(DAddr dest_address, size_t copy_size, 445 void InlineMemoryImplementation(DAddr dest_address, size_t copy_size,
558 std::span<const u8> inlined_buffer); 446 std::span<const u8> inlined_buffer);
559 447
560 Tegra::MaxwellDeviceMemoryManager& device_memory; 448 Tegra::MaxwellDeviceMemoryManager& device_memory;
561 449
562 SlotVector<Buffer> slot_buffers; 450 Common::SlotVector<Buffer> slot_buffers;
563 DelayedDestructionRing<Buffer, 8> delayed_destruction_ring; 451 DelayedDestructionRing<Buffer, 8> delayed_destruction_ring;
564 452
565 const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect{}; 453 const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect{};
@@ -567,13 +455,12 @@ private:
567 u32 last_index_count = 0; 455 u32 last_index_count = 0;
568 456
569 MemoryTracker memory_tracker; 457 MemoryTracker memory_tracker;
570 IntervalSet uncommitted_ranges; 458 Common::RangeSet<DAddr> uncommitted_gpu_modified_ranges;
571 IntervalSet common_ranges; 459 Common::RangeSet<DAddr> gpu_modified_ranges;
572 IntervalSet cached_ranges; 460 std::deque<Common::RangeSet<DAddr>> committed_gpu_modified_ranges;
573 std::deque<IntervalSet> committed_ranges;
574 461
575 // Async Buffers 462 // Async Buffers
576 OverlapCounter async_downloads; 463 Common::OverlapRangeSet<DAddr> async_downloads;
577 std::deque<std::optional<Async_Buffer>> async_buffers; 464 std::deque<std::optional<Async_Buffer>> async_buffers;
578 std::deque<boost::container::small_vector<BufferCopy, 4>> pending_downloads; 465 std::deque<boost::container::small_vector<BufferCopy, 4>> pending_downloads;
579 std::optional<Async_Buffer> current_buffer; 466 std::optional<Async_Buffer> current_buffer;
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h
index 4861b123a..e1019f228 100644
--- a/src/video_core/query_cache.h
+++ b/src/video_core/query_cache.h
@@ -18,12 +18,12 @@
18 18
19#include "common/assert.h" 19#include "common/assert.h"
20#include "common/settings.h" 20#include "common/settings.h"
21#include "common/slot_vector.h"
21#include "video_core/control/channel_state_cache.h" 22#include "video_core/control/channel_state_cache.h"
22#include "video_core/engines/maxwell_3d.h" 23#include "video_core/engines/maxwell_3d.h"
23#include "video_core/host1x/gpu_device_memory_manager.h" 24#include "video_core/host1x/gpu_device_memory_manager.h"
24#include "video_core/memory_manager.h" 25#include "video_core/memory_manager.h"
25#include "video_core/rasterizer_interface.h" 26#include "video_core/rasterizer_interface.h"
26#include "video_core/texture_cache/slot_vector.h"
27 27
28namespace VideoCore { 28namespace VideoCore {
29enum class QueryType { 29enum class QueryType {
@@ -37,7 +37,7 @@ constexpr std::size_t NumQueryTypes = static_cast<size_t>(QueryType::Count);
37 37
38namespace VideoCommon { 38namespace VideoCommon {
39 39
40using AsyncJobId = SlotId; 40using AsyncJobId = Common::SlotId;
41 41
42static constexpr AsyncJobId NULL_ASYNC_JOB_ID{0}; 42static constexpr AsyncJobId NULL_ASYNC_JOB_ID{0};
43 43
@@ -341,7 +341,7 @@ private:
341 static constexpr std::uintptr_t YUZU_PAGESIZE = 4096; 341 static constexpr std::uintptr_t YUZU_PAGESIZE = 4096;
342 static constexpr unsigned YUZU_PAGEBITS = 12; 342 static constexpr unsigned YUZU_PAGEBITS = 12;
343 343
344 SlotVector<AsyncJob> slot_async_jobs; 344 Common::SlotVector<AsyncJob> slot_async_jobs;
345 345
346 VideoCore::RasterizerInterface& rasterizer; 346 VideoCore::RasterizerInterface& rasterizer;
347 Tegra::MaxwellDeviceMemoryManager& device_memory; 347 Tegra::MaxwellDeviceMemoryManager& device_memory;
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index af34c272b..fd471e979 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -90,7 +90,7 @@ public:
90 void PostCopyBarrier(); 90 void PostCopyBarrier();
91 void Finish(); 91 void Finish();
92 92
93 void TickFrame(VideoCommon::SlotVector<Buffer>&) noexcept {} 93 void TickFrame(Common::SlotVector<Buffer>&) noexcept {}
94 94
95 void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value); 95 void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value);
96 96
@@ -251,7 +251,6 @@ struct BufferCacheParams {
251 static constexpr bool NEEDS_BIND_STORAGE_INDEX = true; 251 static constexpr bool NEEDS_BIND_STORAGE_INDEX = true;
252 static constexpr bool USE_MEMORY_MAPS = true; 252 static constexpr bool USE_MEMORY_MAPS = true;
253 static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = true; 253 static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = true;
254 static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
255 254
256 // TODO: Investigate why OpenGL seems to perform worse with persistently mapped buffer uploads 255 // TODO: Investigate why OpenGL seems to perform worse with persistently mapped buffer uploads
257 static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = false; 256 static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = false;
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 3e54edcc2..d4165d8e4 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -30,13 +30,13 @@ class Image;
30class ImageView; 30class ImageView;
31class Sampler; 31class Sampler;
32 32
33using Common::SlotVector;
33using VideoCommon::ImageId; 34using VideoCommon::ImageId;
34using VideoCommon::ImageViewId; 35using VideoCommon::ImageViewId;
35using VideoCommon::ImageViewType; 36using VideoCommon::ImageViewType;
36using VideoCommon::NUM_RT; 37using VideoCommon::NUM_RT;
37using VideoCommon::Region2D; 38using VideoCommon::Region2D;
38using VideoCommon::RenderTargets; 39using VideoCommon::RenderTargets;
39using VideoCommon::SlotVector;
40 40
41struct FormatProperties { 41struct FormatProperties {
42 GLenum compatibility_class; 42 GLenum compatibility_class;
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 31001d142..e5e1e3ab6 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -368,7 +368,7 @@ u32 BufferCacheRuntime::GetStorageBufferAlignment() const {
368 return static_cast<u32>(device.GetStorageBufferAlignment()); 368 return static_cast<u32>(device.GetStorageBufferAlignment());
369} 369}
370 370
371void BufferCacheRuntime::TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept { 371void BufferCacheRuntime::TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept {
372 for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) { 372 for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) {
373 it->ResetUsageTracking(); 373 it->ResetUsageTracking();
374 } 374 }
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index e273f4988..efe960258 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -81,7 +81,7 @@ public:
81 ComputePassDescriptorQueue& compute_pass_descriptor_queue, 81 ComputePassDescriptorQueue& compute_pass_descriptor_queue,
82 DescriptorPool& descriptor_pool); 82 DescriptorPool& descriptor_pool);
83 83
84 void TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept; 84 void TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept;
85 85
86 void Finish(); 86 void Finish();
87 87
@@ -181,7 +181,6 @@ struct BufferCacheParams {
181 static constexpr bool NEEDS_BIND_STORAGE_INDEX = false; 181 static constexpr bool NEEDS_BIND_STORAGE_INDEX = false;
182 static constexpr bool USE_MEMORY_MAPS = true; 182 static constexpr bool USE_MEMORY_MAPS = true;
183 static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = false; 183 static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = false;
184 static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
185 static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = true; 184 static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = true;
186}; 185};
187 186
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 0dbde65d6..aaeb5ef93 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -20,11 +20,11 @@ struct ResolutionScalingInfo;
20 20
21namespace Vulkan { 21namespace Vulkan {
22 22
23using Common::SlotVector;
23using VideoCommon::ImageId; 24using VideoCommon::ImageId;
24using VideoCommon::NUM_RT; 25using VideoCommon::NUM_RT;
25using VideoCommon::Region2D; 26using VideoCommon::Region2D;
26using VideoCommon::RenderTargets; 27using VideoCommon::RenderTargets;
27using VideoCommon::SlotVector;
28using VideoCore::Surface::PixelFormat; 28using VideoCore::Surface::PixelFormat;
29 29
30class BlitImageHelper; 30class BlitImageHelper;
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index e7b910121..da98a634b 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -21,6 +21,7 @@
21#include "common/lru_cache.h" 21#include "common/lru_cache.h"
22#include "common/polyfill_ranges.h" 22#include "common/polyfill_ranges.h"
23#include "common/scratch_buffer.h" 23#include "common/scratch_buffer.h"
24#include "common/slot_vector.h"
24#include "common/thread_worker.h" 25#include "common/thread_worker.h"
25#include "video_core/compatible_formats.h" 26#include "video_core/compatible_formats.h"
26#include "video_core/control/channel_state_cache.h" 27#include "video_core/control/channel_state_cache.h"
@@ -32,7 +33,6 @@
32#include "video_core/texture_cache/image_info.h" 33#include "video_core/texture_cache/image_info.h"
33#include "video_core/texture_cache/image_view_base.h" 34#include "video_core/texture_cache/image_view_base.h"
34#include "video_core/texture_cache/render_targets.h" 35#include "video_core/texture_cache/render_targets.h"
35#include "video_core/texture_cache/slot_vector.h"
36#include "video_core/texture_cache/types.h" 36#include "video_core/texture_cache/types.h"
37#include "video_core/textures/texture.h" 37#include "video_core/textures/texture.h"
38 38
@@ -451,16 +451,16 @@ private:
451 struct PendingDownload { 451 struct PendingDownload {
452 bool is_swizzle; 452 bool is_swizzle;
453 size_t async_buffer_id; 453 size_t async_buffer_id;
454 SlotId object_id; 454 Common::SlotId object_id;
455 }; 455 };
456 456
457 SlotVector<Image> slot_images; 457 Common::SlotVector<Image> slot_images;
458 SlotVector<ImageMapView> slot_map_views; 458 Common::SlotVector<ImageMapView> slot_map_views;
459 SlotVector<ImageView> slot_image_views; 459 Common::SlotVector<ImageView> slot_image_views;
460 SlotVector<ImageAlloc> slot_image_allocs; 460 Common::SlotVector<ImageAlloc> slot_image_allocs;
461 SlotVector<Sampler> slot_samplers; 461 Common::SlotVector<Sampler> slot_samplers;
462 SlotVector<Framebuffer> slot_framebuffers; 462 Common::SlotVector<Framebuffer> slot_framebuffers;
463 SlotVector<BufferDownload> slot_buffer_downloads; 463 Common::SlotVector<BufferDownload> slot_buffer_downloads;
464 464
465 // TODO: This data structure is not optimal and it should be reworked 465 // TODO: This data structure is not optimal and it should be reworked
466 466
diff --git a/src/video_core/texture_cache/types.h b/src/video_core/texture_cache/types.h
index 0453456b4..07c304386 100644
--- a/src/video_core/texture_cache/types.h
+++ b/src/video_core/texture_cache/types.h
@@ -5,21 +5,21 @@
5 5
6#include "common/common_funcs.h" 6#include "common/common_funcs.h"
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "video_core/texture_cache/slot_vector.h" 8#include "common/slot_vector.h"
9 9
10namespace VideoCommon { 10namespace VideoCommon {
11 11
12constexpr size_t NUM_RT = 8; 12constexpr size_t NUM_RT = 8;
13constexpr size_t MAX_MIP_LEVELS = 14; 13constexpr size_t MAX_MIP_LEVELS = 14;
14 14
15constexpr SlotId CORRUPT_ID{0xfffffffe}; 15constexpr Common::SlotId CORRUPT_ID{0xfffffffe};
16 16
17using ImageId = SlotId; 17using ImageId = Common::SlotId;
18using ImageMapId = SlotId; 18using ImageMapId = Common::SlotId;
19using ImageViewId = SlotId; 19using ImageViewId = Common::SlotId;
20using ImageAllocId = SlotId; 20using ImageAllocId = Common::SlotId;
21using SamplerId = SlotId; 21using SamplerId = Common::SlotId;
22using FramebufferId = SlotId; 22using FramebufferId = Common::SlotId;
23 23
24/// Fake image ID for null image views 24/// Fake image ID for null image views
25constexpr ImageId NULL_IMAGE_ID{0}; 25constexpr ImageId NULL_IMAGE_ID{0};
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index dff380cca..fdf3ac846 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -32,9 +32,14 @@ struct Client::Impl {
32 Impl(std::string host_, std::string username_, std::string token_) 32 Impl(std::string host_, std::string username_, std::string token_)
33 : host{std::move(host_)}, username{std::move(username_)}, token{std::move(token_)} { 33 : host{std::move(host_)}, username{std::move(username_)}, token{std::move(token_)} {
34 std::scoped_lock lock{jwt_cache.mutex}; 34 std::scoped_lock lock{jwt_cache.mutex};
35 if (username == jwt_cache.username && token == jwt_cache.token) { 35 if (this->username == jwt_cache.username && this->token == jwt_cache.token) {
36 jwt = jwt_cache.jwt; 36 jwt = jwt_cache.jwt;
37 } 37 }
38
39 // Normalize host expression
40 if (!this->host.empty() && this->host.back() == '/') {
41 static_cast<void>(this->host.pop_back());
42 }
38 } 43 }
39 44
40 /// A generic function handles POST, GET and DELETE request together 45 /// A generic function handles POST, GET and DELETE request together
@@ -71,18 +76,16 @@ struct Client::Impl {
71 const std::string& jwt_ = "", const std::string& username_ = "", 76 const std::string& jwt_ = "", const std::string& username_ = "",
72 const std::string& token_ = "") { 77 const std::string& token_ = "") {
73 if (cli == nullptr) { 78 if (cli == nullptr) {
74 cli = std::make_unique<httplib::Client>(host); 79 cli = std::make_unique<httplib::Client>(host.c_str());
80 cli->set_connection_timeout(TIMEOUT_SECONDS);
81 cli->set_read_timeout(TIMEOUT_SECONDS);
82 cli->set_write_timeout(TIMEOUT_SECONDS);
75 } 83 }
76
77 if (!cli->is_valid()) { 84 if (!cli->is_valid()) {
78 LOG_ERROR(WebService, "Client is invalid, skipping request!"); 85 LOG_ERROR(WebService, "Invalid URL {}", host + path);
79 return {}; 86 return WebResult{WebResult::Code::InvalidURL, "Invalid URL", ""};
80 } 87 }
81 88
82 cli->set_connection_timeout(TIMEOUT_SECONDS);
83 cli->set_read_timeout(TIMEOUT_SECONDS);
84 cli->set_write_timeout(TIMEOUT_SECONDS);
85
86 httplib::Headers params; 89 httplib::Headers params;
87 if (!jwt_.empty()) { 90 if (!jwt_.empty()) {
88 params = { 91 params = {
@@ -107,15 +110,15 @@ struct Client::Impl {
107 request.headers = params; 110 request.headers = params;
108 request.body = data; 111 request.body = data;
109 112
110 httplib::Response response; 113 httplib::Result result = cli->send(request);
111 httplib::Error error;
112 114
113 if (!cli->send(request, response, error)) { 115 if (!result) {
114 LOG_ERROR(WebService, "{} to {} returned null (httplib Error: {})", method, host + path, 116 LOG_ERROR(WebService, "{} to {} returned null", method, host + path);
115 httplib::to_string(error));
116 return WebResult{WebResult::Code::LibError, "Null response", ""}; 117 return WebResult{WebResult::Code::LibError, "Null response", ""};
117 } 118 }
118 119
120 httplib::Response response = result.value();
121
119 if (response.status >= 400) { 122 if (response.status >= 400) {
120 LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path, 123 LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path,
121 response.status); 124 response.status);
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp
index 3d18670ce..3f68de12d 100644
--- a/src/yuzu/configuration/configure_hotkeys.cpp
+++ b/src/yuzu/configuration/configure_hotkeys.cpp
@@ -45,15 +45,23 @@ ConfigureHotkeys::ConfigureHotkeys(Core::HID::HIDCore& hid_core, QWidget* parent
45 45
46 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); 46 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
47 47
48 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); 48 connect(timeout_timer.get(), &QTimer::timeout, [this] {
49 const bool is_button_pressed = pressed_buttons != Core::HID::NpadButton::None ||
50 pressed_home_button || pressed_capture_button;
51 SetPollingResult(!is_button_pressed);
52 });
49 53
50 connect(poll_timer.get(), &QTimer::timeout, [this] { 54 connect(poll_timer.get(), &QTimer::timeout, [this] {
51 const auto buttons = controller->GetNpadButtons(); 55 pressed_buttons |= controller->GetNpadButtons().raw;
52 const auto home_pressed = controller->GetHomeButtons().home != 0; 56 pressed_home_button |= this->controller->GetHomeButtons().home != 0;
53 const auto capture_pressed = controller->GetCaptureButtons().capture != 0; 57 pressed_capture_button |= this->controller->GetCaptureButtons().capture != 0;
54 if (home_pressed || capture_pressed) { 58 if (pressed_buttons != Core::HID::NpadButton::None || pressed_home_button ||
55 SetPollingResult(buttons.raw, false); 59 pressed_capture_button) {
56 return; 60 const QString button_name =
61 GetButtonCombinationName(pressed_buttons, pressed_home_button,
62 pressed_capture_button) +
63 QStringLiteral("...");
64 model->setData(button_model_index, button_name);
57 } 65 }
58 }); 66 });
59 RetranslateUI(); 67 RetranslateUI();
@@ -154,16 +162,14 @@ void ConfigureHotkeys::ConfigureController(QModelIndex index) {
154 162
155 const auto previous_key = model->data(index); 163 const auto previous_key = model->data(index);
156 164
157 input_setter = [this, index, previous_key](const Core::HID::NpadButton button, 165 input_setter = [this, index, previous_key](const bool cancel) {
158 const bool cancel) {
159 if (cancel) { 166 if (cancel) {
160 model->setData(index, previous_key); 167 model->setData(index, previous_key);
161 return; 168 return;
162 } 169 }
163 const auto home_pressed = this->controller->GetHomeButtons().home != 0; 170
164 const auto capture_pressed = this->controller->GetCaptureButtons().capture != 0;
165 const QString button_string = 171 const QString button_string =
166 GetButtonCombinationName(button, home_pressed, capture_pressed); 172 GetButtonCombinationName(pressed_buttons, pressed_home_button, pressed_capture_button);
167 173
168 const auto [key_sequence_used, used_action] = IsUsedControllerKey(button_string); 174 const auto [key_sequence_used, used_action] = IsUsedControllerKey(button_string);
169 175
@@ -177,17 +183,22 @@ void ConfigureHotkeys::ConfigureController(QModelIndex index) {
177 } 183 }
178 }; 184 };
179 185
186 button_model_index = index;
187 pressed_buttons = Core::HID::NpadButton::None;
188 pressed_home_button = false;
189 pressed_capture_button = false;
190
180 model->setData(index, tr("[waiting]")); 191 model->setData(index, tr("[waiting]"));
181 timeout_timer->start(2500); // Cancel after 2.5 seconds 192 timeout_timer->start(2500); // Cancel after 2.5 seconds
182 poll_timer->start(200); // Check for new inputs every 200ms 193 poll_timer->start(100); // Check for new inputs every 100ms
183 // We need to disable configuration to be able to read npad buttons 194 // We need to disable configuration to be able to read npad buttons
184 controller->DisableConfiguration(); 195 controller->DisableConfiguration();
185} 196}
186 197
187void ConfigureHotkeys::SetPollingResult(Core::HID::NpadButton button, const bool cancel) { 198void ConfigureHotkeys::SetPollingResult(const bool cancel) {
188 timeout_timer->stop(); 199 timeout_timer->stop();
189 poll_timer->stop(); 200 poll_timer->stop();
190 (*input_setter)(button, cancel); 201 (*input_setter)(cancel);
191 // Re-Enable configuration 202 // Re-Enable configuration
192 controller->EnableConfiguration(); 203 controller->EnableConfiguration();
193 204
diff --git a/src/yuzu/configuration/configure_hotkeys.h b/src/yuzu/configuration/configure_hotkeys.h
index 5fd1bcbfe..20ea3b515 100644
--- a/src/yuzu/configuration/configure_hotkeys.h
+++ b/src/yuzu/configuration/configure_hotkeys.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include <QStandardItemModel>
7#include <QWidget> 8#include <QWidget>
8 9
9namespace Common { 10namespace Common {
@@ -54,14 +55,20 @@ private:
54 void RestoreControllerHotkey(QModelIndex index); 55 void RestoreControllerHotkey(QModelIndex index);
55 void RestoreHotkey(QModelIndex index); 56 void RestoreHotkey(QModelIndex index);
56 57
58 void SetPollingResult(bool cancel);
59 QString GetButtonCombinationName(Core::HID::NpadButton button, bool home, bool capture) const;
60
57 std::unique_ptr<Ui::ConfigureHotkeys> ui; 61 std::unique_ptr<Ui::ConfigureHotkeys> ui;
58 62
59 QStandardItemModel* model; 63 QStandardItemModel* model;
60 64
61 void SetPollingResult(Core::HID::NpadButton button, bool cancel); 65 bool pressed_home_button;
62 QString GetButtonCombinationName(Core::HID::NpadButton button, bool home, bool capture) const; 66 bool pressed_capture_button;
67 QModelIndex button_model_index;
68 Core::HID::NpadButton pressed_buttons;
69
63 Core::HID::EmulatedController* controller; 70 Core::HID::EmulatedController* controller;
64 std::unique_ptr<QTimer> timeout_timer; 71 std::unique_ptr<QTimer> timeout_timer;
65 std::unique_ptr<QTimer> poll_timer; 72 std::unique_ptr<QTimer> poll_timer;
66 std::optional<std::function<void(Core::HID::NpadButton, bool)>> input_setter; 73 std::optional<std::function<void(bool)>> input_setter;
67}; 74};
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp
index c8e871151..f3c91586c 100644
--- a/src/yuzu/configuration/configure_ui.cpp
+++ b/src/yuzu/configuration/configure_ui.cpp
@@ -248,82 +248,22 @@ void ConfigureUi::RetranslateUI() {
248} 248}
249 249
250void ConfigureUi::InitializeLanguageComboBox() { 250void ConfigureUi::InitializeLanguageComboBox() {
251 // This is a list of lexicographically sorted languages, only the available translations are
252 // shown to the user.
253 static const struct {
254 const QString name;
255 const char* id;
256 } languages[] = {
257 // clang-format off
258 {QStringLiteral(u"Bahasa Indonesia"), "id"}, // Indonesian
259 {QStringLiteral(u"Bahasa Melayu"), "ms"}, // Malay
260 {QStringLiteral(u"Catal\u00E0"), "ca"}, // Catalan
261 {QStringLiteral(u"\u010Ce\u0161tina"), "cs"}, // Czech
262 {QStringLiteral(u"Dansk"), "da"}, // Danish
263 {QStringLiteral(u"Deutsch"), "de"}, // German
264 {QStringLiteral(u"English"), "en"}, // English
265 {QStringLiteral(u"Espa\u00F1ol"), "es"}, // Spanish
266 {QStringLiteral(u"Fran\u00E7ais"), "fr"}, // French
267 {QStringLiteral(u"Hrvatski"), "hr"}, // Croatian
268 {QStringLiteral(u"Italiano"), "it"}, // Italian
269 {QStringLiteral(u"Magyar"), "hu"}, // Hungarian
270 {QStringLiteral(u"Nederlands"), "nl"}, // Dutch
271 {QStringLiteral(u"Norsk bokm\u00E5l"), "nb"}, // Norwegian
272 {QStringLiteral(u"Polski"), "pl"}, // Polish
273 {QStringLiteral(u"Portugu\u00EAs"), "pt_PT"}, // Portuguese
274 {QStringLiteral(u"Portugu\u00EAs (Brasil)"), "pt_BR"}, // Portuguese (Brazil)
275 {QStringLiteral(u"Rom\u00E2n\u0103"), "ro"}, // Romanian
276 {QStringLiteral(u"Srpski"), "sr"}, // Serbian
277 {QStringLiteral(u"Suomi"), "fi"}, // Finnish
278 {QStringLiteral(u"Svenska"), "sv"}, // Swedish
279 {QStringLiteral(u"Ti\u1EBFng Vi\u1EC7t"), "vi"}, // Vietnamese
280 {QStringLiteral(u"Ti\u1EBFng Vi\u1EC7t (Vi\u1EC7t Nam)"), "vi_VN"}, // Vietnamese
281 {QStringLiteral(u"T\u00FCrk\u00E7e"), "tr_TR"}, // Turkish
282 {QStringLiteral(u"\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC"), "el"}, // Greek
283 {QStringLiteral(u"\u0420\u0443\u0441\u0441\u043A\u0438\u0439"), "ru_RU"}, // Russian
284 {QStringLiteral(u"\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430"),
285 "uk"}, // Ukrainian
286 {QStringLiteral(u"\u0627\u0644\u0639\u0631\u0628\u064A\u0629"), "ar"}, // Arabic
287 {QStringLiteral(u"\u0641\u0627\u0631\u0633\u06CC"), "fa"}, // Farsi
288 {QStringLiteral(u"\uD55C\uAD6D\uC5B4"), "ko_KR"}, // Korean
289 {QStringLiteral(u"\u65E5\u672C\u8A9E"), "ja_JP"}, // Japanese
290 {QStringLiteral(u"\u7B80\u4F53\u4E2D\u6587"), "zh_CN"}, // Simplified Chinese
291 {QStringLiteral(u"\u7E41\u9AD4\u4E2D\u6587"), "zh_TW"}, // Traditional Chinese
292 // clang-format on
293 };
294 ui->language_combobox->addItem(tr("<System>"), QString{}); 251 ui->language_combobox->addItem(tr("<System>"), QString{});
295 QDir languages_dir{QStringLiteral(":/languages")}; 252 ui->language_combobox->addItem(tr("English"), QStringLiteral("en"));
296 QStringList language_files = languages_dir.entryList(); 253 QDirIterator it(QStringLiteral(":/languages"), QDirIterator::NoIteratorFlags);
297 for (const auto& lang : languages) { 254 while (it.hasNext()) {
298 if (QString::fromLatin1(lang.id) == QStringLiteral("en")) { 255 QString locale = it.next();
299 ui->language_combobox->addItem(lang.name, QStringLiteral("en"));
300 language_files.removeOne(QStringLiteral("en.qm"));
301 continue;
302 }
303 for (int i = 0; i < language_files.size(); ++i) {
304 QString locale = language_files[i];
305 locale.truncate(locale.lastIndexOf(QLatin1Char{'.'}));
306 if (QString::fromLatin1(lang.id) == locale) {
307 ui->language_combobox->addItem(lang.name, locale);
308 language_files.removeAt(i);
309 break;
310 }
311 }
312 }
313 // Anything remaining will be at the bottom
314 for (const QString& file : language_files) {
315 LOG_CRITICAL(Frontend, "Unexpected Language File: {}", file.toStdString());
316 QString locale = file;
317 locale.truncate(locale.lastIndexOf(QLatin1Char{'.'})); 256 locale.truncate(locale.lastIndexOf(QLatin1Char{'.'}));
318 const QString language_name = QLocale::languageToString(QLocale(locale).language()); 257 locale.remove(0, locale.lastIndexOf(QLatin1Char{'/'}) + 1);
319 const QString lang = QStringLiteral("%1 [%2]").arg(language_name, locale); 258 const QString lang = QLocale::languageToString(QLocale(locale).language());
320 ui->language_combobox->addItem(lang, locale); 259 const QString country = QLocale::countryToString(QLocale(locale).country());
260 ui->language_combobox->addItem(QStringLiteral("%1 (%2)").arg(lang, country), locale);
321 } 261 }
322 262
323 // Unlike other configuration changes, interface language changes need to be reflected on the 263 // Unlike other configuration changes, interface language changes need to be reflected on the
324 // interface immediately. This is done by passing a signal to the main window, and then 264 // interface immediately. This is done by passing a signal to the main window, and then
325 // retranslating when passing back. 265 // retranslating when passing back.
326 connect(ui->language_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, 266 connect(ui->language_combobox, qOverload<int>(&QComboBox::currentIndexChanged), this,
327 &ConfigureUi::OnLanguageChanged); 267 &ConfigureUi::OnLanguageChanged);
328} 268}
329 269
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp
index b7693ad0d..170f14684 100644
--- a/src/yuzu/hotkeys.cpp
+++ b/src/yuzu/hotkeys.cpp
@@ -193,8 +193,7 @@ void ControllerShortcut::ControllerUpdateEvent(Core::HID::ControllerTriggerType
193 if (!Settings::values.controller_navigation) { 193 if (!Settings::values.controller_navigation) {
194 return; 194 return;
195 } 195 }
196 if (button_sequence.npad.raw == Core::HID::NpadButton::None && 196 if (button_sequence.npad.raw == Core::HID::NpadButton::None) {
197 button_sequence.capture.raw == 0 && button_sequence.home.raw == 0) {
198 return; 197 return;
199 } 198 }
200 199