mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-11-01 11:58:19 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 15d18e4db6 | |||
| 8a142b490f |
@@ -165,10 +165,13 @@ bool ArmNce::HandleGuestAccessFault(GuestContext* guest_ctx, void* raw_info, voi
|
|||||||
const Common::ProcessAddress addr =
|
const Common::ProcessAddress addr =
|
||||||
(reinterpret_cast<u64>(info->si_addr) & ~Memory::YUZU_PAGEMASK);
|
(reinterpret_cast<u64>(info->si_addr) & ~Memory::YUZU_PAGEMASK);
|
||||||
auto& memory = guest_ctx->parent->m_running_thread->GetOwnerProcess()->GetMemory();
|
auto& memory = guest_ctx->parent->m_running_thread->GetOwnerProcess()->GetMemory();
|
||||||
if (memory.InvalidateNCE(addr, Memory::YUZU_PAGESIZE)) {
|
const auto result = memory.InvalidateNCE(addr, Memory::YUZU_PAGESIZE);
|
||||||
// We handled the access successfully and are returning to guest code.
|
if (result == Memory::NCEAccessFaultAction::Retry) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (result == Memory::NCEAccessFaultAction::Instruction) {
|
||||||
|
return HandleGuestAlignmentFault(guest_ctx, raw_info, raw_context);
|
||||||
|
}
|
||||||
|
|
||||||
// We couldn't handle the access.
|
// We couldn't handle the access.
|
||||||
return HandleFailedGuestFault(guest_ctx, raw_info, raw_context);
|
return HandleFailedGuestFault(guest_ctx, raw_info, raw_context);
|
||||||
@@ -231,7 +234,7 @@ HaltReason ArmNce::RunThread(Kernel::KThread* thread) {
|
|||||||
hr = ReturnToRunCodeByExceptionLevelChange(m_thread_id, thread_params); // Android: Use "process handle SIGUSR2 -n true -p true -s false" (and SIGURG) in LLDB when debugging
|
hr = ReturnToRunCodeByExceptionLevelChange(m_thread_id, thread_params); // Android: Use "process handle SIGUSR2 -n true -p true -s false" (and SIGURG) in LLDB when debugging
|
||||||
}
|
}
|
||||||
|
|
||||||
// Critical section for thread cleanup
|
// Critical section for thread cleanup.
|
||||||
std::atomic_thread_fence(std::memory_order_acquire);
|
std::atomic_thread_fence(std::memory_order_acquire);
|
||||||
|
|
||||||
// Cache values before releasing thread
|
// Cache values before releasing thread
|
||||||
|
|||||||
+13
-8
@@ -739,7 +739,7 @@ struct Memory::Impl {
|
|||||||
PAddr last_address;
|
PAddr last_address;
|
||||||
};
|
};
|
||||||
|
|
||||||
void InvalidateGPUMemory(u8* p, size_t size) {
|
bool TryInvalidateGPUMemory(const u8* p, size_t size) {
|
||||||
constexpr size_t sys_core = Core::Hardware::NUM_CPU_CORES - 1;
|
constexpr size_t sys_core = Core::Hardware::NUM_CPU_CORES - 1;
|
||||||
const size_t core = (std::min)(system.GetCurrentHostThreadID(),
|
const size_t core = (std::min)(system.GetCurrentHostThreadID(),
|
||||||
sys_core); // any other calls threads go to syscore.
|
sys_core); // any other calls threads go to syscore.
|
||||||
@@ -755,9 +755,13 @@ struct Memory::Impl {
|
|||||||
sys_core_guard.unlock();
|
sys_core_guard.unlock();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
bool has_pending_writes = false;
|
||||||
auto& gpu = system.GPU();
|
auto& gpu = system.GPU();
|
||||||
gpu_device_memory->ApplyOpOnPointer(
|
gpu_device_memory->ApplyOpOnPointer(
|
||||||
p, scratch_buffers[core], [&](DAddr address) { gpu.InvalidateRegion(address, size); });
|
p, scratch_buffers[core], [&](DAddr address) { has_pending_writes |= gpu.MustFlushRegion(address, size); });
|
||||||
|
if (has_pending_writes) return false;
|
||||||
|
gpu_device_memory->ApplyOpOnPointer(p, scratch_buffers[core], [&](DAddr address) { gpu.InvalidateRegion(address, size); });
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
@@ -961,7 +965,7 @@ void Memory::MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug)
|
|||||||
impl->MarkRegionDebug(GetInteger(vaddr), size, debug);
|
impl->MarkRegionDebug(GetInteger(vaddr), size, debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
|
NCEAccessFaultAction Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
|
||||||
[[maybe_unused]] bool mapped = true;
|
[[maybe_unused]] bool mapped = true;
|
||||||
[[maybe_unused]] bool rasterizer = false;
|
[[maybe_unused]] bool rasterizer = false;
|
||||||
|
|
||||||
@@ -973,17 +977,18 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
|
|||||||
mapped = false;
|
mapped = false;
|
||||||
},
|
},
|
||||||
[&] { rasterizer = true; });
|
[&] { rasterizer = true; });
|
||||||
if (rasterizer) {
|
|
||||||
impl->InvalidateGPUMemory(ptr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
if (!rasterizer && mapped) {
|
if (!rasterizer && mapped) {
|
||||||
impl->host_buffer->DeferredMapSeparateHeap(GetInteger(vaddr));
|
impl->host_buffer->DeferredMapSeparateHeap(GetInteger(vaddr));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return mapped && ptr != nullptr;
|
if (rasterizer) {
|
||||||
|
if (!impl->TryInvalidateGPUMemory(ptr, size)) {
|
||||||
|
return NCEAccessFaultAction::Instruction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mapped && ptr != nullptr ? NCEAccessFaultAction::Retry : NCEAccessFaultAction::Unmapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Memory::InvalidateSeparateHeap(void* fault_address) {
|
bool Memory::InvalidateSeparateHeap(void* fault_address) {
|
||||||
|
|||||||
+8
-2
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||||
@@ -46,6 +46,12 @@ constexpr std::size_t YUZU_PAGEBITS = 12;
|
|||||||
constexpr u64 YUZU_PAGESIZE = 1ULL << YUZU_PAGEBITS;
|
constexpr u64 YUZU_PAGESIZE = 1ULL << YUZU_PAGEBITS;
|
||||||
constexpr u64 YUZU_PAGEMASK = YUZU_PAGESIZE - 1;
|
constexpr u64 YUZU_PAGEMASK = YUZU_PAGESIZE - 1;
|
||||||
|
|
||||||
|
enum class NCEAccessFaultAction {
|
||||||
|
Unmapped,
|
||||||
|
Retry,
|
||||||
|
Instruction,
|
||||||
|
};
|
||||||
|
|
||||||
/// Virtual user-space memory regions
|
/// Virtual user-space memory regions
|
||||||
enum : u64 {
|
enum : u64 {
|
||||||
/// TLS (Thread-Local Storage) related.
|
/// TLS (Thread-Local Storage) related.
|
||||||
@@ -493,7 +499,7 @@ public:
|
|||||||
|
|
||||||
void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
|
void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
|
||||||
|
|
||||||
bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size);
|
NCEAccessFaultAction InvalidateNCE(Common::ProcessAddress vaddr, size_t size);
|
||||||
|
|
||||||
bool InvalidateSeparateHeap(void* fault_address);
|
bool InvalidateSeparateHeap(void* fault_address);
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,8 @@ void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
|
|||||||
ClearDownload(device_addr, size);
|
ClearDownload(device_addr, size);
|
||||||
gpu_modified_ranges.Subtract(device_addr, size);
|
gpu_modified_ranges.Subtract(device_addr, size);
|
||||||
}
|
}
|
||||||
memory_tracker.MarkRegionAsCpuModified(device_addr, size);
|
const DAddr page_start = Common::AlignDown(device_addr, DEVICE_PAGESIZE);
|
||||||
|
memory_tracker.MarkRegionAsCpuModified(device_addr, size,IsRegionGpuModified(page_start, Common::AlignUp(device_addr + size, DEVICE_PAGESIZE) - page_start));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
@@ -153,7 +154,7 @@ bool BufferCache<P>::OnCPUWrite(DAddr device_addr, u64 size) {
|
|||||||
if (!is_dirty) {
|
if (!is_dirty) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (memory_tracker.IsRegionGpuModified(device_addr, size)) {
|
if (IsRegionGpuModified(device_addr, size)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
WriteMemory(device_addr, size);
|
WriteMemory(device_addr, size);
|
||||||
@@ -1781,13 +1782,23 @@ bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 si
|
|||||||
u64 largest_copy = 0;
|
u64 largest_copy = 0;
|
||||||
DAddr buffer_start = buffer.CpuAddr();
|
DAddr buffer_start = buffer.CpuAddr();
|
||||||
memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) {
|
memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) {
|
||||||
copies.push_back(BufferCopy{
|
const auto add_upload = [&](DAddr start, DAddr end) {
|
||||||
.src_offset = total_size_bytes,
|
if (start == end) return;
|
||||||
.dst_offset = device_addr_out - buffer_start,
|
const u64 upload_size = end - start;
|
||||||
.size = range_size,
|
copies.push_back(BufferCopy{
|
||||||
|
.src_offset = total_size_bytes,
|
||||||
|
.dst_offset = start - buffer_start,
|
||||||
|
.size = upload_size,
|
||||||
|
});
|
||||||
|
total_size_bytes += upload_size;
|
||||||
|
largest_copy = (std::max)(largest_copy, upload_size);
|
||||||
|
};
|
||||||
|
DAddr upload_start = device_addr_out;
|
||||||
|
gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, [&](DAddr gpu_start, DAddr gpu_end) {
|
||||||
|
add_upload(upload_start, gpu_start);
|
||||||
|
upload_start = gpu_end;
|
||||||
});
|
});
|
||||||
total_size_bytes += range_size;
|
add_upload(upload_start, device_addr_out + range_size);
|
||||||
largest_copy = (std::max)(largest_copy, range_size);
|
|
||||||
});
|
});
|
||||||
if (total_size_bytes == 0) {
|
if (total_size_bytes == 0) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -71,9 +71,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Mark region as CPU modified, notifying the device_tracker about this change
|
/// Mark region as CPU modified, notifying the device_tracker about this change
|
||||||
void MarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 query_size) {
|
void MarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 query_size, bool keep_tracking = false) {
|
||||||
IteratePages<true>(dirty_cpu_addr, query_size, [](Manager* manager, u64 offset, size_t size) {
|
IteratePages<true>(dirty_cpu_addr, query_size, [keep_tracking](Manager* manager, u64 offset, size_t size) {
|
||||||
manager->ChangeRegionState(Type::CPU, true, manager->cpu_addr + offset, size);
|
manager->ChangeRegionState(Type::CPU, true, manager->cpu_addr + offset, size, keep_tracking);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -115,19 +115,20 @@ struct WordManager {
|
|||||||
/// @param enable If enabling or disabling
|
/// @param enable If enabling or disabling
|
||||||
/// @param dirty_addr Base address to mark or unmark as modified
|
/// @param dirty_addr Base address to mark or unmark as modified
|
||||||
/// @param size Size in bytes to mark or unmark as modified
|
/// @param size Size in bytes to mark or unmark as modified
|
||||||
void ChangeRegionState(Type type, bool enable, u64 dirty_addr, u64 size) noexcept {
|
/// @param keep_tracking Keep CPU-modified pages protected for subsequent native writes.
|
||||||
|
void ChangeRegionState(Type type, bool enable, u64 dirty_addr, u64 size, bool keep_tracking = false) noexcept {
|
||||||
std::span<u64> state_words = Span(type);
|
std::span<u64> state_words = Span(type);
|
||||||
[[maybe_unused]] std::span<u64> untracked_words = Span(Type::Untracked);
|
[[maybe_unused]] std::span<u64> untracked_words = Span(Type::Untracked);
|
||||||
[[maybe_unused]] std::span<u64> cached_words = Span(Type::CachedCPU);
|
[[maybe_unused]] std::span<u64> cached_words = Span(Type::CachedCPU);
|
||||||
std::vector<std::pair<VAddr, u64>> ranges;
|
std::vector<std::pair<VAddr, u64>> ranges;
|
||||||
IterateWords(dirty_addr - cpu_addr, size, [&](size_t index, u64 mask) {
|
IterateWords(dirty_addr - cpu_addr, size, [&](size_t index, u64 mask) {
|
||||||
if (type == Type::CPU || type == Type::CachedCPU) {
|
if (type == Type::CPU || type == Type::CachedCPU) {
|
||||||
CollectChangedRanges(!enable, index, untracked_words[index], mask, ranges);
|
CollectChangedRanges(!enable || keep_tracking, index, untracked_words[index], mask, ranges);
|
||||||
}
|
}
|
||||||
if (enable) {
|
if (enable) {
|
||||||
state_words[index] |= mask;
|
state_words[index] |= mask;
|
||||||
if (type == Type::CPU || type == Type::CachedCPU)
|
if (type == Type::CPU || type == Type::CachedCPU)
|
||||||
untracked_words[index] |= mask;
|
untracked_words[index] = (untracked_words[index] & ~mask) | (keep_tracking ? u64{0} : mask);
|
||||||
if (type == Type::CPU)
|
if (type == Type::CPU)
|
||||||
cached_words[index] &= ~mask;
|
cached_words[index] &= ~mask;
|
||||||
} else {
|
} else {
|
||||||
@@ -139,7 +140,7 @@ struct WordManager {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!ranges.empty()) {
|
if (!ranges.empty()) {
|
||||||
ApplyCollectedRanges(ranges, (!enable) ? 1 : -1);
|
ApplyCollectedRanges(ranges, (!enable || keep_tracking) ? 1 : -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -234,6 +234,10 @@ struct GPU::Impl {
|
|||||||
return renderer->ReadRasterizer()->OnCPUWrite(addr, size);
|
return renderer->ReadRasterizer()->OnCPUWrite(addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MustFlushRegion(DAddr addr, u64 size) {
|
||||||
|
return renderer->ReadRasterizer()->MustFlushRegion(addr, size);
|
||||||
|
}
|
||||||
|
|
||||||
/// Notify rasterizer that any caches of the specified region should be flushed and invalidated
|
/// Notify rasterizer that any caches of the specified region should be flushed and invalidated
|
||||||
void FlushAndInvalidateRegion(DAddr addr, u64 size) {
|
void FlushAndInvalidateRegion(DAddr addr, u64 size) {
|
||||||
gpu_thread.FlushAndInvalidateRegion(addr, size, is_async);
|
gpu_thread.FlushAndInvalidateRegion(addr, size, is_async);
|
||||||
@@ -526,6 +530,10 @@ bool GPU::OnCPUWrite(DAddr addr, u64 size) {
|
|||||||
return impl->OnCPUWrite(addr, size);
|
return impl->OnCPUWrite(addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GPU::MustFlushRegion(DAddr addr, u64 size) {
|
||||||
|
return impl->MustFlushRegion(addr, size);
|
||||||
|
}
|
||||||
|
|
||||||
void GPU::FlushAndInvalidateRegion(DAddr addr, u64 size) {
|
void GPU::FlushAndInvalidateRegion(DAddr addr, u64 size) {
|
||||||
impl->FlushAndInvalidateRegion(addr, size);
|
impl->FlushAndInvalidateRegion(addr, size);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -254,6 +254,9 @@ public:
|
|||||||
/// sensible, false otherwise, addr and size must be a valid combination
|
/// sensible, false otherwise, addr and size must be a valid combination
|
||||||
bool OnCPUWrite(DAddr addr, u64 size);
|
bool OnCPUWrite(DAddr addr, u64 size);
|
||||||
|
|
||||||
|
/// Check whether this range still has GPU writes that must be flushed to CPU memory.
|
||||||
|
bool MustFlushRegion(DAddr addr, u64 size);
|
||||||
|
|
||||||
/// Notify rasterizer that any caches of the specified region should be flushed and invalidated
|
/// Notify rasterizer that any caches of the specified region should be flushed and invalidated
|
||||||
void FlushAndInvalidateRegion(DAddr addr, u64 size);
|
void FlushAndInvalidateRegion(DAddr addr, u64 size);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user