This reverts commit c9a3baab5d
.
this commit caused issues in ender magnolia or something, need to make
sure I didn't mess up the revert
Reviewed-on: #382
Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@outlook.com>
This commit is contained in:
parent
0f625fc93c
commit
b4b361e5e9
11 changed files with 306 additions and 93 deletions
|
@ -262,23 +262,18 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (BOOST_NO_HEADERS)
|
if (BOOST_NO_HEADERS)
|
||||||
target_link_libraries(common PUBLIC Boost::algorithm Boost::icl Boost::pool)
|
target_link_libraries(common PUBLIC Boost::algorithm Boost::icl Boost::pool)
|
||||||
else()
|
else()
|
||||||
target_link_libraries(common PUBLIC Boost::headers)
|
target_link_libraries(common PUBLIC Boost::headers)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (lz4_ADDED)
|
if (lz4_ADDED)
|
||||||
target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib)
|
target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(common PUBLIC fmt::fmt stb::headers Threads::Threads)
|
target_link_libraries(common PUBLIC fmt::fmt stb::headers Threads::Threads)
|
||||||
target_link_libraries(common PRIVATE lz4::lz4 LLVM::Demangle zstd::zstd)
|
target_link_libraries(common PRIVATE lz4::lz4 LLVM::Demangle zstd::zstd)
|
||||||
|
|
||||||
if (TARGET unordered_dense::unordered_dense)
|
|
||||||
# weird quirk of system installs
|
|
||||||
target_link_libraries(common PUBLIC unordered_dense::unordered_dense)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(ANDROID)
|
if(ANDROID)
|
||||||
# For ASharedMemory_create
|
# For ASharedMemory_create
|
||||||
target_link_libraries(common PRIVATE android)
|
target_link_libraries(common PRIVATE android)
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -35,60 +33,68 @@ HeapTracker::~HeapTracker() = default;
|
||||||
|
|
||||||
void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
||||||
MemoryPermission perm, bool is_separate_heap) {
|
MemoryPermission perm, bool is_separate_heap) {
|
||||||
bool rebuild_required = false;
|
|
||||||
// When mapping other memory, map pages immediately.
|
// When mapping other memory, map pages immediately.
|
||||||
if (!is_separate_heap) {
|
if (!is_separate_heap) {
|
||||||
m_buffer.Map(virtual_offset, host_offset, length, perm, false);
|
m_buffer.Map(virtual_offset, host_offset, length, perm, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// We are mapping part of a separate heap and insert into mappings.
|
// We are mapping part of a separate heap.
|
||||||
std::scoped_lock lk{m_lock};
|
std::scoped_lock lk{m_lock};
|
||||||
m_map_count++;
|
|
||||||
const auto it = m_mappings.insert_or_assign(virtual_offset, SeparateHeapMap{
|
auto* const map = new SeparateHeapMap{
|
||||||
|
.vaddr = virtual_offset,
|
||||||
.paddr = host_offset,
|
.paddr = host_offset,
|
||||||
.size = length,
|
.size = length,
|
||||||
.tick = m_tick++,
|
.tick = m_tick++,
|
||||||
.perm = perm,
|
.perm = perm,
|
||||||
.is_resident = false,
|
.is_resident = false,
|
||||||
});
|
};
|
||||||
// Update tick before possible rebuild.
|
|
||||||
it.first->second.tick = m_tick++;
|
// Insert into mappings.
|
||||||
// Check if we need to rebuild.
|
m_map_count++;
|
||||||
if (m_resident_map_count >= m_max_resident_map_count)
|
m_mappings.insert(*map);
|
||||||
rebuild_required = true;
|
|
||||||
// Map the area.
|
|
||||||
m_buffer.Map(it.first->first, it.first->second.paddr, it.first->second.size, it.first->second.perm, false);
|
|
||||||
// This map is now resident.
|
|
||||||
it.first->second.is_resident = true;
|
|
||||||
m_resident_map_count++;
|
|
||||||
m_resident_mappings.insert(*it.first);
|
|
||||||
}
|
}
|
||||||
// A rebuild was required, so perform it now.
|
|
||||||
if (rebuild_required)
|
// Finally, map.
|
||||||
this->RebuildSeparateHeapAddressSpace();
|
this->DeferredMapSeparateHeap(virtual_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_heap) {
|
void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_heap) {
|
||||||
// If this is a separate heap...
|
// If this is a separate heap...
|
||||||
if (is_separate_heap) {
|
if (is_separate_heap) {
|
||||||
std::scoped_lock lk{m_lock};
|
std::scoped_lock lk{m_lock};
|
||||||
|
|
||||||
|
const SeparateHeapMap key{
|
||||||
|
.vaddr = virtual_offset,
|
||||||
|
};
|
||||||
|
|
||||||
// Split at the boundaries of the region we are removing.
|
// Split at the boundaries of the region we are removing.
|
||||||
this->SplitHeapMapLocked(virtual_offset);
|
this->SplitHeapMapLocked(virtual_offset);
|
||||||
this->SplitHeapMapLocked(virtual_offset + size);
|
this->SplitHeapMapLocked(virtual_offset + size);
|
||||||
|
|
||||||
// Erase all mappings in range.
|
// Erase all mappings in range.
|
||||||
auto it = m_mappings.find(virtual_offset);
|
auto it = m_mappings.find(key);
|
||||||
while (it != m_mappings.end() && it->first < virtual_offset + size) {
|
while (it != m_mappings.end() && it->vaddr < virtual_offset + size) {
|
||||||
|
// Get underlying item.
|
||||||
|
auto* const item = std::addressof(*it);
|
||||||
|
|
||||||
// If resident, erase from resident map.
|
// If resident, erase from resident map.
|
||||||
if (it->second.is_resident) {
|
if (item->is_resident) {
|
||||||
ASSERT(--m_resident_map_count >= 0);
|
ASSERT(--m_resident_map_count >= 0);
|
||||||
m_resident_mappings.erase(m_resident_mappings.find(it->first));
|
m_resident_mappings.erase(m_resident_mappings.iterator_to(*item));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Erase from map.
|
// Erase from map.
|
||||||
ASSERT(--m_map_count >= 0);
|
ASSERT(--m_map_count >= 0);
|
||||||
it = m_mappings.erase(it);
|
it = m_mappings.erase(it);
|
||||||
|
|
||||||
|
// Free the item.
|
||||||
|
delete item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmap pages.
|
// Unmap pages.
|
||||||
m_buffer.Unmap(virtual_offset, size, false);
|
m_buffer.Unmap(virtual_offset, size, false);
|
||||||
}
|
}
|
||||||
|
@ -110,51 +116,110 @@ void HeapTracker::Protect(size_t virtual_offset, size_t size, MemoryPermission p
|
||||||
|
|
||||||
{
|
{
|
||||||
std::scoped_lock lk2{m_lock};
|
std::scoped_lock lk2{m_lock};
|
||||||
|
|
||||||
|
const SeparateHeapMap key{
|
||||||
|
.vaddr = next,
|
||||||
|
};
|
||||||
|
|
||||||
// Try to get the next mapping corresponding to this address.
|
// Try to get the next mapping corresponding to this address.
|
||||||
const auto it = m_mappings.find(next);
|
const auto it = m_mappings.nfind(key);
|
||||||
|
|
||||||
if (it == m_mappings.end()) {
|
if (it == m_mappings.end()) {
|
||||||
// There are no separate heap mappings remaining.
|
// There are no separate heap mappings remaining.
|
||||||
next = end;
|
next = end;
|
||||||
should_protect = true;
|
should_protect = true;
|
||||||
} else if (it->first == cur) {
|
} else if (it->vaddr == cur) {
|
||||||
// We are in range.
|
// We are in range.
|
||||||
// Update permission bits.
|
// Update permission bits.
|
||||||
it->second.perm = perm;
|
it->perm = perm;
|
||||||
|
|
||||||
// Determine next address and whether we should protect.
|
// Determine next address and whether we should protect.
|
||||||
next = cur + it->second.size;
|
next = cur + it->size;
|
||||||
should_protect = it->second.is_resident;
|
should_protect = it->is_resident;
|
||||||
} else /* if (it->vaddr > cur) */ {
|
} else /* if (it->vaddr > cur) */ {
|
||||||
// We weren't in range, but there is a block coming up that will be.
|
// We weren't in range, but there is a block coming up that will be.
|
||||||
next = it->first;
|
next = it->vaddr;
|
||||||
should_protect = true;
|
should_protect = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clamp to end.
|
// Clamp to end.
|
||||||
next = std::min(next, end);
|
next = std::min(next, end);
|
||||||
|
|
||||||
// Reprotect, if we need to.
|
// Reprotect, if we need to.
|
||||||
if (should_protect)
|
if (should_protect) {
|
||||||
m_buffer.Protect(cur, next - cur, perm);
|
m_buffer.Protect(cur, next - cur, perm);
|
||||||
|
}
|
||||||
|
|
||||||
// Advance.
|
// Advance.
|
||||||
cur = next;
|
cur = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HeapTracker::DeferredMapSeparateHeap(u8* fault_address) {
|
||||||
|
if (m_buffer.IsInVirtualRange(fault_address)) {
|
||||||
|
return this->DeferredMapSeparateHeap(fault_address - m_buffer.VirtualBasePointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) {
|
||||||
|
bool rebuild_required = false;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::scoped_lock lk{m_lock};
|
||||||
|
|
||||||
|
// Check to ensure this was a non-resident separate heap mapping.
|
||||||
|
const auto it = this->GetNearestHeapMapLocked(virtual_offset);
|
||||||
|
if (it == m_mappings.end() || it->is_resident) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update tick before possible rebuild.
|
||||||
|
it->tick = m_tick++;
|
||||||
|
|
||||||
|
// Check if we need to rebuild.
|
||||||
|
if (m_resident_map_count > m_max_resident_map_count) {
|
||||||
|
rebuild_required = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map the area.
|
||||||
|
m_buffer.Map(it->vaddr, it->paddr, it->size, it->perm, false);
|
||||||
|
|
||||||
|
// This map is now resident.
|
||||||
|
it->is_resident = true;
|
||||||
|
m_resident_map_count++;
|
||||||
|
m_resident_mappings.insert(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rebuild_required) {
|
||||||
|
// A rebuild was required, so perform it now.
|
||||||
|
this->RebuildSeparateHeapAddressSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void HeapTracker::RebuildSeparateHeapAddressSpace() {
|
void HeapTracker::RebuildSeparateHeapAddressSpace() {
|
||||||
std::scoped_lock lk{m_rebuild_lock, m_lock};
|
std::scoped_lock lk{m_rebuild_lock, m_lock};
|
||||||
|
|
||||||
ASSERT(!m_resident_mappings.empty());
|
ASSERT(!m_resident_mappings.empty());
|
||||||
|
|
||||||
// Dump half of the mappings.
|
// Dump half of the mappings.
|
||||||
|
//
|
||||||
// Despite being worse in theory, this has proven to be better in practice than more
|
// Despite being worse in theory, this has proven to be better in practice than more
|
||||||
// regularly dumping a smaller amount, because it significantly reduces average case
|
// regularly dumping a smaller amount, because it significantly reduces average case
|
||||||
// lock contention.
|
// lock contention.
|
||||||
std::size_t const desired_count = std::min(m_resident_map_count, m_max_resident_map_count) / 2;
|
const size_t desired_count = std::min(m_resident_map_count, m_max_resident_map_count) / 2;
|
||||||
std::size_t const evict_count = m_resident_map_count - desired_count;
|
const size_t evict_count = m_resident_map_count - desired_count;
|
||||||
auto it = m_resident_mappings.begin();
|
auto it = m_resident_mappings.begin();
|
||||||
for (std::size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) {
|
|
||||||
|
for (size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) {
|
||||||
// Unmark and unmap.
|
// Unmark and unmap.
|
||||||
it->second.is_resident = false;
|
it->is_resident = false;
|
||||||
m_buffer.Unmap(it->first, it->second.size, false);
|
m_buffer.Unmap(it->vaddr, it->size, false);
|
||||||
|
|
||||||
// Advance.
|
// Advance.
|
||||||
ASSERT(--m_resident_map_count >= 0);
|
ASSERT(--m_resident_map_count >= 0);
|
||||||
it = m_resident_mappings.erase(it);
|
it = m_resident_mappings.erase(it);
|
||||||
|
@ -163,32 +228,53 @@ void HeapTracker::RebuildSeparateHeapAddressSpace() {
|
||||||
|
|
||||||
void HeapTracker::SplitHeapMap(VAddr offset, size_t size) {
|
void HeapTracker::SplitHeapMap(VAddr offset, size_t size) {
|
||||||
std::scoped_lock lk{m_lock};
|
std::scoped_lock lk{m_lock};
|
||||||
|
|
||||||
this->SplitHeapMapLocked(offset);
|
this->SplitHeapMapLocked(offset);
|
||||||
this->SplitHeapMapLocked(offset + size);
|
this->SplitHeapMapLocked(offset + size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeapTracker::SplitHeapMapLocked(VAddr offset) {
|
void HeapTracker::SplitHeapMapLocked(VAddr offset) {
|
||||||
auto it = this->GetNearestHeapMapLocked(offset);
|
const auto it = this->GetNearestHeapMapLocked(offset);
|
||||||
if (it != m_mappings.end() && it->first != offset) {
|
if (it == m_mappings.end() || it->vaddr == offset) {
|
||||||
// Adjust left iterator
|
// Not contained or no split required.
|
||||||
auto const orig_size = it->second.size;
|
return;
|
||||||
auto const left_size = offset - it->first;
|
}
|
||||||
it->second.size = left_size;
|
|
||||||
// Insert the new right map.
|
// Cache the original values.
|
||||||
auto const right = SeparateHeapMap{
|
auto* const left = std::addressof(*it);
|
||||||
.paddr = it->second.paddr + left_size,
|
const size_t orig_size = left->size;
|
||||||
.size = orig_size - left_size,
|
|
||||||
.tick = it->second.tick,
|
// Adjust the left map.
|
||||||
.perm = it->second.perm,
|
const size_t left_size = offset - left->vaddr;
|
||||||
.is_resident = it->second.is_resident,
|
left->size = left_size;
|
||||||
};
|
|
||||||
m_map_count++;
|
// Create the new right map.
|
||||||
auto rit = m_mappings.insert_or_assign(it->first + left_size, right);
|
auto* const right = new SeparateHeapMap{
|
||||||
if (rit.first->second.is_resident) {
|
.vaddr = left->vaddr + left_size,
|
||||||
m_resident_map_count++;
|
.paddr = left->paddr + left_size,
|
||||||
m_resident_mappings.insert(*rit.first);
|
.size = orig_size - left_size,
|
||||||
}
|
.tick = left->tick,
|
||||||
|
.perm = left->perm,
|
||||||
|
.is_resident = left->is_resident,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Insert the new right map.
|
||||||
|
m_map_count++;
|
||||||
|
m_mappings.insert(*right);
|
||||||
|
|
||||||
|
// If resident, also insert into resident map.
|
||||||
|
if (right->is_resident) {
|
||||||
|
m_resident_map_count++;
|
||||||
|
m_resident_mappings.insert(*right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) {
|
||||||
|
const SeparateHeapMap key{
|
||||||
|
.vaddr = offset,
|
||||||
|
};
|
||||||
|
|
||||||
|
return m_mappings.find(key);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -1,55 +1,93 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <set>
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
#include <ankerl/unordered_dense.h>
|
|
||||||
#include "common/host_memory.h"
|
#include "common/host_memory.h"
|
||||||
|
#include "common/intrusive_red_black_tree.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
struct SeparateHeapMap {
|
struct SeparateHeapMap {
|
||||||
PAddr paddr{}; //8
|
Common::IntrusiveRedBlackTreeNode addr_node{};
|
||||||
std::size_t size{}; //8 (16)
|
Common::IntrusiveRedBlackTreeNode tick_node{};
|
||||||
std::size_t tick{}; //8 (24)
|
VAddr vaddr{};
|
||||||
// 4 bits needed, sync with host_memory.h if needed
|
PAddr paddr{};
|
||||||
MemoryPermission perm : 4 = MemoryPermission::Read;
|
size_t size{};
|
||||||
bool is_resident : 1 = false;
|
size_t tick{};
|
||||||
|
MemoryPermission perm{};
|
||||||
|
bool is_resident{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SeparateHeapMapAddrComparator {
|
||||||
|
static constexpr int Compare(const SeparateHeapMap& lhs, const SeparateHeapMap& rhs) {
|
||||||
|
if (lhs.vaddr < rhs.vaddr) {
|
||||||
|
return -1;
|
||||||
|
} else if (lhs.vaddr <= (rhs.vaddr + rhs.size - 1)) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SeparateHeapMapTickComparator {
|
||||||
|
static constexpr int Compare(const SeparateHeapMap& lhs, const SeparateHeapMap& rhs) {
|
||||||
|
if (lhs.tick < rhs.tick) {
|
||||||
|
return -1;
|
||||||
|
} else if (lhs.tick > rhs.tick) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return SeparateHeapMapAddrComparator::Compare(lhs, rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
static_assert(sizeof(SeparateHeapMap) == 32); //half a cache line! good for coherency
|
|
||||||
|
|
||||||
class HeapTracker {
|
class HeapTracker {
|
||||||
public:
|
public:
|
||||||
explicit HeapTracker(Common::HostMemory& buffer);
|
explicit HeapTracker(Common::HostMemory& buffer);
|
||||||
~HeapTracker();
|
~HeapTracker();
|
||||||
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm, bool is_separate_heap);
|
|
||||||
|
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm,
|
||||||
|
bool is_separate_heap);
|
||||||
void Unmap(size_t virtual_offset, size_t size, bool is_separate_heap);
|
void Unmap(size_t virtual_offset, size_t size, bool is_separate_heap);
|
||||||
void Protect(size_t virtual_offset, size_t length, MemoryPermission perm);
|
void Protect(size_t virtual_offset, size_t length, MemoryPermission perm);
|
||||||
inline u8* VirtualBasePointer() noexcept {
|
u8* VirtualBasePointer() {
|
||||||
return m_buffer.VirtualBasePointer();
|
return m_buffer.VirtualBasePointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DeferredMapSeparateHeap(u8* fault_address);
|
||||||
|
bool DeferredMapSeparateHeap(size_t virtual_offset);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// TODO: You may want to "fake-map" the first 2GB of 64-bit address space
|
using AddrTreeTraits =
|
||||||
// and dedicate it entirely to a recursive PTE mapping :)
|
Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::addr_node>;
|
||||||
// However Ankerl is way better than using an RB tree, in all senses
|
using AddrTree = AddrTreeTraits::TreeType<SeparateHeapMapAddrComparator>;
|
||||||
using AddrTree = ankerl::unordered_dense::map<VAddr, SeparateHeapMap>;
|
|
||||||
AddrTree m_mappings;
|
using TickTreeTraits =
|
||||||
using TicksTree = ankerl::unordered_dense::map<VAddr, SeparateHeapMap>;
|
Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::tick_node>;
|
||||||
TicksTree m_resident_mappings;
|
using TickTree = TickTreeTraits::TreeType<SeparateHeapMapTickComparator>;
|
||||||
|
|
||||||
|
AddrTree m_mappings{};
|
||||||
|
TickTree m_resident_mappings{};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SplitHeapMap(VAddr offset, size_t size);
|
void SplitHeapMap(VAddr offset, size_t size);
|
||||||
void SplitHeapMapLocked(VAddr offset);
|
void SplitHeapMapLocked(VAddr offset);
|
||||||
|
|
||||||
|
AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
|
||||||
|
|
||||||
void RebuildSeparateHeapAddressSpace();
|
void RebuildSeparateHeapAddressSpace();
|
||||||
inline HeapTracker::AddrTree::iterator GetNearestHeapMapLocked(VAddr offset) noexcept {
|
|
||||||
return m_mappings.find(offset);
|
|
||||||
}
|
|
||||||
private:
|
private:
|
||||||
Common::HostMemory& m_buffer;
|
Common::HostMemory& m_buffer;
|
||||||
const s64 m_max_resident_map_count;
|
const s64 m_max_resident_map_count;
|
||||||
|
|
||||||
std::shared_mutex m_rebuild_lock{};
|
std::shared_mutex m_rebuild_lock{};
|
||||||
std::mutex m_lock{};
|
std::mutex m_lock{};
|
||||||
s64 m_map_count{};
|
s64 m_map_count{};
|
||||||
|
|
|
@ -3,9 +3,47 @@
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
|
||||||
//#include "common/signal_chain.h"
|
#include "common/signal_chain.h"
|
||||||
|
|
||||||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||||
//#include "core/hle/kernel/k_process.h"
|
#include "core/hle/kernel/k_process.h"
|
||||||
//#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
thread_local Core::Memory::Memory* g_current_memory{};
|
||||||
|
std::once_flag g_registered{};
|
||||||
|
struct sigaction g_old_segv {};
|
||||||
|
|
||||||
|
void HandleSigSegv(int sig, siginfo_t* info, void* ctx) {
|
||||||
|
if (g_current_memory && g_current_memory->InvalidateSeparateHeap(info->si_addr)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_old_segv.sa_sigaction(sig, info, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
ScopedJitExecution::ScopedJitExecution(Kernel::KProcess* process) {
|
||||||
|
g_current_memory = std::addressof(process->GetMemory());
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedJitExecution::~ScopedJitExecution() {
|
||||||
|
g_current_memory = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScopedJitExecution::RegisterHandler() {
|
||||||
|
std::call_once(g_registered, [] {
|
||||||
|
struct sigaction sa {};
|
||||||
|
sa.sa_sigaction = &HandleSigSegv;
|
||||||
|
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||||
|
Common::SigAction(SIGSEGV, std::addressof(sa), std::addressof(g_old_segv));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Core
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -26,4 +26,24 @@ constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
|
||||||
return static_cast<HaltReason>(hr);
|
return static_cast<HaltReason>(hr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
|
||||||
|
class ScopedJitExecution {
|
||||||
|
public:
|
||||||
|
explicit ScopedJitExecution(Kernel::KProcess* process);
|
||||||
|
~ScopedJitExecution();
|
||||||
|
static void RegisterHandler();
|
||||||
|
};
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
class ScopedJitExecution {
|
||||||
|
public:
|
||||||
|
explicit ScopedJitExecution(Kernel::KProcess* process) {}
|
||||||
|
~ScopedJitExecution() {}
|
||||||
|
static void RegisterHandler() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|
|
@ -343,11 +343,15 @@ bool ArmDynarmic32::IsInThumbMode() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) {
|
HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) {
|
||||||
|
ScopedJitExecution sj(thread->GetOwnerProcess());
|
||||||
|
|
||||||
m_jit->ClearExclusiveState();
|
m_jit->ClearExclusiveState();
|
||||||
return TranslateHaltReason(m_jit->Run());
|
return TranslateHaltReason(m_jit->Run());
|
||||||
}
|
}
|
||||||
|
|
||||||
HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) {
|
HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) {
|
||||||
|
ScopedJitExecution sj(thread->GetOwnerProcess());
|
||||||
|
|
||||||
m_jit->ClearExclusiveState();
|
m_jit->ClearExclusiveState();
|
||||||
return TranslateHaltReason(m_jit->Step());
|
return TranslateHaltReason(m_jit->Step());
|
||||||
}
|
}
|
||||||
|
@ -389,6 +393,7 @@ ArmDynarmic32::ArmDynarmic32(System& system, bool uses_wall_clock, Kernel::KProc
|
||||||
m_cp15(std::make_shared<DynarmicCP15>(*this)), m_core_index{core_index} {
|
m_cp15(std::make_shared<DynarmicCP15>(*this)), m_core_index{core_index} {
|
||||||
auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl();
|
auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl();
|
||||||
m_jit = MakeJit(&page_table_impl);
|
m_jit = MakeJit(&page_table_impl);
|
||||||
|
ScopedJitExecution::RegisterHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
ArmDynarmic32::~ArmDynarmic32() = default;
|
ArmDynarmic32::~ArmDynarmic32() = default;
|
||||||
|
|
|
@ -374,11 +374,15 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
|
||||||
}
|
}
|
||||||
|
|
||||||
HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) {
|
HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) {
|
||||||
|
ScopedJitExecution sj(thread->GetOwnerProcess());
|
||||||
|
|
||||||
m_jit->ClearExclusiveState();
|
m_jit->ClearExclusiveState();
|
||||||
return TranslateHaltReason(m_jit->Run());
|
return TranslateHaltReason(m_jit->Run());
|
||||||
}
|
}
|
||||||
|
|
||||||
HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) {
|
HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) {
|
||||||
|
ScopedJitExecution sj(thread->GetOwnerProcess());
|
||||||
|
|
||||||
m_jit->ClearExclusiveState();
|
m_jit->ClearExclusiveState();
|
||||||
return TranslateHaltReason(m_jit->Step());
|
return TranslateHaltReason(m_jit->Step());
|
||||||
}
|
}
|
||||||
|
@ -418,6 +422,7 @@ ArmDynarmic64::ArmDynarmic64(System& system, bool uses_wall_clock, Kernel::KProc
|
||||||
auto& page_table = process->GetPageTable().GetBasePageTable();
|
auto& page_table = process->GetPageTable().GetBasePageTable();
|
||||||
auto& page_table_impl = page_table.GetImpl();
|
auto& page_table_impl = page_table.GetImpl();
|
||||||
m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth());
|
m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth());
|
||||||
|
ScopedJitExecution::RegisterHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
ArmDynarmic64::~ArmDynarmic64() = default;
|
ArmDynarmic64::~ArmDynarmic64() = default;
|
||||||
|
|
|
@ -1266,6 +1266,10 @@ void KProcess::InitializeInterfaces() {
|
||||||
|
|
||||||
#ifdef HAS_NCE
|
#ifdef HAS_NCE
|
||||||
if (this->IsApplication() && Settings::IsNceEnabled()) {
|
if (this->IsApplication() && Settings::IsNceEnabled()) {
|
||||||
|
// Register the scoped JIT handler before creating any NCE instances
|
||||||
|
// so that its signal handler will appear first in the signal chain.
|
||||||
|
Core::ScopedJitExecution::RegisterHandler();
|
||||||
|
|
||||||
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
||||||
m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i);
|
m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i);
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,8 @@ struct Memory::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
buffer.emplace(system.DeviceMemory().buffer);
|
heap_tracker.emplace(system.DeviceMemory().buffer);
|
||||||
|
buffer = std::addressof(*heap_tracker);
|
||||||
#else
|
#else
|
||||||
buffer = std::addressof(system.DeviceMemory().buffer);
|
buffer = std::addressof(system.DeviceMemory().buffer);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1023,8 +1024,9 @@ struct Memory::Impl {
|
||||||
std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers;
|
std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers;
|
||||||
std::mutex sys_core_guard;
|
std::mutex sys_core_guard;
|
||||||
|
|
||||||
|
std::optional<Common::HeapTracker> heap_tracker;
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
std::optional<Common::HeapTracker> buffer;
|
Common::HeapTracker* buffer{};
|
||||||
#else
|
#else
|
||||||
Common::HostMemory* buffer{};
|
Common::HostMemory* buffer{};
|
||||||
#endif
|
#endif
|
||||||
|
@ -1228,7 +1230,22 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
|
||||||
if (rasterizer) {
|
if (rasterizer) {
|
||||||
impl->InvalidateGPUMemory(ptr, size);
|
impl->InvalidateGPUMemory(ptr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
if (!rasterizer && mapped) {
|
||||||
|
impl->buffer->DeferredMapSeparateHeap(GetInteger(vaddr));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return mapped && ptr != nullptr;
|
return mapped && ptr != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Memory::InvalidateSeparateHeap(void* fault_address) {
|
||||||
|
#ifdef __linux__
|
||||||
|
return impl->buffer->DeferredMapSeparateHeap(static_cast<u8*>(fault_address));
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Core::Memory
|
} // namespace Core::Memory
|
||||||
|
|
|
@ -487,8 +487,13 @@ public:
|
||||||
* marked as debug or non-debug.
|
* marked as debug or non-debug.
|
||||||
*/
|
*/
|
||||||
void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug);
|
void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug);
|
||||||
|
|
||||||
void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
|
void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
|
||||||
|
|
||||||
bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size);
|
bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size);
|
||||||
|
|
||||||
|
bool InvalidateSeparateHeap(void* fault_address);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
|
|
||||||
|
|
2
src/dynarmic/externals/CMakeLists.txt
vendored
2
src/dynarmic/externals/CMakeLists.txt
vendored
|
@ -60,7 +60,7 @@ AddJsonPackage(
|
||||||
# endif()
|
# endif()
|
||||||
# endif()
|
# endif()
|
||||||
|
|
||||||
# unordered_dense - already in root
|
# unordered_dense
|
||||||
|
|
||||||
# AddJsonPackage(
|
# AddJsonPackage(
|
||||||
# NAME unordered-dense
|
# NAME unordered-dense
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue