mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-25 18:55:57 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b807013aac | |||
| bebc19da32 | |||
| 99bf8cf51a |
@@ -76,7 +76,6 @@ add_library(
|
||||
logging.h
|
||||
lz4_compression.cpp
|
||||
lz4_compression.h
|
||||
make_unique_for_overwrite.h
|
||||
math_util.h
|
||||
memory_detect.cpp
|
||||
memory_detect.h
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <class T>
|
||||
requires(!std::is_array_v<T>)
|
||||
std::unique_ptr<T> make_unique_for_overwrite() {
|
||||
return std::unique_ptr<T>(new T);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
requires std::is_unbounded_array_v<T>
|
||||
std::unique_ptr<T> make_unique_for_overwrite(std::size_t n) {
|
||||
return std::unique_ptr<T>(new std::remove_extent_t<T>[n]);
|
||||
}
|
||||
|
||||
template <class T, class... Args>
|
||||
requires std::is_bounded_array_v<T>
|
||||
void make_unique_for_overwrite(Args&&...) = delete;
|
||||
|
||||
} // namespace Common
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
#include <iterator>
|
||||
#include <cstring>
|
||||
|
||||
#include "common/make_unique_for_overwrite.h"
|
||||
#include <memory>
|
||||
|
||||
namespace Common {
|
||||
|
||||
@@ -38,8 +37,9 @@ public:
|
||||
ScratchBuffer() = default;
|
||||
|
||||
explicit ScratchBuffer(size_type initial_capacity)
|
||||
: last_requested_size{initial_capacity}, buffer_capacity{initial_capacity},
|
||||
buffer{Common::make_unique_for_overwrite<T[]>(initial_capacity)} {}
|
||||
: last_requested_size{initial_capacity}
|
||||
, buffer_capacity{initial_capacity}
|
||||
, buffer{std::make_unique_for_overwrite<T[]>(initial_capacity)} {}
|
||||
|
||||
~ScratchBuffer() = default;
|
||||
ScratchBuffer(const ScratchBuffer&) = delete;
|
||||
@@ -64,7 +64,7 @@ public:
|
||||
/// The previously held data will remain intact.
|
||||
void resize(size_type size) {
|
||||
if (size > buffer_capacity) {
|
||||
auto new_buffer = Common::make_unique_for_overwrite<T[]>(size);
|
||||
auto new_buffer = std::make_unique_for_overwrite<T[]>(size);
|
||||
std::memcpy(new_buffer.get(), buffer.get(), buffer_capacity * sizeof(T));
|
||||
buffer = std::move(new_buffer);
|
||||
buffer_capacity = size;
|
||||
@@ -77,7 +77,7 @@ public:
|
||||
void resize_destructive(size_type size) {
|
||||
if (size > buffer_capacity) {
|
||||
buffer_capacity = size;
|
||||
buffer = Common::make_unique_for_overwrite<T[]>(buffer_capacity);
|
||||
buffer = std::make_unique_for_overwrite<T[]>(buffer_capacity);
|
||||
}
|
||||
last_requested_size = size;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,14 @@
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <mutex>
|
||||
#else
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#endif
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
@@ -19,54 +26,60 @@
|
||||
namespace Common {
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::vector<std::pair<u64, u64>> vector_regions {};
|
||||
|
||||
// Workaround for handling non-commited memory accessed by Dynarmic; usually result of an error
|
||||
struct VectorRegion {
|
||||
u64 start_page;
|
||||
u64 end_page;
|
||||
};
|
||||
|
||||
static std::mutex& GetVectorRegionsMutex() {
|
||||
static std::mutex* m = new std::mutex();
|
||||
return *m;
|
||||
}
|
||||
|
||||
static std::vector<VectorRegion>& GetVectorRegions() {
|
||||
static std::vector<VectorRegion>* v = new std::vector<VectorRegion>();
|
||||
return *v;
|
||||
}
|
||||
|
||||
static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
||||
DWORD code = info->ExceptionRecord->ExceptionCode;
|
||||
u64 exception_addr = reinterpret_cast<u64>(info->ExceptionRecord->ExceptionAddress);
|
||||
|
||||
if (code != EXCEPTION_ACCESS_VIOLATION) {
|
||||
// Not our problem
|
||||
if (info->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
u64 addr = 0, addr2 = 0;
|
||||
const u64 fault_addr = info->ExceptionRecord->ExceptionInformation[1];
|
||||
const u64 access_type = info->ExceptionRecord->ExceptionInformation[0];
|
||||
const bool is_write = (access_type == 1);
|
||||
const u64 fault_page = fault_addr >> HostPageBits;
|
||||
|
||||
for (auto region: vector_regions) {
|
||||
auto addr_shifted = exception_addr >> HostPageBits;
|
||||
if (region.first <= addr_shifted && addr_shifted <= region.second) {
|
||||
addr = addr_shifted;
|
||||
}
|
||||
u64 addr = 0;
|
||||
u64 addr2 = 0;
|
||||
|
||||
// Page-boundary accesses
|
||||
if (auto addr_ = (exception_addr + 0x40) >> HostPageBits; addr_ != addr_shifted && region.first <= addr_ && addr_ <= region.second) {
|
||||
addr2 = addr_;
|
||||
}
|
||||
|
||||
if (addr != 0 || addr2 != 0) {
|
||||
break;
|
||||
{
|
||||
std::lock_guard lock(GetVectorRegionsMutex());
|
||||
for (const auto& region : GetVectorRegions()) {
|
||||
if (fault_page >= region.start_page && fault_page < region.end_page) {
|
||||
addr = fault_page;
|
||||
}
|
||||
const u64 page2 = (fault_addr + 0x3F) >> HostPageBits;
|
||||
if (page2 != fault_page && page2 >= region.start_page && page2 < region.end_page) {
|
||||
addr2 = page2;
|
||||
}
|
||||
if (addr != 0 || addr2 != 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (addr == 0 && addr2 == 0) {
|
||||
// Not our problem
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
LOG_ERROR(HW_Memory, "Accessing an unallocated region of a SparseLargeVector at {:#x}; this shouldn't happen and is likely a Dynarmic error!", exception_addr);
|
||||
LOG_ERROR(HW_Memory, "Accessing an unallocated region of a SparseLargeVector at {:#x}; this shouldn't happen and is likely a Dynarmic error!", fault_addr);
|
||||
|
||||
// Commit this region
|
||||
if (addr != 0) {
|
||||
if (!CommitVectorPage(addr << HostPageBits, false)) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
if (addr != 0 && !CommitVectorPage(addr << HostPageBits, is_write)) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
// Commit next region if needed
|
||||
if (addr2 != 0) {
|
||||
if (!CommitVectorPage(addr2 << HostPageBits, false)) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
if (addr2 != 0 && !CommitVectorPage(addr2 << HostPageBits, is_write)) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
@@ -74,23 +87,31 @@ static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
||||
|
||||
bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
|
||||
MEMORY_BASIC_INFORMATION info {};
|
||||
auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info));
|
||||
const auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info));
|
||||
const DWORD perm = write ? PAGE_READWRITE : PAGE_READONLY;
|
||||
|
||||
if (res == 0) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to query large buffer region at {:#x} with error {}, will try committing anyway", addr, GetLastError());
|
||||
} else if (info.State == MEM_COMMIT) {
|
||||
DWORD old_protect {};
|
||||
if (!VirtualProtect(reinterpret_cast<void*>(addr), HostPageSize, perm, &old_protect)) {
|
||||
LOG_ERROR(HW_Memory, "VirtualProtect failed at {:#x}, error {}", addr, GetLastError());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if (info.State != MEM_RESERVE) {
|
||||
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} that is not mapped or is already committed (state {:#x})", addr, info.State);
|
||||
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} (state {:#x})", addr, info.State);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto perm = write ? PAGE_READWRITE : PAGE_READONLY;
|
||||
void* res2 = VirtualAlloc(reinterpret_cast<LPVOID>(addr), HostPageSize, MEM_COMMIT, perm);
|
||||
if (res2 == nullptr) {
|
||||
if (VirtualAlloc(reinterpret_cast<LPVOID>(addr), HostPageSize, MEM_COMMIT, perm) == nullptr) {
|
||||
LOG_ERROR(HW_Memory, "Failed to commit large buffer region at {:#x}, error {}", addr, GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef MAP_NOCORE
|
||||
@@ -102,57 +123,103 @@ bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
|
||||
|
||||
void DecommitVectorPage(uintptr_t base) noexcept {
|
||||
#if defined(_WIN32)
|
||||
VirtualFree(reinterpret_cast<LPVOID>(base), HostPageSize, MEM_DECOMMIT);
|
||||
if (!VirtualFree(reinterpret_cast<LPVOID>(base), HostPageSize, MEM_DECOMMIT)) {
|
||||
LOG_WARNING(HW_Memory, "VirtualFree(MEM_DECOMMIT) failed at {:#x}, error {}", base, GetLastError());
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
// Linux's MADV_DONTNEED zeros out pages for us
|
||||
madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_DONTNEED);
|
||||
if (madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_DONTNEED) != 0) {
|
||||
LOG_WARNING(HW_Memory, "madvise(MADV_DONTNEED) failed at {:#x}: {}", base, std::strerror(errno));
|
||||
}
|
||||
#else
|
||||
madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_FREE);
|
||||
if (madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_FREE) != 0) {
|
||||
LOG_WARNING(HW_Memory, "madvise(MADV_FREE) failed at {:#x}: {}", base, std::strerror(errno));
|
||||
}
|
||||
std::memset(reinterpret_cast<void*>(base), 0, HostPageSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept {
|
||||
if (auto page = HostPageSize; size % page != 0) {
|
||||
if (size == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto page = HostPageSize;
|
||||
if (size % page != 0) {
|
||||
LOG_WARNING(HW_Memory, "Allocating unaligned large vector with size {:#x}; aligning to {} page size", size, page);
|
||||
if (size > SIZE_MAX - (page - 1)) {
|
||||
LOG_CRITICAL(HW_Memory, "Size {:#x} would overflow page alignment", size);
|
||||
return nullptr;
|
||||
}
|
||||
size = AlignUp(size, page);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// We will never use this memory entirely so instead of committing it up front let's just reserve it and commit each page individually
|
||||
void* base = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
|
||||
|
||||
if (base != nullptr) {
|
||||
vector_regions.emplace_back(reinterpret_cast<u64>(base), reinterpret_cast<u64>(base) + size);
|
||||
{
|
||||
std::lock_guard lock(GetVectorRegionsMutex());
|
||||
GetVectorRegions().push_back({
|
||||
reinterpret_cast<u64>(base) >> HostPageBits,
|
||||
(reinterpret_cast<u64>(base) + size) >> HostPageBits,
|
||||
});
|
||||
}
|
||||
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, []() { AddVectoredExceptionHandler(1, FakePageFaultHandler); });
|
||||
} else {
|
||||
// Try committing everything instead??
|
||||
LOG_WARNING(HW_Memory, "Failed to reserve large vector region with error {}, trying to commit instead..", GetLastError());
|
||||
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
ASSERT_MSG(base, "Failed to reserve {:#x} sized region with error {}", size, GetLastError());
|
||||
#else
|
||||
void* base = mmap(nullptr, size, PROT_READ, MAP_ANON | MAP_PRIVATE | MAP_NOCORE, -1, 0);
|
||||
if (base == MAP_FAILED)
|
||||
base = nullptr;
|
||||
ASSERT_MSG(base, "Failed to allocate {:#x} sized region with error {}", size, strerror(errno));
|
||||
int flags = MAP_ANON | MAP_PRIVATE;
|
||||
#ifdef MAP_NORESERVE
|
||||
flags |= MAP_NORESERVE;
|
||||
#endif
|
||||
#if defined(MAP_NOCORE)
|
||||
flags |= MAP_NOCORE;
|
||||
#endif
|
||||
void* base = mmap(nullptr, size, PROT_READ, flags, -1, 0);
|
||||
if (base == MAP_FAILED) {
|
||||
base = nullptr;
|
||||
}
|
||||
#ifdef MADV_HUGEPAGE
|
||||
if (base != nullptr) {
|
||||
madvise(base, size, MADV_HUGEPAGE);
|
||||
}
|
||||
#endif
|
||||
ASSERT_MSG(base, "Failed to allocate {:#x} sized region with error {}", size, std::strerror(errno));
|
||||
#endif
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
||||
if (auto page = HostPageSize; size % page != 0) {
|
||||
if (base == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto page = HostPageSize; size % page != 0) {
|
||||
size = AlignUp(size, page);
|
||||
}
|
||||
if (!base)
|
||||
return;
|
||||
|
||||
#ifdef _WIN32
|
||||
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
||||
{
|
||||
std::lock_guard lock(GetVectorRegionsMutex());
|
||||
auto& regions = GetVectorRegions();
|
||||
const u64 base_page = reinterpret_cast<u64>(base) >> HostPageBits;
|
||||
regions.erase(std::remove_if(regions.begin(), regions.end(),
|
||||
[base_page](const VectorRegion& r) { return r.start_page == base_page; }), regions.end());
|
||||
}
|
||||
if (!VirtualFree(base, 0, MEM_RELEASE)) {
|
||||
LOG_ERROR(HW_Memory, "VirtualFree failed, error {}", GetLastError());
|
||||
}
|
||||
#else
|
||||
ASSERT(munmap(base, size) == 0);
|
||||
if (munmap(base, size) != 0) {
|
||||
LOG_ERROR(HW_Memory, "munmap failed: {}", std::strerror(errno));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
} // namespace Common
|
||||
+146
-108
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* virtual_buffer.h */
|
||||
@@ -7,10 +7,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
@@ -28,9 +32,9 @@ constexpr u64 HostPageBits = 12;
|
||||
constexpr u64 HostPageMask = ~(HostPageSize - 1);
|
||||
bool CommitVectorPage(uintptr_t addr, bool write) noexcept;
|
||||
#else
|
||||
const u64 HostPageSize = sysconf(_SC_PAGESIZE);
|
||||
const u64 HostPageBits = std::countr_zero(HostPageSize);
|
||||
const u64 HostPageMask = ~(HostPageSize - 1);
|
||||
inline const u64 HostPageSize = static_cast<u64>(sysconf(_SC_PAGESIZE));
|
||||
inline const u64 HostPageBits = std::countr_zero(HostPageSize);
|
||||
inline const u64 HostPageMask = ~(HostPageSize - 1);
|
||||
#endif
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept;
|
||||
@@ -43,20 +47,18 @@ template <typename T>
|
||||
// requires std::is_trivially_copyable_v<T>
|
||||
class SparseLargeVector final {
|
||||
public:
|
||||
constexpr SparseLargeVector() = default;
|
||||
SparseLargeVector() = default;
|
||||
|
||||
explicit SparseLargeVector(std::size_t count) noexcept
|
||||
: alloc_size{count * sizeof(T)}
|
||||
{
|
||||
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
|
||||
// each item in vector holds information for 64 pages
|
||||
auto denom = HostPageSize * 64;
|
||||
committed_pages = std::vector<std::atomic<u64>>((alloc_size + denom - 1) / denom);
|
||||
explicit SparseLargeVector(std::size_t count) noexcept {
|
||||
if (count > SIZE_MAX / sizeof(T)) {
|
||||
LOG_CRITICAL(Common_Memory, "SparseLargeVector size overflow: {} elements", count);
|
||||
return;
|
||||
}
|
||||
Allocate(count * sizeof(T));
|
||||
}
|
||||
|
||||
~SparseLargeVector() noexcept {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
Release();
|
||||
}
|
||||
|
||||
SparseLargeVector(const SparseLargeVector&) = delete;
|
||||
@@ -65,145 +67,181 @@ public:
|
||||
SparseLargeVector& operator=(SparseLargeVector&& other) = delete;
|
||||
|
||||
void ResizeAndClear(std::size_t count) noexcept {
|
||||
if (auto const new_size = count * sizeof(T); new_size != alloc_size) {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
alloc_size = new_size;
|
||||
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
|
||||
auto denom = HostPageSize * 64;
|
||||
committed_pages = std::vector<std::atomic<u64>>((alloc_size + denom - 1) / denom);
|
||||
if (count > SIZE_MAX / sizeof(T)) {
|
||||
LOG_CRITICAL(Common_Memory, "SparseLargeVector resize overflow: {} elements", count);
|
||||
return;
|
||||
}
|
||||
const std::size_t new_size = count * sizeof(T);
|
||||
if (new_size == alloc_size) {
|
||||
ZeroRegion(0, alloc_size / sizeof(T));
|
||||
return;
|
||||
}
|
||||
Release();
|
||||
Allocate(new_size);
|
||||
}
|
||||
|
||||
/// Returns a reference to the value of the requested index and allocates memory if needed.
|
||||
T& GetAndFault(std::size_t index) noexcept {
|
||||
if (index > alloc_size / sizeof(T)) {
|
||||
UNREACHABLE_MSG("Out of bounds RW access on SparseLargeVector @ {}", index);
|
||||
if (base_ptr == nullptr || index >= size()) [[unlikely]] {
|
||||
LOG_CRITICAL(Common_Memory, "SparseLargeVector RW access out of bounds @ {} (size {})", index, size());
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if (!IsCommittedPage(index)) {
|
||||
CommitPage(index);
|
||||
const u64 byte_offset = static_cast<u64>(index) * sizeof(T);
|
||||
if (!CommitPage(byte_offset)) [[unlikely]] {
|
||||
LOG_CRITICAL(Common_Memory, "SparseLargeVector commit failed @ {} (offset {:#x})", index, byte_offset);
|
||||
std::abort();
|
||||
}
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
/// Returns a reference to the value of the requested index if initialized, or will otherwise return a zero-initialized object.
|
||||
const T& GetOrDefault(std::size_t index) const {
|
||||
const T& GetOrDefault(std::size_t index) const noexcept {
|
||||
if (base_ptr == nullptr || index >= size()) [[unlikely]] {
|
||||
LOG_CRITICAL(Common_Memory, "SparseLargeVector RO access out of bounds @ {}", index);
|
||||
return DefaultValue();
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (!IsCommittedPage(index)) {
|
||||
return *reinterpret_cast<const T*>(&default_val);
|
||||
if (!IsPageCommitted(static_cast<u64>(index) * sizeof(T))) {
|
||||
return DefaultValue();
|
||||
}
|
||||
#endif
|
||||
// On non-Windows, OS page table should optimize this by pointing to a zero page if unallocated.
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
void Set(std::size_t index, const T& value) noexcept {
|
||||
if (index > alloc_size / sizeof(T)) {
|
||||
LOG_CRITICAL(Common_Memory, "Out of bounds write on SparseLargeVector @ {}", index);
|
||||
if (base_ptr == nullptr || index >= size()) [[unlikely]] {
|
||||
LOG_CRITICAL(Common_Memory, "SparseLargeVector write out of bounds @ {}", index);
|
||||
return;
|
||||
}
|
||||
const u64 byte_offset = static_cast<u64>(index) * sizeof(T);
|
||||
if (!CommitPage(byte_offset)) [[unlikely]] {
|
||||
LOG_CRITICAL(Common_Memory, "SparseLargeVector commit failed for write @ {}", index);
|
||||
return;
|
||||
}
|
||||
if (!IsCommittedPage(index))
|
||||
CommitPage(index);
|
||||
base_ptr[index] = value;
|
||||
}
|
||||
|
||||
void ZeroRegion(std::size_t start, std::size_t end_) noexcept {
|
||||
u64 base = reinterpret_cast<u64>(&base_ptr[start]);
|
||||
const u64 end = reinterpret_cast<u64>(&base_ptr[end_]);
|
||||
if (base_ptr == nullptr || start >= end_) return;
|
||||
|
||||
const u64 end_page = AlignUp(base, HostPageSize);
|
||||
const u64 first_size = (std::min)(end_page, end) - base;
|
||||
const u64 start_off = static_cast<u64>(start) * sizeof(T);
|
||||
const u64 end_off = static_cast<u64>(end_) * sizeof(T);
|
||||
const u64 first_page_end = (start_off + HostPageSize - 1) & HostPageMask;
|
||||
|
||||
if (IsCommittedPage(start)) {
|
||||
std::memset(reinterpret_cast<void*>(base), 0, first_size);
|
||||
if (start_off < first_page_end) {
|
||||
const u64 chunk_end = (std::min)(first_page_end, end_off);
|
||||
const u64 chunk_size = chunk_end - start_off;
|
||||
if (chunk_size != 0 && IsPageCommitted(start_off)) {
|
||||
std::memset(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(base_ptr) + start_off), 0, chunk_size);
|
||||
}
|
||||
if (end_off <= first_page_end) return;
|
||||
}
|
||||
|
||||
if (end <= end_page)
|
||||
return;
|
||||
|
||||
base = end_page;
|
||||
|
||||
for (u64 page = base; page < end; page += HostPageSize) {
|
||||
auto index = (page - reinterpret_cast<u64>(base_ptr)) / sizeof(T);
|
||||
if (!IsCommittedPage(index)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (end - page >= HostPageSize) {
|
||||
DecommitPage(index);
|
||||
for (u64 off = first_page_end; off < end_off; off += HostPageSize) {
|
||||
if (!IsPageCommitted(off)) continue;
|
||||
const u64 remaining = end_off - off;
|
||||
if (remaining >= HostPageSize) {
|
||||
DecommitPage(off);
|
||||
} else {
|
||||
std::memset(reinterpret_cast<void*>(page), 0, end - page);
|
||||
std::memset(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(base_ptr) + off), 0, remaining);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void CommitRegion(size_t index, size_t end_) {
|
||||
const u64 base = static_cast<u64>(index) * sizeof(T);
|
||||
const u64 end = static_cast<u64>(end_) * sizeof(T);
|
||||
|
||||
for (u64 page = AlignDown(base, HostPageSize); page < end; page += HostPageSize) {
|
||||
if (!IsCommittedPage(page / sizeof(T))) {
|
||||
CommitPage(page / sizeof(T));
|
||||
void CommitRegion(std::size_t index, std::size_t end_) noexcept {
|
||||
if (base_ptr == nullptr || index >= end_) return;
|
||||
const u64 start_off = static_cast<u64>(index) * sizeof(T);
|
||||
const u64 end_off = static_cast<u64>(end_) * sizeof(T);
|
||||
const u64 start_page = start_off & HostPageMask;
|
||||
for (u64 off = start_page; off < end_off; off += HostPageSize) {
|
||||
if (!IsPageCommitted(off)) {
|
||||
(void)CommitPage(off);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr T& GetUnchecked(size_t index) {
|
||||
return base_ptr[index];
|
||||
}
|
||||
T& GetUnchecked(std::size_t index) noexcept { return base_ptr[index]; }
|
||||
|
||||
[[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept {
|
||||
return GetOrDefault(index);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T* data() const noexcept {
|
||||
return base_ptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::size_t size() const noexcept {
|
||||
return alloc_size / sizeof(T);
|
||||
}
|
||||
[[nodiscard]] const T& operator[](std::size_t index) const noexcept { return GetOrDefault(index); }
|
||||
[[nodiscard]] const T* data() const noexcept { return base_ptr; }
|
||||
[[nodiscard]] std::size_t size() const noexcept { return alloc_size / sizeof(T); }
|
||||
|
||||
private:
|
||||
[[nodiscard]] constexpr bool IsCommittedPage(std::size_t index) const noexcept {
|
||||
if (index > alloc_size / sizeof(T)) {
|
||||
LOG_CRITICAL(Common_Memory, "Out of bounds access on large vector @ {}", index);
|
||||
void Allocate(std::size_t new_size) noexcept {
|
||||
alloc_size = new_size;
|
||||
if (alloc_size == 0) {
|
||||
base_ptr = nullptr;
|
||||
committed_pages.reset();
|
||||
return;
|
||||
}
|
||||
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
const std::size_t num_pages = NumPages();
|
||||
const std::size_t num_words = (num_pages + 63) / 64;
|
||||
committed_pages = std::make_unique<std::atomic<u64>[]>(num_words);
|
||||
}
|
||||
|
||||
void Release() noexcept {
|
||||
if (base_ptr != nullptr) {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
base_ptr = nullptr;
|
||||
}
|
||||
committed_pages.reset();
|
||||
alloc_size = 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 NumPages() const noexcept {
|
||||
return (alloc_size + HostPageSize - 1) >> HostPageBits;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsPageCommitted(u64 byte_offset) const noexcept {
|
||||
const u64 page_index = byte_offset >> HostPageBits;
|
||||
if (committed_pages == nullptr || page_index >= NumPages()) return false;
|
||||
const auto val = committed_pages[page_index >> 6].load(std::memory_order_acquire);
|
||||
return (val >> (page_index & 63)) & 1;
|
||||
}
|
||||
|
||||
void SetPageBit(u64 page_index, bool value) noexcept {
|
||||
if (committed_pages == nullptr) return;
|
||||
const u64 bit = 1ULL << (page_index & 63);
|
||||
auto& atom = committed_pages[page_index >> 6];
|
||||
if (value) {
|
||||
atom.fetch_or(bit, std::memory_order_release);
|
||||
} else {
|
||||
atom.fetch_and(~bit, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
bool CommitPage(u64 byte_offset) noexcept {
|
||||
const u64 page_index = byte_offset >> HostPageBits;
|
||||
const uintptr_t page_addr = (reinterpret_cast<uintptr_t>(base_ptr) + byte_offset) & HostPageMask;
|
||||
|
||||
if (IsPageCommitted(byte_offset)) return true;
|
||||
|
||||
#if defined(_WIN32)
|
||||
if (!CommitVectorPage(page_addr, true)) return false;
|
||||
#else
|
||||
if (mprotect(reinterpret_cast<void*>(page_addr), HostPageSize, PROT_READ | PROT_WRITE) != 0) {
|
||||
LOG_ERROR(Common_Memory, "mprotect failed at {:#x}: {}", page_addr, std::strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
auto page = (index * sizeof(T)) >> HostPageBits;
|
||||
auto val = committed_pages[page >> 6].load(std::memory_order_acquire);
|
||||
return (val >> (page & 63)) & 1;
|
||||
}
|
||||
|
||||
constexpr void CommitPage(std::size_t index) noexcept {
|
||||
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
||||
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
||||
#if defined(_WIN32)
|
||||
CommitVectorPage(page, true);
|
||||
#else
|
||||
mprotect(reinterpret_cast<void*>(page), HostPageSize, PROT_READ | PROT_WRITE);
|
||||
#endif
|
||||
|
||||
committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
|
||||
SetPageBit(page_index, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr void DecommitPage(std::size_t index) noexcept {
|
||||
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
||||
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
||||
void DecommitPage(u64 byte_offset) noexcept {
|
||||
const u64 page_index = byte_offset >> HostPageBits;
|
||||
const uintptr_t page_addr = (reinterpret_cast<uintptr_t>(base_ptr) + byte_offset) & HostPageMask;
|
||||
DecommitVectorPage(page_addr);
|
||||
SetPageBit(page_index, false);
|
||||
}
|
||||
|
||||
committed_pages[page_index >> 6].fetch_and(~(1ULL << (page_index & 63)), std::memory_order_release);
|
||||
DecommitVectorPage(page);
|
||||
[[nodiscard]] const T& DefaultValue() const noexcept {
|
||||
return *reinterpret_cast<const T*>(&default_val);
|
||||
}
|
||||
|
||||
std::size_t alloc_size{};
|
||||
T* base_ptr{};
|
||||
|
||||
std::vector<std::atomic<u64>> committed_pages{};
|
||||
#ifdef _WIN32
|
||||
const std::array<u8, sizeof(T)> default_val{};
|
||||
#endif
|
||||
std::unique_ptr<std::atomic<u64>[]> committed_pages{};
|
||||
alignas(T) const std::array<u8, sizeof(T)> default_val{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
} // namespace Common
|
||||
@@ -109,8 +109,7 @@ VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::op
|
||||
auto reference = std::make_unique<FileReference>();
|
||||
this->InsertReferenceIntoListLocked(*reference);
|
||||
|
||||
auto file = std::shared_ptr<RealVfsFile>(
|
||||
new RealVfsFile(*this, std::move(reference), path, perms, size, std::move(parent_path)));
|
||||
auto file = std::make_shared<RealVfsFile>(*this, std::move(reference), path, perms, size, std::move(parent_path));
|
||||
cache[path] = file;
|
||||
|
||||
return file;
|
||||
@@ -177,7 +176,7 @@ bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
|
||||
|
||||
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) {
|
||||
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
|
||||
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
|
||||
return std::make_shared<RealVfsDirectory>(*this, path, perms);
|
||||
}
|
||||
|
||||
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
|
||||
@@ -185,7 +184,7 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode p
|
||||
if (!FS::CreateDirs(path)) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
|
||||
return std::make_shared<RealVfsDirectory>(*this, path, perms);
|
||||
}
|
||||
|
||||
VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
|
||||
|
||||
@@ -82,6 +82,9 @@ class RealVfsFile : public VfsFile {
|
||||
friend class RealVfsFilesystem;
|
||||
|
||||
public:
|
||||
RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
|
||||
const std::string& path, OpenMode perms = OpenMode::Read,
|
||||
std::optional<u64> size = {}, std::optional<std::string> parent_path = {});
|
||||
~RealVfsFile() override;
|
||||
|
||||
std::string GetName() const override;
|
||||
@@ -95,9 +98,6 @@ public:
|
||||
bool Rename(std::string_view name) override;
|
||||
|
||||
private:
|
||||
RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
|
||||
const std::string& path, OpenMode perms = OpenMode::Read,
|
||||
std::optional<u64> size = {}, std::optional<std::string> parent_path = {});
|
||||
|
||||
RealVfsFilesystem& base;
|
||||
std::unique_ptr<FileReference> reference;
|
||||
@@ -113,6 +113,8 @@ class RealVfsDirectory : public VfsDirectory {
|
||||
friend class RealVfsFilesystem;
|
||||
|
||||
public:
|
||||
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path,
|
||||
OpenMode perms = OpenMode::Read);
|
||||
~RealVfsDirectory() override;
|
||||
|
||||
VirtualFile GetFileRelative(std::string_view relative_path) const override;
|
||||
@@ -138,9 +140,6 @@ public:
|
||||
std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
|
||||
|
||||
private:
|
||||
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path,
|
||||
OpenMode perms = OpenMode::Read);
|
||||
|
||||
template <typename T, typename R>
|
||||
std::vector<std::shared_ptr<R>> IterateEntries() const;
|
||||
|
||||
|
||||
@@ -517,7 +517,7 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
|
||||
// Layer ordering. Composition sorts back-to-front. Now with enums for calrity.
|
||||
s32 z_index = Background;
|
||||
if (is_overlay) {
|
||||
z_index = Overlay;
|
||||
z_index = this->IsOverlayOpenLocked(*applet) ? Overlay : Background;
|
||||
} else if (inherited_foreground) {
|
||||
z_index = is_obscured ? Foreground : ForegroundVisible;
|
||||
}
|
||||
|
||||
@@ -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 2024 yuzu Emulator Project
|
||||
@@ -24,7 +24,7 @@ IReceiverService::~IReceiverService() = default;
|
||||
|
||||
Result IReceiverService::OpenReceiver(Out<SharedPointer<IReceiver>> out_receiver) {
|
||||
LOG_DEBUG(Service_PSC, "called");
|
||||
*out_receiver = std::shared_ptr<IReceiver>(new IReceiver(system));
|
||||
*out_receiver = std::make_shared<IReceiver>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,19 @@ NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service
|
||||
AbstractPad{hid_core_.kernel},
|
||||
}}
|
||||
{
|
||||
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
|
||||
for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
|
||||
auto& controller = controller_data[aruid_index][i];
|
||||
controller.device = hid_core.GetEmulatedControllerByIndex(i);
|
||||
Core::HID::ControllerUpdateCallback engine_callback{
|
||||
.on_change = [this, i, kernel = &hid_core.kernel](Core::HID::ControllerTriggerType type) {
|
||||
ControllerUpdate(*kernel, type, i);
|
||||
},
|
||||
.is_npad_service = true,
|
||||
};
|
||||
controller.callback_key = controller.device->SetCallback(engine_callback);
|
||||
}
|
||||
}
|
||||
for (std::size_t i = 0; i < abstracted_pads.size(); ++i) {
|
||||
abstracted_pads[i].SetNpadId(IndexToNpadIdType(i));
|
||||
}
|
||||
@@ -93,16 +106,6 @@ Result NPad::Activate(u64 aruid) {
|
||||
for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
|
||||
auto& controller = controller_data[aruid_index][i];
|
||||
controller.shared_memory = &data->shared_memory_format->npad.npad_entry[i].internal_state;
|
||||
controller.device = hid_core.GetEmulatedControllerByIndex(i);
|
||||
if (!controller.callback_key) {
|
||||
Core::HID::ControllerUpdateCallback engine_callback{
|
||||
.on_change = [this, i](Core::HID::ControllerTriggerType type) {
|
||||
ControllerUpdate(hid_core.kernel, type, i);
|
||||
},
|
||||
.is_npad_service = true,
|
||||
};
|
||||
controller.callback_key = controller.device->SetCallback(engine_callback);
|
||||
}
|
||||
}
|
||||
|
||||
// Prefill controller buffers
|
||||
|
||||
@@ -94,13 +94,13 @@ void Layer::ConfigureDraw(const Device& device, PresentPushConstants* out_push_c
|
||||
const u32 scaled_width = texture_info ? texture_info->scaled_width : texture_width;
|
||||
const u32 scaled_height = texture_info ? texture_info->scaled_height : texture_height;
|
||||
const bool use_accelerated = texture_info.has_value();
|
||||
const bool is_applet =
|
||||
(framebuffer.layer_stack_mask & Service::Nvnflinger::LayerStackBit(
|
||||
Service::Nvnflinger::LayerStackId::Recording)) == 0;
|
||||
|
||||
RefreshResources(device, framebuffer);
|
||||
SetAntiAliasPass(device);
|
||||
#ifdef HAS_RESHADE
|
||||
const bool is_applet =
|
||||
(framebuffer.layer_stack_mask & Service::Nvnflinger::LayerStackBit(
|
||||
Service::Nvnflinger::LayerStackId::Recording)) == 0;
|
||||
SetPostProcessPass(device, is_applet);
|
||||
#endif
|
||||
|
||||
@@ -141,8 +141,11 @@ void Layer::ConfigureDraw(const Device& device, PresentPushConstants* out_push_c
|
||||
source_image_view = fsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect);
|
||||
crop_rect = {0, 0, 1, 1};
|
||||
} else if (auto* sgsr = std::get_if<SGSR>(&sr_filter)) {
|
||||
source_image_view = sgsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect);
|
||||
crop_rect = {0, 0, 1, 1};
|
||||
if (!is_applet) {
|
||||
source_image_view = sgsr->Draw(device, scheduler, image_index, source_image,
|
||||
source_image_view, render_extent, crop_rect);
|
||||
crop_rect = {0, 0, 1, 1};
|
||||
}
|
||||
}
|
||||
|
||||
SetMatrixData(device, *out_push_constants, layout);
|
||||
|
||||
Reference in New Issue
Block a user