diff options
| author | 2023-05-29 00:35:51 +0100 | |
|---|---|---|
| committer | 2023-07-02 23:09:48 +0100 | |
| commit | 6f7cb69c94bef0795f054d881e061745f69d1eda (patch) | |
| tree | cc0bec2fed92a5645886dde773add00c84d8b9f4 /src/core/memory.h | |
| parent | Merge pull request #10998 from Morph1984/qt-stop-messing-with-me (diff) | |
| download | yuzu-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.h | 212 |
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; | |||
| 24 | class KProcess; | 28 | class KProcess; |
| 25 | } // namespace Kernel | 29 | } // namespace Kernel |
| 26 | 30 | ||
| 31 | namespace Tegra { | ||
| 32 | class MemoryManager; | ||
| 33 | } | ||
| 34 | |||
| 27 | namespace Core::Memory { | 35 | namespace 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 | ||
| 465 | private: | 478 | private: |
| 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 | ||
| 485 | enum 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 | |||
| 502 | namespace { | ||
| 503 | template <typename M, typename T, GuestMemoryFlags FLAGS> | ||
| 504 | class 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 | |||
| 511 | public: | ||
| 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 | |||
| 613 | protected: | ||
| 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 | |||
| 632 | template <typename M, typename T, GuestMemoryFlags FLAGS> | ||
| 633 | class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> { | ||
| 634 | public: | ||
| 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 | |||
| 676 | template <typename T, GuestMemoryFlags FLAGS> | ||
| 677 | using CpuGuestMemory = GuestMemory<Memory, T, FLAGS>; | ||
| 678 | template <typename T, GuestMemoryFlags FLAGS> | ||
| 679 | using CpuGuestMemoryScoped = GuestMemoryScoped<Memory, T, FLAGS>; | ||
| 680 | template <typename T, GuestMemoryFlags FLAGS> | ||
| 681 | using GpuGuestMemory = GuestMemory<Tegra::MemoryManager, T, FLAGS>; | ||
| 682 | template <typename T, GuestMemoryFlags FLAGS> | ||
| 683 | using GpuGuestMemoryScoped = GuestMemoryScoped<Tegra::MemoryManager, T, FLAGS>; | ||
| 472 | } // namespace Core::Memory | 684 | } // namespace Core::Memory |