summaryrefslogtreecommitdiff
path: root/src/video_core/buffer_cache
diff options
context:
space:
mode:
authorGravatar Fernando Sahmkow2021-07-04 18:08:49 +0200
committerGravatar Fernando Sahmkow2021-07-09 22:20:36 +0200
commit0e4d4b4beba3521dbadfe489b54309ba33dc65f2 (patch)
treeec9087c006f1a2dc65886e3db646020e0ef7f56c /src/video_core/buffer_cache
parentMerge pull request #6573 from lat9nq/cpu-settings-cleanup-2 (diff)
downloadyuzu-0e4d4b4beba3521dbadfe489b54309ba33dc65f2.tar.gz
yuzu-0e4d4b4beba3521dbadfe489b54309ba33dc65f2.tar.xz
yuzu-0e4d4b4beba3521dbadfe489b54309ba33dc65f2.zip
Buffer Cache: Fix High Downloads and don't predownload on Extreme.
Diffstat (limited to 'src/video_core/buffer_cache')
-rw-r--r--src/video_core/buffer_cache/buffer_base.h14
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h195
2 files changed, 120 insertions, 89 deletions
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h
index b121d36a3..a56b4c3a8 100644
--- a/src/video_core/buffer_cache/buffer_base.h
+++ b/src/video_core/buffer_cache/buffer_base.h
@@ -226,19 +226,19 @@ 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 } 236 }
237 237
238 /// Call 'func' for each GPU modified range and unmark those pages as GPU modified 238 /// Call 'func' for each GPU modified range and unmark those pages as GPU modified
239 template <typename Func> 239 template <typename Func>
240 void ForEachDownloadRange(Func&& func) { 240 void ForEachDownloadRange(Func&& func) {
241 ForEachModifiedRange<Type::GPU>(cpu_addr, SizeBytes(), func); 241 ForEachModifiedRange<Type::GPU>(cpu_addr, SizeBytes(), true, func);
242 } 242 }
243 243
244 /// Mark buffer as picked 244 /// Mark buffer as picked
@@ -415,7 +415,7 @@ private:
415 * @param func Function to call for each turned off region 415 * @param func Function to call for each turned off region
416 */ 416 */
417 template <Type type, typename Func> 417 template <Type type, typename Func>
418 void ForEachModifiedRange(VAddr query_cpu_range, s64 size, Func&& func) { 418 void ForEachModifiedRange(VAddr query_cpu_range, s64 size, bool clear, Func&& func) {
419 static_assert(type != Type::Untracked); 419 static_assert(type != Type::Untracked);
420 420
421 const s64 difference = query_cpu_range - cpu_addr; 421 const s64 difference = query_cpu_range - cpu_addr;
@@ -467,7 +467,9 @@ private:
467 bits = (bits << left_offset) >> left_offset; 467 bits = (bits << left_offset) >> left_offset;
468 468
469 const u64 current_word = state_words[word_index] & bits; 469 const u64 current_word = state_words[word_index] & bits;
470 state_words[word_index] &= ~bits; 470 if (clear) {
471 state_words[word_index] &= ~bits;
472 }
471 473
472 if constexpr (type == Type::CPU) { 474 if constexpr (type == Type::CPU) {
473 const u64 current_bits = untracked_words[word_index] & bits; 475 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..d28930e80 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -14,6 +14,7 @@
14#include <unordered_map> 14#include <unordered_map>
15#include <vector> 15#include <vector>
16 16
17#include <boost/icl/interval_set.hpp>
17#include <boost/container/small_vector.hpp> 18#include <boost/container/small_vector.hpp>
18 19
19#include "common/common_types.h" 20#include "common/common_types.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 {
@@ -153,6 +157,7 @@ public:
153 157
154 /// Commit asynchronous downloads 158 /// Commit asynchronous downloads
155 void CommitAsyncFlushes(); 159 void CommitAsyncFlushes();
160 void CommitAsyncFlushesHigh();
156 161
157 /// Pop asynchronous downloads 162 /// Pop asynchronous downloads
158 void PopAsyncFlushes(); 163 void PopAsyncFlushes();
@@ -160,6 +165,9 @@ public:
160 /// Return true when a CPU region is modified from the GPU 165 /// Return true when a CPU region is modified from the GPU
161 [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size); 166 [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size);
162 167
168 /// Return true when a CPU region is modified from the GPU
169 [[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size);
170
163 std::mutex mutex; 171 std::mutex mutex;
164 172
165private: 173private:
@@ -272,8 +280,6 @@ private:
272 280
273 void DeleteBuffer(BufferId buffer_id); 281 void DeleteBuffer(BufferId buffer_id);
274 282
275 void ReplaceBufferDownloads(BufferId old_buffer_id, BufferId new_buffer_id);
276
277 void NotifyBufferDeletion(); 283 void NotifyBufferDeletion();
278 284
279 [[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr) const; 285 [[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr) const;
@@ -328,8 +334,9 @@ private:
328 std::vector<BufferId> cached_write_buffer_ids; 334 std::vector<BufferId> cached_write_buffer_ids;
329 335
330 // TODO: This data structure is not optimal and it should be reworked 336 // TODO: This data structure is not optimal and it should be reworked
331 std::vector<BufferId> uncommitted_downloads; 337 IntervalSet uncommitted_ranges;
332 std::deque<std::vector<BufferId>> committed_downloads; 338 std::deque<IntervalSet> committed_ranges;
339 std::deque<boost::container::small_vector<BufferCopy, 4>> pending_downloads;
333 340
334 size_t immediate_buffer_capacity = 0; 341 size_t immediate_buffer_capacity = 0;
335 std::unique_ptr<u8[]> immediate_buffer_alloc; 342 std::unique_ptr<u8[]> immediate_buffer_alloc;
@@ -547,82 +554,101 @@ void BufferCache<P>::FlushCachedWrites() {
547 554
548template <class P> 555template <class P>
549bool BufferCache<P>::HasUncommittedFlushes() const noexcept { 556bool BufferCache<P>::HasUncommittedFlushes() const noexcept {
550 return !uncommitted_downloads.empty(); 557 return !uncommitted_ranges.empty();
551} 558}
552 559
553template <class P> 560template <class P>
554bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept { 561bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
555 return !committed_downloads.empty() && !committed_downloads.front().empty(); 562 return false;
556}
557
558template <class P>
559void BufferCache<P>::CommitAsyncFlushes() {
560 // This is intentionally passing the value by copy
561 committed_downloads.push_front(uncommitted_downloads);
562 uncommitted_downloads.clear();
563} 563}
564 564
565template <class P> 565template <class P>
566void BufferCache<P>::PopAsyncFlushes() { 566void BufferCache<P>::CommitAsyncFlushesHigh() {
567 if (committed_downloads.empty()) { 567 const IntervalSet& intervals = uncommitted_ranges;
568 return; 568 if (intervals.empty()) {
569 } 569 return;
570 auto scope_exit_pop_download = detail::ScopeExit([this] { committed_downloads.pop_back(); }); 570 }
571 const std::span<const BufferId> download_ids = committed_downloads.back(); 571 MICROPROFILE_SCOPE(GPU_DownloadMemory);
572 if (download_ids.empty()) { 572
573 return; 573 boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads;
574 } 574 u64 total_size_bytes = 0;
575 MICROPROFILE_SCOPE(GPU_DownloadMemory); 575 u64 largest_copy = 0;
576 576 for (auto& interval : intervals) {
577 boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads; 577 const std::size_t size = interval.upper() - interval.lower();
578 u64 total_size_bytes = 0; 578 const VAddr cpu_addr = interval.lower();
579 u64 largest_copy = 0; 579 const VAddr cpu_addr_end = interval.upper();
580 for (const BufferId buffer_id : download_ids) { 580 ForEachBufferInRange(cpu_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
581 slot_buffers[buffer_id].ForEachDownloadRange([&](u64 range_offset, u64 range_size) { 581 boost::container::small_vector<BufferCopy, 1> copies;
582 buffer.ForEachDownloadRange(cpu_addr, size, false, [&](u64 range_offset, u64 range_size) {
583 VAddr cpu_addr_base = buffer.CpuAddr() + range_offset;
584 VAddr cpu_addr_end2 = cpu_addr_base + range_size;
585 const s64 difference = s64(cpu_addr_end2 - cpu_addr_end);
586 cpu_addr_end2 -= u64(std::max<s64>(difference, 0));
587 const s64 difference2 = s64(cpu_addr - cpu_addr_base);
588 cpu_addr_base += u64(std::max<s64>(difference2, 0));
589 const u64 new_size = cpu_addr_end2 - cpu_addr_base;
590 const u64 new_offset = cpu_addr_base - buffer.CpuAddr();
591 ASSERT(!IsRegionCpuModified(cpu_addr_base, new_size));
582 downloads.push_back({ 592 downloads.push_back({
583 BufferCopy{ 593 BufferCopy{
584 .src_offset = range_offset, 594 .src_offset = new_offset,
585 .dst_offset = total_size_bytes, 595 .dst_offset = total_size_bytes,
586 .size = range_size, 596 .size = new_size,
587 }, 597 },
588 buffer_id, 598 buffer_id,
589 }); 599 });
590 total_size_bytes += range_size; 600 total_size_bytes += new_size;
591 largest_copy = std::max(largest_copy, range_size); 601 buffer.UnmarkRegionAsGpuModified(cpu_addr_base, new_size);
592 }); 602 largest_copy = std::max(largest_copy, new_size);
593 } 603 });
594 if (downloads.empty()) { 604 });
595 return; 605 }
596 } 606 if (downloads.empty()) {
597 if constexpr (USE_MEMORY_MAPS) { 607 return;
598 auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes); 608 }
599 for (auto& [copy, buffer_id] : downloads) { 609 if constexpr (USE_MEMORY_MAPS) {
600 // Have in mind the staging buffer offset for the copy 610 auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
601 copy.dst_offset += download_staging.offset; 611 for (auto& [copy, buffer_id] : downloads) {
602 const std::array copies{copy}; 612 // Have in mind the staging buffer offset for the copy
603 runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies); 613 copy.dst_offset += download_staging.offset;
604 } 614 const std::array copies{copy};
605 runtime.Finish(); 615 runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies);
606 for (const auto& [copy, buffer_id] : downloads) { 616 }
607 const Buffer& buffer = slot_buffers[buffer_id]; 617 runtime.Finish();
608 const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset; 618 for (const auto& [copy, buffer_id] : downloads) {
609 // Undo the modified offset 619 const Buffer& buffer = slot_buffers[buffer_id];
610 const u64 dst_offset = copy.dst_offset - download_staging.offset; 620 const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
611 const u8* read_mapped_memory = download_staging.mapped_span.data() + dst_offset; 621 // Undo the modified offset
612 cpu_memory.WriteBlockUnsafe(cpu_addr, read_mapped_memory, copy.size); 622 const u64 dst_offset = copy.dst_offset - download_staging.offset;
613 } 623 const u8* read_mapped_memory = download_staging.mapped_span.data() + dst_offset;
624 cpu_memory.WriteBlockUnsafe(cpu_addr, read_mapped_memory, copy.size);
625 }
626 } else {
627 const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
628 for (const auto& [copy, buffer_id] : downloads) {
629 Buffer& buffer = slot_buffers[buffer_id];
630 buffer.ImmediateDownload(copy.src_offset, immediate_buffer.subspan(0, copy.size));
631 const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
632 cpu_memory.WriteBlockUnsafe(cpu_addr, immediate_buffer.data(), copy.size);
633 }
634 }
635}
636
637template <class P>
638void BufferCache<P>::CommitAsyncFlushes() {
639 if (Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::High) {
640 CommitAsyncFlushesHigh();
614 } else { 641 } else {
615 const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy); 642 uncommitted_ranges.clear();
616 for (const auto& [copy, buffer_id] : downloads) {
617 Buffer& buffer = slot_buffers[buffer_id];
618 buffer.ImmediateDownload(copy.src_offset, immediate_buffer.subspan(0, copy.size));
619 const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
620 cpu_memory.WriteBlockUnsafe(cpu_addr, immediate_buffer.data(), copy.size);
621 }
622 } 643 }
623} 644}
624 645
625template <class P> 646template <class P>
647void BufferCache<P>::PopAsyncFlushes() {
648
649}
650
651template <class P>
626bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) { 652bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
627 const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE); 653 const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE);
628 for (u64 page = addr >> PAGE_BITS; page < page_end;) { 654 for (u64 page = addr >> PAGE_BITS; page < page_end;) {
@@ -642,6 +668,25 @@ bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
642} 668}
643 669
644template <class P> 670template <class P>
671bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) {
672 const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE);
673 for (u64 page = addr >> PAGE_BITS; page < page_end;) {
674 const BufferId image_id = page_table[page];
675 if (!image_id) {
676 ++page;
677 continue;
678 }
679 Buffer& buffer = slot_buffers[image_id];
680 if (buffer.IsRegionCpuModified(addr, size)) {
681 return true;
682 }
683 const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes();
684 page = Common::DivCeil(end_addr, PAGE_SIZE);
685 }
686 return false;
687}
688
689template <class P>
645void BufferCache<P>::BindHostIndexBuffer() { 690void BufferCache<P>::BindHostIndexBuffer() {
646 Buffer& buffer = slot_buffers[index_buffer.buffer_id]; 691 Buffer& buffer = slot_buffers[index_buffer.buffer_id];
647 TouchBuffer(buffer); 692 TouchBuffer(buffer);
@@ -1010,16 +1055,13 @@ void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 s
1010 Buffer& buffer = slot_buffers[buffer_id]; 1055 Buffer& buffer = slot_buffers[buffer_id];
1011 buffer.MarkRegionAsGpuModified(cpu_addr, size); 1056 buffer.MarkRegionAsGpuModified(cpu_addr, size);
1012 1057
1013 const bool is_accuracy_high = Settings::IsGPULevelHigh(); 1058 const bool is_accuracy_high = Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::High;
1014 const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); 1059 const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue();
1015 if (!is_accuracy_high || !is_async) { 1060 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; 1061 return;
1021 } 1062 }
1022 uncommitted_downloads.push_back(buffer_id); 1063 const IntervalType base_interval{cpu_addr, cpu_addr + size};
1064 uncommitted_ranges.add(base_interval);
1023} 1065}
1024 1066
1025template <class P> 1067template <class P>
@@ -1103,7 +1145,6 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id,
1103 if (!copies.empty()) { 1145 if (!copies.empty()) {
1104 runtime.CopyBuffer(slot_buffers[new_buffer_id], overlap, copies); 1146 runtime.CopyBuffer(slot_buffers[new_buffer_id], overlap, copies);
1105 } 1147 }
1106 ReplaceBufferDownloads(overlap_id, new_buffer_id);
1107 DeleteBuffer(overlap_id); 1148 DeleteBuffer(overlap_id);
1108} 1149}
1109 1150
@@ -1244,7 +1285,7 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si
1244 boost::container::small_vector<BufferCopy, 1> copies; 1285 boost::container::small_vector<BufferCopy, 1> copies;
1245 u64 total_size_bytes = 0; 1286 u64 total_size_bytes = 0;
1246 u64 largest_copy = 0; 1287 u64 largest_copy = 0;
1247 buffer.ForEachDownloadRange(cpu_addr, size, [&](u64 range_offset, u64 range_size) { 1288 buffer.ForEachDownloadRange(cpu_addr, size, true, [&](u64 range_offset, u64 range_size) {
1248 copies.push_back(BufferCopy{ 1289 copies.push_back(BufferCopy{
1249 .src_offset = range_offset, 1290 .src_offset = range_offset,
1250 .dst_offset = total_size_bytes, 1291 .dst_offset = total_size_bytes,
@@ -1316,18 +1357,6 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id) {
1316} 1357}
1317 1358
1318template <class P> 1359template <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() { 1360void BufferCache<P>::NotifyBufferDeletion() {
1332 if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { 1361 if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
1333 dirty_uniform_buffers.fill(~u32{0}); 1362 dirty_uniform_buffers.fill(~u32{0});