6 changed files with 3 additions and 660 deletions
-
8src/common/settings.h
-
2src/video_core/CMakeLists.txt
-
84src/video_core/renderer_vulkan/renderer_vulkan.cpp
-
4src/video_core/renderer_vulkan/renderer_vulkan.h
-
446src/video_core/vulkan_common/hybrid_memory.cpp
-
119src/video_core/vulkan_common/hybrid_memory.h
@ -1,446 +0,0 @@ |
|||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||
|
|
||||
#include <cstring>
|
|
||||
#include <fstream>
|
|
||||
#include <algorithm>
|
|
||||
|
|
||||
#include "common/logging/log.h"
|
|
||||
#include "video_core/vulkan_common/hybrid_memory.h"
|
|
||||
|
|
||||
#if defined(__linux__) || defined(__ANDROID__)
|
|
||||
#include <sys/mman.h>
|
|
||||
#include <unistd.h>
|
|
||||
#include <poll.h>
|
|
||||
#include <sys/syscall.h>
|
|
||||
#include <linux/userfaultfd.h>
|
|
||||
#include <sys/ioctl.h>
|
|
||||
#include <fcntl.h>
|
|
||||
#elif defined(_WIN32)
|
|
||||
#include <windows.h>
|
|
||||
#endif
|
|
||||
|
|
||||
namespace Vulkan { |
|
||||
|
|
||||
void PredictiveReuseManager::RecordUsage(u64 address, u64 size, bool write_access) { |
|
||||
std::lock_guard<std::mutex> guard(mutex); |
|
||||
|
|
||||
// Add to history, removing oldest entries if we're past max_history
|
|
||||
access_history.push_back({address, size, write_access, current_timestamp++}); |
|
||||
if (access_history.size() > max_history) { |
|
||||
access_history.erase(access_history.begin()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
bool PredictiveReuseManager::IsHotRegion(u64 address, u64 size) const { |
|
||||
std::lock_guard<std::mutex> guard(mutex); |
|
||||
|
|
||||
// Check if this memory region has been accessed frequently
|
|
||||
const u64 end_address = address + size; |
|
||||
int access_count = 0; |
|
||||
|
|
||||
for (const auto& access : access_history) { |
|
||||
const u64 access_end = access.address + access.size; |
|
||||
|
|
||||
// Check for overlap
|
|
||||
if (!(end_address <= access.address || address >= access_end)) { |
|
||||
access_count++; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Consider a region "hot" if it has been accessed in at least 10% of recent accesses
|
|
||||
return access_count >= static_cast<int>(std::max<size_t>(1, max_history / 10)); |
|
||||
} |
|
||||
|
|
||||
void PredictiveReuseManager::EvictRegion(u64 address, u64 size) { |
|
||||
std::lock_guard<std::mutex> guard(mutex); |
|
||||
|
|
||||
// Remove any history entries that overlap with this region
|
|
||||
const u64 end_address = address + size; |
|
||||
|
|
||||
access_history.erase( |
|
||||
std::remove_if(access_history.begin(), access_history.end(), |
|
||||
[address, end_address](const MemoryAccess& access) { |
|
||||
const u64 access_end = access.address + access.size; |
|
||||
// Check for overlap
|
|
||||
return !(end_address <= access.address || address >= access_end); |
|
||||
}), |
|
||||
access_history.end() |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
void PredictiveReuseManager::ClearHistory() { |
|
||||
std::lock_guard<std::mutex> guard(mutex); |
|
||||
access_history.clear(); |
|
||||
current_timestamp = 0; |
|
||||
} |
|
||||
|
|
||||
#if defined(__linux__) || defined(__ANDROID__) || defined(_WIN32)
|
|
||||
void FaultManagedAllocator::Touch(size_t addr) { |
|
||||
lru.remove(addr); |
|
||||
lru.push_front(addr); |
|
||||
dirty_set.insert(addr); |
|
||||
} |
|
||||
|
|
||||
void FaultManagedAllocator::EnforceLimit() { |
|
||||
while (lru.size() > MaxPages) { |
|
||||
size_t evict = lru.back(); |
|
||||
lru.pop_back(); |
|
||||
|
|
||||
auto it = page_map.find(evict); |
|
||||
if (it != page_map.end()) { |
|
||||
if (dirty_set.count(evict)) { |
|
||||
// Compress and store dirty page before evicting
|
|
||||
std::vector<u8> compressed((u8*)it->second, (u8*)it->second + PageSize); |
|
||||
compressed_store[evict] = std::move(compressed); |
|
||||
dirty_set.erase(evict); |
|
||||
} |
|
||||
|
|
||||
#if defined(__linux__) || defined(__ANDROID__)
|
|
||||
munmap(it->second, PageSize); |
|
||||
#elif defined(_WIN32)
|
|
||||
VirtualFree(it->second, 0, MEM_RELEASE); |
|
||||
#endif
|
|
||||
page_map.erase(it); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void* FaultManagedAllocator::GetOrAlloc(size_t addr) { |
|
||||
std::lock_guard<std::mutex> guard(lock); |
|
||||
|
|
||||
if (page_map.count(addr)) { |
|
||||
Touch(addr); |
|
||||
return page_map[addr]; |
|
||||
} |
|
||||
|
|
||||
#if defined(__linux__) || defined(__ANDROID__)
|
|
||||
void* mem = mmap(nullptr, PageSize, PROT_READ | PROT_WRITE, |
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
|
||||
|
|
||||
if (mem == MAP_FAILED) { |
|
||||
LOG_ERROR(Render_Vulkan, "Failed to mmap memory for fault handler"); |
|
||||
return nullptr; |
|
||||
} |
|
||||
#elif defined(_WIN32)
|
|
||||
void* mem = VirtualAlloc(nullptr, PageSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); |
|
||||
if (!mem) { |
|
||||
LOG_ERROR(Render_Vulkan, "Failed to VirtualAlloc memory for fault handler"); |
|
||||
return nullptr; |
|
||||
} |
|
||||
#endif
|
|
||||
|
|
||||
if (compressed_store.count(addr)) { |
|
||||
// Decompress stored page data
|
|
||||
std::memcpy(mem, compressed_store[addr].data(), compressed_store[addr].size()); |
|
||||
compressed_store.erase(addr); |
|
||||
} else { |
|
||||
std::memset(mem, 0, PageSize); |
|
||||
} |
|
||||
|
|
||||
page_map[addr] = mem; |
|
||||
lru.push_front(addr); |
|
||||
dirty_set.insert(addr); |
|
||||
EnforceLimit(); |
|
||||
|
|
||||
return mem; |
|
||||
} |
|
||||
|
|
||||
#if defined(_WIN32)
|
|
||||
// Static member initialization
|
|
||||
FaultManagedAllocator* FaultManagedAllocator::current_instance = nullptr; |
|
||||
|
|
||||
LONG WINAPI FaultManagedAllocator::VectoredExceptionHandler(PEXCEPTION_POINTERS exception_info) { |
|
||||
// Only handle access violations (page faults)
|
|
||||
if (exception_info->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) { |
|
||||
return EXCEPTION_CONTINUE_SEARCH; |
|
||||
} |
|
||||
|
|
||||
if (!current_instance) { |
|
||||
return EXCEPTION_CONTINUE_SEARCH; |
|
||||
} |
|
||||
|
|
||||
// Get the faulting address - use ULONG_PTR for Windows
|
|
||||
const ULONG_PTR fault_addr = static_cast<ULONG_PTR>(exception_info->ExceptionRecord->ExceptionInformation[1]); |
|
||||
const ULONG_PTR base_addr = reinterpret_cast<ULONG_PTR>(current_instance->base_address); |
|
||||
|
|
||||
// Check if the address is within our managed range
|
|
||||
if (fault_addr < base_addr || |
|
||||
fault_addr >= (base_addr + static_cast<ULONG_PTR>(current_instance->memory_size))) { |
|
||||
return EXCEPTION_CONTINUE_SEARCH; |
|
||||
} |
|
||||
|
|
||||
// Calculate the base address of the page
|
|
||||
const ULONG_PTR page_addr = fault_addr & ~(static_cast<ULONG_PTR>(PageSize) - 1); |
|
||||
const size_t relative_addr = static_cast<size_t>(page_addr - base_addr); |
|
||||
|
|
||||
// Handle the fault by allocating memory
|
|
||||
void* page = current_instance->GetOrAlloc(relative_addr); |
|
||||
if (!page) { |
|
||||
return EXCEPTION_CONTINUE_SEARCH; |
|
||||
} |
|
||||
|
|
||||
// Copy the page data to the faulting address
|
|
||||
DWORD old_protect; |
|
||||
void* target_addr = reinterpret_cast<void*>(page_addr); |
|
||||
|
|
||||
// Make the target page writable
|
|
||||
if (VirtualProtect(target_addr, PageSize, PAGE_READWRITE, &old_protect)) { |
|
||||
std::memcpy(target_addr, page, PageSize); |
|
||||
// Restore original protection
|
|
||||
VirtualProtect(target_addr, PageSize, old_protect, &old_protect); |
|
||||
return EXCEPTION_CONTINUE_EXECUTION; |
|
||||
} |
|
||||
|
|
||||
return EXCEPTION_CONTINUE_SEARCH; |
|
||||
} |
|
||||
|
|
||||
void FaultManagedAllocator::ExceptionHandlerThread() { |
|
||||
while (running) { |
|
||||
// Sleep to avoid busy waiting
|
|
||||
Sleep(10); |
|
||||
} |
|
||||
} |
|
||||
#endif
|
|
||||
|
|
||||
void FaultManagedAllocator::Initialize(void* base, size_t size) { |
|
||||
#if defined(__linux__) || defined(__ANDROID__)
|
|
||||
uffd = static_cast<int>(syscall(SYS_userfaultfd, O_CLOEXEC | O_NONBLOCK)); |
|
||||
if (uffd < 0) { |
|
||||
LOG_ERROR(Render_Vulkan, "Failed to create userfaultfd, fault handling disabled"); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
struct uffdio_api api = { .api = UFFD_API }; |
|
||||
ioctl(uffd, UFFDIO_API, &api); |
|
||||
|
|
||||
struct uffdio_register reg = { |
|
||||
.range = { .start = (uintptr_t)base, .len = size }, |
|
||||
.mode = UFFDIO_REGISTER_MODE_MISSING |
|
||||
}; |
|
||||
|
|
||||
if (ioctl(uffd, UFFDIO_REGISTER, ®) < 0) { |
|
||||
LOG_ERROR(Render_Vulkan, "Failed to register memory range with userfaultfd"); |
|
||||
close(uffd); |
|
||||
uffd = -1; |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
running = true; |
|
||||
fault_handler = std::thread(&FaultManagedAllocator::FaultThread, this); |
|
||||
#elif defined(_WIN32)
|
|
||||
// Setup Windows memory for fault handling
|
|
||||
base_address = base; |
|
||||
memory_size = size; |
|
||||
|
|
||||
// Reserve memory range but don't commit it yet - it will be demand-paged
|
|
||||
DWORD oldProtect; |
|
||||
VirtualProtect(base, size, PAGE_NOACCESS, &oldProtect); |
|
||||
|
|
||||
// Install a vectored exception handler
|
|
||||
current_instance = this; |
|
||||
AddVectoredExceptionHandler(1, VectoredExceptionHandler); |
|
||||
|
|
||||
running = true; |
|
||||
exception_handler = std::thread(&FaultManagedAllocator::ExceptionHandlerThread, this); |
|
||||
|
|
||||
LOG_INFO(Render_Vulkan, "Windows fault-managed memory initialized at {:p}, size: {}", |
|
||||
base, size); |
|
||||
#endif
|
|
||||
} |
|
||||
|
|
||||
#if defined(__linux__) || defined(__ANDROID__)
|
|
||||
void FaultManagedAllocator::FaultThread() { |
|
||||
struct pollfd pfd = { uffd, POLLIN, 0 }; |
|
||||
|
|
||||
while (running) { |
|
||||
if (poll(&pfd, 1, 10) > 0) { |
|
||||
struct uffd_msg msg; |
|
||||
read(uffd, &msg, sizeof(msg)); |
|
||||
|
|
||||
if (msg.event == UFFD_EVENT_PAGEFAULT) { |
|
||||
size_t addr = msg.arg.pagefault.address & ~(PageSize - 1); |
|
||||
void* page = GetOrAlloc(addr); |
|
||||
|
|
||||
if (page) { |
|
||||
struct uffdio_copy copy = { |
|
||||
.dst = (uintptr_t)addr, |
|
||||
.src = (uintptr_t)page, |
|
||||
.len = PageSize, |
|
||||
.mode = 0 |
|
||||
}; |
|
||||
|
|
||||
ioctl(uffd, UFFDIO_COPY, ©); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
#endif
|
|
||||
|
|
||||
void* FaultManagedAllocator::Translate(size_t addr) { |
|
||||
std::lock_guard<std::mutex> guard(lock); |
|
||||
|
|
||||
size_t base = addr & ~(PageSize - 1); |
|
||||
if (!page_map.count(base)) { |
|
||||
return nullptr; |
|
||||
} |
|
||||
|
|
||||
Touch(base); |
|
||||
return (u8*)page_map[base] + (addr % PageSize); |
|
||||
} |
|
||||
|
|
||||
void FaultManagedAllocator::SaveSnapshot(const std::string& path) { |
|
||||
std::lock_guard<std::mutex> guard(lock); |
|
||||
|
|
||||
std::ofstream out(path, std::ios::binary); |
|
||||
if (!out) { |
|
||||
LOG_ERROR(Render_Vulkan, "Failed to open snapshot file for writing: {}", path); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
for (auto& [addr, mem] : page_map) { |
|
||||
out.write(reinterpret_cast<const char*>(&addr), sizeof(addr)); |
|
||||
out.write(reinterpret_cast<const char*>(mem), PageSize); |
|
||||
} |
|
||||
|
|
||||
LOG_INFO(Render_Vulkan, "Saved memory snapshot to {}", path); |
|
||||
} |
|
||||
|
|
||||
void FaultManagedAllocator::SaveDifferentialSnapshot(const std::string& path) { |
|
||||
std::lock_guard<std::mutex> guard(lock); |
|
||||
|
|
||||
std::ofstream out(path, std::ios::binary); |
|
||||
if (!out) { |
|
||||
LOG_ERROR(Render_Vulkan, "Failed to open diff snapshot file for writing: {}", path); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
size_t dirty_count = 0; |
|
||||
for (const auto& addr : dirty_set) { |
|
||||
if (page_map.count(addr)) { |
|
||||
out.write(reinterpret_cast<const char*>(&addr), sizeof(addr)); |
|
||||
out.write(reinterpret_cast<const char*>(page_map[addr]), PageSize); |
|
||||
dirty_count++; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
LOG_INFO(Render_Vulkan, "Saved differential snapshot to {} ({} dirty pages)", |
|
||||
path, dirty_count); |
|
||||
} |
|
||||
|
|
||||
void FaultManagedAllocator::ClearDirtySet() { |
|
||||
std::lock_guard<std::mutex> guard(lock); |
|
||||
dirty_set.clear(); |
|
||||
LOG_DEBUG(Render_Vulkan, "Cleared dirty page tracking"); |
|
||||
} |
|
||||
|
|
||||
FaultManagedAllocator::~FaultManagedAllocator() { |
|
||||
running = false; |
|
||||
|
|
||||
#if defined(__linux__) || defined(__ANDROID__)
|
|
||||
if (fault_handler.joinable()) { |
|
||||
fault_handler.join(); |
|
||||
} |
|
||||
|
|
||||
for (auto& [addr, mem] : page_map) { |
|
||||
munmap(mem, PageSize); |
|
||||
} |
|
||||
|
|
||||
if (uffd != -1) { |
|
||||
close(uffd); |
|
||||
} |
|
||||
#elif defined(_WIN32)
|
|
||||
if (exception_handler.joinable()) { |
|
||||
exception_handler.join(); |
|
||||
} |
|
||||
|
|
||||
// Remove the vectored exception handler
|
|
||||
RemoveVectoredExceptionHandler(VectoredExceptionHandler); |
|
||||
current_instance = nullptr; |
|
||||
|
|
||||
for (auto& [addr, mem] : page_map) { |
|
||||
VirtualFree(mem, 0, MEM_RELEASE); |
|
||||
} |
|
||||
|
|
||||
// Free the base memory if needed
|
|
||||
if (base_address) { |
|
||||
VirtualFree(base_address, 0, MEM_RELEASE); |
|
||||
base_address = nullptr; |
|
||||
} |
|
||||
#endif
|
|
||||
} |
|
||||
#endif // defined(__linux__) || defined(__ANDROID__) || defined(_WIN32)
|
|
||||
|
|
||||
HybridMemory::HybridMemory(const Device& device_, MemoryAllocator& allocator, size_t reuse_history) |
|
||||
: device(device_), memory_allocator(allocator), reuse_manager(reuse_history) { |
|
||||
} |
|
||||
|
|
||||
HybridMemory::~HybridMemory() = default; |
|
||||
|
|
||||
void HybridMemory::InitializeGuestMemory(void* base, size_t size) { |
|
||||
#if defined(__linux__) || defined(__ANDROID__) || defined(_WIN32)
|
|
||||
fmaa.Initialize(base, size); |
|
||||
LOG_INFO(Render_Vulkan, "Initialized fault-managed guest memory at {:p}, size: {}", |
|
||||
base, size); |
|
||||
#else
|
|
||||
LOG_INFO(Render_Vulkan, "Fault-managed memory not supported on this platform"); |
|
||||
#endif
|
|
||||
} |
|
||||
|
|
||||
void* HybridMemory::TranslateAddress(size_t addr) { |
|
||||
#if defined(__linux__) || defined(__ANDROID__) || defined(_WIN32)
|
|
||||
return fmaa.Translate(addr); |
|
||||
#else
|
|
||||
return nullptr; |
|
||||
#endif
|
|
||||
} |
|
||||
|
|
||||
ComputeBuffer HybridMemory::CreateComputeBuffer(VkDeviceSize size, VkBufferUsageFlags usage, |
|
||||
MemoryUsage memory_type) { |
|
||||
ComputeBuffer buffer; |
|
||||
buffer.size = size; |
|
||||
|
|
||||
VkBufferCreateInfo buffer_ci = { |
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, |
|
||||
.pNext = nullptr, |
|
||||
.flags = 0, |
|
||||
.size = size, |
|
||||
.usage = usage | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, |
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE, |
|
||||
.queueFamilyIndexCount = 0, |
|
||||
.pQueueFamilyIndices = nullptr, |
|
||||
}; |
|
||||
|
|
||||
// Using CreateBuffer directly handles memory allocation internally
|
|
||||
buffer.buffer = memory_allocator.CreateBuffer(buffer_ci, memory_type); |
|
||||
|
|
||||
LOG_DEBUG(Render_Vulkan, "Created compute buffer: size={}, usage={:x}", |
|
||||
size, usage); |
|
||||
|
|
||||
return buffer; |
|
||||
} |
|
||||
|
|
||||
void HybridMemory::SaveSnapshot(const std::string& path) { |
|
||||
#if defined(__linux__) || defined(__ANDROID__) || defined(_WIN32)
|
|
||||
fmaa.SaveSnapshot(path); |
|
||||
#else
|
|
||||
LOG_ERROR(Render_Vulkan, "Memory snapshots not supported on this platform"); |
|
||||
#endif
|
|
||||
} |
|
||||
|
|
||||
void HybridMemory::SaveDifferentialSnapshot(const std::string& path) { |
|
||||
#if defined(__linux__) || defined(__ANDROID__) || defined(_WIN32)
|
|
||||
fmaa.SaveDifferentialSnapshot(path); |
|
||||
#else
|
|
||||
LOG_ERROR(Render_Vulkan, "Differential memory snapshots not supported on this platform"); |
|
||||
#endif
|
|
||||
} |
|
||||
|
|
||||
void HybridMemory::ResetDirtyTracking() { |
|
||||
#if defined(__linux__) || defined(__ANDROID__) || defined(_WIN32)
|
|
||||
fmaa.ClearDirtySet(); |
|
||||
#endif
|
|
||||
} |
|
||||
|
|
||||
} // namespace Vulkan
|
|
||||
@ -1,119 +0,0 @@ |
|||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project |
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
|
|
||||
#pragma once |
|
||||
|
|
||||
#include <memory> |
|
||||
#include <string> |
|
||||
#include <vector> |
|
||||
#include <unordered_map> |
|
||||
#include <mutex> |
|
||||
#include <atomic> |
|
||||
#include <functional> |
|
||||
#include <list> |
|
||||
#include <set> |
|
||||
#include <map> |
|
||||
#include <thread> |
|
||||
|
|
||||
#include "common/common_types.h" |
|
||||
#include "video_core/vulkan_common/vulkan_device.h" |
|
||||
#include "video_core/vulkan_common/vulkan_memory_allocator.h" |
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h" |
|
||||
|
|
||||
namespace Vulkan { |
|
||||
|
|
||||
struct ComputeBuffer { |
|
||||
vk::Buffer buffer{}; |
|
||||
VkDeviceSize size = 0; |
|
||||
}; |
|
||||
|
|
||||
class PredictiveReuseManager { |
|
||||
public: |
|
||||
explicit PredictiveReuseManager(size_t history_size) : max_history{history_size} {} |
|
||||
|
|
||||
void RecordUsage(u64 address, u64 size, bool write_access); |
|
||||
bool IsHotRegion(u64 address, u64 size) const; |
|
||||
void EvictRegion(u64 address, u64 size); |
|
||||
void ClearHistory(); |
|
||||
|
|
||||
private: |
|
||||
struct MemoryAccess { |
|
||||
u64 address; |
|
||||
u64 size; |
|
||||
bool write_access; |
|
||||
u64 timestamp; |
|
||||
}; |
|
||||
|
|
||||
std::vector<MemoryAccess> access_history; |
|
||||
const size_t max_history; |
|
||||
u64 current_timestamp{0}; |
|
||||
mutable std::mutex mutex; |
|
||||
}; |
|
||||
|
|
||||
#if defined(__linux__) || defined(__ANDROID__) || defined(_WIN32) |
|
||||
class FaultManagedAllocator { |
|
||||
public: |
|
||||
static constexpr size_t PageSize = 0x1000; |
|
||||
static constexpr size_t MaxPages = 16384; |
|
||||
|
|
||||
void Initialize(void* base, size_t size); |
|
||||
void* Translate(size_t addr); |
|
||||
void SaveSnapshot(const std::string& path); |
|
||||
void SaveDifferentialSnapshot(const std::string& path); |
|
||||
void ClearDirtySet(); |
|
||||
~FaultManagedAllocator(); |
|
||||
|
|
||||
private: |
|
||||
std::map<size_t, void*> page_map; |
|
||||
std::list<size_t> lru; |
|
||||
std::set<size_t> dirty_set; |
|
||||
std::unordered_map<size_t, std::vector<u8>> compressed_store; |
|
||||
std::mutex lock; |
|
||||
|
|
||||
#if defined(__linux__) || defined(__ANDROID__) |
|
||||
int uffd = -1; |
|
||||
std::atomic<bool> running{false}; |
|
||||
std::thread fault_handler; |
|
||||
void FaultThread(); |
|
||||
#elif defined(_WIN32) |
|
||||
void* base_address = nullptr; |
|
||||
size_t memory_size = 0; |
|
||||
HANDLE exception_port = nullptr; |
|
||||
std::atomic<bool> running{false}; |
|
||||
std::thread exception_handler; |
|
||||
void ExceptionHandlerThread(); |
|
||||
static LONG WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS exception_info); |
|
||||
static FaultManagedAllocator* current_instance; |
|
||||
#endif |
|
||||
|
|
||||
void Touch(size_t addr); |
|
||||
void EnforceLimit(); |
|
||||
void* GetOrAlloc(size_t addr); |
|
||||
}; |
|
||||
#endif |
|
||||
|
|
||||
class HybridMemory { |
|
||||
public: |
|
||||
explicit HybridMemory(const Device& device, MemoryAllocator& allocator, size_t reuse_history = 32); |
|
||||
~HybridMemory(); |
|
||||
|
|
||||
void InitializeGuestMemory(void* base, size_t size); |
|
||||
void* TranslateAddress(size_t addr); |
|
||||
|
|
||||
ComputeBuffer CreateComputeBuffer(VkDeviceSize size, VkBufferUsageFlags usage, MemoryUsage memory_type); |
|
||||
|
|
||||
void SaveSnapshot(const std::string& path); |
|
||||
void SaveDifferentialSnapshot(const std::string& path); |
|
||||
void ResetDirtyTracking(); |
|
||||
|
|
||||
private: |
|
||||
const Device& device; |
|
||||
MemoryAllocator& memory_allocator; |
|
||||
PredictiveReuseManager reuse_manager; |
|
||||
|
|
||||
#if defined(__linux__) || defined(__ANDROID__) || defined(_WIN32) |
|
||||
FaultManagedAllocator fmaa; |
|
||||
#endif |
|
||||
}; |
|
||||
|
|
||||
} // namespace Vulkan |
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue