mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-11-03 16:39:01 -06:00 
			
		
		
		
	MemoryTracking: Initial setup of atomic writes.
This commit is contained in:
		@@ -27,6 +27,7 @@
 | 
			
		||||
#include "core/file_sys/savedata_factory.h"
 | 
			
		||||
#include "core/file_sys/vfs_concat.h"
 | 
			
		||||
#include "core/file_sys/vfs_real.h"
 | 
			
		||||
#include "core/gpu_dirty_memory_manager.h"
 | 
			
		||||
#include "core/hid/hid_core.h"
 | 
			
		||||
#include "core/hle/kernel/k_memory_manager.h"
 | 
			
		||||
#include "core/hle/kernel/k_process.h"
 | 
			
		||||
@@ -54,6 +55,7 @@
 | 
			
		||||
#include "video_core/renderer_base.h"
 | 
			
		||||
#include "video_core/video_core.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MICROPROFILE_DEFINE(ARM_CPU0, "ARM", "CPU 0", MP_RGB(255, 64, 64));
 | 
			
		||||
MICROPROFILE_DEFINE(ARM_CPU1, "ARM", "CPU 1", MP_RGB(255, 64, 64));
 | 
			
		||||
MICROPROFILE_DEFINE(ARM_CPU2, "ARM", "CPU 2", MP_RGB(255, 64, 64));
 | 
			
		||||
