early-access version 1766

This commit is contained in:
pineappleEA 2021-06-09 01:36:03 +02:00
parent 9612b12e5c
commit 10b3fd6931
3 changed files with 110 additions and 40 deletions

View file

@ -1,7 +1,7 @@
yuzu emulator early access yuzu emulator early access
============= =============
This is the source code for early-access 1763. This is the source code for early-access 1766.
## Legal Notice ## Legal Notice

View file

@ -6,20 +6,11 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <unistd.h> #include <unistd.h>
#elif defined(_WIN32) // ^^^ Linux ^^^ vvv Windows vvv #elif defined(_WIN32) // ^^^ Linux ^^^ vvv Windows vvv
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0A00 // Windows 10
#include <windows.h>
#include <boost/icl/separate_interval_set.hpp>
#include <iterator> #include <iterator>
#include <unordered_map> #include <unordered_map>
#include <boost/icl/separate_interval_set.hpp>
#pragma comment(lib, "mincore.lib") #include <windows.h>
#include "common/dynamic_library.h"
#endif // ^^^ Windows ^^^ #endif // ^^^ Windows ^^^
#include <mutex> #include <mutex>
@ -31,43 +22,99 @@
namespace Common { namespace Common {
static constexpr size_t PageAlignment = 0x1000; constexpr size_t PageAlignment = 0x1000;
static constexpr size_t HugePageSize = 0x200000; constexpr size_t HugePageSize = 0x200000;
#ifdef _WIN32 #ifdef _WIN32
// Manually imported for MinGW compatibility
#ifndef MEM_RESERVE_PLACEHOLDER
#define MEM_RESERVE_PLACEHOLDER 0x0004000
#endif
#ifndef MEM_REPLACE_PLACEHOLDER
#define MEM_REPLACE_PLACEHOLDER 0x00004000
#endif
using PFN_CreateFileMapping2 = _Ret_maybenull_ HANDLE(WINAPI*)(
_In_ HANDLE File, _In_opt_ SECURITY_ATTRIBUTES* SecurityAttributes, _In_ ULONG DesiredAccess,
_In_ ULONG PageProtection, _In_ ULONG AllocationAttributes, _In_ ULONG64 MaximumSize,
_In_opt_ PCWSTR Name,
_Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
_In_ ULONG ParameterCount);
using PFN_VirtualAlloc2 = _Ret_maybenull_ PVOID(WINAPI*)(
_In_opt_ HANDLE Process, _In_opt_ PVOID BaseAddress, _In_ SIZE_T Size,
_In_ ULONG AllocationType, _In_ ULONG PageProtection,
_Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
_In_ ULONG ParameterCount);
using PFN_MapViewOfFile3 = _Ret_maybenull_ __out_data_source(FILE)
PVOID(WINAPI*)(_In_ HANDLE FileMapping, _In_opt_ HANDLE Process, _In_opt_ PVOID BaseAddress,
_In_ ULONG64 Offset, _In_ SIZE_T ViewSize, _In_ ULONG AllocationType,
_In_ ULONG PageProtection,
_Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
_In_ ULONG ParameterCount);
using PFN_UnmapViewOfFile2 = BOOL(WINAPI*)(_In_ HANDLE Process, _In_ PVOID BaseAddress,
_In_ ULONG UnmapFlags);
template <typename T>
static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn) {
if (!dll.GetSymbol(name, &pfn)) {
LOG_CRITICAL(HW_Memory, "Failed to load {}", name);
throw std::bad_alloc{};
}
}
class HostMemory::Impl { class HostMemory::Impl {
public: public:
explicit Impl(size_t backing_size_, size_t virtual_size_) explicit Impl(size_t backing_size_, size_t virtual_size_)
: backing_size{backing_size_}, virtual_size{virtual_size_}, process{GetCurrentProcess()} { : backing_size{backing_size_}, virtual_size{virtual_size_}, process{GetCurrentProcess()},
kernelbase_dll("Kernelbase") {
if (!kernelbase_dll.IsOpen()) {
LOG_CRITICAL(HW_Memory, "Failed to load Kernelbase.dll");
throw std::bad_alloc{};
}
GetFuncAddress(kernelbase_dll, "CreateFileMapping2", pfn_CreateFileMapping2);
GetFuncAddress(kernelbase_dll, "VirtualAlloc2", pfn_VirtualAlloc2);
GetFuncAddress(kernelbase_dll, "MapViewOfFile3", pfn_MapViewOfFile3);
GetFuncAddress(kernelbase_dll, "UnmapViewOfFile2", pfn_UnmapViewOfFile2);
// Allocate backing file map // Allocate backing file map
backing_handle = backing_handle =
CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ, pfn_CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ,
PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0); PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0);
if (!backing_handle) { if (!backing_handle) {
LOG_CRITICAL(HW_Memory, "Failed to allocate {} MiB of backing memory",
backing_size >> 20);
throw std::bad_alloc{}; throw std::bad_alloc{};
} }
// Allocate a virtual memory for the backing file map as placeholder // Allocate a virtual memory for the backing file map as placeholder
backing_base = static_cast<u8*>(VirtualAlloc2(process, nullptr, backing_size, backing_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, backing_size,
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
PAGE_NOACCESS, nullptr, 0)); PAGE_NOACCESS, nullptr, 0));
if (!backing_base) { if (!backing_base) {
Release(); Release();
LOG_CRITICAL(HW_Memory, "Failed to reserve {} MiB of virtual memory",
backing_size >> 20);
throw std::bad_alloc{}; throw std::bad_alloc{};
} }
// Map backing placeholder // Map backing placeholder
void* const ret = MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size, void* const ret = pfn_MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size,
MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0); MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0);
if (ret != backing_base) { if (ret != backing_base) {
Release(); Release();
LOG_CRITICAL(HW_Memory, "Failed to map {} MiB of virtual memory", backing_size >> 20);
throw std::bad_alloc{}; throw std::bad_alloc{};
} }
// Allocate virtual address placeholder // Allocate virtual address placeholder
virtual_base = static_cast<u8*>(VirtualAlloc2(process, nullptr, virtual_size, virtual_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, virtual_size,
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
PAGE_NOACCESS, nullptr, 0)); PAGE_NOACCESS, nullptr, 0));
if (!virtual_base) { if (!virtual_base) {
Release(); Release();
LOG_CRITICAL(HW_Memory, "Failed to reserve {} GiB of virtual memory",
virtual_size >> 30);
throw std::bad_alloc{}; throw std::bad_alloc{};
} }
} }
@ -132,7 +179,7 @@ private:
void Release() { void Release() {
if (!placeholders.empty()) { if (!placeholders.empty()) {
for (const auto& placeholder : placeholders) { for (const auto& placeholder : placeholders) {
if (!UnmapViewOfFile2(process, virtual_base + placeholder.lower(), if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder.lower(),
MEM_PRESERVE_PLACEHOLDER)) { MEM_PRESERVE_PLACEHOLDER)) {
LOG_CRITICAL(HW_Memory, "Failed to unmap virtual memory placeholder"); LOG_CRITICAL(HW_Memory, "Failed to unmap virtual memory placeholder");
} }
@ -145,7 +192,7 @@ private:
} }
} }
if (backing_base) { if (backing_base) {
if (!UnmapViewOfFile2(process, backing_base, MEM_PRESERVE_PLACEHOLDER)) { if (!pfn_UnmapViewOfFile2(process, backing_base, MEM_PRESERVE_PLACEHOLDER)) {
LOG_CRITICAL(HW_Memory, "Failed to unmap backing memory placeholder"); LOG_CRITICAL(HW_Memory, "Failed to unmap backing memory placeholder");
} }
if (!VirtualFreeEx(process, backing_base, 0, MEM_RELEASE)) { if (!VirtualFreeEx(process, backing_base, 0, MEM_RELEASE)) {
@ -180,7 +227,7 @@ private:
const bool split_left = unmap_begin > placeholder_begin; const bool split_left = unmap_begin > placeholder_begin;
const bool split_right = unmap_end < placeholder_end; const bool split_right = unmap_end < placeholder_end;
if (!UnmapViewOfFile2(process, virtual_base + placeholder_begin, if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder_begin,
MEM_PRESERVE_PLACEHOLDER)) { MEM_PRESERVE_PLACEHOLDER)) {
LOG_CRITICAL(HW_Memory, "Failed to unmap placeholder"); LOG_CRITICAL(HW_Memory, "Failed to unmap placeholder");
} }
@ -231,7 +278,7 @@ private:
} }
void MapView(size_t virtual_offset, size_t host_offset, size_t length) { void MapView(size_t virtual_offset, size_t host_offset, size_t length) {
if (!MapViewOfFile3(backing_handle, process, virtual_base + virtual_offset, host_offset, if (!pfn_MapViewOfFile3(backing_handle, process, virtual_base + virtual_offset, host_offset,
length, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0)) { length, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0)) {
LOG_CRITICAL(HW_Memory, "Failed to map placeholder"); LOG_CRITICAL(HW_Memory, "Failed to map placeholder");
} }
@ -275,6 +322,12 @@ private:
HANDLE process{}; ///< Current process handle HANDLE process{}; ///< Current process handle
HANDLE backing_handle{}; ///< File based backing memory HANDLE backing_handle{}; ///< File based backing memory
DynamicLibrary kernelbase_dll;
PFN_CreateFileMapping2 pfn_CreateFileMapping2{};
PFN_VirtualAlloc2 pfn_VirtualAlloc2{};
PFN_MapViewOfFile3 pfn_MapViewOfFile3{};
PFN_UnmapViewOfFile2 pfn_UnmapViewOfFile2{};
std::mutex placeholder_mutex; ///< Mutex for placeholders std::mutex placeholder_mutex; ///< Mutex for placeholders
boost::icl::separate_interval_set<size_t> placeholders; ///< Mapped placeholders boost::icl::separate_interval_set<size_t> placeholders; ///< Mapped placeholders
std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset

View file

@ -8,6 +8,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/scope_exit.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/ipc_helpers.h" #include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/hle_ipc.h"
@ -119,11 +120,20 @@ ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memor
context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
// In the event that something fails here, stub a result to prevent the game from crashing.
// This is a work-around in the event that somehow we process a service request after the
// session has been closed by the game. This has been observed to happen rarely in Pokemon
// Sword/Shield and is likely a result of us using host threads/scheduling for services.
// TODO(bunnei): Find a better solution here.
auto error_guard = SCOPE_GUARD({ CompleteSyncRequest(*context); });
// Ensure we have a session request handler // Ensure we have a session request handler
if (manager->HasSessionRequestHandler(*context)) { if (manager->HasSessionRequestHandler(*context)) {
if (auto strong_ptr = manager->GetServiceThread().lock()) { if (auto strong_ptr = manager->GetServiceThread().lock()) {
strong_ptr->QueueSyncRequest(*parent, std::move(context)); strong_ptr->QueueSyncRequest(*parent, std::move(context));
return ResultSuccess;
// We succeeded.
error_guard.Cancel();
} else { } else {
ASSERT_MSG(false, "strong_ptr is nullptr!"); ASSERT_MSG(false, "strong_ptr is nullptr!");
} }
@ -136,7 +146,9 @@ ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memor
ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) { ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) {
ResultCode result = ResultSuccess; ResultCode result = ResultSuccess;
// If the session has been converted to a domain, handle the domain request // If the session has been converted to a domain, handle the domain request
if (manager->HasSessionRequestHandler(context)) {
if (IsDomain() && context.HasDomainMessageHeader()) { if (IsDomain() && context.HasDomainMessageHeader()) {
result = HandleDomainSyncRequest(context); result = HandleDomainSyncRequest(context);
// If there is no domain header, the regular session handler is used // If there is no domain header, the regular session handler is used
@ -144,6 +156,11 @@ ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) {
// If this ServerSession has an associated HLE handler, forward the request to it. // If this ServerSession has an associated HLE handler, forward the request to it.
result = manager->SessionHandler().HandleSyncRequest(*this, context); result = manager->SessionHandler().HandleSyncRequest(*this, context);
} }
} else {
ASSERT_MSG(false, "Session handler is invalid, stubbing response!");
IPC::ResponseBuilder rb(context, 2);
rb.Push(ResultSuccess);
}
if (convert_to_domain) { if (convert_to_domain) {
ASSERT_MSG(!IsDomain(), "ServerSession is already a domain instance."); ASSERT_MSG(!IsDomain(), "ServerSession is already a domain instance.");