summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/command_generator.cpp9
-rw-r--r--src/common/settings.h2
-rw-r--r--src/core/file_sys/content_archive.cpp1
-rw-r--r--src/core/hle/service/bcat/backend/boxcat.cpp3
-rw-r--r--src/input_common/main.cpp4
-rw-r--r--src/tests/video_core/buffer_base.cpp2
-rw-r--r--src/video_core/buffer_cache/buffer_base.h19
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h292
-rw-r--r--src/video_core/dma_pusher.cpp10
-rw-r--r--src/video_core/engines/maxwell_dma.cpp36
-rw-r--r--src/video_core/engines/maxwell_dma.h17
-rw-r--r--src/video_core/fence_manager.h7
-rw-r--r--src/video_core/gpu.cpp5
-rw-r--r--src/video_core/rasterizer_interface.h8
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp21
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h14
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp21
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h14
-rw-r--r--src/video_core/texture_cache/types.h4
-rw-r--r--src/web_service/web_backend.cpp10
-rw-r--r--src/yuzu/configuration/config.cpp2
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp6
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h1
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui18
-rw-r--r--src/yuzu/main.cpp16
25 files changed, 416 insertions, 126 deletions
diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp
index b99d0fc91..45b2eef52 100644
--- a/src/audio_core/command_generator.cpp
+++ b/src/audio_core/command_generator.cpp
@@ -42,6 +42,15 @@ void ApplyMix(std::span<s32> output, std::span<const s32> input, s32 gain, s32 s
42 42
43s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, float gain, float delta, 43s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, float gain, float delta,
44 s32 sample_count) { 44 s32 sample_count) {
45 // XC2 passes in NaN mix volumes, causing further issues as we handle everything as s32 rather
46 // than float, so the NaN propogation is lost. As the samples get further modified for
47 // volume etc, they can get out of NaN range, so a later heuristic for catching this is
48 // more difficult. Handle it here by setting these samples to silence.
49 if (std::isnan(gain)) {
50 gain = 0.0f;
51 delta = 0.0f;
52 }
53
45 s32 x = 0; 54 s32 x = 0;
46 for (s32 i = 0; i < sample_count; i++) { 55 for (s32 i = 0; i < sample_count; i++) {
47 x = static_cast<s32>(static_cast<float>(input[i]) * gain); 56 x = static_cast<s32>(static_cast<float>(input[i]) * gain);
diff --git a/src/common/settings.h b/src/common/settings.h
index d2e91a2c9..71d0f864f 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -330,7 +330,7 @@ struct Values {
330 Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"}; 330 Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"};
331 Setting<bool> accelerate_astc{true, "accelerate_astc"}; 331 Setting<bool> accelerate_astc{true, "accelerate_astc"};
332 Setting<bool> use_vsync{true, "use_vsync"}; 332 Setting<bool> use_vsync{true, "use_vsync"};
333 Setting<bool> disable_fps_limit{false, "disable_fps_limit"}; 333 BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"};
334 Setting<bool> use_assembly_shaders{false, "use_assembly_shaders"}; 334 Setting<bool> use_assembly_shaders{false, "use_assembly_shaders"};
335 Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; 335 Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
336 Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; 336 Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 24eff210f..7019a7a68 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -5,7 +5,6 @@
5#include <algorithm> 5#include <algorithm>
6#include <cstring> 6#include <cstring>
7#include <optional> 7#include <optional>
8#include <ranges>
9#include <utility> 8#include <utility>
10 9
11#include "common/logging/log.h" 10#include "common/logging/log.h"
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp
index dc15cf58b..7ca7f2aac 100644
--- a/src/core/hle/service/bcat/backend/boxcat.cpp
+++ b/src/core/hle/service/bcat/backend/boxcat.cpp
@@ -7,6 +7,9 @@
7#ifdef __GNUC__ 7#ifdef __GNUC__
8#pragma GCC diagnostic push 8#pragma GCC diagnostic push
9#pragma GCC diagnostic ignored "-Wshadow" 9#pragma GCC diagnostic ignored "-Wshadow"
10#ifndef __clang__
11#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
12#endif
10#endif 13#endif
11#include <httplib.h> 14#include <httplib.h>
12#include <mbedtls/sha256.h> 15#include <mbedtls/sha256.h>
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 7399c3648..8de3d4520 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -294,8 +294,8 @@ void InputSubsystem::ReloadInputDevices() {
294 impl->udp->ReloadSockets(); 294 impl->udp->ReloadSockets();
295} 295}
296 296
297std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers( 297std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers([
298 Polling::DeviceType type) const { 298 [maybe_unused]] Polling::DeviceType type) const {
299#ifdef HAVE_SDL2 299#ifdef HAVE_SDL2
300 return impl->sdl->GetPollers(type); 300 return impl->sdl->GetPollers(type);
301#else 301#else
diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp
index edced69bb..9f5a54de4 100644
--- a/src/tests/video_core/buffer_base.cpp
+++ b/src/tests/video_core/buffer_base.cpp
@@ -536,7 +536,7 @@ TEST_CASE("BufferBase: Cached write downloads") {
536 REQUIRE(rasterizer.Count() == 63); 536 REQUIRE(rasterizer.Count() == 63);
537 buffer.MarkRegionAsGpuModified(c + PAGE, PAGE); 537 buffer.MarkRegionAsGpuModified(c + PAGE, PAGE);
538 int num = 0; 538 int num = 0;
539 buffer.ForEachDownloadRange(c, WORD, [&](u64 offset, u64 size) { ++num; }); 539 buffer.ForEachDownloadRangeAndClear(c, WORD, [&](u64 offset, u64 size) { ++num; });
540 buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; }); 540 buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; });
541 REQUIRE(num == 0); 541 REQUIRE(num == 0);
542 REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE)); 542 REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE));
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h
index b121d36a3..c3318095c 100644
--- a/src/video_core/buffer_cache/buffer_base.h
+++ b/src/video_core/buffer_cache/buffer_base.h
@@ -226,19 +226,24 @@ public:
226 /// Call 'func' for each CPU modified range and unmark those pages as CPU modified 226 /// Call 'func' for each CPU modified range and unmark those pages as CPU modified
227 template <typename Func> 227 template <typename Func>
228 void ForEachUploadRange(VAddr query_cpu_range, u64 size, Func&& func) { 228 void ForEachUploadRange(VAddr query_cpu_range, u64 size, Func&& func) {
229 ForEachModifiedRange<Type::CPU>(query_cpu_range, size, func); 229 ForEachModifiedRange<Type::CPU>(query_cpu_range, size, true, func);
230 } 230 }
231 231
232 /// Call 'func' for each GPU modified range and unmark those pages as GPU modified 232 /// Call 'func' for each GPU modified range and unmark those pages as GPU modified
233 template <typename Func> 233 template <typename Func>
234 void ForEachDownloadRange(VAddr query_cpu_range, u64 size, Func&& func) { 234 void ForEachDownloadRange(VAddr query_cpu_range, u64 size, bool clear, Func&& func) {
235 ForEachModifiedRange<Type::GPU>(query_cpu_range, size, func); 235 ForEachModifiedRange<Type::GPU>(query_cpu_range, size, clear, func);
236 }
237
238 template <typename Func>
239 void ForEachDownloadRangeAndClear(VAddr query_cpu_range, u64 size, Func&& func) {
240 ForEachModifiedRange<Type::GPU>(query_cpu_range, size, true, func);
236 } 241 }
237 242
238 /// Call 'func' for each GPU modified range and unmark those pages as GPU modified 243 /// Call 'func' for each GPU modified range and unmark those pages as GPU modified
239 template <typename Func> 244 template <typename Func>
240 void ForEachDownloadRange(Func&& func) { 245 void ForEachDownloadRange(Func&& func) {
241 ForEachModifiedRange<Type::GPU>(cpu_addr, SizeBytes(), func); 246 ForEachModifiedRange<Type::GPU>(cpu_addr, SizeBytes(), true, func);
242 } 247 }
243 248
244 /// Mark buffer as picked 249 /// Mark buffer as picked
@@ -415,7 +420,7 @@ private:
415 * @param func Function to call for each turned off region 420 * @param func Function to call for each turned off region
416 */ 421 */
417 template <Type type, typename Func> 422 template <Type type, typename Func>
418 void ForEachModifiedRange(VAddr query_cpu_range, s64 size, Func&& func) { 423 void ForEachModifiedRange(VAddr query_cpu_range, s64 size, bool clear, Func&& func) {
419 static_assert(type != Type::Untracked); 424 static_assert(type != Type::Untracked);
420 425
421 const s64 difference = query_cpu_range - cpu_addr; 426 const s64 difference = query_cpu_range - cpu_addr;
@@ -467,7 +472,9 @@ private:
467 bits = (bits << left_offset) >> left_offset; 472 bits = (bits << left_offset) >> left_offset;
468 473
469 const u64 current_word = state_words[word_index] & bits; 474 const u64 current_word = state_words[word_index] & bits;
470 state_words[word_index] &= ~bits; 475 if (clear) {
476 state_words[word_index] &= ~bits;
477 }
471 478
472 if constexpr (type == Type::CPU) { 479 if constexpr (type == Type::CPU) {
473 const u64 current_bits = untracked_words[word_index] & bits; 480 const u64 current_bits = untracked_words[word_index] & bits;
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index cad7f902d..2871682f6 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -15,6 +15,7 @@
15#include <vector> 15#include <vector>
16 16
17#include <boost/container/small_vector.hpp> 17#include <boost/container/small_vector.hpp>
18#include <boost/icl/interval_set.hpp>
18 19
19#include "common/common_types.h" 20#include "common/common_types.h"
20#include "common/div_ceil.h" 21#include "common/div_ceil.h"
@@ -77,6 +78,9 @@ class BufferCache {
77 using Runtime = typename P::Runtime; 78 using Runtime = typename P::Runtime;
78 using Buffer = typename P::Buffer; 79 using Buffer = typename P::Buffer;
79 80
81 using IntervalSet = boost::icl::interval_set<VAddr>;
82 using IntervalType = typename IntervalSet::interval_type;
83
80 struct Empty {}; 84 struct Empty {};
81 85
82 struct OverlapResult { 86 struct OverlapResult {
@@ -148,18 +152,26 @@ public:
148 /// Return true when there are uncommitted buffers to be downloaded 152 /// Return true when there are uncommitted buffers to be downloaded
149 [[nodiscard]] bool HasUncommittedFlushes() const noexcept; 153 [[nodiscard]] bool HasUncommittedFlushes() const noexcept;
150 154
155 void AccumulateFlushes();
156
151 /// Return true when the caller should wait for async downloads 157 /// Return true when the caller should wait for async downloads
152 [[nodiscard]] bool ShouldWaitAsyncFlushes() const noexcept; 158 [[nodiscard]] bool ShouldWaitAsyncFlushes() const noexcept;
153 159
154 /// Commit asynchronous downloads 160 /// Commit asynchronous downloads
155 void CommitAsyncFlushes(); 161 void CommitAsyncFlushes();
162 void CommitAsyncFlushesHigh();
156 163
157 /// Pop asynchronous downloads 164 /// Pop asynchronous downloads
158 void PopAsyncFlushes(); 165 void PopAsyncFlushes();
159 166
167 [[nodiscard]] bool DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount);
168
160 /// Return true when a CPU region is modified from the GPU 169 /// Return true when a CPU region is modified from the GPU
161 [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size); 170 [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size);
162 171
172 /// Return true when a CPU region is modified from the CPU
173 [[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size);
174
163 std::mutex mutex; 175 std::mutex mutex;
164 176
165private: 177private:
@@ -190,6 +202,36 @@ private:
190 } 202 }
191 } 203 }
192 204
205 template <typename Func>
206 void ForEachWrittenRange(VAddr cpu_addr, u64 size, Func&& func) {
207 const VAddr start_address = cpu_addr;
208 const VAddr end_address = start_address + size;
209 const VAddr search_base =
210 static_cast<VAddr>(std::min<s64>(0LL, static_cast<s64>(start_address - size)));
211 const IntervalType search_interval{search_base, search_base + 1};
212 auto it = common_ranges.lower_bound(search_interval);
213 if (it == common_ranges.end()) {
214 it = common_ranges.begin();
215 }
216 for (; it != common_ranges.end(); it++) {
217 VAddr inter_addr_end = it->upper();
218 VAddr inter_addr = it->lower();
219 if (inter_addr >= end_address) {
220 break;
221 }
222 if (inter_addr_end <= start_address) {
223 continue;
224 }
225 if (inter_addr_end > end_address) {
226 inter_addr_end = end_address;
227 }
228 if (inter_addr < start_address) {
229 inter_addr = start_address;
230 }
231 func(inter_addr, inter_addr_end);
232 }
233 }
234
193 static bool IsRangeGranular(VAddr cpu_addr, size_t size) { 235 static bool IsRangeGranular(VAddr cpu_addr, size_t size) {
194 return (cpu_addr & ~Core::Memory::PAGE_MASK) == 236 return (cpu_addr & ~Core::Memory::PAGE_MASK) ==
195 ((cpu_addr + size) & ~Core::Memory::PAGE_MASK); 237 ((cpu_addr + size) & ~Core::Memory::PAGE_MASK);
@@ -272,8 +314,6 @@ private:
272 314
273 void DeleteBuffer(BufferId buffer_id); 315 void DeleteBuffer(BufferId buffer_id);
274 316
275 void ReplaceBufferDownloads(BufferId old_buffer_id, BufferId new_buffer_id);
276
277 void NotifyBufferDeletion(); 317 void NotifyBufferDeletion();
278 318
279 [[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr) const; 319 [[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr) const;
@@ -327,9 +367,9 @@ private:
327 367
328 std::vector<BufferId> cached_write_buffer_ids; 368 std::vector<BufferId> cached_write_buffer_ids;
329 369
330 // TODO: This data structure is not optimal and it should be reworked 370 IntervalSet uncommitted_ranges;
331 std::vector<BufferId> uncommitted_downloads; 371 IntervalSet common_ranges;
332 std::deque<std::vector<BufferId>> committed_downloads; 372 std::deque<IntervalSet> committed_ranges;
333 373
334 size_t immediate_buffer_capacity = 0; 374 size_t immediate_buffer_capacity = 0;
335 std::unique_ptr<u8[]> immediate_buffer_alloc; 375 std::unique_ptr<u8[]> immediate_buffer_alloc;
@@ -352,6 +392,7 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
352 // Ensure the first slot is used for the null buffer 392 // Ensure the first slot is used for the null buffer
353 void(slot_buffers.insert(runtime, NullBufferParams{})); 393 void(slot_buffers.insert(runtime, NullBufferParams{}));
354 deletion_iterator = slot_buffers.end(); 394 deletion_iterator = slot_buffers.end();
395 common_ranges.clear();
355} 396}
356 397
357template <class P> 398template <class P>
@@ -422,6 +463,68 @@ void BufferCache<P>::DownloadMemory(VAddr cpu_addr, u64 size) {
422} 463}
423 464
424template <class P> 465template <class P>
466bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) {
467 const std::optional<VAddr> cpu_src_address = gpu_memory.GpuToCpuAddress(src_address);
468 const std::optional<VAddr> cpu_dest_address = gpu_memory.GpuToCpuAddress(dest_address);
469 if (!cpu_src_address || !cpu_dest_address) {
470 return false;
471 }
472 const bool source_dirty = IsRegionGpuModified(*cpu_src_address, amount);
473 const bool dest_dirty = IsRegionGpuModified(*cpu_dest_address, amount);
474 if (!source_dirty && !dest_dirty) {
475 return false;
476 }
477
478 const IntervalType subtract_interval{*cpu_dest_address, *cpu_dest_address + amount};
479 uncommitted_ranges.subtract(subtract_interval);
480 for (auto& interval_set : committed_ranges) {
481 interval_set.subtract(subtract_interval);
482 }
483
484 BufferId buffer_a;
485 BufferId buffer_b;
486 do {
487 has_deleted_buffers = false;
488 buffer_a = FindBuffer(*cpu_src_address, static_cast<u32>(amount));
489 buffer_b = FindBuffer(*cpu_dest_address, static_cast<u32>(amount));
490 } while (has_deleted_buffers);
491 auto& src_buffer = slot_buffers[buffer_a];
492 auto& dest_buffer = slot_buffers[buffer_b];
493 SynchronizeBuffer(src_buffer, *cpu_src_address, static_cast<u32>(amount));
494 SynchronizeBuffer(dest_buffer, *cpu_dest_address, static_cast<u32>(amount));
495 std::array copies{BufferCopy{
496 .src_offset = src_buffer.Offset(*cpu_src_address),
497 .dst_offset = dest_buffer.Offset(*cpu_dest_address),
498 .size = amount,
499 }};
500
501 boost::container::small_vector<IntervalType, 4> tmp_intervals;
502 auto mirror = [&](VAddr base_address, VAddr base_address_end) {
503 const u64 size = base_address_end - base_address;
504 const VAddr diff = base_address - *cpu_src_address;
505 const VAddr new_base_address = *cpu_dest_address + diff;
506 const IntervalType add_interval{new_base_address, new_base_address + size};
507 uncommitted_ranges.add(add_interval);
508 tmp_intervals.push_back(add_interval);
509 };
510 ForEachWrittenRange(*cpu_src_address, amount, mirror);
511 // This subtraction in this order is important for overlapping copies.
512 common_ranges.subtract(subtract_interval);
513 for (const IntervalType add_interval : tmp_intervals) {
514 common_ranges.add(add_interval);
515 }
516
517 runtime.CopyBuffer(dest_buffer, src_buffer, copies);
518 if (source_dirty) {
519 dest_buffer.MarkRegionAsGpuModified(*cpu_dest_address, amount);
520 }
521 std::vector<u8> tmp_buffer(amount);
522 cpu_memory.ReadBlockUnsafe(*cpu_src_address, tmp_buffer.data(), amount);
523 cpu_memory.WriteBlockUnsafe(*cpu_dest_address, tmp_buffer.data(), amount);
524 return true;
525}
526
527template <class P>
425void BufferCache<P>::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, 528void BufferCache<P>::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr,
426 u32 size) { 529 u32 size) {
427 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); 530 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
@@ -547,29 +650,30 @@ void BufferCache<P>::FlushCachedWrites() {
547 650
548template <class P> 651template <class P>
549bool BufferCache<P>::HasUncommittedFlushes() const noexcept { 652bool BufferCache<P>::HasUncommittedFlushes() const noexcept {
550 return !uncommitted_downloads.empty(); 653 return !uncommitted_ranges.empty() || !committed_ranges.empty();
551} 654}
552 655
553template <class P> 656template <class P>
554bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept { 657void BufferCache<P>::AccumulateFlushes() {
555 return !committed_downloads.empty() && !committed_downloads.front().empty(); 658 if (Settings::values.gpu_accuracy.GetValue() != Settings::GPUAccuracy::High) {
659 uncommitted_ranges.clear();
660 return;
661 }
662 if (uncommitted_ranges.empty()) {
663 return;
664 }
665 committed_ranges.emplace_back(std::move(uncommitted_ranges));
556} 666}
557 667
558template <class P> 668template <class P>
559void BufferCache<P>::CommitAsyncFlushes() { 669bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
560 // This is intentionally passing the value by copy 670 return false;
561 committed_downloads.push_front(uncommitted_downloads);
562 uncommitted_downloads.clear();
563} 671}
564 672
565template <class P> 673template <class P>
566void BufferCache<P>::PopAsyncFlushes() { 674void BufferCache<P>::CommitAsyncFlushesHigh() {
567 if (committed_downloads.empty()) { 675 AccumulateFlushes();
568 return; 676 if (committed_ranges.empty()) {
569 }
570 auto scope_exit_pop_download = detail::ScopeExit([this] { committed_downloads.pop_back(); });
571 const std::span<const BufferId> download_ids = committed_downloads.back();
572 if (download_ids.empty()) {
573 return; 677 return;
574 } 678 }
575 MICROPROFILE_SCOPE(GPU_DownloadMemory); 679 MICROPROFILE_SCOPE(GPU_DownloadMemory);
@@ -577,20 +681,43 @@ void BufferCache<P>::PopAsyncFlushes() {
577 boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads; 681 boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads;
578 u64 total_size_bytes = 0; 682 u64 total_size_bytes = 0;
579 u64 largest_copy = 0; 683 u64 largest_copy = 0;
580 for (const BufferId buffer_id : download_ids) { 684 for (const IntervalSet& intervals : committed_ranges) {
581 slot_buffers[buffer_id].ForEachDownloadRange([&](u64 range_offset, u64 range_size) { 685 for (auto& interval : intervals) {
582 downloads.push_back({ 686 const std::size_t size = interval.upper() - interval.lower();
583 BufferCopy{ 687 const VAddr cpu_addr = interval.lower();
584 .src_offset = range_offset, 688 ForEachBufferInRange(cpu_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
585 .dst_offset = total_size_bytes, 689 boost::container::small_vector<BufferCopy, 1> copies;
586 .size = range_size, 690 buffer.ForEachDownloadRangeAndClear(
587 }, 691 cpu_addr, size, [&](u64 range_offset, u64 range_size) {
588 buffer_id, 692 const VAddr buffer_addr = buffer.CpuAddr();
693 const auto add_download = [&](VAddr start, VAddr end) {
694 const u64 new_offset = start - buffer_addr;
695 const u64 new_size = end - start;
696 downloads.push_back({
697 BufferCopy{
698 .src_offset = new_offset,
699 .dst_offset = total_size_bytes,
700 .size = new_size,
701 },
702 buffer_id,
703 });
704 // Align up to avoid cache conflicts
705 constexpr u64 align = 256ULL;
706 constexpr u64 mask = ~(align - 1ULL);
707 total_size_bytes += (new_size + align - 1) & mask;
708 largest_copy = std::max(largest_copy, new_size);
709 };
710
711 const VAddr start_address = buffer_addr + range_offset;
712 const VAddr end_address = start_address + range_size;
713 ForEachWrittenRange(start_address, range_size, add_download);
714 const IntervalType subtract_interval{start_address, end_address};
715 common_ranges.subtract(subtract_interval);
716 });
589 }); 717 });
590 total_size_bytes += range_size; 718 }
591 largest_copy = std::max(largest_copy, range_size);
592 });
593 } 719 }
720 committed_ranges.clear();
594 if (downloads.empty()) { 721 if (downloads.empty()) {
595 return; 722 return;
596 } 723 }
@@ -623,6 +750,19 @@ void BufferCache<P>::PopAsyncFlushes() {
623} 750}
624 751
625template <class P> 752template <class P>
753void BufferCache<P>::CommitAsyncFlushes() {
754 if (Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::High) {
755 CommitAsyncFlushesHigh();
756 } else {
757 uncommitted_ranges.clear();
758 committed_ranges.clear();
759 }
760}
761
762template <class P>
763void BufferCache<P>::PopAsyncFlushes() {}
764
765template <class P>
626bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) { 766bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
627 const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE); 767 const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE);
628 for (u64 page = addr >> PAGE_BITS; page < page_end;) { 768 for (u64 page = addr >> PAGE_BITS; page < page_end;) {
@@ -642,6 +782,25 @@ bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
642} 782}
643 783
644template <class P> 784template <class P>
785bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) {
786 const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE);
787 for (u64 page = addr >> PAGE_BITS; page < page_end;) {
788 const BufferId image_id = page_table[page];
789 if (!image_id) {
790 ++page;
791 continue;
792 }
793 Buffer& buffer = slot_buffers[image_id];
794 if (buffer.IsRegionCpuModified(addr, size)) {
795 return true;
796 }
797 const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes();
798 page = Common::DivCeil(end_addr, PAGE_SIZE);
799 }
800 return false;
801}
802
803template <class P>
645void BufferCache<P>::BindHostIndexBuffer() { 804void BufferCache<P>::BindHostIndexBuffer() {
646 Buffer& buffer = slot_buffers[index_buffer.buffer_id]; 805 Buffer& buffer = slot_buffers[index_buffer.buffer_id];
647 TouchBuffer(buffer); 806 TouchBuffer(buffer);
@@ -649,7 +808,9 @@ void BufferCache<P>::BindHostIndexBuffer() {
649 const u32 size = index_buffer.size; 808 const u32 size = index_buffer.size;
650 SynchronizeBuffer(buffer, index_buffer.cpu_addr, size); 809 SynchronizeBuffer(buffer, index_buffer.cpu_addr, size);
651 if constexpr (HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) { 810 if constexpr (HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
652 runtime.BindIndexBuffer(buffer, offset, size); 811 const u32 new_offset = offset + maxwell3d.regs.index_array.first *
812 maxwell3d.regs.index_array.FormatSizeInBytes();
813 runtime.BindIndexBuffer(buffer, new_offset, size);
653 } else { 814 } else {
654 runtime.BindIndexBuffer(maxwell3d.regs.draw.topology, maxwell3d.regs.index_array.format, 815 runtime.BindIndexBuffer(maxwell3d.regs.draw.topology, maxwell3d.regs.index_array.format,
655 maxwell3d.regs.index_array.first, maxwell3d.regs.index_array.count, 816 maxwell3d.regs.index_array.first, maxwell3d.regs.index_array.count,
@@ -863,7 +1024,7 @@ void BufferCache<P>::UpdateIndexBuffer() {
863 const GPUVAddr gpu_addr_end = index_array.EndAddress(); 1024 const GPUVAddr gpu_addr_end = index_array.EndAddress();
864 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr_begin); 1025 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr_begin);
865 const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin); 1026 const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
866 const u32 draw_size = index_array.count * index_array.FormatSizeInBytes(); 1027 const u32 draw_size = (index_array.count + index_array.first) * index_array.FormatSizeInBytes();
867 const u32 size = std::min(address_size, draw_size); 1028 const u32 size = std::min(address_size, draw_size);
868 if (size == 0 || !cpu_addr) { 1029 if (size == 0 || !cpu_addr) {
869 index_buffer = NULL_BINDING; 1030 index_buffer = NULL_BINDING;
@@ -1010,16 +1171,16 @@ void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 s
1010 Buffer& buffer = slot_buffers[buffer_id]; 1171 Buffer& buffer = slot_buffers[buffer_id];
1011 buffer.MarkRegionAsGpuModified(cpu_addr, size); 1172 buffer.MarkRegionAsGpuModified(cpu_addr, size);
1012 1173
1013 const bool is_accuracy_high = Settings::IsGPULevelHigh(); 1174 const IntervalType base_interval{cpu_addr, cpu_addr + size};
1175 common_ranges.add(base_interval);
1176
1177 const bool is_accuracy_high =
1178 Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::High;
1014 const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); 1179 const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue();
1015 if (!is_accuracy_high || !is_async) { 1180 if (!is_async && !is_accuracy_high) {
1016 return;
1017 }
1018 if (std::ranges::find(uncommitted_downloads, buffer_id) != uncommitted_downloads.end()) {
1019 // Already inserted
1020 return; 1181 return;
1021 } 1182 }
1022 uncommitted_downloads.push_back(buffer_id); 1183 uncommitted_ranges.add(base_interval);
1023} 1184}
1024 1185
1025template <class P> 1186template <class P>
@@ -1103,7 +1264,6 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id,
1103 if (!copies.empty()) { 1264 if (!copies.empty()) {
1104 runtime.CopyBuffer(slot_buffers[new_buffer_id], overlap, copies); 1265 runtime.CopyBuffer(slot_buffers[new_buffer_id], overlap, copies);
1105 } 1266 }
1106 ReplaceBufferDownloads(overlap_id, new_buffer_id);
1107 DeleteBuffer(overlap_id); 1267 DeleteBuffer(overlap_id);
1108} 1268}
1109 1269
@@ -1244,14 +1404,28 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si
1244 boost::container::small_vector<BufferCopy, 1> copies; 1404 boost::container::small_vector<BufferCopy, 1> copies;
1245 u64 total_size_bytes = 0; 1405 u64 total_size_bytes = 0;
1246 u64 largest_copy = 0; 1406 u64 largest_copy = 0;
1247 buffer.ForEachDownloadRange(cpu_addr, size, [&](u64 range_offset, u64 range_size) { 1407 buffer.ForEachDownloadRangeAndClear(cpu_addr, size, [&](u64 range_offset, u64 range_size) {
1248 copies.push_back(BufferCopy{ 1408 const VAddr buffer_addr = buffer.CpuAddr();
1249 .src_offset = range_offset, 1409 const auto add_download = [&](VAddr start, VAddr end) {
1250 .dst_offset = total_size_bytes, 1410 const u64 new_offset = start - buffer_addr;
1251 .size = range_size, 1411 const u64 new_size = end - start;
1252 }); 1412 copies.push_back(BufferCopy{
1253 total_size_bytes += range_size; 1413 .src_offset = new_offset,
1254 largest_copy = std::max(largest_copy, range_size); 1414 .dst_offset = total_size_bytes,
1415 .size = new_size,
1416 });
1417 // Align up to avoid cache conflicts
1418 constexpr u64 align = 256ULL;
1419 constexpr u64 mask = ~(align - 1ULL);
1420 total_size_bytes += (new_size + align - 1) & mask;
1421 largest_copy = std::max(largest_copy, new_size);
1422 };
1423
1424 const VAddr start_address = buffer_addr + range_offset;
1425 const VAddr end_address = start_address + range_size;
1426 ForEachWrittenRange(start_address, range_size, add_download);
1427 const IntervalType subtract_interval{start_address, end_address};
1428 common_ranges.subtract(subtract_interval);
1255 }); 1429 });
1256 if (total_size_bytes == 0) { 1430 if (total_size_bytes == 0) {
1257 return; 1431 return;
@@ -1316,18 +1490,6 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id) {
1316} 1490}
1317 1491
1318template <class P> 1492template <class P>
1319void BufferCache<P>::ReplaceBufferDownloads(BufferId old_buffer_id, BufferId new_buffer_id) {
1320 const auto replace = [old_buffer_id, new_buffer_id](std::vector<BufferId>& buffers) {
1321 std::ranges::replace(buffers, old_buffer_id, new_buffer_id);
1322 if (auto it = std::ranges::find(buffers, new_buffer_id); it != buffers.end()) {
1323 buffers.erase(std::remove(it + 1, buffers.end(), new_buffer_id), buffers.end());
1324 }
1325 };
1326 replace(uncommitted_downloads);
1327 std::ranges::for_each(committed_downloads, replace);
1328}
1329
1330template <class P>
1331void BufferCache<P>::NotifyBufferDeletion() { 1493void BufferCache<P>::NotifyBufferDeletion() {
1332 if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { 1494 if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
1333 dirty_uniform_buffers.fill(~u32{0}); 1495 dirty_uniform_buffers.fill(~u32{0});
@@ -1349,15 +1511,9 @@ typename BufferCache<P>::Binding BufferCache<P>::StorageBufferBinding(GPUVAddr s
1349 if (!cpu_addr || size == 0) { 1511 if (!cpu_addr || size == 0) {
1350 return NULL_BINDING; 1512 return NULL_BINDING;
1351 } 1513 }
1352 // HACK(Rodrigo): This is the number of bytes bound in host beyond the guest API's range.
1353 // It exists due to some games like Astral Chain operate out of bounds.
1354 // Binding the whole map range would be technically correct, but games have large maps that make
1355 // this approach unaffordable for now.
1356 static constexpr u32 arbitrary_extra_bytes = 0xc000;
1357 const u32 bytes_to_map_end = static_cast<u32>(gpu_memory.BytesToMapEnd(gpu_addr));
1358 const Binding binding{ 1514 const Binding binding{
1359 .cpu_addr = *cpu_addr, 1515 .cpu_addr = *cpu_addr,
1360 .size = std::min(size + arbitrary_extra_bytes, bytes_to_map_end), 1516 .size = size,
1361 .buffer_id = BufferId{}, 1517 .buffer_id = BufferId{},
1362 }; 1518 };
1363 return binding; 1519 return binding;
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 8b33c04ab..8d28bd884 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -4,6 +4,7 @@
4 4
5#include "common/cityhash.h" 5#include "common/cityhash.h"
6#include "common/microprofile.h" 6#include "common/microprofile.h"
7#include "common/settings.h"
7#include "core/core.h" 8#include "core/core.h"
8#include "core/memory.h" 9#include "core/memory.h"
9#include "video_core/dma_pusher.h" 10#include "video_core/dma_pusher.h"
@@ -76,8 +77,13 @@ bool DmaPusher::Step() {
76 77
77 // Push buffer non-empty, read a word 78 // Push buffer non-empty, read a word
78 command_headers.resize(command_list_header.size); 79 command_headers.resize(command_list_header.size);
79 gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(), 80 if (Settings::IsGPULevelHigh()) {
80 command_list_header.size * sizeof(u32)); 81 gpu.MemoryManager().ReadBlock(dma_get, command_headers.data(),
82 command_list_header.size * sizeof(u32));
83 } else {
84 gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(),
85 command_list_header.size * sizeof(u32));
86 }
81 } 87 }
82 for (std::size_t index = 0; index < command_headers.size();) { 88 for (std::size_t index = 0; index < command_headers.size();) {
83 const CommandHeader& command_header = command_headers[index]; 89 const CommandHeader& command_header = command_headers[index];
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 2ee980bab..24481952b 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -21,6 +21,10 @@ MaxwellDMA::MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_)
21 21
22MaxwellDMA::~MaxwellDMA() = default; 22MaxwellDMA::~MaxwellDMA() = default;
23 23
24void MaxwellDMA::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
25 rasterizer = rasterizer_;
26}
27
24void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) { 28void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
25 ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register"); 29 ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register");
26 30
@@ -44,7 +48,6 @@ void MaxwellDMA::Launch() {
44 48
45 // TODO(Subv): Perform more research and implement all features of this engine. 49 // TODO(Subv): Perform more research and implement all features of this engine.
46 const LaunchDMA& launch = regs.launch_dma; 50 const LaunchDMA& launch = regs.launch_dma;
47 ASSERT(launch.remap_enable == 0);
48 ASSERT(launch.semaphore_type == LaunchDMA::SemaphoreType::NONE); 51 ASSERT(launch.semaphore_type == LaunchDMA::SemaphoreType::NONE);
49 ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE); 52 ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE);
50 ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED); 53 ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED);
@@ -77,11 +80,29 @@ void MaxwellDMA::CopyPitchToPitch() {
77 // When `multi_line_enable` bit is disabled the copy is performed as if we were copying a 1D 80 // When `multi_line_enable` bit is disabled the copy is performed as if we were copying a 1D
78 // buffer of length `line_length_in`. 81 // buffer of length `line_length_in`.
79 // Otherwise we copy a 2D image of dimensions (line_length_in, line_count). 82 // Otherwise we copy a 2D image of dimensions (line_length_in, line_count).
83 auto& accelerate = rasterizer->AccessAccelerateDMA();
80 if (!regs.launch_dma.multi_line_enable) { 84 if (!regs.launch_dma.multi_line_enable) {
81 memory_manager.CopyBlock(regs.offset_out, regs.offset_in, regs.line_length_in); 85 const bool is_buffer_clear = regs.launch_dma.remap_enable != 0 &&
86 regs.remap_const.dst_x == RemapConst::Swizzle::CONST_A;
87 // TODO: allow multisized components.
88 if (is_buffer_clear) {
89 ASSERT(regs.remap_const.component_size_minus_one == 3);
90 std::vector<u32> tmp_buffer(regs.line_length_in, regs.remap_consta_value);
91 memory_manager.WriteBlock(regs.offset_out, reinterpret_cast<u8*>(tmp_buffer.data()),
92 regs.line_length_in * sizeof(u32));
93 return;
94 }
95 UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0);
96 if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) {
97 std::vector<u8> tmp_buffer(regs.line_length_in);
98 memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), regs.line_length_in);
99 memory_manager.WriteBlock(regs.offset_out, tmp_buffer.data(), regs.line_length_in);
100 }
82 return; 101 return;
83 } 102 }
84 103
104 UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0);
105
85 // Perform a line-by-line copy. 106 // Perform a line-by-line copy.
86 // We're going to take a subrect of size (line_length_in, line_count) from the source rectangle. 107 // We're going to take a subrect of size (line_length_in, line_count) from the source rectangle.
87 // There is no need to manually flush/invalidate the regions because CopyBlock does that for us. 108 // There is no need to manually flush/invalidate the regions because CopyBlock does that for us.
@@ -105,6 +126,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
105 } 126 }
106 127
107 // Deswizzle the input and copy it over. 128 // Deswizzle the input and copy it over.
129 UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0);
108 const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in; 130 const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in;
109 const Parameters& src_params = regs.src_params; 131 const Parameters& src_params = regs.src_params;
110 const u32 width = src_params.width; 132 const u32 width = src_params.width;
@@ -134,6 +156,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
134 156
135void MaxwellDMA::CopyPitchToBlockLinear() { 157void MaxwellDMA::CopyPitchToBlockLinear() {
136 UNIMPLEMENTED_IF_MSG(regs.dst_params.block_size.width != 0, "Block width is not one"); 158 UNIMPLEMENTED_IF_MSG(regs.dst_params.block_size.width != 0, "Block width is not one");
159 UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0);
137 160
138 const auto& dst_params = regs.dst_params; 161 const auto& dst_params = regs.dst_params;
139 const u32 bytes_per_pixel = regs.pitch_in / regs.line_length_in; 162 const u32 bytes_per_pixel = regs.pitch_in / regs.line_length_in;
@@ -156,13 +179,8 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
156 write_buffer.resize(dst_size); 179 write_buffer.resize(dst_size);
157 } 180 }
158 181
159 if (Settings::IsGPULevelExtreme()) { 182 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
160 memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); 183 memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
161 memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
162 } else {
163 memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size);
164 memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
165 }
166 184
167 // If the input is linear and the output is tiled, swizzle the input and copy it over. 185 // If the input is linear and the output is tiled, swizzle the input and copy it over.
168 if (regs.dst_params.block_size.depth > 0) { 186 if (regs.dst_params.block_size.depth > 0) {
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index c77f02a22..4ed0d0996 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -21,8 +21,18 @@ namespace Tegra {
21class MemoryManager; 21class MemoryManager;
22} 22}
23 23
24namespace VideoCore {
25class RasterizerInterface;
26}
27
24namespace Tegra::Engines { 28namespace Tegra::Engines {
25 29
30class AccelerateDMAInterface {
31public:
32 /// Write the value to the register identified by method.
33 virtual bool BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) = 0;
34};
35
26/** 36/**
27 * This engine is known as gk104_copy. Documentation can be found in: 37 * This engine is known as gk104_copy. Documentation can be found in:
28 * https://github.com/NVIDIA/open-gpu-doc/blob/master/classes/dma-copy/clb0b5.h 38 * https://github.com/NVIDIA/open-gpu-doc/blob/master/classes/dma-copy/clb0b5.h
@@ -187,6 +197,8 @@ public:
187 }; 197 };
188 static_assert(sizeof(RemapConst) == 12); 198 static_assert(sizeof(RemapConst) == 12);
189 199
200 void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
201
190 explicit MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_); 202 explicit MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_);
191 ~MaxwellDMA() override; 203 ~MaxwellDMA() override;
192 204
@@ -213,6 +225,7 @@ private:
213 Core::System& system; 225 Core::System& system;
214 226
215 MemoryManager& memory_manager; 227 MemoryManager& memory_manager;
228 VideoCore::RasterizerInterface* rasterizer;
216 229
217 std::vector<u8> read_buffer; 230 std::vector<u8> read_buffer;
218 std::vector<u8> write_buffer; 231 std::vector<u8> write_buffer;
@@ -240,7 +253,9 @@ private:
240 u32 pitch_out; 253 u32 pitch_out;
241 u32 line_length_in; 254 u32 line_length_in;
242 u32 line_count; 255 u32 line_count;
243 u32 reserved06[0xb8]; 256 u32 reserved06[0xb6];
257 u32 remap_consta_value;
258 u32 remap_constb_value;
244 RemapConst remap_const; 259 RemapConst remap_const;
245 Parameters dst_params; 260 Parameters dst_params;
246 u32 reserved07[0x1]; 261 u32 reserved07[0x1];
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h
index f055b61e9..34dc6c596 100644
--- a/src/video_core/fence_manager.h
+++ b/src/video_core/fence_manager.h
@@ -8,6 +8,7 @@
8#include <queue> 8#include <queue>
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/settings.h"
11#include "core/core.h" 12#include "core/core.h"
12#include "video_core/delayed_destruction_ring.h" 13#include "video_core/delayed_destruction_ring.h"
13#include "video_core/gpu.h" 14#include "video_core/gpu.h"
@@ -53,6 +54,12 @@ public:
53 delayed_destruction_ring.Tick(); 54 delayed_destruction_ring.Tick();
54 } 55 }
55 56
57 // Unlike other fences, this one doesn't
58 void SignalOrdering() {
59 std::scoped_lock lock{buffer_cache.mutex};
60 buffer_cache.AccumulateFlushes();
61 }
62
56 void SignalSemaphore(GPUVAddr addr, u32 value) { 63 void SignalSemaphore(GPUVAddr addr, u32 value) {
57 TryReleasePendingFences(); 64 TryReleasePendingFences();
58 const bool should_flush = ShouldFlush(); 65 const bool should_flush = ShouldFlush();
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 35cc561be..ff024f530 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -50,6 +50,7 @@ void GPU::BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer_) {
50 maxwell_3d->BindRasterizer(rasterizer); 50 maxwell_3d->BindRasterizer(rasterizer);
51 fermi_2d->BindRasterizer(rasterizer); 51 fermi_2d->BindRasterizer(rasterizer);
52 kepler_compute->BindRasterizer(rasterizer); 52 kepler_compute->BindRasterizer(rasterizer);
53 maxwell_dma->BindRasterizer(rasterizer);
53} 54}
54 55
55Engines::Maxwell3D& GPU::Maxwell3D() { 56Engines::Maxwell3D& GPU::Maxwell3D() {
@@ -268,11 +269,13 @@ void GPU::CallPullerMethod(const MethodCall& method_call) {
268 case BufferMethods::SemaphoreAddressHigh: 269 case BufferMethods::SemaphoreAddressHigh:
269 case BufferMethods::SemaphoreAddressLow: 270 case BufferMethods::SemaphoreAddressLow:
270 case BufferMethods::SemaphoreSequence: 271 case BufferMethods::SemaphoreSequence:
271 case BufferMethods::RefCnt:
272 case BufferMethods::UnkCacheFlush: 272 case BufferMethods::UnkCacheFlush:
273 case BufferMethods::WrcacheFlush: 273 case BufferMethods::WrcacheFlush:
274 case BufferMethods::FenceValue: 274 case BufferMethods::FenceValue:
275 break; 275 break;
276 case BufferMethods::RefCnt:
277 rasterizer->SignalReference();
278 break;
276 case BufferMethods::FenceAction: 279 case BufferMethods::FenceAction:
277 ProcessFenceActionMethod(); 280 ProcessFenceActionMethod();
278 break; 281 break;
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index 0cec4225b..58014c1c3 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -15,7 +15,10 @@
15 15
16namespace Tegra { 16namespace Tegra {
17class MemoryManager; 17class MemoryManager;
18namespace Engines {
19class AccelerateDMAInterface;
18} 20}
21} // namespace Tegra
19 22
20namespace VideoCore { 23namespace VideoCore {
21 24
@@ -63,6 +66,9 @@ public:
63 /// Signal a GPU based syncpoint as a fence 66 /// Signal a GPU based syncpoint as a fence
64 virtual void SignalSyncPoint(u32 value) = 0; 67 virtual void SignalSyncPoint(u32 value) = 0;
65 68
69 /// Signal a GPU based reference as point
70 virtual void SignalReference() = 0;
71
66 /// Release all pending fences. 72 /// Release all pending fences.
67 virtual void ReleaseFences() = 0; 73 virtual void ReleaseFences() = 0;
68 74
@@ -116,6 +122,8 @@ public:
116 return false; 122 return false;
117 } 123 }
118 124
125 [[nodiscard]] virtual Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() = 0;
126
119 /// Attempt to use a faster method to display the framebuffer to screen 127 /// Attempt to use a faster method to display the framebuffer to screen
120 [[nodiscard]] virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config, 128 [[nodiscard]] virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config,
121 VAddr framebuffer_addr, u32 pixel_stride) { 129 VAddr framebuffer_addr, u32 pixel_stride) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 07ad0e205..82c84127a 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -171,7 +171,7 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra
171 buffer_cache_runtime(device), 171 buffer_cache_runtime(device),
172 buffer_cache(*this, maxwell3d, kepler_compute, gpu_memory, cpu_memory_, buffer_cache_runtime), 172 buffer_cache(*this, maxwell3d, kepler_compute, gpu_memory, cpu_memory_, buffer_cache_runtime),
173 shader_cache(*this, emu_window_, gpu, maxwell3d, kepler_compute, gpu_memory, device), 173 shader_cache(*this, emu_window_, gpu, maxwell3d, kepler_compute, gpu_memory, device),
174 query_cache(*this, maxwell3d, gpu_memory), 174 query_cache(*this, maxwell3d, gpu_memory), accelerate_dma(buffer_cache),
175 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache), 175 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache),
176 async_shaders(emu_window_) { 176 async_shaders(emu_window_) {
177 if (device.UseAsynchronousShaders()) { 177 if (device.UseAsynchronousShaders()) {
@@ -634,6 +634,13 @@ void RasterizerOpenGL::SignalSyncPoint(u32 value) {
634 fence_manager.SignalSyncPoint(value); 634 fence_manager.SignalSyncPoint(value);
635} 635}
636 636
637void RasterizerOpenGL::SignalReference() {
638 if (!gpu.IsAsync()) {
639 return;
640 }
641 fence_manager.SignalOrdering();
642}
643
637void RasterizerOpenGL::ReleaseFences() { 644void RasterizerOpenGL::ReleaseFences() {
638 if (!gpu.IsAsync()) { 645 if (!gpu.IsAsync()) {
639 return; 646 return;
@@ -650,6 +657,7 @@ void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
650 657
651void RasterizerOpenGL::WaitForIdle() { 658void RasterizerOpenGL::WaitForIdle() {
652 glMemoryBarrier(GL_ALL_BARRIER_BITS); 659 glMemoryBarrier(GL_ALL_BARRIER_BITS);
660 SignalReference();
653} 661}
654 662
655void RasterizerOpenGL::FragmentBarrier() { 663void RasterizerOpenGL::FragmentBarrier() {
@@ -693,6 +701,10 @@ bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surf
693 return true; 701 return true;
694} 702}
695 703
704Tegra::Engines::AccelerateDMAInterface& RasterizerOpenGL::AccessAccelerateDMA() {
705 return accelerate_dma;
706}
707
696bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config, 708bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
697 VAddr framebuffer_addr, u32 pixel_stride) { 709 VAddr framebuffer_addr, u32 pixel_stride) {
698 if (framebuffer_addr == 0) { 710 if (framebuffer_addr == 0) {
@@ -1388,4 +1400,11 @@ void RasterizerOpenGL::EndTransformFeedback() {
1388 glEndTransformFeedback(); 1400 glEndTransformFeedback();
1389} 1401}
1390 1402
1403AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {}
1404
1405bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) {
1406 std::scoped_lock lock{buffer_cache.mutex};
1407 return buffer_cache.DMACopy(src_address, dest_address, amount);
1408}
1409
1391} // namespace OpenGL 1410} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 482efed7a..ccee9ba33 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -19,6 +19,7 @@
19#include "common/common_types.h" 19#include "common/common_types.h"
20#include "video_core/engines/const_buffer_info.h" 20#include "video_core/engines/const_buffer_info.h"
21#include "video_core/engines/maxwell_3d.h" 21#include "video_core/engines/maxwell_3d.h"
22#include "video_core/engines/maxwell_dma.h"
22#include "video_core/rasterizer_accelerated.h" 23#include "video_core/rasterizer_accelerated.h"
23#include "video_core/rasterizer_interface.h" 24#include "video_core/rasterizer_interface.h"
24#include "video_core/renderer_opengl/gl_buffer_cache.h" 25#include "video_core/renderer_opengl/gl_buffer_cache.h"
@@ -58,6 +59,16 @@ struct BindlessSSBO {
58}; 59};
59static_assert(sizeof(BindlessSSBO) * CHAR_BIT == 128); 60static_assert(sizeof(BindlessSSBO) * CHAR_BIT == 128);
60 61
62class AccelerateDMA : public Tegra::Engines::AccelerateDMAInterface {
63public:
64 explicit AccelerateDMA(BufferCache& buffer_cache);
65
66 bool BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) override;
67
68private:
69 BufferCache& buffer_cache;
70};
71
61class RasterizerOpenGL : public VideoCore::RasterizerAccelerated { 72class RasterizerOpenGL : public VideoCore::RasterizerAccelerated {
62public: 73public:
63 explicit RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, 74 explicit RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
@@ -83,6 +94,7 @@ public:
83 void ModifyGPUMemory(GPUVAddr addr, u64 size) override; 94 void ModifyGPUMemory(GPUVAddr addr, u64 size) override;
84 void SignalSemaphore(GPUVAddr addr, u32 value) override; 95 void SignalSemaphore(GPUVAddr addr, u32 value) override;
85 void SignalSyncPoint(u32 value) override; 96 void SignalSyncPoint(u32 value) override;
97 void SignalReference() override;
86 void ReleaseFences() override; 98 void ReleaseFences() override;
87 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 99 void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
88 void WaitForIdle() override; 100 void WaitForIdle() override;
@@ -93,6 +105,7 @@ public:
93 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, 105 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
94 const Tegra::Engines::Fermi2D::Surface& dst, 106 const Tegra::Engines::Fermi2D::Surface& dst,
95 const Tegra::Engines::Fermi2D::Config& copy_config) override; 107 const Tegra::Engines::Fermi2D::Config& copy_config) override;
108 Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override;
96 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, 109 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
97 u32 pixel_stride) override; 110 u32 pixel_stride) override;
98 void LoadDiskResources(u64 title_id, std::stop_token stop_loading, 111 void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
@@ -233,6 +246,7 @@ private:
233 BufferCache buffer_cache; 246 BufferCache buffer_cache;
234 ShaderCacheOpenGL shader_cache; 247 ShaderCacheOpenGL shader_cache;
235 QueryCache query_cache; 248 QueryCache query_cache;
249 AccelerateDMA accelerate_dma;
236 FenceManagerOpenGL fence_manager; 250 FenceManagerOpenGL fence_manager;
237 251
238 VideoCommon::Shader::AsyncShaders async_shaders; 252 VideoCommon::Shader::AsyncShaders async_shaders;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index bd4d649cc..e378a5679 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -251,7 +251,7 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
251 buffer_cache(*this, maxwell3d, kepler_compute, gpu_memory, cpu_memory_, buffer_cache_runtime), 251 buffer_cache(*this, maxwell3d, kepler_compute, gpu_memory, cpu_memory_, buffer_cache_runtime),
252 pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler, 252 pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler,
253 descriptor_pool, update_descriptor_queue), 253 descriptor_pool, update_descriptor_queue),
254 query_cache{*this, maxwell3d, gpu_memory, device, scheduler}, 254 query_cache{*this, maxwell3d, gpu_memory, device, scheduler}, accelerate_dma{buffer_cache},
255 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler), 255 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler),
256 wfi_event(device.GetLogical().CreateEvent()), async_shaders(emu_window_) { 256 wfi_event(device.GetLogical().CreateEvent()), async_shaders(emu_window_) {
257 scheduler.SetQueryCache(query_cache); 257 scheduler.SetQueryCache(query_cache);
@@ -580,6 +580,13 @@ void RasterizerVulkan::SignalSyncPoint(u32 value) {
580 fence_manager.SignalSyncPoint(value); 580 fence_manager.SignalSyncPoint(value);
581} 581}
582 582
583void RasterizerVulkan::SignalReference() {
584 if (!gpu.IsAsync()) {
585 return;
586 }
587 fence_manager.SignalOrdering();
588}
589
583void RasterizerVulkan::ReleaseFences() { 590void RasterizerVulkan::ReleaseFences() {
584 if (!gpu.IsAsync()) { 591 if (!gpu.IsAsync()) {
585 return; 592 return;
@@ -612,6 +619,7 @@ void RasterizerVulkan::WaitForIdle() {
612 cmdbuf.SetEvent(event, flags); 619 cmdbuf.SetEvent(event, flags);
613 cmdbuf.WaitEvents(event, flags, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, {}, {}, {}); 620 cmdbuf.WaitEvents(event, flags, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, {}, {}, {});
614 }); 621 });
622 SignalReference();
615} 623}
616 624
617void RasterizerVulkan::FragmentBarrier() { 625void RasterizerVulkan::FragmentBarrier() {
@@ -652,6 +660,10 @@ bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surf
652 return true; 660 return true;
653} 661}
654 662
663Tegra::Engines::AccelerateDMAInterface& RasterizerVulkan::AccessAccelerateDMA() {
664 return accelerate_dma;
665}
666
655bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config, 667bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config,
656 VAddr framebuffer_addr, u32 pixel_stride) { 668 VAddr framebuffer_addr, u32 pixel_stride) {
657 if (!framebuffer_addr) { 669 if (!framebuffer_addr) {
@@ -690,6 +702,13 @@ void RasterizerVulkan::FlushWork() {
690 draw_counter = 0; 702 draw_counter = 0;
691} 703}
692 704
705AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {}
706
707bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) {
708 std::scoped_lock lock{buffer_cache.mutex};
709 return buffer_cache.DMACopy(src_address, dest_address, amount);
710}
711
693void RasterizerVulkan::SetupShaderDescriptors( 712void RasterizerVulkan::SetupShaderDescriptors(
694 const std::array<Shader*, Maxwell::MaxShaderProgram>& shaders, bool is_indexed) { 713 const std::array<Shader*, Maxwell::MaxShaderProgram>& shaders, bool is_indexed) {
695 image_view_indices.clear(); 714 image_view_indices.clear();
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 41459c5c5..3a78de258 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -13,6 +13,7 @@
13#include <boost/container/static_vector.hpp> 13#include <boost/container/static_vector.hpp>
14 14
15#include "common/common_types.h" 15#include "common/common_types.h"
16#include "video_core/engines/maxwell_dma.h"
16#include "video_core/rasterizer_accelerated.h" 17#include "video_core/rasterizer_accelerated.h"
17#include "video_core/rasterizer_interface.h" 18#include "video_core/rasterizer_interface.h"
18#include "video_core/renderer_vulkan/blit_image.h" 19#include "video_core/renderer_vulkan/blit_image.h"
@@ -49,6 +50,16 @@ struct VKScreenInfo;
49 50
50class StateTracker; 51class StateTracker;
51 52
53class AccelerateDMA : public Tegra::Engines::AccelerateDMAInterface {
54public:
55 explicit AccelerateDMA(BufferCache& buffer_cache);
56
57 bool BufferCopy(GPUVAddr start_address, GPUVAddr end_address, u64 amount) override;
58
59private:
60 BufferCache& buffer_cache;
61};
62
52class RasterizerVulkan final : public VideoCore::RasterizerAccelerated { 63class RasterizerVulkan final : public VideoCore::RasterizerAccelerated {
53public: 64public:
54 explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, 65 explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
@@ -75,6 +86,7 @@ public:
75 void ModifyGPUMemory(GPUVAddr addr, u64 size) override; 86 void ModifyGPUMemory(GPUVAddr addr, u64 size) override;
76 void SignalSemaphore(GPUVAddr addr, u32 value) override; 87 void SignalSemaphore(GPUVAddr addr, u32 value) override;
77 void SignalSyncPoint(u32 value) override; 88 void SignalSyncPoint(u32 value) override;
89 void SignalReference() override;
78 void ReleaseFences() override; 90 void ReleaseFences() override;
79 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 91 void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
80 void WaitForIdle() override; 92 void WaitForIdle() override;
@@ -85,6 +97,7 @@ public:
85 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, 97 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
86 const Tegra::Engines::Fermi2D::Surface& dst, 98 const Tegra::Engines::Fermi2D::Surface& dst,
87 const Tegra::Engines::Fermi2D::Config& copy_config) override; 99 const Tegra::Engines::Fermi2D::Config& copy_config) override;
100 Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override;
88 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, 101 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
89 u32 pixel_stride) override; 102 u32 pixel_stride) override;
90 103
@@ -185,6 +198,7 @@ private:
185 BufferCache buffer_cache; 198 BufferCache buffer_cache;
186 VKPipelineCache pipeline_cache; 199 VKPipelineCache pipeline_cache;
187 VKQueryCache query_cache; 200 VKQueryCache query_cache;
201 AccelerateDMA accelerate_dma;
188 VKFenceManager fence_manager; 202 VKFenceManager fence_manager;
189 203
190 vk::Event wfi_event; 204 vk::Event wfi_event;
diff --git a/src/video_core/texture_cache/types.h b/src/video_core/texture_cache/types.h
index 9fbdc1ac6..47a11cb2f 100644
--- a/src/video_core/texture_cache/types.h
+++ b/src/video_core/texture_cache/types.h
@@ -133,8 +133,8 @@ struct BufferImageCopy {
133}; 133};
134 134
135struct BufferCopy { 135struct BufferCopy {
136 size_t src_offset; 136 u64 src_offset;
137 size_t dst_offset; 137 u64 dst_offset;
138 size_t size; 138 size_t size;
139}; 139};
140 140
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index e04f7dfc6..b1e02c57a 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -8,7 +8,17 @@
8#include <string> 8#include <string>
9 9
10#include <fmt/format.h> 10#include <fmt/format.h>
11
12#ifdef __GNUC__
13#pragma GCC diagnostic push
14#ifndef __clang__
15#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
16#endif
17#endif
11#include <httplib.h> 18#include <httplib.h>
19#ifdef __GNUC__
20#pragma GCC diagnostic pop
21#endif
12 22
13#include "common/logging/log.h" 23#include "common/logging/log.h"
14#include "web_service/web_backend.h" 24#include "web_service/web_backend.h"
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 87cb9dc93..8c71ad5c1 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -839,7 +839,6 @@ void Config::ReadRendererValues() {
839 ReadGlobalSetting(Settings::values.use_nvdec_emulation); 839 ReadGlobalSetting(Settings::values.use_nvdec_emulation);
840 ReadGlobalSetting(Settings::values.accelerate_astc); 840 ReadGlobalSetting(Settings::values.accelerate_astc);
841 ReadGlobalSetting(Settings::values.use_vsync); 841 ReadGlobalSetting(Settings::values.use_vsync);
842 ReadGlobalSetting(Settings::values.disable_fps_limit);
843 ReadGlobalSetting(Settings::values.use_assembly_shaders); 842 ReadGlobalSetting(Settings::values.use_assembly_shaders);
844 ReadGlobalSetting(Settings::values.use_asynchronous_shaders); 843 ReadGlobalSetting(Settings::values.use_asynchronous_shaders);
845 ReadGlobalSetting(Settings::values.use_fast_gpu_time); 844 ReadGlobalSetting(Settings::values.use_fast_gpu_time);
@@ -1369,7 +1368,6 @@ void Config::SaveRendererValues() {
1369 WriteGlobalSetting(Settings::values.use_nvdec_emulation); 1368 WriteGlobalSetting(Settings::values.use_nvdec_emulation);
1370 WriteGlobalSetting(Settings::values.accelerate_astc); 1369 WriteGlobalSetting(Settings::values.accelerate_astc);
1371 WriteGlobalSetting(Settings::values.use_vsync); 1370 WriteGlobalSetting(Settings::values.use_vsync);
1372 WriteGlobalSetting(Settings::values.disable_fps_limit);
1373 WriteGlobalSetting(Settings::values.use_assembly_shaders); 1371 WriteGlobalSetting(Settings::values.use_assembly_shaders);
1374 WriteGlobalSetting(Settings::values.use_asynchronous_shaders); 1372 WriteGlobalSetting(Settings::values.use_asynchronous_shaders);
1375 WriteGlobalSetting(Settings::values.use_fast_gpu_time); 1373 WriteGlobalSetting(Settings::values.use_fast_gpu_time);
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index 8d13c9857..a9e611125 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -28,7 +28,6 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
28 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); 28 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
29 29
30 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); 30 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
31 ui->disable_fps_limit->setChecked(Settings::values.disable_fps_limit.GetValue());
32 ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue()); 31 ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue());
33 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); 32 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());
34 ui->use_caches_gc->setChecked(Settings::values.use_caches_gc.GetValue()); 33 ui->use_caches_gc->setChecked(Settings::values.use_caches_gc.GetValue());
@@ -59,8 +58,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
59 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, 58 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
60 ui->anisotropic_filtering_combobox); 59 ui->anisotropic_filtering_combobox);
61 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync); 60 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync);
62 ConfigurationShared::ApplyPerGameSetting(&Settings::values.disable_fps_limit,
63 ui->disable_fps_limit, disable_fps_limit);
64 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders, 61 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders,
65 ui->use_assembly_shaders, use_assembly_shaders); 62 ui->use_assembly_shaders, use_assembly_shaders);
66 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders, 63 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders,
@@ -103,7 +100,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
103 if (Settings::IsConfiguringGlobal()) { 100 if (Settings::IsConfiguringGlobal()) {
104 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal()); 101 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
105 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal()); 102 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal());
106 ui->disable_fps_limit->setEnabled(Settings::values.disable_fps_limit.UsingGlobal());
107 ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal()); 103 ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal());
108 ui->use_asynchronous_shaders->setEnabled( 104 ui->use_asynchronous_shaders->setEnabled(
109 Settings::values.use_asynchronous_shaders.UsingGlobal()); 105 Settings::values.use_asynchronous_shaders.UsingGlobal());
@@ -116,8 +112,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
116 } 112 }
117 113
118 ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync); 114 ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync);
119 ConfigurationShared::SetColoredTristate(ui->disable_fps_limit,
120 Settings::values.disable_fps_limit, disable_fps_limit);
121 ConfigurationShared::SetColoredTristate( 115 ConfigurationShared::SetColoredTristate(
122 ui->use_assembly_shaders, Settings::values.use_assembly_shaders, use_assembly_shaders); 116 ui->use_assembly_shaders, Settings::values.use_assembly_shaders, use_assembly_shaders);
123 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders, 117 ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders,
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h
index 6ac5f20ec..9148aacf2 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.h
+++ b/src/yuzu/configuration/configure_graphics_advanced.h
@@ -35,7 +35,6 @@ private:
35 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui; 35 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui;
36 36
37 ConfigurationShared::CheckState use_vsync; 37 ConfigurationShared::CheckState use_vsync;
38 ConfigurationShared::CheckState disable_fps_limit;
39 ConfigurationShared::CheckState use_assembly_shaders; 38 ConfigurationShared::CheckState use_assembly_shaders;
40 ConfigurationShared::CheckState use_asynchronous_shaders; 39 ConfigurationShared::CheckState use_asynchronous_shaders;
41 ConfigurationShared::CheckState use_fast_gpu_time; 40 ConfigurationShared::CheckState use_fast_gpu_time;
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index 18c43629e..ad0840355 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -77,24 +77,6 @@
77 </widget> 77 </widget>
78 </item> 78 </item>
79 <item> 79 <item>
80 <widget class="QCheckBox" name="disable_fps_limit">
81 <property name="enabled">
82 <bool>true</bool>
83 </property>
84 <property name="toolTip">
85 <string>
86 &lt;html&gt;&lt;head/&gt;&lt;body&gt;
87 &lt;p&gt;Presents guest frames as they become available, disabling the FPS limit in most titles.&lt;/p&gt;
88 &lt;p&gt;NOTE: Will cause instabilities.&lt;/p&gt;
89 &lt;/body&gt;&lt;/html&gt;
90 </string>
91 </property>
92 <property name="text">
93 <string>Disable framerate limit (experimental)</string>
94 </property>
95 </widget>
96 </item>
97 <item>
98 <widget class="QCheckBox" name="use_assembly_shaders"> 80 <widget class="QCheckBox" name="use_assembly_shaders">
99 <property name="toolTip"> 81 <property name="toolTip">
100 <string>Enabling this reduces shader stutter. Enables OpenGL assembly shaders on supported Nvidia devices (NV_gpu_program5 is required). This feature is experimental.</string> 82 <string>Enabling this reduces shader stutter. Enables OpenGL assembly shaders on supported Nvidia devices (NV_gpu_program5 is required). This feature is experimental.</string>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 5ed3b90b8..fbd5001e9 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1355,6 +1355,9 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index, S
1355 1355
1356 ConfigureVibration::SetAllVibrationDevices(); 1356 ConfigureVibration::SetAllVibrationDevices();
1357 1357
1358 // Disable fps limit toggle when booting a new title
1359 Settings::values.disable_fps_limit.SetValue(false);
1360
1358 // Save configurations 1361 // Save configurations
1359 UpdateUISettings(); 1362 UpdateUISettings();
1360 game_list->SaveInterfaceLayout(); 1363 game_list->SaveInterfaceLayout();
@@ -1428,8 +1431,10 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index, S
1428 std::filesystem::path{filename.toStdU16String()}.filename()); 1431 std::filesystem::path{filename.toStdU16String()}.filename());
1429 } 1432 }
1430 const bool is_64bit = system.Kernel().CurrentProcess()->Is64BitProcess(); 1433 const bool is_64bit = system.Kernel().CurrentProcess()->Is64BitProcess();
1431 const auto instruction_set_suffix = is_64bit ? " (64-bit)" : " (32-bit)"; 1434 const auto instruction_set_suffix = is_64bit ? tr("(64-bit)") : tr("(32-bit)");
1432 title_name += instruction_set_suffix; 1435 title_name = tr("%1 %2", "%1 is the title name. %2 indicates if the title is 64-bit or 32-bit")
1436 .arg(QString::fromStdString(title_name), instruction_set_suffix)
1437 .toStdString();
1433 LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version); 1438 LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version);
1434 const auto gpu_vendor = system.GPU().Renderer().GetDeviceVendor(); 1439 const auto gpu_vendor = system.GPU().Renderer().GetDeviceVendor();
1435 UpdateWindowTitle(title_name, title_version, gpu_vendor); 1440 UpdateWindowTitle(title_name, title_version, gpu_vendor);
@@ -2913,7 +2918,12 @@ void GMainWindow::UpdateStatusBar() {
2913 } else { 2918 } else {
2914 emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0)); 2919 emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0));
2915 } 2920 }
2916 game_fps_label->setText(tr("Game: %1 FPS").arg(results.average_game_fps, 0, 'f', 0)); 2921 if (Settings::values.disable_fps_limit) {
2922 game_fps_label->setText(
2923 tr("Game: %1 FPS (Limit off)").arg(results.average_game_fps, 0, 'f', 0));
2924 } else {
2925 game_fps_label->setText(tr("Game: %1 FPS").arg(results.average_game_fps, 0, 'f', 0));
2926 }
2917 emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2)); 2927 emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2));
2918 2928
2919 emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue()); 2929 emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue());