mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-04-10 03:18:55 +02:00
Compare commits
7 commits
1bd04ee5a9
...
2f04b26392
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f04b26392 | ||
|
|
36dee9bad1 | ||
|
|
fcab05e9e2 | ||
|
|
6960994bf4 | ||
|
|
a54eadb12b | ||
|
|
9ace6742d7 | ||
|
|
79f29abcba |
13 changed files with 100 additions and 526 deletions
|
|
@ -334,7 +334,7 @@ pacman -Syuu --needed --noconfirm $packages
|
|||
<summary>HaikuOS</summary>
|
||||
|
||||
```sh
|
||||
pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel boost1.89_devel vulkan_devel qt6_base_devel qt6_declarative_devel libsdl2_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt5_devel qt6_5compat_devel glslang qt6_devel qt6_charts_devel
|
||||
pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel boost1.90_devel vulkan_devel qt6_base_devel qt6_declarative_devel libsdl2_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt5_devel qt6_5compat_devel glslang qt6_devel qt6_charts_devel
|
||||
```
|
||||
|
||||
[Caveats](./Caveats.md#haikuos).
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ Use this when you need to connect to a multiplayer room for LDN functionality in
|
|||
- Multiplayer Options Configured in Eden Settings
|
||||
- Network Access
|
||||
|
||||
## Steps
|
||||
### Steps
|
||||
There are 2 primary methods that you can use to connect to an existing room, depending on how the room is hosted.
|
||||
|
||||
- Joining a Public Lobby
|
||||
|
|
@ -70,7 +70,7 @@ There are 2 primary methods that you can use to connect to an existing room, dep
|
|||
|
||||
</aside>
|
||||
|
||||
### Joining a Public Lobby
|
||||
## Joining a Public Lobby
|
||||
1. Open Eden and navigate to *Multiplayer → Browse Public Game Lobby*.
|
||||
2. The **Public Room Browser** will now open and display a list of publicly accessible rooms. Find one you want to connect to and double click it.
|
||||
|
||||
|
|
@ -90,7 +90,7 @@ If the hoster has not made the lobby public, or you don't want to find it in the
|
|||
|
||||
---
|
||||
|
||||
# Hosting a Multiplayer Room
|
||||
## Hosting a Multiplayer Room
|
||||
Use this guide for when you want to host a multiplayer lobby to play with others in Eden. In order to have someone access the room from outside your local network, see the *Access Your Multiplayer Room Externally* section for next steps.
|
||||
|
||||
**Click [Here](https://evilperson1337.notion.site/Hosting-a-Multiplayer-Room-2c357c2edaf6819481dbe8a99926cea2) for a version of this guide with images & visual elements.**
|
||||
|
|
@ -100,7 +100,7 @@ Use this guide for when you want to host a multiplayer lobby to play with others
|
|||
- Network Access
|
||||
- Ability to allow programs through the firewall on your device.
|
||||
|
||||
## Steps
|
||||
### Steps
|
||||
1. Open Eden and navigate to *Emulation → Multiplayer → Create Room.*
|
||||
2. Fill out the following information in the popup dialog box.
|
||||
|
||||
|
|
@ -120,7 +120,7 @@ Use this guide for when you want to host a multiplayer lobby to play with others
|
|||
|
||||
---
|
||||
|
||||
# Access Your Multiplayer Room Externally
|
||||
## Access Your Multiplayer Room Externally
|
||||
Quite often the person with whom you want to play is located off of your internal network (LAN). If you want to host a room and play with them you will need to get your devices to communicate with each other. This guide will go over your options on how to do this so that you can play together.
|
||||
|
||||
**Click [Here](https://evilperson1337.notion.site/Access-Your-Multiplayer-Room-Externally-2c357c2edaf681c0ab2ce2ee624d809d) for a version of this guide with images & visual elements.**
|
||||
|
|
@ -129,9 +129,9 @@ Quite often the person with whom you want to play is located off of your interna
|
|||
- Eden set up and Functioning
|
||||
- Network Access
|
||||
|
||||
## Options
|
||||
### Options
|
||||
|
||||
### Port Forwarding
|
||||
#### Port Forwarding
|
||||
|
||||
- **Difficulty Level**: High
|
||||
|
||||
|
|
@ -148,8 +148,9 @@ The process works by creating a static mapping—often called a “port-forward
|
|||
|
||||
For our purposes we would pick the port we want to expose (*e.g. 24872*) and we would access our router's configuration and create a port-forward rule to send the traffic from an external connection to your local machine over our specified port (*24872)*. The exact way to do so, varies greatly by router manufacturer - and sometimes require contacting your ISP to do so depending on your agreement. You can look up your router on [*portforward.com*](https://portforward.com/router.htm) which may have instructions on how to do so for your specific equipment. If it is not there, you will have to use Google/ChatGPT to determine the steps for your equipment.
|
||||
|
||||
Remember you can't have one port open for multiple devices at the same time - you must only host from one device (or do more convoluted networking which we will not cover here).
|
||||
|
||||
### Use a Tunnelling Service
|
||||
#### Use a Tunnelling Service
|
||||
- **Difficulty Level**: Easy
|
||||
|
||||
<aside>
|
||||
|
|
@ -167,7 +168,7 @@ For our purposes we would spawn the listener for the port that way chose when ho
|
|||
- [*Playit.GG*](https://playit.gg/)
|
||||
|
||||
|
||||
### Use a VPN Service
|
||||
#### Use a VPN Service
|
||||
|
||||
- **Difficulty**: Easy
|
||||
|
||||
|
|
@ -189,7 +190,7 @@ The VPN solution is a good compromise between the tunnelling solution and port f
|
|||
|
||||
---
|
||||
|
||||
# Finding the Server Information for a Multiplayer Room
|
||||
## Finding the Server Information for a Multiplayer Room
|
||||
Use this guide when you need to determine the connection information for the Public Multiplayer Lobby you are connected to.
|
||||
|
||||
**Click [Here](https://evilperson1337.notion.site/Finding-the-Server-Information-for-a-Multiplayer-Room-2c557c2edaf6809e94e8ed3429b9eb26) for a version of this guide with images & visual elements.**
|
||||
|
|
@ -198,7 +199,7 @@ Use this guide when you need to determine the connection information for the Pub
|
|||
- Eden set up and configured
|
||||
- Internet Access
|
||||
|
||||
## Steps
|
||||
### Steps
|
||||
|
||||
### Method 1: Grabbing the Address from the Log File
|
||||
1. Open Eden and Connect to the room you want to identify.
|
||||
|
|
@ -222,7 +223,7 @@ Use this guide when you need to determine the connection information for the Pub
|
|||
2. Open the terminal supported by your operating system.
|
||||
3. Run one of the following commands, replacing *<Name>* with the name of the server from step 1.
|
||||
|
||||
### PowerShell Command [Windows Users]
|
||||
#### PowerShell Command [Windows Users]
|
||||
|
||||
```powershell
|
||||
# Calls the API to get the address and port information
|
||||
|
|
@ -235,7 +236,7 @@ Use this guide when you need to determine the connection information for the Pub
|
|||
#}
|
||||
```
|
||||
|
||||
### CURL Command [MacOS/Linux Users] **Requires jq*
|
||||
#### CURL Command [MacOS/Linux Users] **Requires jq*
|
||||
|
||||
```bash
|
||||
# Calls the API to get the address and port information
|
||||
|
|
@ -252,7 +253,7 @@ Use this guide when you need to determine the connection information for the Pub
|
|||
|
||||
---
|
||||
|
||||
# Multiplayer for Local Co-Op Games
|
||||
## Multiplayer for Local Co-Op Games
|
||||
Use this guide when you want to play with a friend on a different system for games that only support local co-op.
|
||||
|
||||
**Click [Here](https://evilperson1337.notion.site/Multiplayer-for-Local-Co-Op-Games-2c657c2edaf680c59975ec6b52022a2d) for a version of this guide with images & visual elements.**
|
||||
|
|
@ -271,7 +272,7 @@ In either situation at its core, we are emulating an input device on the host ma
|
|||
- Parsec is free to use for personal, non-commercial use. For instructions on how to set up an account and install the client you should refer to the Parsec documentation on it's site.
|
||||
- Parsec client installed on your machine and remote (friend's) machine
|
||||
|
||||
## Steps
|
||||
### Steps
|
||||
|
||||
<aside>
|
||||
|
||||
|
|
@ -294,3 +295,22 @@ This guide will assume you are the one hosting the game and go over things *Pars
|
|||
10. Set up the remote player's controller.
|
||||
11. Hit **OK** to apply the changes.
|
||||
12. Launch the game you want to play and enter the co-op mode. How this works depends on the game, so you will have to look in the menus or online to find out.
|
||||
|
||||
## Metaserver troubleshooting
|
||||
|
||||
If you can't connect to the metaserver, it's likely your ISP is blocking the requests.
|
||||
|
||||
### Linux and Steamdeck
|
||||
|
||||
Most Linux systems and Steamdeck should allow to modify the base `/etc/hosts` file, this should fix the DNS lookup issue; hence add the following to said file:
|
||||
```
|
||||
28.165.181.135 api.ynet-fun.xyz api.ynet-fun.xyz
|
||||
```
|
||||
|
||||
### Zapret
|
||||
|
||||
In `lists/list-general.txt` add the following:
|
||||
```
|
||||
api.ynet-fun.xyz
|
||||
ynet-fun.xyz
|
||||
```
|
||||
|
|
|
|||
|
|
@ -66,8 +66,6 @@ add_library(
|
|||
fs/path_util.cpp
|
||||
fs/path_util.h
|
||||
hash.h
|
||||
heap_tracker.cpp
|
||||
heap_tracker.h
|
||||
hex_util.cpp
|
||||
hex_util.h
|
||||
host_memory.cpp
|
||||
|
|
|
|||
|
|
@ -1,282 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fstream>
|
||||
#include "common/heap_tracker.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
namespace {
|
||||
|
||||
s64 GetMaxPermissibleResidentMapCount() {
|
||||
// Default value.
|
||||
s64 value = 65530;
|
||||
|
||||
// Try to read how many mappings we can make.
|
||||
std::ifstream s("/proc/sys/vm/max_map_count");
|
||||
s >> value;
|
||||
|
||||
// Print, for debug.
|
||||
LOG_INFO(HW_Memory, "Current maximum map count: {}", value);
|
||||
|
||||
// Allow 20000 maps for other code and to account for split inaccuracy.
|
||||
return std::max<s64>(value - 20000, 0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
HeapTracker::HeapTracker(Common::HostMemory& buffer)
|
||||
: m_buffer(buffer), m_max_resident_map_count(GetMaxPermissibleResidentMapCount()) {}
|
||||
HeapTracker::~HeapTracker() = default;
|
||||
|
||||
void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
||||
MemoryPermission perm, bool is_separate_heap) {
|
||||
// When mapping other memory, map pages immediately.
|
||||
if (!is_separate_heap) {
|
||||
m_buffer.Map(virtual_offset, host_offset, length, perm, false);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
// We are mapping part of a separate heap.
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
auto* const map = new SeparateHeapMap{
|
||||
.vaddr = virtual_offset,
|
||||
.paddr = host_offset,
|
||||
.size = length,
|
||||
.tick = m_tick++,
|
||||
.perm = perm,
|
||||
.is_resident = false,
|
||||
};
|
||||
|
||||
// Insert into mappings.
|
||||
m_map_count++;
|
||||
m_mappings.insert(*map);
|
||||
}
|
||||
|
||||
// Finally, map.
|
||||
this->DeferredMapSeparateHeap(virtual_offset);
|
||||
}
|
||||
|
||||
void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_heap) {
|
||||
// If this is a separate heap...
|
||||
if (is_separate_heap) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
const SeparateHeapMap key{
|
||||
.vaddr = virtual_offset,
|
||||
};
|
||||
|
||||
// Split at the boundaries of the region we are removing.
|
||||
this->SplitHeapMapLocked(virtual_offset);
|
||||
this->SplitHeapMapLocked(virtual_offset + size);
|
||||
|
||||
// Erase all mappings in range.
|
||||
auto it = m_mappings.find(key);
|
||||
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 (item->is_resident) {
|
||||
ASSERT(--m_resident_map_count >= 0);
|
||||
m_resident_mappings.erase(m_resident_mappings.iterator_to(*item));
|
||||
}
|
||||
|
||||
// Erase from map.
|
||||
ASSERT(--m_map_count >= 0);
|
||||
it = m_mappings.erase(it);
|
||||
|
||||
// Free the item.
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
// Unmap pages.
|
||||
m_buffer.Unmap(virtual_offset, size, false);
|
||||
}
|
||||
|
||||
void HeapTracker::Protect(size_t virtual_offset, size_t size, MemoryPermission perm) {
|
||||
// Ensure no rebuild occurs while reprotecting.
|
||||
std::shared_lock lk{m_rebuild_lock};
|
||||
|
||||
// Split at the boundaries of the region we are reprotecting.
|
||||
this->SplitHeapMap(virtual_offset, size);
|
||||
|
||||
// Declare tracking variables.
|
||||
const VAddr end = virtual_offset + size;
|
||||
VAddr cur = virtual_offset;
|
||||
|
||||
while (cur < end) {
|
||||
VAddr next = cur;
|
||||
bool should_protect = false;
|
||||
|
||||
{
|
||||
std::scoped_lock lk2{m_lock};
|
||||
|
||||
const SeparateHeapMap key{
|
||||
.vaddr = next,
|
||||
};
|
||||
|
||||
// Try to get the next mapping corresponding to this address.
|
||||
const auto it = m_mappings.nfind(key);
|
||||
|
||||
if (it == m_mappings.end()) {
|
||||
// There are no separate heap mappings remaining.
|
||||
next = end;
|
||||
should_protect = true;
|
||||
} else if (it->vaddr == cur) {
|
||||
// We are in range.
|
||||
// Update permission bits.
|
||||
it->perm = perm;
|
||||
|
||||
// Determine next address and whether we should protect.
|
||||
next = cur + it->size;
|
||||
should_protect = it->is_resident;
|
||||
} else /* if (it->vaddr > cur) */ {
|
||||
// We weren't in range, but there is a block coming up that will be.
|
||||
next = it->vaddr;
|
||||
should_protect = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Clamp to end.
|
||||
next = (std::min)(next, end);
|
||||
// Reprotect, if we need to.
|
||||
if (should_protect) {
|
||||
m_buffer.Protect(cur, next - cur, perm);
|
||||
}
|
||||
|
||||
// Advance.
|
||||
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() {
|
||||
std::scoped_lock lk{m_rebuild_lock, m_lock};
|
||||
|
||||
ASSERT(!m_resident_mappings.empty());
|
||||
|
||||
// Dump half of the mappings.
|
||||
//
|
||||
// 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
|
||||
// lock contention.
|
||||
std::size_t const 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;
|
||||
auto it = m_resident_mappings.begin();
|
||||
|
||||
for (size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) {
|
||||
// Unmark and unmap.
|
||||
it->is_resident = false;
|
||||
m_buffer.Unmap(it->vaddr, it->size, false);
|
||||
|
||||
// Advance.
|
||||
ASSERT(--m_resident_map_count >= 0);
|
||||
it = m_resident_mappings.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void HeapTracker::SplitHeapMap(VAddr offset, size_t size) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
this->SplitHeapMapLocked(offset);
|
||||
this->SplitHeapMapLocked(offset + size);
|
||||
}
|
||||
|
||||
void HeapTracker::SplitHeapMapLocked(VAddr offset) {
|
||||
const auto it = this->GetNearestHeapMapLocked(offset);
|
||||
if (it == m_mappings.end() || it->vaddr == offset) {
|
||||
// Not contained or no split required.
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache the original values.
|
||||
auto* const left = std::addressof(*it);
|
||||
const size_t orig_size = left->size;
|
||||
|
||||
// Adjust the left map.
|
||||
const size_t left_size = offset - left->vaddr;
|
||||
left->size = left_size;
|
||||
|
||||
// Create the new right map.
|
||||
auto* const right = new SeparateHeapMap{
|
||||
.vaddr = left->vaddr + left_size,
|
||||
.paddr = left->paddr + left_size,
|
||||
.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
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <shared_mutex>
|
||||
|
||||
#include "common/host_memory.h"
|
||||
#include "common/intrusive_red_black_tree.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
struct SeparateHeapMap {
|
||||
Common::IntrusiveRedBlackTreeNode addr_node{};
|
||||
Common::IntrusiveRedBlackTreeNode tick_node{};
|
||||
VAddr vaddr{};
|
||||
PAddr paddr{};
|
||||
size_t size{};
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class HeapTracker {
|
||||
public:
|
||||
explicit HeapTracker(Common::HostMemory& buffer);
|
||||
~HeapTracker();
|
||||
|
||||
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 Protect(size_t virtual_offset, size_t length, MemoryPermission perm);
|
||||
u8* VirtualBasePointer() {
|
||||
return m_buffer.VirtualBasePointer();
|
||||
}
|
||||
|
||||
bool DeferredMapSeparateHeap(u8* fault_address);
|
||||
bool DeferredMapSeparateHeap(size_t virtual_offset);
|
||||
|
||||
private:
|
||||
using AddrTreeTraits =
|
||||
Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::addr_node>;
|
||||
using AddrTree = AddrTreeTraits::TreeType<SeparateHeapMapAddrComparator>;
|
||||
|
||||
using TickTreeTraits =
|
||||
Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::tick_node>;
|
||||
using TickTree = TickTreeTraits::TreeType<SeparateHeapMapTickComparator>;
|
||||
|
||||
AddrTree m_mappings{};
|
||||
TickTree m_resident_mappings{};
|
||||
|
||||
private:
|
||||
void SplitHeapMap(VAddr offset, size_t size);
|
||||
void SplitHeapMapLocked(VAddr offset);
|
||||
|
||||
AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
|
||||
|
||||
void RebuildSeparateHeapAddressSpace();
|
||||
|
||||
private:
|
||||
Common::HostMemory& m_buffer;
|
||||
const s64 m_max_resident_map_count;
|
||||
|
||||
std::shared_mutex m_rebuild_lock{};
|
||||
std::mutex m_lock{};
|
||||
s64 m_map_count{};
|
||||
s64 m_resident_map_count{};
|
||||
size_t m_tick{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
|
@ -572,9 +572,8 @@ public:
|
|||
if (True(perms & MemoryPermission::Execute))
|
||||
prot_flags |= PROT_EXEC;
|
||||
#endif
|
||||
int flags = (fd >= 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED;
|
||||
void* ret = mmap(virtual_base + virtual_offset, length, prot_flags, flags, fd, host_offset);
|
||||
ASSERT_MSG(ret != MAP_FAILED, "mmap: {} {}", strerror(errno), fd);
|
||||
int ret = mprotect(virtual_base + virtual_offset, length, prot_flags);
|
||||
ASSERT_MSG(ret == 0, "mprotect: {} {}", strerror(errno), fd);
|
||||
}
|
||||
|
||||
void Unmap(size_t virtual_offset, size_t length) {
|
||||
|
|
@ -588,8 +587,8 @@ public:
|
|||
auto [merged_pointer, merged_size] =
|
||||
free_manager.FreeBlock(virtual_base + virtual_offset, length);
|
||||
|
||||
void* ret = mmap(merged_pointer, merged_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
|
||||
ASSERT_MSG(ret != MAP_FAILED, "mmap: {}", strerror(errno));
|
||||
int ret = mprotect(merged_pointer, merged_size, PROT_NONE);
|
||||
ASSERT_MSG(ret == 0, "mmap: {}", strerror(errno));
|
||||
}
|
||||
|
||||
void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@
|
|||
namespace Common {
|
||||
|
||||
template <typename BaseAddr>
|
||||
MultiLevelPageTable<BaseAddr>::MultiLevelPageTable(std::size_t address_space_bits_,
|
||||
std::size_t first_level_bits_,
|
||||
std::size_t page_bits_)
|
||||
: address_space_bits{address_space_bits_},
|
||||
first_level_bits{first_level_bits_}, page_bits{page_bits_} {
|
||||
MultiLevelPageTable<BaseAddr>::MultiLevelPageTable(std::size_t address_space_bits_, std::size_t first_level_bits_, std::size_t page_bits_)
|
||||
: address_space_bits{address_space_bits_}
|
||||
, first_level_bits{first_level_bits_}
|
||||
, page_bits{page_bits_}
|
||||
{
|
||||
if (page_bits == 0) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -30,12 +30,9 @@ MultiLevelPageTable<BaseAddr>::MultiLevelPageTable(std::size_t address_space_bit
|
|||
void* base{VirtualAlloc(nullptr, alloc_size, MEM_RESERVE, PAGE_READWRITE)};
|
||||
#else
|
||||
void* base{mmap(nullptr, alloc_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)};
|
||||
|
||||
if (base == MAP_FAILED) {
|
||||
if (base == MAP_FAILED)
|
||||
base = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
ASSERT(base);
|
||||
base_ptr = reinterpret_cast<BaseAddr*>(base);
|
||||
}
|
||||
|
|
@ -56,29 +53,21 @@ template <typename BaseAddr>
|
|||
void MultiLevelPageTable<BaseAddr>::ReserveRange(u64 start, std::size_t size) {
|
||||
const u64 new_start = start >> first_level_shift;
|
||||
const u64 new_end = (start + size) >> first_level_shift;
|
||||
for (u64 i = new_start; i <= new_end; i++) {
|
||||
if (!first_level_map[i]) {
|
||||
for (u64 i = new_start; i <= new_end; i++)
|
||||
if (!first_level_map[i])
|
||||
AllocateLevel(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename BaseAddr>
|
||||
void MultiLevelPageTable<BaseAddr>::AllocateLevel(u64 level) {
|
||||
void* ptr = reinterpret_cast<char *>(base_ptr) + level * first_level_chunk_size;
|
||||
void MultiLevelPageTable<BaseAddr>::AllocateLevel(u64 index) {
|
||||
void* ptr = reinterpret_cast<char *>(base_ptr) + index * first_level_chunk_size;
|
||||
#ifdef _WIN32
|
||||
void* base{VirtualAlloc(ptr, first_level_chunk_size, MEM_COMMIT, PAGE_READWRITE)};
|
||||
#else
|
||||
void* base{mmap(ptr, first_level_chunk_size, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)};
|
||||
|
||||
if (base == MAP_FAILED) {
|
||||
base = nullptr;
|
||||
}
|
||||
#endif
|
||||
void* base = VirtualAlloc(ptr, first_level_chunk_size, MEM_COMMIT, PAGE_READWRITE);
|
||||
ASSERT(base);
|
||||
|
||||
first_level_map[level] = base;
|
||||
#else
|
||||
void* base = ptr;
|
||||
#endif
|
||||
first_level_map[index] = base;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
#include "common/assert.h"
|
||||
#include "common/atomic_ops.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/heap_tracker.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/page_table.h"
|
||||
#include "common/scope_exit.h"
|
||||
|
|
@ -55,37 +54,24 @@ struct Memory::Impl {
|
|||
} else {
|
||||
current_page_table->fastmem_arena = nullptr;
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
heap_tracker.emplace(system.DeviceMemory().buffer);
|
||||
buffer = std::addressof(*heap_tracker);
|
||||
#else
|
||||
buffer = std::addressof(system.DeviceMemory().buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
|
||||
Common::PhysicalAddress target, Common::MemoryPermission perms,
|
||||
bool separate_heap) {
|
||||
void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, Common::PhysicalAddress target, Common::MemoryPermission perms, bool separate_heap) {
|
||||
ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
|
||||
ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base));
|
||||
ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}",
|
||||
GetInteger(target));
|
||||
MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, target,
|
||||
Common::PageType::Memory);
|
||||
ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", GetInteger(target));
|
||||
MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, target, Common::PageType::Memory);
|
||||
|
||||
if (current_page_table->fastmem_arena) {
|
||||
buffer->Map(GetInteger(base), GetInteger(target) - DramMemoryMap::Base, size, perms,
|
||||
separate_heap);
|
||||
buffer->Map(GetInteger(base), GetInteger(target) - DramMemoryMap::Base, size, perms, separate_heap);
|
||||
}
|
||||
}
|
||||
|
||||
void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
|
||||
bool separate_heap) {
|
||||
void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, bool separate_heap) {
|
||||
ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
|
||||
ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base));
|
||||
MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0,
|
||||
Common::PageType::Unmapped);
|
||||
MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0, Common::PageType::Unmapped);
|
||||
|
||||
if (current_page_table->fastmem_arena) {
|
||||
buffer->Unmap(GetInteger(base), size, separate_heap);
|
||||
|
|
@ -857,12 +843,7 @@ struct Memory::Impl {
|
|||
std::array<Common::ScratchBuffer<u32>, Core::Hardware::NUM_CPU_CORES> scratch_buffers{};
|
||||
std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers;
|
||||
std::mutex sys_core_guard;
|
||||
#ifdef __ANDROID__
|
||||
std::optional<Common::HeapTracker> heap_tracker;
|
||||
Common::HeapTracker* buffer{};
|
||||
#else
|
||||
Common::HostMemory* buffer{};
|
||||
#endif
|
||||
};
|
||||
|
||||
Memory::Memory(Core::System& system_) : system{system_} {
|
||||
|
|
@ -1055,30 +1036,14 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
|
|||
u8* const ptr = impl->GetPointerImpl(
|
||||
GetInteger(vaddr),
|
||||
[&] {
|
||||
LOG_ERROR(HW_Memory, "Unmapped InvalidateNCE for {} bytes @ {:#x}", size,
|
||||
GetInteger(vaddr));
|
||||
LOG_ERROR(HW_Memory, "Unmapped InvalidateNCE for {} bytes @ {:#x}", size, GetInteger(vaddr));
|
||||
mapped = false;
|
||||
},
|
||||
[&] { rasterizer = true; });
|
||||
if (rasterizer) {
|
||||
impl->InvalidateGPUMemory(ptr, size);
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
if (!rasterizer && mapped) {
|
||||
impl->buffer->DeferredMapSeparateHeap(GetInteger(vaddr));
|
||||
}
|
||||
#endif
|
||||
|
||||
return mapped && ptr != nullptr;
|
||||
}
|
||||
|
||||
bool Memory::InvalidateSeparateHeap(void* fault_address) {
|
||||
#ifdef __ANDROID__
|
||||
return impl->buffer->DeferredMapSeparateHeap(static_cast<u8*>(fault_address));
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Core::Memory
|
||||
|
|
|
|||
|
|
@ -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-FileCopyrightText: 2014 Citra Emulator Project
|
||||
|
|
@ -490,13 +490,8 @@ public:
|
|||
* marked as debug or non-debug.
|
||||
*/
|
||||
void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug);
|
||||
|
||||
void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
|
||||
|
||||
bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size);
|
||||
|
||||
bool InvalidateSeparateHeap(void* fault_address);
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
|
||||
|
|
|
|||
|
|
@ -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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
|
|
@ -51,10 +51,12 @@ void RenderdocAPI::ToggleCapture() {
|
|||
if (!rdoc_api) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* api = static_cast<RENDERDOC_API_1_6_0*>(rdoc_api);
|
||||
if (!is_capturing) {
|
||||
rdoc_api->StartFrameCapture(NULL, NULL);
|
||||
api->StartFrameCapture(NULL, NULL);
|
||||
} else {
|
||||
rdoc_api->EndFrameCapture(NULL, NULL);
|
||||
api->EndFrameCapture(NULL, NULL);
|
||||
}
|
||||
is_capturing = !is_capturing;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
struct RENDERDOC_API_1_7_0;
|
||||
|
||||
namespace Tools {
|
||||
|
||||
class RenderdocAPI {
|
||||
|
|
@ -18,7 +16,7 @@ public:
|
|||
void ToggleCapture();
|
||||
|
||||
private:
|
||||
RENDERDOC_API_1_7_0* rdoc_api{};
|
||||
void* rdoc_api{};
|
||||
bool is_capturing{false};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -82,8 +82,6 @@ private:
|
|||
|
||||
std::thread thread;
|
||||
mach_port_t server_port;
|
||||
|
||||
void MessagePump();
|
||||
};
|
||||
|
||||
MachHandler::MachHandler() {
|
||||
|
|
@ -97,15 +95,7 @@ MachHandler::MachHandler() {
|
|||
KCHECK(mach_port_request_notification(mach_task_self(), server_port, MACH_NOTIFY_PORT_DESTROYED, 0, server_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev));
|
||||
#undef KCHECK
|
||||
|
||||
thread = std::thread(&MachHandler::MessagePump, this);
|
||||
thread.detach();
|
||||
}
|
||||
|
||||
MachHandler::~MachHandler() {
|
||||
mach_port_deallocate(mach_task_self(), server_port);
|
||||
}
|
||||
|
||||
void MachHandler::MessagePump() {
|
||||
thread = std::thread([this] {
|
||||
mach_msg_return_t mr;
|
||||
MachMessage request;
|
||||
MachMessage reply;
|
||||
|
|
@ -128,6 +118,12 @@ void MachHandler::MessagePump() {
|
|||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.detach();
|
||||
}
|
||||
|
||||
MachHandler::~MachHandler() {
|
||||
mach_port_deallocate(mach_task_self(), server_port);
|
||||
}
|
||||
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
|
|
@ -46,22 +46,18 @@ class SigHandler {
|
|||
});
|
||||
}
|
||||
static void SigAction(int sig, siginfo_t* info, void* raw_context);
|
||||
|
||||
bool supports_fast_mem = true;
|
||||
void* signal_stack_memory = nullptr;
|
||||
std::vector<u8> signal_stack_memory;
|
||||
ankerl::unordered_dense::map<u64, CodeBlockInfo> code_block_infos;
|
||||
std::shared_mutex code_block_infos_mutex;
|
||||
struct sigaction old_sa_segv;
|
||||
struct sigaction old_sa_bus;
|
||||
std::size_t signal_stack_size;
|
||||
bool supports_fast_mem = true;
|
||||
public:
|
||||
SigHandler() noexcept {
|
||||
signal_stack_size = std::max<size_t>(SIGSTKSZ, 2 * 1024 * 1024);
|
||||
signal_stack_memory = mmap(nullptr, signal_stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
|
||||
signal_stack_memory.resize(std::max<std::size_t>(SIGSTKSZ, 2 * 1024 * 1024), 0);
|
||||
stack_t signal_stack{};
|
||||
signal_stack.ss_sp = signal_stack_memory;
|
||||
signal_stack.ss_size = signal_stack_size;
|
||||
signal_stack.ss_sp = signal_stack_memory.data();
|
||||
signal_stack.ss_size = signal_stack_memory.size();
|
||||
signal_stack.ss_flags = 0;
|
||||
if (sigaltstack(&signal_stack, nullptr) != 0) {
|
||||
fmt::print(stderr, "dynarmic: POSIX SigHandler: init failure at sigaltstack\n");
|
||||
|
|
@ -88,10 +84,6 @@ public:
|
|||
#endif
|
||||
}
|
||||
|
||||
~SigHandler() noexcept {
|
||||
munmap(signal_stack_memory, signal_stack_size);
|
||||
}
|
||||
|
||||
void AddCodeBlock(u64 offset, CodeBlockInfo cbi) noexcept {
|
||||
std::unique_lock guard(code_block_infos_mutex);
|
||||
code_block_infos.insert_or_assign(offset, cbi);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue