Compare commits

...

45 Commits

Author SHA1 Message Date
lizzie 709fcb9e6c fixup 2026-02-09 11:02:07 +00:00
lizzie 70f8179eec ref 2026-02-09 10:59:29 +00:00
lizzie 767fceb14d license 2026-02-09 10:59:29 +00:00
Caio Oliveira ca32a5222d Fix license headers 2026-02-09 10:59:29 +00:00
lizzie 58052aef42 try both legacy and metal surfaces 2026-02-09 10:59:29 +00:00
lizzie c1fc6dcfe0 protect/map shenanigans with macOS 2026-02-09 10:59:29 +00:00
lizzie 816eb96f48 fix macos shenanigans 2026-02-09 10:59:29 +00:00
lizzie 1c1e7059d3 fx 2026-02-09 10:59:29 +00:00
lizzie 28a10ff6fc instance stuffs 2026-02-09 10:59:29 +00:00
lizzie 629ca92420 fixup extra stuff + enable gamemode 2026-02-09 10:59:29 +00:00
lizzie ebf7eeee7e fix apple crash? 2026-02-09 10:59:29 +00:00
lizzie c3be6fea9f use older macOS surface 2026-02-09 10:59:29 +00:00
lizzie 61b131ed4e enable macos surface 2026-02-09 10:59:29 +00:00
lizzie 7fb9d74a18 fix bundle, link with IOKit 2026-02-09 10:59:29 +00:00
lizzie fb1ecbca49 Update license 2026-02-09 10:59:10 +00:00
lizzie bf56de094b fix aarch64 gcc 2026-02-09 10:59:10 +00:00
lizzie 019921c10a disable NCE temporarily to fix mvk issues
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:59:10 +00:00
lizzie df4a609bb4 oops
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:59:10 +00:00
lizzie a7957f2401 dont break strict aliasing
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:59:10 +00:00
lizzie 9606875e96 fix moltvenk
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:59:10 +00:00
crueter 8adffb4dca cleanup cmake
Signed-off-by: crueter <crueter@eden-emu.dev>
2026-02-09 10:59:10 +00:00
crueter 583b3f95fd fix comp
Signed-off-by: crueter <crueter@eden-emu.dev>
2026-02-09 10:59:10 +00:00
crueter 38c6dd1c52 fix cpm-fetch
Signed-off-by: crueter <crueter@eden-emu.dev>
2026-02-09 10:59:10 +00:00
crueter d270527d2c fix android, macos, linux
Signed-off-by: crueter <crueter@eden-emu.dev>
2026-02-09 10:59:10 +00:00
lizzie 3d1759dfa5 16k page size for apple
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:59:10 +00:00
lizzie f8a28e9f55 better virtual base lookup algo for apple
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:59:10 +00:00
lizzie 38d496b35b fix apple clang
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:59:10 +00:00
crueter a3d63b74e4 fix cmake defaults/include stuff for Sequoia
Signed-off-by: crueter <crueter@eden-emu.dev>
2026-02-09 10:59:04 +00:00
lizzie 424c797502 [nce] signal hanlder fixes for sigaction
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:58:57 +00:00
lizzie e1670ffadb [docs, nce] use macos handler, cross arm inst
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:58:57 +00:00
lizzie 0a9bdcfaf1 [nce] fix tls using c23 kw
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:58:57 +00:00
lizzie ea5d69bd88 [nce] fix yoruself amd64
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:58:57 +00:00
lizzie c3ec202817 [nce] fix macos
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:58:57 +00:00
lizzie 39a18e536c [nce] fix linux build
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:58:57 +00:00
lizzie 84bca82516 [nce] fix thread kill
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:58:57 +00:00
crueter b9f8d30e7a [cmake] enable nce on macos
Signed-off-by: crueter <crueter@eden-emu.dev>
2026-02-09 10:58:57 +00:00
lizzie 1470b0f290 [nce] fix extra mangled symbols i forgot
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:58:57 +00:00
lizzie b9247438f9 [nce] add extra underscore, bother with preventing mangling later
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:58:57 +00:00
lizzie 4949611e23 [nce] more annoying syscalls and stuff
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:58:57 +00:00
lizzie d5c1adfabf [nce] fix apple gettid and tkill
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:58:57 +00:00
lizzie a33207de2f [nce] add syscall number for nearest thing to a tkill
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:58:57 +00:00
lizzie 59034611a2 [nce] more arm macos fixes
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:58:57 +00:00
lizzie 727fc36add [nce] more apple fixes
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:58:57 +00:00
lizzie c95ae23a4e [nce] common ctx
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:58:48 +00:00
lizzie 6a756fbe36 [nce, dynarmic] macOS port
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-02-09 10:58:47 +00:00
14 changed files with 358 additions and 166 deletions
+6
View File
@@ -261,4 +261,10 @@ if(ANDROID)
target_link_libraries(common PRIVATE android)
endif()
# IOPS (needed for power state) requires linking to IOKit
if (APPLE)
find_library(IOKIT_LIBRARY IOKit REQUIRED)
target_link_libraries(common PRIVATE ${IOKIT_LIBRARY})
endif()
create_target_directory_groups(common)
+83 -24
View File
@@ -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 2021 yuzu Emulator Project
@@ -30,6 +30,7 @@
#include <sys/random.h>
#include <mach/vm_map.h>
#include <mach/mach.h>
#include <map>
#endif
// FreeBSD
@@ -396,21 +397,68 @@ private:
#else // ^^^ Windows ^^^ vvv POSIX vvv
#ifdef ARCHITECTURE_arm64
// Use NCE friendly mapping techniques
#if defined(ARCHITECTURE_arm64) && defined(HAS_NCE)
static void* ChooseVirtualBase(size_t virtual_size) {
constexpr uintptr_t Map39BitSize = (1ULL << 39);
constexpr uintptr_t Map36BitSize = (1ULL << 36);
// This is not a cryptographic application, we just want something random.
std::mt19937_64 rng;
#ifdef __APPLE__
// TODO: Fatal flaw, regions may change if map inserts elements, very rare, but MAY HAPPEN!
std::map<u64, u64> aspace_list;
kern_return_t krc = KERN_SUCCESS;
vm_address_t address = 0;
vm_size_t r_size = 0;
uint32_t depth = 1;
do {
struct vm_region_submap_info_64 info;
mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
krc = vm_region_recurse_64(mach_task_self(), &address, &r_size, &depth, (vm_region_info_64_t)&info, &count);
if (krc == KERN_INVALID_ADDRESS)
break;
if (info.is_submap){
depth++;
} else {
aspace_list.insert({ u64(address), u64(r_size) });
address += r_size;
}
} while(1);
for (auto it = aspace_list.begin(); it != aspace_list.end(); it++) {
auto const next = std::next(it);
// properties of hole
auto const addr = it->first + it->second;
auto const size = (next != aspace_list.end()) ? next->first - addr : std::numeric_limits<u64>::max() - addr;
ASSERT(next == aspace_list.end() || it->first < next->first);
if (size > virtual_size && uintptr_t(addr + size) >= Map36BitSize) {
// valid address for NCE
if (uintptr_t(addr) >= Map36BitSize) { //common case: hole after 39 bit
if (size >= virtual_size) {
void* p = mmap(reinterpret_cast<void*>(addr), virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (p == MAP_FAILED)
continue;
return p;
}
// skip
} else { //edge case: hole before 39 bit
auto const rem_size = size - (Map36BitSize - uintptr_t(addr));
if (rem_size >= virtual_size) {
void* p = mmap(reinterpret_cast<void*>(Map36BitSize), virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (p == MAP_FAILED)
continue;
return p;
}
// skip
}
}
}
UNREACHABLE();
#else
constexpr uintptr_t Map39BitSize = (1ULL << 39);
// We want to ensure we are allocating at an address aligned to the L2 block size.
// For Qualcomm devices, we must also allocate memory above 36 bits.
const size_t lower = Map36BitSize / HugePageSize;
const size_t upper = (Map39BitSize - virtual_size) / HugePageSize;
const size_t range = upper - lower;
// This is not a cryptographic application, we just want something random.
std::mt19937_64 rng;
// Try up to 64 times to allocate memory at random addresses in the range.
for (int i = 0; i < 64; i++) {
// Calculate a possible location.
@@ -434,10 +482,9 @@ static void* ChooseVirtualBase(size_t virtual_size) {
}
return MAP_FAILED;
#endif
}
#else
static void* ChooseVirtualBase(size_t virtual_size) {
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__) || defined(__AIX__)
void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0);
@@ -446,7 +493,6 @@ static void* ChooseVirtualBase(size_t virtual_size) {
#endif
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
}
#endif
#if defined(__sun__) || defined(__HAIKU__) || defined(__NetBSD__) || defined(__DragonFly__)
@@ -500,9 +546,10 @@ class HostMemory::Impl {
public:
explicit Impl(size_t backing_size_, size_t virtual_size_)
: backing_size{backing_size_}, virtual_size{virtual_size_} {
long page_size = sysconf(_SC_PAGESIZE);
ASSERT_MSG(page_size == 0x1000, "page size {:#x} is incompatible with 4K paging",
page_size);
// TODO: Solve all 4k paging issues
//long page_size = sysconf(_SC_PAGESIZE);
//ASSERT_MSG(page_size == 0x1000, "page size {:#x} is incompatible with 4K paging",
// page_size);
// Backing memory initialization
#if defined(__sun__) || defined(__HAIKU__) || defined(__NetBSD__) || defined(__DragonFly__)
fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
@@ -572,12 +619,21 @@ public:
prot_flags |= PROT_READ;
if (True(perms & MemoryPermission::Write))
prot_flags |= PROT_WRITE;
#ifdef ARCHITECTURE_arm64
// W^X only supported (and needed) with NCE
#ifdef HAS_NCE
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);
u8* addr = virtual_base + virtual_offset;
#ifdef __APPLE__
// The way Steve Jobs intended
addr = (u8*)trunc_page(u64(addr) + host_offset);
length = round_page(length);
host_offset = 0;
#endif
void* ret = mmap(addr, length, prot_flags, flags, fd, host_offset);
ASSERT_MSG(ret != MAP_FAILED, "mmap: {}", strerror(errno));
}
@@ -602,18 +658,21 @@ public:
AdjustMap(&virtual_offset, &length);
int flags = PROT_NONE;
if (read) {
if (read)
flags |= PROT_READ;
}
if (write) {
if (write)
flags |= PROT_WRITE;
}
#ifdef HAS_NCE
if (execute) {
if (execute)
flags |= PROT_EXEC;
}
#endif
int ret = mprotect(virtual_base + virtual_offset, length, flags);
u8* addr = virtual_base + virtual_offset;
#ifdef __APPLE__
// The way Steve Jobs intended
addr = (u8*)trunc_page(u64(addr));
length = round_page(length);
#endif
int ret = mprotect(addr, length, flags);
ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno));
}
+11 -14
View File
@@ -1,3 +1,6 @@
// 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
@@ -10,27 +13,16 @@
namespace Common {
#ifdef __ANDROID__
template <typename T>
T* LookupLibcSymbol(const char* name) {
#if defined(__BIONIC__)
Common::DynamicLibrary provider("libc.so");
if (!provider.IsOpen()) {
UNREACHABLE_MSG("Failed to open libc!");
}
#else
// For other operating environments, we assume the symbol is not overridden.
const char* base = nullptr;
Common::DynamicLibrary provider(base);
#endif
ASSERT_MSG(provider.IsOpen(), "Failed to open libc!");
void* sym = provider.GetSymbolAddress(name);
if (sym == nullptr) {
sym = dlsym(RTLD_DEFAULT, name);
}
if (sym == nullptr) {
UNREACHABLE_MSG("Unable to find symbol {}!", name);
}
ASSERT_MSG(sym != nullptr, "Unable to find symbol {}!", name);
return reinterpret_cast<T*>(sym);
}
@@ -38,5 +30,10 @@ int SigAction(int signum, const struct sigaction* act, struct sigaction* oldact)
static auto libc_sigaction = LookupLibcSymbol<decltype(sigaction)>("sigaction");
return libc_sigaction(signum, act, oldact);
}
#else
int SigAction(int signum, const struct sigaction* act, struct sigaction* oldact) {
return sigaction(signum, act, oldact);
}
#endif
} // namespace Common
+87 -71
View File
@@ -13,12 +13,14 @@
#include "core/arm/nce/patcher.h"
#include "core/core.h"
#include "core/memory.h"
#include "core/hle/kernel/k_process.h"
#include "dynarmic/common/context.h"
#include <signal.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <pthread.h>
namespace Core {
@@ -33,95 +35,67 @@ static_assert(offsetof(NativeExecutionParameters, native_context) == TpidrEl0Nat
static_assert(offsetof(NativeExecutionParameters, lock) == TpidrEl0Lock);
static_assert(offsetof(NativeExecutionParameters, magic) == TpidrEl0TlsMagic);
fpsimd_context* GetFloatingPointState(mcontext_t& host_ctx) {
_aarch64_ctx* header = reinterpret_cast<_aarch64_ctx*>(&host_ctx.__reserved);
while (header->magic != FPSIMD_MAGIC) {
header = reinterpret_cast<_aarch64_ctx*>(reinterpret_cast<char*>(header) + header->size);
}
return reinterpret_cast<fpsimd_context*>(header);
}
using namespace Common::Literals;
constexpr u32 StackSize = 128_KiB;
} // namespace
void* ArmNce::RestoreGuestContext(void* raw_context) {
// Retrieve the host context.
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
// Thread-local parameters will be located in x9.
auto* tpidr = reinterpret_cast<NativeExecutionParameters*>(host_ctx.regs[9]);
auto* guest_ctx = static_cast<GuestContext*>(tpidr->native_context);
// Retrieve the host floating point state.
auto* fpctx = GetFloatingPointState(host_ctx);
// Save host callee-saved registers.
std::memcpy(guest_ctx->host_ctx.host_saved_vregs.data(), &fpctx->vregs[8],
sizeof(guest_ctx->host_ctx.host_saved_vregs));
std::memcpy(guest_ctx->host_ctx.host_saved_regs.data(), &host_ctx.regs[19],
sizeof(guest_ctx->host_ctx.host_saved_regs));
// Save stack pointer.
guest_ctx->host_ctx.host_sp = host_ctx.sp;
CTX_DECLARE(raw_context);
// Restore all guest state except tpidr_el0.
host_ctx.sp = guest_ctx->sp;
host_ctx.pc = guest_ctx->pc;
host_ctx.pstate = guest_ctx->pstate;
fpctx->fpcr = guest_ctx->fpcr;
fpctx->fpsr = guest_ctx->fpsr;
std::memcpy(host_ctx.regs, guest_ctx->cpu_registers.data(), sizeof(host_ctx.regs));
std::memcpy(fpctx->vregs, guest_ctx->vector_registers.data(), sizeof(fpctx->vregs));
// Thread-local parameters will be located in x9.
auto* tpidr = reinterpret_cast<NativeExecutionParameters*>(CTX_X(9));
auto* guest_ctx = static_cast<GuestContext*>(tpidr->native_context);
// Save host callee-saved registers.
std::memcpy(guest_ctx->host_ctx.host_saved_vregs.data(), &CTX_Q(8),
sizeof(guest_ctx->host_ctx.host_saved_vregs));
// Save stack pointer.
guest_ctx->host_ctx.host_sp = CTX_SP;
CTX_PC = guest_ctx->sp;
CTX_SP = guest_ctx->pc;
CTX_PSTATE = guest_ctx->pstate;
CTX_FPCR = guest_ctx->fpcr;
CTX_FPSR = guest_ctx->fpsr;
std::memcpy(&CTX_X(0), guest_ctx->cpu_registers.data(), sizeof(guest_ctx->cpu_registers));
std::memcpy(&CTX_Q(0), guest_ctx->vector_registers.data(), sizeof(guest_ctx->vector_registers));
// Return the new thread-local storage pointer.
return tpidr;
}
void ArmNce::SaveGuestContext(GuestContext* guest_ctx, void* raw_context) {
// Retrieve the host context.
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
// Retrieve the host floating point state.
auto* fpctx = GetFloatingPointState(host_ctx);
CTX_DECLARE(raw_context);
// Save all guest registers except tpidr_el0.
std::memcpy(guest_ctx->cpu_registers.data(), host_ctx.regs, sizeof(host_ctx.regs));
std::memcpy(guest_ctx->vector_registers.data(), fpctx->vregs, sizeof(fpctx->vregs));
guest_ctx->fpsr = fpctx->fpsr;
guest_ctx->fpcr = fpctx->fpcr;
guest_ctx->pstate = static_cast<u32>(host_ctx.pstate);
guest_ctx->pc = host_ctx.pc;
guest_ctx->sp = host_ctx.sp;
std::memcpy(guest_ctx->cpu_registers.data(), &CTX_X(0), sizeof(guest_ctx->cpu_registers));
std::memcpy(guest_ctx->vector_registers.data(), &CTX_Q(0), sizeof(guest_ctx->vector_registers));
guest_ctx->fpsr = CTX_FPSR;
guest_ctx->fpcr = CTX_FPCR;
guest_ctx->pc = CTX_PC;
guest_ctx->sp = CTX_SP;
guest_ctx->pstate = u32(CTX_PSTATE);
// Restore stack pointer.
host_ctx.sp = guest_ctx->host_ctx.host_sp;
CTX_SP = guest_ctx->host_ctx.host_sp;
// Restore host callee-saved registers.
std::memcpy(&host_ctx.regs[19], guest_ctx->host_ctx.host_saved_regs.data(),
std::memcpy(&CTX_X(19), guest_ctx->host_ctx.host_saved_regs.data(),
sizeof(guest_ctx->host_ctx.host_saved_regs));
std::memcpy(&fpctx->vregs[8], guest_ctx->host_ctx.host_saved_vregs.data(),
std::memcpy(&CTX_Q(8), guest_ctx->host_ctx.host_saved_vregs.data(),
sizeof(guest_ctx->host_ctx.host_saved_vregs));
// Return from the call on exit by setting pc to x30.
host_ctx.pc = guest_ctx->host_ctx.host_saved_regs[11];
CTX_PC = guest_ctx->host_ctx.host_saved_regs[11];
// Clear esr_el1 and return it.
host_ctx.regs[0] = guest_ctx->esr_el1.exchange(0);
CTX_X(0) = guest_ctx->esr_el1.exchange(0);
}
bool ArmNce::HandleFailedGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
CTX_DECLARE(raw_context);
auto* info = static_cast<siginfo_t*>(raw_info);
// We can't handle the access, so determine why we crashed.
const bool is_prefetch_abort = host_ctx.pc == reinterpret_cast<u64>(info->si_addr);
auto const is_prefetch_abort = CTX_PC == reinterpret_cast<u64>(info->si_addr);
// For data aborts, skip the instruction and return to guest code.
// This will allow games to continue in many scenarios where they would otherwise crash.
if (!is_prefetch_abort) {
host_ctx.pc += 4;
CTX_PC += 4;
return true;
}
@@ -142,17 +116,13 @@ bool ArmNce::HandleFailedGuestFault(GuestContext* guest_ctx, void* raw_info, voi
}
bool ArmNce::HandleGuestAlignmentFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
auto* fpctx = GetFloatingPointState(host_ctx);
auto& memory = guest_ctx->parent->m_running_thread->GetOwnerProcess()->GetMemory();
CTX_DECLARE(raw_context);
auto& memory = guest_ctx->system->ApplicationMemory();
// Match and execute an instruction.
auto next_pc = MatchAndExecuteOneInstruction(memory, &host_ctx, fpctx);
if (next_pc) {
host_ctx.pc = *next_pc;
if (auto next_pc = MatchAndExecuteOneInstruction(memory, raw_context); next_pc) {
CTX_PC = *next_pc;
return true;
}
// We couldn't handle the access.
return HandleFailedGuestFault(guest_ctx, raw_info, raw_context);
}
@@ -276,9 +246,51 @@ ArmNce::ArmNce(System& system, bool uses_wall_clock, std::size_t core_index)
ArmNce::~ArmNce() = default;
// Borrowed from libusb
static unsigned int posix_gettid(void) {
static thread_local unsigned int tl_tid;
int tid;
if (tl_tid)
return tl_tid;
#if defined(__ANDROID__)
tid = gettid();
#elif defined(__APPLE__)
#ifdef HAVE_PTHREAD_THREADID_NP
uint64_t thread_id;
if (pthread_threadid_np(NULL, &thread_id) == 0)
tid = (int)thread_id;
else
tid = -1;
#else
tid = (int)pthread_mach_thread_np(pthread_self());
#endif
#elif defined(__HAIKU__)
tid = get_pthread_thread_id(pthread_self());
#elif defined(__linux__)
tid = (int)syscall(SYS_gettid);
#elif defined(__NetBSD__)
tid = _lwp_self();
#elif defined(__OpenBSD__)
/* The following only works with OpenBSD > 5.1 as it requires
* real thread support. For 5.1 and earlier, -1 is returned. */
tid = syscall(SYS_getthrid);
#elif defined(__sun__)
tid = _lwp_self();
#else
tid = -1;
#endif
if (tid == -1) {
/* If we don't have a thread ID, at least return a unique
* value that can be used to distinguish individual
* threads. */
tid = (int)(intptr_t)pthread_self();
}
return tl_tid = (unsigned int)tid;
}
void ArmNce::Initialize() {
if (m_thread_id == -1) {
m_thread_id = gettid();
m_thread_id = posix_gettid();
}
// Configure signal stack.
@@ -309,7 +321,7 @@ void ArmNce::Initialize() {
&ArmNce::ReturnToRunCodeByExceptionLevelChangeSignalHandler);
return_to_run_code_action.sa_mask = signal_mask;
Common::SigAction(ReturnToRunCodeByExceptionLevelChangeSignal, &return_to_run_code_action,
nullptr);
nullptr);
struct sigaction break_from_run_code_action {};
break_from_run_code_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
@@ -381,7 +393,11 @@ void ArmNce::SignalInterrupt(Kernel::KThread* thread) {
if (params->is_running) {
// We should signal to the running thread.
// The running thread will unlock the thread context.
#ifdef __linux__
syscall(SYS_tkill, m_thread_id, BreakFromRunCodeSignal);
#else
pthread_kill(pthread_t(m_thread_id), int(BreakFromRunCodeSignal));
#endif
} else {
// If the thread is no longer running, we have nothing to do.
UnlockThreadParameters(params);
+90 -22
View File
@@ -9,10 +9,15 @@
/* static HaltReason Core::ArmNce::ReturnToRunCodeByTrampoline(void* tpidr, Core::GuestContext* ctx, u64 trampoline_addr) */
#ifdef __APPLE__
.global __ZN4Core6ArmNce27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEy
__ZN4Core6ArmNce27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEy:
#else
.section .text._ZN4Core6ArmNce27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm, "ax", %progbits
.global _ZN4Core6ArmNce27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm
.type _ZN4Core6ArmNce27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm, %function
.global _ZN4Core6ArmNce27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm
_ZN4Core6ArmNce27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm:
#endif
/* Back up host sp to x3. */
/* Back up host tpidr_el0 to x4. */
mov x3, sp
@@ -50,38 +55,52 @@ _ZN4Core6ArmNce27ReturnToRunCodeByTrampolineEPvPNS_12GuestContextEm:
/* static HaltReason Core::ArmNce::ReturnToRunCodeByExceptionLevelChange(int tid, void* tpidr) */
#ifdef __APPLE__
.global __ZN4Core6ArmNce37ReturnToRunCodeByExceptionLevelChangeEiPv
__ZN4Core6ArmNce37ReturnToRunCodeByExceptionLevelChangeEiPv:
#else
.section .text._ZN4Core6ArmNce37ReturnToRunCodeByExceptionLevelChangeEiPv, "ax", %progbits
.global _ZN4Core6ArmNce37ReturnToRunCodeByExceptionLevelChangeEiPv
.type _ZN4Core6ArmNce37ReturnToRunCodeByExceptionLevelChangeEiPv, %function
.global _ZN4Core6ArmNce37ReturnToRunCodeByExceptionLevelChangeEiPv
_ZN4Core6ArmNce37ReturnToRunCodeByExceptionLevelChangeEiPv:
#endif
/* This jumps to the signal handler, which will restore the entire context. */
/* On entry, x0 = thread id, which is already in the right place. */
/* Move tpidr to x9 so it is not trampled. */
mov x9, x1
/* Set up arguments. */
mov x8, #(__NR_tkill)
/* On entry, x0 = thread id, which is already in the right place. Even on macOS. */
mov x9, x1 /* Move tpidr to x9 so it is not trampled. */
mov x1, #(ReturnToRunCodeByExceptionLevelChangeSignal)
/* Tail call the signal handler. */
svc #0
/* Block execution from flowing here. */
brk #1000
#ifdef __APPLE__
/* I can never be happy, why no tkill in mach kernel? Ugh ... */
/* Signature: 328 AUE_PTHREADKILL ALL { int __pthread_kill(int thread_port, int sig); } */
mov x16, #(328)
svc #0x80 /* Tail call the signal handler. */
brk #0xF000 /* See: https://discourse.llvm.org/t/stepping-over-a-brk-instruction-on-arm64/69766/7 */
#else
/* Signature: int tgkill(pid_t tgid, pid_t tid, int sig); */
mov x8, #(__NR_tkill)
svc #0 /* Tail call the signal handler. */
brk #1000 /* Block execution from flowing here. */
#endif
/* static void Core::ArmNce::ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* info, void* raw_context) */
#ifdef __APPLE__
.global __ZN4Core6ArmNce50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_
__ZN4Core6ArmNce50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_:
#else
.section .text._ZN4Core6ArmNce50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_, "ax", %progbits
.global _ZN4Core6ArmNce50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_
.type _ZN4Core6ArmNce50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_, %function
.global _ZN4Core6ArmNce50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_
_ZN4Core6ArmNce50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_:
#endif
stp x29, x30, [sp, #-0x10]!
mov x29, sp
/* Call the context restorer with the raw context. */
mov x0, x2
#ifdef __APPLE__
bl __ZN4Core6ArmNce19RestoreGuestContextEPv
#else
bl _ZN4Core6ArmNce19RestoreGuestContextEPv
#endif
/* Save the old value of tpidr_el0. */
mrs x8, tpidr_el0
@@ -92,7 +111,11 @@ _ZN4Core6ArmNce50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_:
msr tpidr_el0, x0
/* Unlock the context. */
#ifdef __APPLE__
bl __ZN4Core6ArmNce22UnlockThreadParametersEPv
#else
bl _ZN4Core6ArmNce22UnlockThreadParametersEPv
#endif
/* Returning from here will enter the guest. */
ldp x29, x30, [sp], #0x10
@@ -100,10 +123,15 @@ _ZN4Core6ArmNce50ReturnToRunCodeByExceptionLevelChangeSignalHandlerEiPvS1_:
/* static void Core::ArmNce::BreakFromRunCodeSignalHandler(int sig, void* info, void* raw_context) */
#ifdef __APPLE__
.global __ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_
__ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_:
#else
.section .text._ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_, "ax", %progbits
.global _ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_
.type _ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_, %function
.global _ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_
_ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_:
#endif
/* Check to see if we have the correct TLS magic. */
mrs x8, tpidr_el0
ldr w9, [x8, #(TpidrEl0TlsMagic)]
@@ -121,7 +149,11 @@ _ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_:
/* Tail call the restorer. */
mov x1, x2
#ifdef __APPLE__
b __ZN4Core6ArmNce16SaveGuestContextEPNS_12GuestContextEPv
#else
b _ZN4Core6ArmNce16SaveGuestContextEPNS_12GuestContextEPv
#endif
/* Returning from here will enter host code. */
@@ -131,10 +163,15 @@ _ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_:
/* static void Core::ArmNce::GuestAlignmentFaultSignalHandler(int sig, void* info, void* raw_context) */
#ifdef __APPLE__
.global __ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_
__ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_:
#else
.section .text._ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_, "ax", %progbits
.global _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_
.type _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_, %function
.global _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_
_ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_:
#endif
/* Check to see if we have the correct TLS magic. */
mrs x8, tpidr_el0
ldr w9, [x8, #(TpidrEl0TlsMagic)]
@@ -146,7 +183,11 @@ _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_:
/* Incorrect TLS magic, so this is a host fault. */
/* Tail call the handler. */
#ifdef __APPLE__
b __ZN4Core6ArmNce24HandleHostAlignmentFaultEiPvS1_
#else
b _ZN4Core6ArmNce24HandleHostAlignmentFaultEiPvS1_
#endif
1:
/* Correct TLS magic, so this is a guest fault. */
@@ -163,7 +204,11 @@ _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_:
msr tpidr_el0, x3
/* Call the handler. */
#ifdef __APPLE__
bl __ZN4Core6ArmNce25HandleGuestAlignmentFaultEPNS_12GuestContextEPvS3_
#else
bl _ZN4Core6ArmNce25HandleGuestAlignmentFaultEPNS_12GuestContextEPvS3_
#endif
/* If the handler returned false, we want to preserve the host tpidr_el0. */
cbz x0, 2f
@@ -177,10 +222,15 @@ _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_:
ret
/* static void Core::ArmNce::GuestAccessFaultSignalHandler(int sig, void* info, void* raw_context) */
#ifdef __APPLE__
.global __ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_
__ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_:
#else
.section .text._ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_, "ax", %progbits
.global _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_
.type _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_, %function
.global _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_
_ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_:
#endif
/* Check to see if we have the correct TLS magic. */
mrs x8, tpidr_el0
ldr w9, [x8, #(TpidrEl0TlsMagic)]
@@ -192,7 +242,11 @@ _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_:
/* Incorrect TLS magic, so this is a host fault. */
/* Tail call the handler. */
#ifdef __APPLE__
b __ZN4Core6ArmNce21HandleHostAccessFaultEiPvS1_
#else
b _ZN4Core6ArmNce21HandleHostAccessFaultEiPvS1_
#endif
1:
/* Correct TLS magic, so this is a guest fault. */
@@ -209,7 +263,11 @@ _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_:
msr tpidr_el0, x3
/* Call the handler. */
#ifdef __APPLE__
bl __ZN4Core6ArmNce22HandleGuestAccessFaultEPNS_12GuestContextEPvS3_
#else
bl _ZN4Core6ArmNce22HandleGuestAccessFaultEPNS_12GuestContextEPvS3_
#endif
/* If the handler returned false, we want to preserve the host tpidr_el0. */
cbz x0, 2f
@@ -224,10 +282,15 @@ _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_:
/* static void Core::ArmNce::LockThreadParameters(void* tpidr) */
#ifdef __APPLE__
.global __ZN4Core6ArmNce20LockThreadParametersEPv
__ZN4Core6ArmNce20LockThreadParametersEPv:
#else
.section .text._ZN4Core6ArmNce20LockThreadParametersEPv, "ax", %progbits
.global _ZN4Core6ArmNce20LockThreadParametersEPv
.type _ZN4Core6ArmNce20LockThreadParametersEPv, %function
.global _ZN4Core6ArmNce20LockThreadParametersEPv
_ZN4Core6ArmNce20LockThreadParametersEPv:
#endif
/* Offset to lock member. */
add x0, x0, #(TpidrEl0Lock)
@@ -252,10 +315,15 @@ _ZN4Core6ArmNce20LockThreadParametersEPv:
/* static void Core::ArmNce::UnlockThreadParameters(void* tpidr) */
#ifdef __APPLE__
.global __ZN4Core6ArmNce22UnlockThreadParametersEPv
__ZN4Core6ArmNce22UnlockThreadParametersEPv:
#else
.section .text._ZN4Core6ArmNce22UnlockThreadParametersEPv, "ax", %progbits
.global _ZN4Core6ArmNce22UnlockThreadParametersEPv
.type _ZN4Core6ArmNce22UnlockThreadParametersEPv, %function
.global _ZN4Core6ArmNce22UnlockThreadParametersEPv
_ZN4Core6ArmNce22UnlockThreadParametersEPv:
#endif
/* Offset to lock member. */
add x0, x0, #(TpidrEl0Lock)
+16 -6
View File
@@ -1,3 +1,6 @@
// 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 */
@@ -5,22 +8,24 @@
#define __ASSEMBLY__
#ifdef __APPLE__
/* https://cpip.readthedocs.io/en/stable/_static/dictobject.c/signal.h_bbe000f9714f274340a28e000a369354.html */
#define ReturnToRunCodeByExceptionLevelChangeSignal 31
#define BreakFromRunCodeSignal 16
#define GuestAccessFaultSignal 11
#define GuestAlignmentFaultSignal 10
#else
#include <asm-generic/signal.h>
#include <asm-generic/unistd.h>
#define ReturnToRunCodeByExceptionLevelChangeSignal SIGUSR2
#define BreakFromRunCodeSignal SIGURG
#define GuestAccessFaultSignal SIGSEGV
#define GuestAlignmentFaultSignal SIGBUS
#endif
#define GuestContextSp 0xF8
#define GuestContextHostContext 0x320
#define HostContextSpTpidrEl0 0xE0
#define HostContextTpidrEl0 0xE8
#define HostContextRegs 0x0
#define HostContextVregs 0x60
#define TpidrEl0NativeContext 0x10
#define TpidrEl0Lock 0x18
#define TpidrEl0TlsMagic 0x20
@@ -28,3 +33,8 @@
#define SpinLockLocked 0
#define SpinLockUnlocked 1
#define HostContextSpTpidrEl0 0xE0
#define HostContextTpidrEl0 0xE8
#define HostContextRegs 0x0
#define HostContextVregs 0x60
+15 -12
View File
@@ -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
@@ -7,6 +7,8 @@
#include <numeric>
#include "core/arm/nce/interpreter_visitor.h"
#include "core/memory.h"
#include "dynarmic/common/context.h"
namespace Core {
@@ -761,24 +763,25 @@ bool InterpreterVisitor::LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3
return this->SIMDOffset(scale, shift, opc_0, Rm, option, Rn, Vt);
}
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context,
fpsimd_context* fpsimd_context) {
std::span<u64, 31> regs(reinterpret_cast<u64*>(context->regs), 31);
std::span<u128, 32> vregs(reinterpret_cast<u128*>(fpsimd_context->vregs), 32);
u64& sp = *reinterpret_cast<u64*>(&context->sp);
const u64& pc = *reinterpret_cast<u64*>(&context->pc);
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, void* raw_context) {
CTX_DECLARE(raw_context);
std::span<u64, 31> regs(reinterpret_cast<u64*>(&CTX_X(0)), 31);
std::span<u128, 32> vregs(reinterpret_cast<u128*>(&CTX_Q(0)), 32);
InterpreterVisitor visitor(memory, regs, vregs, sp, pc);
u32 instruction = memory.Read32(pc);
// Store temporal to not break aliasing rules :)
u64 tmp_sp = CTX_SP;
u64 tmp_pc = CTX_PC;
u32 instruction = memory.Read32(tmp_pc);
bool was_executed = false;
InterpreterVisitor visitor(memory, regs, vregs, tmp_sp, tmp_pc);
if (auto decoder = Dynarmic::A64::Decode<VisitorBase>(instruction)) {
was_executed = decoder->get().call(visitor, instruction);
} else {
LOG_ERROR(Core_ARM, "Unallocated encoding: {:#x}", instruction);
}
return was_executed ? std::optional<u64>(pc + 4) : std::nullopt;
CTX_SP = tmp_sp;
CTX_PC = tmp_pc;
return was_executed ? std::optional<u64>(tmp_pc + 4) : std::nullopt;
}
} // namespace Core
+3 -3
View File
@@ -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
@@ -9,6 +9,7 @@
#include <atomic>
#include <signal.h>
#include <span>
#include <unistd.h>
#include <span>
@@ -105,7 +106,6 @@ private:
const u64& m_pc;
};
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context,
fpsimd_context* fpsimd_context);
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, void* raw_context);
} // namespace Core
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -5,4 +8,7 @@
#include "video_core/vulkan_common/vulkan.h"
#if defined(__APPLE__) && !defined(VK_STRUCTURE_TYPE_OH_SURFACE_CREATE_INFO_OHOS)
# define VK_STRUCTURE_TYPE_OH_SURFACE_CREATE_INFO_OHOS VK_STRUCTURE_TYPE_SURFACE_CREATE_INFO_OHOS
#endif
#include <vulkan/vk_enum_string_helper.h>
+8 -1
View File
@@ -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
@@ -11,6 +11,7 @@
#define VK_USE_PLATFORM_WIN32_KHR
#elif defined(__APPLE__)
#define VK_USE_PLATFORM_METAL_EXT
#define VK_USE_PLATFORM_MACOS_MVK
#elif defined(__ANDROID__)
#define VK_USE_PLATFORM_ANDROID_KHR
#elif defined(__HAIKU__)
@@ -32,6 +33,12 @@
#ifndef VK_KHR_MAINTENANCE_9_EXTENSION_NAME
#define VK_KHR_MAINTENANCE_9_EXTENSION_NAME "VK_KHR_maintenance9"
#endif
#ifndef VK_EXT_METAL_SURFACE_EXTENSION_NAME
#define VK_EXT_METAL_SURFACE_EXTENSION_NAME "VK_EXT_metal_surface"
#endif
#ifndef VK_MVK_MACOS_SURFACE_EXTENSION_NAME
#define VK_MVK_MACOS_SURFACE_EXTENSION_NAME "VK_MVK_macos_surface"
#endif
// Sanitize macros
#undef CreateEvent
@@ -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 2020 yuzu Emulator Project
@@ -48,6 +48,8 @@ namespace {
#elif defined(__APPLE__)
case Core::Frontend::WindowSystemType::Cocoa:
extensions.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME);
//extensions.push_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME);
//extensions.push_back(VK_MVK_MOLTENVK_EXTENSION_NAME);
break;
#elif defined(__ANDROID__)
case Core::Frontend::WindowSystemType::Android:
@@ -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 2020 yuzu Emulator Project
@@ -32,15 +32,30 @@ vk::SurfaceKHR CreateSurface(
}
#elif defined(__APPLE__)
if (window_info.type == Core::Frontend::WindowSystemType::Cocoa) {
const VkMetalSurfaceCreateInfoEXT macos_ci = {
const VkMetalSurfaceCreateInfoEXT metal_ci = {
.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT,
.pNext = nullptr,
.flags = 0,
.pLayer = static_cast<const CAMetalLayer*>(window_info.render_surface),
};
const auto vkCreateMetalSurfaceEXT = reinterpret_cast<PFN_vkCreateMetalSurfaceEXT>(
dld.vkGetInstanceProcAddr(*instance, "vkCreateMetalSurfaceEXT"));
if (!vkCreateMetalSurfaceEXT ||
vkCreateMetalSurfaceEXT(*instance, &macos_ci, nullptr, &unsafe_surface) != VK_SUCCESS) {
LOG_ERROR(Render_Vulkan, "Failed to initialize Metal surface");
throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
const auto vkCreateMetalSurfaceEXT = PFN_vkCreateMetalSurfaceEXT(dld.vkGetInstanceProcAddr(*instance, "vkCreateMetalSurfaceEXT"));
if (!vkCreateMetalSurfaceEXT || vkCreateMetalSurfaceEXT(*instance, &metal_ci, nullptr, &unsafe_surface) != VK_SUCCESS) {
// TODO: Way to fallback? - where's my vulkan headers
// Attempt to make a macOS surface instead then...
// This is the deprecated VkMacOSSurfaceCreateInfoMVK(3) version; but should work if the above failed
// https://registry.khronos.org/vulkan/specs/latest/man/html/VkMacOSSurfaceCreateInfoMVK.html
const VkMacOSSurfaceCreateInfoMVK macos_legacy_ci = {
.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK,
.pNext = nullptr,
.flags = 0,
.pView = static_cast<const void*>(window_info.render_surface),
};
const auto vkCreateMacOSSurfaceMVK = PFN_vkCreateMacOSSurfaceMVK(dld.vkGetInstanceProcAddr(*instance, "vkCreateMacOSSurfaceMVK"));
if (!vkCreateMacOSSurfaceMVK || vkCreateMacOSSurfaceMVK(*instance, &macos_legacy_ci, nullptr, &unsafe_surface) != VK_SUCCESS) {
LOG_ERROR(Render_Vulkan, "Failed to initialize Metal/macOS surface");
throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
}
LOG_ERROR(Render_Vulkan, "Failed to initialize Metal/macOS surface");
}
}
#elif defined(__ANDROID__)
@@ -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 2020 yuzu Emulator Project
@@ -432,10 +432,9 @@ VkResult Free(VkDevice device, VkCommandPool handle, Span<VkCommandBuffer> buffe
Instance Instance::Create(u32 version, Span<const char*> layers, Span<const char*> extensions,
InstanceDispatch& dispatch) {
VkFlags ci_flags{};
#ifdef __APPLE__
constexpr VkFlags ci_flags{VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR};
#else
constexpr VkFlags ci_flags{};
ci_flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
#endif
// DO NOT TOUCH, breaks RNDA3!!
// Don't know why, but gloom + yellow line glitch appears
@@ -463,6 +462,7 @@ Instance Instance::Create(u32 version, Span<const char*> layers, Span<const char
if (!Proc(dispatch.vkDestroyInstance, dispatch, "vkDestroyInstance", instance)) {
// We successfully created an instance but the destroy function couldn't be loaded.
// This is a good moment to panic.
LOG_ERROR(Render_Vulkan, "No vkDestroyInstance found");
throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
}
return Instance(instance, dispatch);