@@ -540,6 +542,9 @@ struct System::Impl {
 | 
			
		||||
 | 
			
		||||
    std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
 | 
			
		||||
    std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{};
 | 
			
		||||
 | 
			
		||||
    std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES>
 | 
			
		||||
        gpu_dirty_memory_write_manager{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
System::System() : impl{std::make_unique<Impl>(*this)} {}
 | 
			
		||||
@@ -629,10 +634,31 @@ void System::PrepareReschedule(const u32 core_index) {
 | 
			
		||||
    impl->kernel.PrepareReschedule(core_index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Core::GPUDirtyMemoryManager& System::CurrentGPUDirtyMemoryManager() {
 | 
			
		||||
    const std::size_t core = impl->kernel.GetCurrentHostThreadID();
 | 
			
		||||
    return impl->gpu_dirty_memory_write_manager[core < Core::Hardware::NUM_CPU_CORES
 | 
			
		||||
                                                    ? core
 | 
			
		||||
                                                    : Core::Hardware::NUM_CPU_CORES - 1];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Provides a constant reference to the current gou dirty memory manager.
 | 
			
		||||
const Core::GPUDirtyMemoryManager& System::CurrentGPUDirtyMemoryManager() const {
 | 
			
		||||
    const std::size_t core = impl->kernel.GetCurrentHostThreadID();
 | 
			
		||||
    return impl->gpu_dirty_memory_write_manager[core < Core::Hardware::NUM_CPU_CORES
 | 
			
		||||
                                                    ? core
 | 
			
		||||
                                                    : Core::Hardware::NUM_CPU_CORES - 1];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t System::GetCurrentHostThreadID() const {
 | 
			
		||||
    return impl->kernel.GetCurrentHostThreadID();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void System::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) {
 | 
			
		||||
    for (auto& manager : impl->gpu_dirty_memory_write_manager) {
 | 
			
		||||
        manager.Gather(callback);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PerfStatsResults System::GetAndResetPerfStats() {
 | 
			
		||||
    return impl->GetAndResetPerfStats();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -108,9 +108,10 @@ class CpuManager;
 | 
			
		||||
class Debugger;
 | 
			
		||||
class DeviceMemory;
 | 
			
		||||
class ExclusiveMonitor;
 | 
			
		||||
class SpeedLimiter;
 | 
			
		||||
class GPUDirtyMemoryManager;
 | 
			
		||||
class PerfStats;
 | 
			
		||||
class Reporter;
 | 
			
		||||
class SpeedLimiter;
 | 
			
		||||
class TelemetrySession;
 | 
			
		||||
 | 
			
		||||
struct PerfStatsResults;
 | 
			
		||||
@@ -225,6 +226,14 @@ public:
 | 
			
		||||
    /// Prepare the core emulation for a reschedule
 | 
			
		||||
    void PrepareReschedule(u32 core_index);
 | 
			
		||||
 | 
			
		||||
    /// Provides a reference to the gou dirty memory manager.
 | 
			
		||||
    [[nodiscard]] Core::GPUDirtyMemoryManager& CurrentGPUDirtyMemoryManager();
 | 
			
		||||
 | 
			
		||||
    /// Provides a constant reference to the current gou dirty memory manager.
 | 
			
		||||
    [[nodiscard]] const Core::GPUDirtyMemoryManager& CurrentGPUDirtyMemoryManager() const;
 | 
			
		||||
 | 
			
		||||
    void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback);
 | 
			
		||||
 | 
			
		||||
    [[nodiscard]] size_t GetCurrentHostThreadID() const;
 | 
			
		||||
 | 
			
		||||
    /// Gets and resets core performance statistics
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										112
									
								
								src/core/gpu_dirty_memory_manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								src/core/gpu_dirty_memory_manager.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,112 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <bit>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "core/memory.h"
 | 
			
		||||
 | 
			
		||||
namespace Core {
 | 
			
		||||
 | 
			
		||||
class GPUDirtyMemoryManager {
 | 
			
		||||
public:
 | 
			
		||||
    GPUDirtyMemoryManager() : current{default_transform} {}
 | 
			
		||||
 | 
			
		||||
    ~GPUDirtyMemoryManager() = default;
 | 
			
		||||
 | 
			
		||||
    void Collect(VAddr address, size_t size) {
 | 
			
		||||
        TransformAddress t = BuildTransform(address, size);
 | 
			
		||||
        TransformAddress tmp, original;
 | 
			
		||||
        do {
 | 
			
		||||
            tmp = current.load(std::memory_order_acquire);
 | 
			
		||||
            original = tmp;
 | 
			
		||||
            if (tmp.address != t.address) {
 | 
			
		||||
                if (IsValid(tmp.address)) {
 | 
			
		||||
                    std::scoped_lock lk(guard);
 | 
			
		||||
                    back_buffer.emplace_back(tmp);
 | 
			
		||||
                    current.exchange(t, std::memory_order_relaxed);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                tmp.address = t.address;
 | 
			
		||||
                tmp.mask = 0;
 | 
			
		||||
            }
 | 
			
		||||
            if ((tmp.mask | t.mask) == tmp.mask) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            tmp.mask |= t.mask;
 | 
			
		||||
        } while (!current.compare_exchange_weak(original, tmp, std::memory_order_release,
 | 
			
		||||
                                                std::memory_order_relaxed));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Gather(std::function<void(VAddr, size_t)>& callback) {
 | 
			
		||||
        {
 | 
			
		||||
            std::scoped_lock lk(guard);
 | 
			
		||||
            TransformAddress t = current.exchange(default_transform, std::memory_order_relaxed);
 | 
			
		||||
            front_buffer.swap(back_buffer);
 | 
			
		||||
            if (IsValid(t.address)) {
 | 
			
		||||
                front_buffer.emplace_back(t);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        for (auto& transform : front_buffer) {
 | 
			
		||||
            size_t offset = 0;
 | 
			
		||||
            u64 mask = transform.mask;
 | 
			
		||||
            while (mask != 0) {
 | 
			
		||||
                const size_t empty_bits = std::countr_zero(mask);
 | 
			
		||||
                offset += empty_bits << align_bits;
 | 
			
		||||
                mask = mask >> empty_bits;
 | 
			
		||||
 | 
			
		||||
                const size_t continuous_bits = std::countr_one(mask);
 | 
			
		||||
                callback((transform.address << Memory::YUZU_PAGEBITS) + offset,
 | 
			
		||||
                         continuous_bits << align_bits);
 | 
			
		||||
                mask = continuous_bits < align_size ? (mask >> continuous_bits) : 0;
 | 
			
		||||
                offset += continuous_bits << align_bits;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        front_buffer.clear();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    struct alignas(16) TransformAddress {
 | 
			
		||||
        VAddr address;
 | 
			
		||||
        u64 mask;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    constexpr static size_t align_bits = 6U;
 | 
			
		||||
    constexpr static size_t align_size = 1U << align_bits;
 | 
			
		||||
    constexpr static size_t align_mask = align_size - 1;
 | 
			
		||||
    constexpr static TransformAddress default_transform = {.address = ~0ULL, .mask = 0ULL};
 | 
			
		||||
 | 
			
		||||
    bool IsValid(VAddr address) {
 | 
			
		||||
        return address < (1ULL << 39);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <typename T>
 | 
			
		||||
    T CreateMask(size_t top_bit, size_t minor_bit) {
 | 
			
		||||
        T mask = ~T(0);
 | 
			
		||||
        mask <<= (sizeof(T) * 8 - top_bit);
 | 
			
		||||
        mask >>= (sizeof(T) * 8 - top_bit);
 | 
			
		||||
        mask >>= minor_bit;
 | 
			
		||||
        mask <<= minor_bit;
 | 
			
		||||
        return mask;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TransformAddress BuildTransform(VAddr address, size_t size) {
 | 
			
		||||
        const size_t minor_address = address & Memory::YUZU_PAGEMASK;
 | 
			
		||||
        const size_t minor_bit = minor_address >> align_bits;
 | 
			
		||||
        const size_t top_bit = (minor_address + size + align_mask) >> align_bits;
 | 
			
		||||
        TransformAddress result{};
 | 
			
		||||
        result.address = address >> Memory::YUZU_PAGEBITS;
 | 
			
		||||
        result.mask = CreateMask<u64>(top_bit, minor_bit);
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::atomic<TransformAddress> current{};
 | 
			
		||||
    std::mutex guard;
 | 
			
		||||
    std::vector<TransformAddress> back_buffer;
 | 
			
		||||
    std::vector<TransformAddress> front_buffer;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace Core
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
#include "common/swap.h"
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/device_memory.h"
 | 
			
		||||
#include "core/gpu_dirty_memory_manager.h"
 | 
			
		||||
#include "core/hardware_properties.h"
 | 
			
		||||
#include "core/hle/kernel/k_page_table.h"
 | 
			
		||||
#include "core/hle/kernel/k_process.h"
 | 
			
		||||
@@ -678,7 +679,7 @@ struct Memory::Impl {
 | 
			
		||||
                LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8,
 | 
			
		||||
                          GetInteger(vaddr), static_cast<u64>(data));
 | 
			
		||||
            },
 | 
			
		||||
            [&]() { system.GPU().InvalidateRegion(GetInteger(vaddr), sizeof(T)); });
 | 
			
		||||
            [&]() { system.CurrentGPUDirtyMemoryManager().Collect(GetInteger(vaddr), sizeof(T)); });
 | 
			
		||||
        if (ptr) {
 | 
			
		||||
            std::memcpy(ptr, &data, sizeof(T));
 | 
			
		||||
        }
 | 
			
		||||
@@ -692,7 +693,7 @@ struct Memory::Impl {
 | 
			
		||||
                LOG_ERROR(HW_Memory, "Unmapped WriteExclusive{} @ 0x{:016X} = 0x{:016X}",
 | 
			
		||||
                          sizeof(T) * 8, GetInteger(vaddr), static_cast<u64>(data));
 | 
			
		||||
            },
 | 
			
		||||
            [&]() { system.GPU().InvalidateRegion(GetInteger(vaddr), sizeof(T)); });
 | 
			
		||||
            [&]() { system.CurrentGPUDirtyMemoryManager().Collect(GetInteger(vaddr), sizeof(T)); });
 | 
			
		||||
        if (ptr) {
 | 
			
		||||
            const auto volatile_pointer = reinterpret_cast<volatile T*>(ptr);
 | 
			
		||||
            return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
 | 
			
		||||
@@ -707,7 +708,7 @@ struct Memory::Impl {
 | 
			
		||||
                LOG_ERROR(HW_Memory, "Unmapped WriteExclusive128 @ 0x{:016X} = 0x{:016X}{:016X}",
 | 
			
		||||
                          GetInteger(vaddr), static_cast<u64>(data[1]), static_cast<u64>(data[0]));
 | 
			
		||||
            },
 | 
			
		||||
            [&]() { system.GPU().InvalidateRegion(GetInteger(vaddr), sizeof(u128)); });
 | 
			
		||||
            [&]() { system.CurrentGPUDirtyMemoryManager().Collect(GetInteger(vaddr), sizeof(u128)); });
 | 
			
		||||
        if (ptr) {
 | 
			
		||||
            const auto volatile_pointer = reinterpret_cast<volatile u64*>(ptr);
 | 
			
		||||
            return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
 | 
			
		||||
 
 | 
			
		||||
@@ -115,7 +115,21 @@ void BufferCache<P>::WriteMemory(VAddr cpu_addr, u64 size) {
 | 
			
		||||
 | 
			
		||||
template <class P>
 | 
			
		||||
void BufferCache<P>::CachedWriteMemory(VAddr cpu_addr, u64 size) {
 | 
			
		||||
    memory_tracker.CachedCpuWrite(cpu_addr, size);
 | 
			
		||||
    const bool is_dirty = IsRegionRegistered(cpu_addr, size);
 | 
			
		||||
    if (!is_dirty) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    VAddr aligned_start = Common::AlignDown(cpu_addr, YUZU_PAGESIZE);
 | 
			
		||||
    VAddr aligned_end = Common::AlignUp(cpu_addr + size, YUZU_PAGESIZE);
 | 
			
		||||
    if (!IsRegionGpuModified(aligned_start, aligned_end - aligned_start)) {
 | 
			
		||||
        WriteMemory(cpu_addr, size);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    tmp_buffer.resize_destructive(size);
 | 
			
		||||
    cpu_memory.ReadBlockUnsafe(cpu_addr, tmp_buffer.data(), size);
 | 
			
		||||
 | 
			
		||||
    InlineMemoryImplementation(cpu_addr, size, tmp_buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <class P>
 | 
			
		||||
@@ -1553,6 +1567,14 @@ bool BufferCache<P>::InlineMemory(VAddr dest_address, size_t copy_size,
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    InlineMemoryImplementation(dest_address, copy_size, inlined_buffer);
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <class P>
 | 
			
		||||
void BufferCache<P>::InlineMemoryImplementation(VAddr dest_address, size_t copy_size,
 | 
			
		||||
                                  std::span<const u8> inlined_buffer) {
 | 
			
		||||
    const IntervalType subtract_interval{dest_address, dest_address + copy_size};
 | 
			
		||||
    ClearDownload(subtract_interval);
 | 
			
		||||
    common_ranges.subtract(subtract_interval);
 | 
			
		||||
@@ -1574,8 +1596,6 @@ bool BufferCache<P>::InlineMemory(VAddr dest_address, size_t copy_size,
 | 
			
		||||
    } else {
 | 
			
		||||
        buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <class P>
 | 
			
		||||
 
 | 
			
		||||
@@ -543,6 +543,8 @@ private:
 | 
			
		||||
 | 
			
		||||
    void ClearDownload(IntervalType subtract_interval);
 | 
			
		||||
 | 
			
		||||
    void InlineMemoryImplementation(VAddr dest_address, size_t copy_size, std::span<const u8> inlined_buffer);
 | 
			
		||||
 | 
			
		||||
    VideoCore::RasterizerInterface& rasterizer;
 | 
			
		||||
    Core::Memory::Memory& cpu_memory;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -95,7 +95,9 @@ struct GPU::Impl {
 | 
			
		||||
 | 
			
		||||
    /// Synchronizes CPU writes with Host GPU memory.
 | 
			
		||||
    void InvalidateGPUCache() {
 | 
			
		||||
        rasterizer->InvalidateGPUCache();
 | 
			
		||||
        std::function<void(VAddr, size_t)> callback_writes(
 | 
			
		||||
            [this](VAddr address, size_t size) { rasterizer->OnCPUWrite(address, size); });
 | 
			
		||||
        system.GatherGPUDirtyMemory(callback_writes);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Signal the ending of command list.
 | 
			
		||||
 
 | 
			
		||||
@@ -570,7 +570,7 @@ void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) {
 | 
			
		||||
    if (addr == 0 || size == 0) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    pipeline_cache.OnCPUWrite(addr, size);
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        std::scoped_lock lock{texture_cache.mutex};
 | 
			
		||||
        texture_cache.WriteMemory(addr, size);
 | 
			
		||||
@@ -579,14 +579,11 @@ void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) {
 | 
			
		||||
        std::scoped_lock lock{buffer_cache.mutex};
 | 
			
		||||
        buffer_cache.CachedWriteMemory(addr, size);
 | 
			
		||||
    }
 | 
			
		||||
    pipeline_cache.InvalidateRegion(addr, size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RasterizerVulkan::InvalidateGPUCache() {
 | 
			
		||||
    pipeline_cache.SyncGuestHost();
 | 
			
		||||
    {
 | 
			
		||||
        std::scoped_lock lock{buffer_cache.mutex};
 | 
			
		||||
        buffer_cache.FlushCachedWrites();
 | 
			
		||||
    }
 | 
			
		||||
    gpu.InvalidateGPUCache();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RasterizerVulkan::UnmapMemory(VAddr addr, u64 size) {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user