summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorGravatar Kelebek12023-05-29 00:35:51 +0100
committerGravatar Kelebek12023-07-02 23:09:48 +0100
commit6f7cb69c94bef0795f054d881e061745f69d1eda (patch)
treecc0bec2fed92a5645886dde773add00c84d8b9f4 /src/core
parentMerge pull request #10998 from Morph1984/qt-stop-messing-with-me (diff)
downloadyuzu-6f7cb69c94bef0795f054d881e061745f69d1eda.tar.gz
yuzu-6f7cb69c94bef0795f054d881e061745f69d1eda.tar.xz
yuzu-6f7cb69c94bef0795f054d881e061745f69d1eda.zip
Use spans over guest memory where possible instead of copying data.
Diffstat (limited to 'src/core')
-rw-r--r--src/core/core_timing.cpp3
-rw-r--r--src/core/core_timing.h2
-rw-r--r--src/core/hle/service/hle_ipc.cpp32
-rw-r--r--src/core/memory.cpp54
-rw-r--r--src/core/memory.h212
5 files changed, 285 insertions, 18 deletions
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index e6112a3c9..b98a0cb33 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -70,7 +70,7 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
70 -> std::optional<std::chrono::nanoseconds> { return std::nullopt; }; 70 -> std::optional<std::chrono::nanoseconds> { return std::nullopt; };
71 ev_lost = CreateEvent("_lost_event", empty_timed_callback); 71 ev_lost = CreateEvent("_lost_event", empty_timed_callback);
72 if (is_multicore) { 72 if (is_multicore) {
73 timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this)); 73 timer_thread = std::make_unique<std::jthread>(ThreadEntry, std::ref(*this));
74 } 74 }
75} 75}
76 76
@@ -255,7 +255,6 @@ void CoreTiming::ThreadLoop() {
255#ifdef _WIN32 255#ifdef _WIN32
256 while (!paused && !event.IsSet() && wait_time > 0) { 256 while (!paused && !event.IsSet() && wait_time > 0) {
257 wait_time = *next_time - GetGlobalTimeNs().count(); 257 wait_time = *next_time - GetGlobalTimeNs().count();
258
259 if (wait_time >= timer_resolution_ns) { 258 if (wait_time >= timer_resolution_ns) {
260 Common::Windows::SleepForOneTick(); 259 Common::Windows::SleepForOneTick();
261 } else { 260 } else {
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 5bca1c78d..c20e906fb 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -163,7 +163,7 @@ private:
163 Common::Event pause_event{}; 163 Common::Event pause_event{};
164 std::mutex basic_lock; 164 std::mutex basic_lock;
165 std::mutex advance_lock; 165 std::mutex advance_lock;
166 std::unique_ptr<std::thread> timer_thread; 166 std::unique_ptr<std::jthread> timer_thread;
167 std::atomic<bool> paused{}; 167 std::atomic<bool> paused{};
168 std::atomic<bool> paused_set{}; 168 std::atomic<bool> paused_set{};
169 std::atomic<bool> wait_set{}; 169 std::atomic<bool> wait_set{};
diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp
index 2290df705..f6a1e54f2 100644
--- a/src/core/hle/service/hle_ipc.cpp
+++ b/src/core/hle/service/hle_ipc.cpp
@@ -329,8 +329,22 @@ std::vector<u8> HLERequestContext::ReadBufferCopy(std::size_t buffer_index) cons
329} 329}
330 330
331std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { 331std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
332 static thread_local std::array<Common::ScratchBuffer<u8>, 2> read_buffer_a; 332 static thread_local std::array read_buffer_a{
333 static thread_local std::array<Common::ScratchBuffer<u8>, 2> read_buffer_x; 333 Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
334 Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
335 };
336 static thread_local std::array read_buffer_data_a{
337 Common::ScratchBuffer<u8>(),
338 Common::ScratchBuffer<u8>(),
339 };
340 static thread_local std::array read_buffer_x{
341 Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
342 Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
343 };
344 static thread_local std::array read_buffer_data_x{
345 Common::ScratchBuffer<u8>(),
346 Common::ScratchBuffer<u8>(),
347 };
334 348
335 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && 349 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
336 BufferDescriptorA()[buffer_index].Size()}; 350 BufferDescriptorA()[buffer_index].Size()};
@@ -339,19 +353,17 @@ std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) cons
339 BufferDescriptorA().size() > buffer_index, { return {}; }, 353 BufferDescriptorA().size() > buffer_index, { return {}; },
340 "BufferDescriptorA invalid buffer_index {}", buffer_index); 354 "BufferDescriptorA invalid buffer_index {}", buffer_index);
341 auto& read_buffer = read_buffer_a[buffer_index]; 355 auto& read_buffer = read_buffer_a[buffer_index];
342 read_buffer.resize_destructive(BufferDescriptorA()[buffer_index].Size()); 356 return read_buffer.Read(BufferDescriptorA()[buffer_index].Address(),
343 memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), read_buffer.data(), 357 BufferDescriptorA()[buffer_index].Size(),
344 read_buffer.size()); 358 &read_buffer_data_a[buffer_index]);
345 return read_buffer;
346 } else { 359 } else {
347 ASSERT_OR_EXECUTE_MSG( 360 ASSERT_OR_EXECUTE_MSG(
348 BufferDescriptorX().size() > buffer_index, { return {}; }, 361 BufferDescriptorX().size() > buffer_index, { return {}; },
349 "BufferDescriptorX invalid buffer_index {}", buffer_index); 362 "BufferDescriptorX invalid buffer_index {}", buffer_index);
350 auto& read_buffer = read_buffer_x[buffer_index]; 363 auto& read_buffer = read_buffer_x[buffer_index];
351 read_buffer.resize_destructive(BufferDescriptorX()[buffer_index].Size()); 364 return read_buffer.Read(BufferDescriptorX()[buffer_index].Address(),
352 memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), read_buffer.data(), 365 BufferDescriptorX()[buffer_index].Size(),
353 read_buffer.size()); 366 &read_buffer_data_x[buffer_index]);
354 return read_buffer;
355 } 367 }
356} 368}
357 369
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 257406f09..805963178 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -266,6 +266,22 @@ struct Memory::Impl {
266 ReadBlockImpl<true>(*system.ApplicationProcess(), src_addr, dest_buffer, size); 266 ReadBlockImpl<true>(*system.ApplicationProcess(), src_addr, dest_buffer, size);
267 } 267 }
268 268
269 const u8* GetSpan(const VAddr src_addr, const std::size_t size) const {
270 if (current_page_table->blocks[src_addr >> YUZU_PAGEBITS] ==
271 current_page_table->blocks[(src_addr + size) >> YUZU_PAGEBITS]) {
272 return GetPointerSilent(src_addr);
273 }
274 return nullptr;
275 }
276
277 u8* GetSpan(const VAddr src_addr, const std::size_t size) {
278 if (current_page_table->blocks[src_addr >> YUZU_PAGEBITS] ==
279 current_page_table->blocks[(src_addr + size) >> YUZU_PAGEBITS]) {
280 return GetPointerSilent(src_addr);
281 }
282 return nullptr;
283 }
284
269 template <bool UNSAFE> 285 template <bool UNSAFE>
270 void WriteBlockImpl(const Kernel::KProcess& process, const Common::ProcessAddress dest_addr, 286 void WriteBlockImpl(const Kernel::KProcess& process, const Common::ProcessAddress dest_addr,
271 const void* src_buffer, const std::size_t size) { 287 const void* src_buffer, const std::size_t size) {
@@ -559,7 +575,7 @@ struct Memory::Impl {
559 } 575 }
560 } 576 }
561 577
562 const Common::ProcessAddress end = base + size; 578 const auto end = base + size;
563 ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}", 579 ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
564 base + page_table.pointers.size()); 580 base + page_table.pointers.size());
565 581
@@ -570,14 +586,18 @@ struct Memory::Impl {
570 while (base != end) { 586 while (base != end) {
571 page_table.pointers[base].Store(nullptr, type); 587 page_table.pointers[base].Store(nullptr, type);
572 page_table.backing_addr[base] = 0; 588 page_table.backing_addr[base] = 0;
573 589 page_table.blocks[base] = 0;
574 base += 1; 590 base += 1;
575 } 591 }
576 } else { 592 } else {
593 auto orig_base = base;
577 while (base != end) { 594 while (base != end) {
578 page_table.pointers[base].Store( 595 auto host_ptr =
579 system.DeviceMemory().GetPointer<u8>(target) - (base << YUZU_PAGEBITS), type); 596 system.DeviceMemory().GetPointer<u8>(target) - (base << YUZU_PAGEBITS);
580 page_table.backing_addr[base] = GetInteger(target) - (base << YUZU_PAGEBITS); 597 auto backing = GetInteger(target) - (base << YUZU_PAGEBITS);
598 page_table.pointers[base].Store(host_ptr, type);
599 page_table.backing_addr[base] = backing;
600 page_table.blocks[base] = orig_base << YUZU_PAGEBITS;
581 601
582 ASSERT_MSG(page_table.pointers[base].Pointer(), 602 ASSERT_MSG(page_table.pointers[base].Pointer(),
583 "memory mapping base yield a nullptr within the table"); 603 "memory mapping base yield a nullptr within the table");
@@ -747,6 +767,14 @@ struct Memory::Impl {
747 VAddr last_address; 767 VAddr last_address;
748 }; 768 };
749 769
770 void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size) {
771 system.GPU().InvalidateRegion(GetInteger(dest_addr), size);
772 }
773
774 void FlushRegion(Common::ProcessAddress dest_addr, size_t size) {
775 system.GPU().FlushRegion(GetInteger(dest_addr), size);
776 }
777
750 Core::System& system; 778 Core::System& system;
751 Common::PageTable* current_page_table = nullptr; 779 Common::PageTable* current_page_table = nullptr;
752 std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES> 780 std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES>
@@ -881,6 +909,14 @@ void Memory::ReadBlockUnsafe(const Common::ProcessAddress src_addr, void* dest_b
881 impl->ReadBlockUnsafe(src_addr, dest_buffer, size); 909 impl->ReadBlockUnsafe(src_addr, dest_buffer, size);
882} 910}
883 911
912const u8* Memory::GetSpan(const VAddr src_addr, const std::size_t size) const {
913 return impl->GetSpan(src_addr, size);
914}
915
916u8* Memory::GetSpan(const VAddr src_addr, const std::size_t size) {
917 return impl->GetSpan(src_addr, size);
918}
919
884void Memory::WriteBlock(const Common::ProcessAddress dest_addr, const void* src_buffer, 920void Memory::WriteBlock(const Common::ProcessAddress dest_addr, const void* src_buffer,
885 const std::size_t size) { 921 const std::size_t size) {
886 impl->WriteBlock(dest_addr, src_buffer, size); 922 impl->WriteBlock(dest_addr, src_buffer, size);
@@ -924,4 +960,12 @@ void Memory::MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug)
924 impl->MarkRegionDebug(GetInteger(vaddr), size, debug); 960 impl->MarkRegionDebug(GetInteger(vaddr), size, debug);
925} 961}
926 962
963void Memory::InvalidateRegion(Common::ProcessAddress dest_addr, size_t size) {
964 impl->InvalidateRegion(dest_addr, size);
965}
966
967void Memory::FlushRegion(Common::ProcessAddress dest_addr, size_t size) {
968 impl->FlushRegion(dest_addr, size);
969}
970
927} // namespace Core::Memory 971} // namespace Core::Memory
diff --git a/src/core/memory.h b/src/core/memory.h
index ea01824f8..ea33c769c 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -5,8 +5,12 @@
5 5
6#include <cstddef> 6#include <cstddef>
7#include <memory> 7#include <memory>
8#include <optional>
8#include <span> 9#include <span>
9#include <string> 10#include <string>
11#include <vector>
12
13#include "common/scratch_buffer.h"
10#include "common/typed_address.h" 14#include "common/typed_address.h"
11#include "core/hle/result.h" 15#include "core/hle/result.h"
12 16
@@ -24,6 +28,10 @@ class PhysicalMemory;
24class KProcess; 28class KProcess;
25} // namespace Kernel 29} // namespace Kernel
26 30
31namespace Tegra {
32class MemoryManager;
33}
34
27namespace Core::Memory { 35namespace Core::Memory {
28 36
29/** 37/**
@@ -343,6 +351,9 @@ public:
343 */ 351 */
344 void ReadBlockUnsafe(Common::ProcessAddress src_addr, void* dest_buffer, std::size_t size); 352 void ReadBlockUnsafe(Common::ProcessAddress src_addr, void* dest_buffer, std::size_t size);
345 353
354 const u8* GetSpan(const VAddr src_addr, const std::size_t size) const;
355 u8* GetSpan(const VAddr src_addr, const std::size_t size);
356
346 /** 357 /**
347 * Writes a range of bytes into the current process' address space at the specified 358 * Writes a range of bytes into the current process' address space at the specified
348 * virtual address. 359 * virtual address.
@@ -461,6 +472,8 @@ public:
461 void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug); 472 void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug);
462 473
463 void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers); 474 void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
475 void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size);
476 void FlushRegion(Common::ProcessAddress dest_addr, size_t size);
464 477
465private: 478private:
466 Core::System& system; 479 Core::System& system;
@@ -469,4 +482,203 @@ private:
469 std::unique_ptr<Impl> impl; 482 std::unique_ptr<Impl> impl;
470}; 483};
471 484
485enum GuestMemoryFlags : u32 {
486 Read = 1 << 0,
487 Write = 1 << 1,
488 Safe = 1 << 2,
489 Cached = 1 << 3,
490
491 SafeRead = Read | Safe,
492 SafeWrite = Write | Safe,
493 SafeReadWrite = SafeRead | SafeWrite,
494 SafeReadCachedWrite = SafeReadWrite | Cached,
495
496 UnsafeRead = Read,
497 UnsafeWrite = Write,
498 UnsafeReadWrite = UnsafeRead | UnsafeWrite,
499 UnsafeReadCachedWrite = UnsafeReadWrite | Cached,
500};
501
502namespace {
503template <typename M, typename T, GuestMemoryFlags FLAGS>
504class GuestMemory {
505 using iterator = T*;
506 using const_iterator = const T*;
507 using value_type = T;
508 using element_type = T;
509 using iterator_category = std::contiguous_iterator_tag;
510
511public:
512 GuestMemory() = delete;
513 explicit GuestMemory(M& memory_, u64 addr_, std::size_t size_,
514 Common::ScratchBuffer<T>* backup = nullptr)
515 : memory{memory_}, addr{addr_}, size{size_} {
516 static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write);
517 if constexpr (FLAGS & GuestMemoryFlags::Read) {
518 Read(addr, size, backup);
519 }
520 }
521
522 ~GuestMemory() = default;
523
524 T* data() noexcept {
525 return data_span.data();
526 }
527
528 const T* data() const noexcept {
529 return data_span.data();
530 }
531
532 [[nodiscard]] T* begin() noexcept {
533 return data();
534 }
535
536 [[nodiscard]] const T* begin() const noexcept {
537 return data();
538 }
539
540 [[nodiscard]] T* end() noexcept {
541 return data() + size;
542 }
543
544 [[nodiscard]] const T* end() const noexcept {
545 return data() + size;
546 }
547
548 T& operator[](size_t index) noexcept {
549 return data_span[index];
550 }
551
552 const T& operator[](size_t index) const noexcept {
553 return data_span[index];
554 }
555
556 void SetAddressAndSize(u64 addr_, std::size_t size_) noexcept {
557 addr = addr_;
558 size = size_;
559 addr_changed = true;
560 }
561
562 std::span<T> Read(u64 addr_, std::size_t size_,
563 Common::ScratchBuffer<T>* backup = nullptr) noexcept {
564 addr = addr_;
565 size = size_;
566 if (size == 0) {
567 is_data_copy = true;
568 return {};
569 }
570
571 if (TrySetSpan()) {
572 if constexpr (FLAGS & GuestMemoryFlags::Safe) {
573 memory.FlushRegion(addr, size * sizeof(T));
574 }
575 } else {
576 if (backup) {
577 backup->resize_destructive(size);
578 data_span = *backup;
579 } else {
580 data_copy.resize(size);
581 data_span = std::span(data_copy);
582 }
583 is_data_copy = true;
584 span_valid = true;
585 if constexpr (FLAGS & GuestMemoryFlags::Safe) {
586 memory.ReadBlock(addr, data_span.data(), size * sizeof(T));
587 } else {
588 memory.ReadBlockUnsafe(addr, data_span.data(), size * sizeof(T));
589 }
590 }
591 return data_span;
592 }
593
594 void Write(std::span<T> write_data) noexcept {
595 if constexpr (FLAGS & GuestMemoryFlags::Cached) {
596 memory.WriteBlockCached(addr, write_data.data(), size * sizeof(T));
597 } else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
598 memory.WriteBlock(addr, write_data.data(), size * sizeof(T));
599 } else {
600 memory.WriteBlockUnsafe(addr, write_data.data(), size * sizeof(T));
601 }
602 }
603
604 bool TrySetSpan() noexcept {
605 if (u8* ptr = memory.GetSpan(addr, size * sizeof(T)); ptr) {
606 data_span = {reinterpret_cast<T*>(ptr), size};
607 span_valid = true;
608 return true;
609 }
610 return false;
611 }
612
613protected:
614 bool IsDataCopy() const noexcept {
615 return is_data_copy;
616 }
617
618 bool AddressChanged() const noexcept {
619 return addr_changed;
620 }
621
622 M& memory;
623 u64 addr;
624 size_t size;
625 std::span<T> data_span{};
626 std::vector<T> data_copy;
627 bool span_valid{false};
628 bool is_data_copy{false};
629 bool addr_changed{false};
630};
631
632template <typename M, typename T, GuestMemoryFlags FLAGS>
633class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> {
634public:
635 GuestMemoryScoped() = delete;
636 explicit GuestMemoryScoped(M& memory_, u64 addr_, std::size_t size_,
637 Common::ScratchBuffer<T>* backup = nullptr)
638 : GuestMemory<M, T, FLAGS>(memory_, addr_, size_, backup) {
639 if constexpr (!(FLAGS & GuestMemoryFlags::Read)) {
640 if (!this->TrySetSpan()) {
641 if (backup) {
642 this->data_span = *backup;
643 this->span_valid = true;
644 this->is_data_copy = true;
645 }
646 }
647 }
648 }
649
650 ~GuestMemoryScoped() {
651 if constexpr (FLAGS & GuestMemoryFlags::Write) {
652 if (this->size == 0) [[unlikely]] {
653 return;
654 }
655
656 if (this->AddressChanged() || this->IsDataCopy()) {
657 ASSERT(this->span_valid);
658 if constexpr (FLAGS & GuestMemoryFlags::Cached) {
659 this->memory.WriteBlockCached(this->addr, this->data_span.data(),
660 this->size * sizeof(T));
661 } else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
662 this->memory.WriteBlock(this->addr, this->data_span.data(),
663 this->size * sizeof(T));
664 } else {
665 this->memory.WriteBlockUnsafe(this->addr, this->data_span.data(),
666 this->size * sizeof(T));
667 }
668 } else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
669 this->memory.InvalidateRegion(this->addr, this->size * sizeof(T));
670 }
671 }
672 }
673};
674} // namespace
675
676template <typename T, GuestMemoryFlags FLAGS>
677using CpuGuestMemory = GuestMemory<Memory, T, FLAGS>;
678template <typename T, GuestMemoryFlags FLAGS>
679using CpuGuestMemoryScoped = GuestMemoryScoped<Memory, T, FLAGS>;
680template <typename T, GuestMemoryFlags FLAGS>
681using GpuGuestMemory = GuestMemory<Tegra::MemoryManager, T, FLAGS>;
682template <typename T, GuestMemoryFlags FLAGS>
683using GpuGuestMemoryScoped = GuestMemoryScoped<Tegra::MemoryManager, T, FLAGS>;
472} // namespace Core::Memory 684} // namespace Core::Memory