summaryrefslogtreecommitdiff
path: root/src/core/memory.h
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/memory.h
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/memory.h')
-rw-r--r--src/core/memory.h212
1 files changed, 212 insertions, 0 deletions
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