Add initial macOS support.
This commit is contained in:
parent
36d528743a
commit
66fa29059c
|
@ -58,3 +58,6 @@
|
||||||
[submodule "externals/ext-boost"]
|
[submodule "externals/ext-boost"]
|
||||||
path = externals/ext-boost
|
path = externals/ext-boost
|
||||||
url = https://github.com/shadps4-emu/ext-boost.git
|
url = https://github.com/shadps4-emu/ext-boost.git
|
||||||
|
[submodule "externals/date"]
|
||||||
|
path = externals/date
|
||||||
|
url = https://github.com/HowardHinnant/date.git
|
||||||
|
|
|
@ -6,6 +6,10 @@ cmake_minimum_required(VERSION 3.16.3)
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
enable_language(OBJC)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (NOT CMAKE_BUILD_TYPE)
|
if (NOT CMAKE_BUILD_TYPE)
|
||||||
set(CMAKE_BUILD_TYPE Release)
|
set(CMAKE_BUILD_TYPE Release)
|
||||||
endif()
|
endif()
|
||||||
|
@ -62,6 +66,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||||
|
|
||||||
find_package(Boost 1.84.0 CONFIG)
|
find_package(Boost 1.84.0 CONFIG)
|
||||||
find_package(cryptopp 8.9.0 MODULE)
|
find_package(cryptopp 8.9.0 MODULE)
|
||||||
|
find_package(date 3.0.1 CONFIG)
|
||||||
find_package(fmt 10.2.1 CONFIG)
|
find_package(fmt 10.2.1 CONFIG)
|
||||||
find_package(glslang 14.2.0 CONFIG)
|
find_package(glslang 14.2.0 CONFIG)
|
||||||
find_package(magic_enum 0.9.6 CONFIG)
|
find_package(magic_enum 0.9.6 CONFIG)
|
||||||
|
@ -75,6 +80,13 @@ find_package(xxHash 0.8.2 MODULE)
|
||||||
find_package(zlib-ng 2.2.0 MODULE)
|
find_package(zlib-ng 2.2.0 MODULE)
|
||||||
find_package(Zydis 4.1.0 CONFIG)
|
find_package(Zydis 4.1.0 CONFIG)
|
||||||
|
|
||||||
|
include(CheckSymbolExists)
|
||||||
|
check_symbol_exists(pthread_mutex_timedlock "pthread.h" HAVE_PTHREAD_MUTEX_TIMEDLOCK)
|
||||||
|
# Windows always has the function through winpthreads
|
||||||
|
if(HAVE_PTHREAD_MUTEX_TIMEDLOCK OR WIN32)
|
||||||
|
add_compile_options(-DHAVE_PTHREAD_MUTEX_TIMEDLOCK)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_subdirectory(externals)
|
add_subdirectory(externals)
|
||||||
include_directories(src)
|
include_directories(src)
|
||||||
|
|
||||||
|
@ -296,6 +308,8 @@ set(CORE src/core/aerolib/stubs.cpp
|
||||||
src/core/file_format/splash.cpp
|
src/core/file_format/splash.cpp
|
||||||
src/core/file_sys/fs.cpp
|
src/core/file_sys/fs.cpp
|
||||||
src/core/file_sys/fs.h
|
src/core/file_sys/fs.h
|
||||||
|
src/core/instruction_emulator.cpp
|
||||||
|
src/core/instruction_emulator.h
|
||||||
src/core/loader.cpp
|
src/core/loader.cpp
|
||||||
src/core/loader.h
|
src/core/loader.h
|
||||||
src/core/loader/dwarf.cpp
|
src/core/loader/dwarf.cpp
|
||||||
|
@ -539,9 +553,15 @@ endif()
|
||||||
|
|
||||||
create_target_directory_groups(shadps4)
|
create_target_directory_groups(shadps4)
|
||||||
|
|
||||||
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient)
|
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient date::date-tz)
|
||||||
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3)
|
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3)
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
|
# Link MoltenVK for Vulkan support
|
||||||
|
find_library(MOLTENVK MoltenVK REQUIRED)
|
||||||
|
target_link_libraries(shadps4 PRIVATE ${MOLTENVK})
|
||||||
|
endif()
|
||||||
|
|
||||||
if (NOT ENABLE_QT_GUI)
|
if (NOT ENABLE_QT_GUI)
|
||||||
target_link_libraries(shadps4 PRIVATE SDL3::SDL3)
|
target_link_libraries(shadps4 PRIVATE SDL3::SDL3)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -134,6 +134,13 @@ if (WIN32)
|
||||||
target_compile_options(sirit PUBLIC "-Wno-error=unused-command-line-argument")
|
target_compile_options(sirit PUBLIC "-Wno-error=unused-command-line-argument")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# date
|
||||||
|
if (NOT TARGET date::date-tz)
|
||||||
|
option(BUILD_TZ_LIB "" ON)
|
||||||
|
option(USE_SYSTEM_TZ_DB "" ON)
|
||||||
|
add_subdirectory(date)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Tracy
|
# Tracy
|
||||||
option(TRACY_ENABLE "" ON)
|
option(TRACY_ENABLE "" ON)
|
||||||
option(TRACY_NO_CRASH_HANDLER "" ON) # Otherwise texture cache exceptions will be treaten as a crash
|
option(TRACY_NO_CRASH_HANDLER "" ON) # Otherwise texture cache exceptions will be treaten as a crash
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 1ead6715dec030d340a316c927c877a3c4e5a00c
|
|
@ -9,6 +9,7 @@
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
#include <mach/mach.h>
|
#include <mach/mach.h>
|
||||||
|
#include <pthread.h>
|
||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
|
|
|
@ -245,14 +245,20 @@ struct AddressSpace::Impl {
|
||||||
Impl() {
|
Impl() {
|
||||||
// Allocate virtual address placeholder for our address space.
|
// Allocate virtual address placeholder for our address space.
|
||||||
void* hint_address = reinterpret_cast<void*>(SYSTEM_MANAGED_MIN);
|
void* hint_address = reinterpret_cast<void*>(SYSTEM_MANAGED_MIN);
|
||||||
|
#ifdef __APPLE__
|
||||||
|
constexpr int virtual_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
|
||||||
|
#else
|
||||||
|
constexpr int virtual_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED;
|
||||||
|
#endif
|
||||||
virtual_size = SystemSize + UserSize;
|
virtual_size = SystemSize + UserSize;
|
||||||
virtual_base = reinterpret_cast<u8*>(
|
virtual_base = reinterpret_cast<u8*>(
|
||||||
mmap(hint_address, virtual_size, PROT_READ | PROT_WRITE,
|
mmap(hint_address, virtual_size, PROT_READ | PROT_WRITE, virtual_flags, -1, 0));
|
||||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED, -1, 0));
|
|
||||||
if (virtual_base == MAP_FAILED) {
|
if (virtual_base == MAP_FAILED) {
|
||||||
LOG_CRITICAL(Kernel_Vmm, "mmap failed: {}", strerror(errno));
|
LOG_CRITICAL(Kernel_Vmm, "mmap failed: {}", strerror(errno));
|
||||||
throw std::bad_alloc{};
|
throw std::bad_alloc{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef __APPLE__
|
||||||
madvise(virtual_base, virtual_size, MADV_HUGEPAGE);
|
madvise(virtual_base, virtual_size, MADV_HUGEPAGE);
|
||||||
|
|
||||||
backing_fd = memfd_create("BackingDmem", 0);
|
backing_fd = memfd_create("BackingDmem", 0);
|
||||||
|
@ -260,6 +266,15 @@ struct AddressSpace::Impl {
|
||||||
LOG_CRITICAL(Kernel_Vmm, "memfd_create failed: {}", strerror(errno));
|
LOG_CRITICAL(Kernel_Vmm, "memfd_create failed: {}", strerror(errno));
|
||||||
throw std::bad_alloc{};
|
throw std::bad_alloc{};
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
const auto shm_path = fmt::format("/BackingDmem{}", getpid());
|
||||||
|
backing_fd = shm_open(shm_path.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
|
||||||
|
if (backing_fd < 0) {
|
||||||
|
LOG_CRITICAL(Kernel_Vmm, "shm_open failed: {}", strerror(errno));
|
||||||
|
throw std::bad_alloc{};
|
||||||
|
}
|
||||||
|
shm_unlink(shm_path.c_str());
|
||||||
|
#endif
|
||||||
|
|
||||||
// Defined to extend the file with zeros
|
// Defined to extend the file with zeros
|
||||||
int ret = ftruncate(backing_fd, BackingSize);
|
int ret = ftruncate(backing_fd, BackingSize);
|
||||||
|
|
|
@ -0,0 +1,291 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <Zydis/Zydis.h>
|
||||||
|
#include <xbyak/xbyak.h>
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/types.h"
|
||||||
|
#include "instruction_emulator.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
static Xbyak::Reg ZydisToXbyakRegister(const ZydisRegister reg) {
|
||||||
|
if (reg >= ZYDIS_REGISTER_EAX && reg <= ZYDIS_REGISTER_R15D) {
|
||||||
|
return Xbyak::Reg32(reg - ZYDIS_REGISTER_EAX);
|
||||||
|
} else if (reg >= ZYDIS_REGISTER_RAX && reg <= ZYDIS_REGISTER_R15) {
|
||||||
|
return Xbyak::Reg64(reg - ZYDIS_REGISTER_RAX);
|
||||||
|
} else {
|
||||||
|
UNREACHABLE_MSG("Unsupported register: {}", static_cast<u32>(reg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Xbyak::Reg ZydisToXbyakRegisterOperand(const ZydisDecodedOperand& operand) {
|
||||||
|
ASSERT_MSG(operand.type == ZYDIS_OPERAND_TYPE_REGISTER, "Expected register operand, got type: {}", static_cast<u32>(operand.type));
|
||||||
|
|
||||||
|
return ZydisToXbyakRegister(operand.reg.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Xbyak::Address ZydisToXbyakMemoryOperand(const ZydisDecodedOperand& operand) {
|
||||||
|
ASSERT_MSG(operand.type == ZYDIS_OPERAND_TYPE_MEMORY, "Expected memory operand, got type: {}", static_cast<u32>(operand.type));
|
||||||
|
|
||||||
|
Xbyak::RegExp expression{};
|
||||||
|
if (operand.mem.base != ZYDIS_REGISTER_NONE) {
|
||||||
|
expression = expression + ZydisToXbyakRegister(operand.mem.base);
|
||||||
|
}
|
||||||
|
if (operand.mem.index != ZYDIS_REGISTER_NONE) {
|
||||||
|
if (operand.mem.scale != 0) {
|
||||||
|
expression = expression + ZydisToXbyakRegister(operand.mem.index) * operand.mem.scale;
|
||||||
|
} else {
|
||||||
|
expression = expression + ZydisToXbyakRegister(operand.mem.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (operand.mem.disp.size != 0 && operand.mem.disp.value != 0) {
|
||||||
|
expression = expression + operand.mem.disp.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Xbyak::util::ptr[expression];
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unique_ptr<Xbyak::Operand> ZydisToXbyakOperand(const ZydisDecodedOperand& operand) {
|
||||||
|
switch (operand.type) {
|
||||||
|
case ZYDIS_OPERAND_TYPE_REGISTER: {
|
||||||
|
return std::make_unique<Xbyak::Reg>(ZydisToXbyakRegisterOperand(operand));
|
||||||
|
}
|
||||||
|
case ZYDIS_OPERAND_TYPE_MEMORY: {
|
||||||
|
return std::make_unique<Xbyak::Address>(ZydisToXbyakMemoryOperand(operand));
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
UNREACHABLE_MSG("Unsupported operand type: {}", static_cast<u32>(operand.type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
|
||||||
|
static bool OperandUsesRegister(const Xbyak::Operand* operand, int index) {
|
||||||
|
if (operand->isREG()) {
|
||||||
|
return operand->getIdx() == index;
|
||||||
|
}
|
||||||
|
if (operand->isMEM()) {
|
||||||
|
const Xbyak::RegExp& reg_exp = operand->getAddress().getRegExp();
|
||||||
|
return reg_exp.getBase().getIdx() == index || reg_exp.getIndex().getIdx() == index;
|
||||||
|
}
|
||||||
|
UNREACHABLE_MSG("Unsupported operand kind: {}", static_cast<u32>(operand->getKind()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsRegisterAllocated(const std::initializer_list<const Xbyak::Operand*>& allocated_registers, const int index) {
|
||||||
|
return std::ranges::find_if(
|
||||||
|
allocated_registers.begin(), allocated_registers.end(),
|
||||||
|
[index](const Xbyak::Operand* operand) { return OperandUsesRegister(operand, index); }) != allocated_registers.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Xbyak::Reg AllocateScratchRegister(const std::initializer_list<const Xbyak::Operand*> allocated_registers, const u32 bits) {
|
||||||
|
for (int index = Xbyak::Operand::R8; index <= Xbyak::Operand::R15; index++) {
|
||||||
|
if (!IsRegisterAllocated(allocated_registers, index)) {
|
||||||
|
return Xbyak::Reg32e(index, static_cast<int>(bits));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UNREACHABLE_MSG("Out of scratch registers!");
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr u32 MaxSavedRegisters = 3;
|
||||||
|
static pthread_key_t register_save_slots[MaxSavedRegisters];
|
||||||
|
static std::once_flag register_save_init_flag;
|
||||||
|
|
||||||
|
static_assert(sizeof(void*) == sizeof(u64), "Cannot fit a register inside a thread local storage slot.");
|
||||||
|
|
||||||
|
static void InitializeRegisterSaveSlots() {
|
||||||
|
for (u32 i = 0; i < MaxSavedRegisters; i++) {
|
||||||
|
ASSERT_MSG(pthread_key_create(®ister_save_slots[i], nullptr) == 0,
|
||||||
|
"Unable to allocate thread-local register save slot {}", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SaveRegisters(Xbyak::CodeGenerator& c, const std::initializer_list<Xbyak::Reg> regs) {
|
||||||
|
ASSERT_MSG(regs.size() <= MaxSavedRegisters, "Not enough space to save {} registers.", regs.size());
|
||||||
|
|
||||||
|
std::call_once(register_save_init_flag, &InitializeRegisterSaveSlots);
|
||||||
|
|
||||||
|
u32 index = 0;
|
||||||
|
for (const auto& reg : regs) {
|
||||||
|
const auto offset = reinterpret_cast<void*>(register_save_slots[index++] * sizeof(void*));
|
||||||
|
|
||||||
|
c.putSeg(Xbyak::util::gs);
|
||||||
|
c.mov(Xbyak::util::qword[offset], reg.cvt64());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RestoreRegisters(Xbyak::CodeGenerator& c, const std::initializer_list<Xbyak::Reg> regs) {
|
||||||
|
ASSERT_MSG(regs.size() <= MaxSavedRegisters, "Not enough space to restore {} registers.", regs.size());
|
||||||
|
|
||||||
|
std::call_once(register_save_init_flag, &InitializeRegisterSaveSlots);
|
||||||
|
|
||||||
|
u32 index = 0;
|
||||||
|
for (const auto& reg : regs) {
|
||||||
|
const auto offset = reinterpret_cast<void*>(register_save_slots[index++] * sizeof(void*));
|
||||||
|
|
||||||
|
c.putSeg(Xbyak::util::gs);
|
||||||
|
c.mov(reg.cvt64(), Xbyak::util::qword[offset]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GenerateANDN(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||||
|
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||||
|
const auto src1 = ZydisToXbyakRegisterOperand(operands[1]);
|
||||||
|
const auto src2 = ZydisToXbyakOperand(operands[2]);
|
||||||
|
|
||||||
|
const auto scratch = AllocateScratchRegister({&dst, &src1, src2.get()}, dst.getBit());
|
||||||
|
|
||||||
|
SaveRegisters(c, {scratch});
|
||||||
|
|
||||||
|
c.mov(scratch, src1);
|
||||||
|
c.not_(scratch);
|
||||||
|
c.and_(scratch, *src2);
|
||||||
|
c.mov(dst, scratch);
|
||||||
|
|
||||||
|
RestoreRegisters(c, {scratch});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GenerateBEXTR(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||||
|
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||||
|
const auto src = ZydisToXbyakOperand(operands[1]);
|
||||||
|
const auto start_len = ZydisToXbyakRegisterOperand(operands[2]);
|
||||||
|
|
||||||
|
const Xbyak::Reg32e shift(Xbyak::Operand::RCX, static_cast<int>(start_len.getBit()));
|
||||||
|
const auto scratch1 = AllocateScratchRegister({&dst, src.get(), &start_len, &shift}, dst.getBit());
|
||||||
|
const auto scratch2 = AllocateScratchRegister({&dst, src.get(), &start_len, &shift, &scratch1}, dst.getBit());
|
||||||
|
|
||||||
|
if (dst.getIdx() == shift.getIdx()) {
|
||||||
|
SaveRegisters(c, {scratch1, scratch2});
|
||||||
|
} else {
|
||||||
|
SaveRegisters(c, {scratch1, scratch2, shift});
|
||||||
|
}
|
||||||
|
|
||||||
|
c.mov(scratch1, *src);
|
||||||
|
if (shift.getIdx() != start_len.getIdx()) {
|
||||||
|
c.mov(shift, start_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
c.shr(scratch1, shift.cvt8());
|
||||||
|
c.shr(shift, 8);
|
||||||
|
c.mov(scratch2, 1);
|
||||||
|
c.shl(scratch2, shift.cvt8());
|
||||||
|
c.dec(scratch2);
|
||||||
|
|
||||||
|
c.mov(dst, scratch1);
|
||||||
|
c.and_(dst, scratch2);
|
||||||
|
|
||||||
|
if (dst.getIdx() == shift.getIdx()) {
|
||||||
|
RestoreRegisters(c, {scratch1, scratch2});
|
||||||
|
} else {
|
||||||
|
RestoreRegisters(c, {scratch1, scratch2, shift});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GenerateBLSI(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||||
|
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||||
|
const auto src = ZydisToXbyakOperand(operands[1]);
|
||||||
|
|
||||||
|
const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit());
|
||||||
|
|
||||||
|
SaveRegisters(c, {scratch});
|
||||||
|
|
||||||
|
c.mov(scratch, *src);
|
||||||
|
c.neg(scratch);
|
||||||
|
c.and_(scratch, *src);
|
||||||
|
c.mov(dst, scratch);
|
||||||
|
|
||||||
|
RestoreRegisters(c, {scratch});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GenerateBLSMSK(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||||
|
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||||
|
const auto src = ZydisToXbyakOperand(operands[1]);
|
||||||
|
|
||||||
|
const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit());
|
||||||
|
|
||||||
|
SaveRegisters(c, {scratch});
|
||||||
|
|
||||||
|
c.mov(scratch, *src);
|
||||||
|
c.dec(scratch);
|
||||||
|
c.xor_(scratch, *src);
|
||||||
|
c.mov(dst, scratch);
|
||||||
|
|
||||||
|
RestoreRegisters(c, {scratch});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GenerateBLSR(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||||
|
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||||
|
const auto src = ZydisToXbyakOperand(operands[1]);
|
||||||
|
|
||||||
|
const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit());
|
||||||
|
|
||||||
|
SaveRegisters(c, {scratch});
|
||||||
|
|
||||||
|
c.mov(scratch, *src);
|
||||||
|
c.dec(scratch);
|
||||||
|
c.and_(scratch, *src);
|
||||||
|
c.mov(dst, scratch);
|
||||||
|
|
||||||
|
RestoreRegisters(c, {scratch});
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using InstructionGenerator = void(*)(const ZydisDecodedOperand*, Xbyak::CodeGenerator&);
|
||||||
|
static const std::unordered_map<ZydisMnemonic, InstructionGenerator> InstructionGenerators = {
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// BMI1 instructions that are not supported by Rosetta 2 on Apple Silicon.
|
||||||
|
{ZYDIS_MNEMONIC_ANDN, &GenerateANDN},
|
||||||
|
{ZYDIS_MNEMONIC_BEXTR, &GenerateBEXTR},
|
||||||
|
{ZYDIS_MNEMONIC_BLSI, &GenerateBLSI},
|
||||||
|
{ZYDIS_MNEMONIC_BLSMSK, &GenerateBLSMSK},
|
||||||
|
{ZYDIS_MNEMONIC_BLSR, &GenerateBLSR},
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
void PatchInstructions(u64 segment_addr, u64 segment_size, Xbyak::CodeGenerator& c) {
|
||||||
|
if (InstructionGenerators.empty()) {
|
||||||
|
// Nothing to patch on this platform.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZydisDecoder instr_decoder;
|
||||||
|
ZydisDecodedInstruction instruction;
|
||||||
|
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT];
|
||||||
|
ZydisDecoderInit(&instr_decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64);
|
||||||
|
|
||||||
|
u8* code = reinterpret_cast<u8*>(segment_addr);
|
||||||
|
u8* end = code + segment_size;
|
||||||
|
while (code < end) {
|
||||||
|
ZyanStatus status =
|
||||||
|
ZydisDecoderDecodeFull(&instr_decoder, code, end - code, &instruction, operands);
|
||||||
|
if (!ZYAN_SUCCESS(status)) {
|
||||||
|
code++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InstructionGenerators.contains(instruction.mnemonic)) {
|
||||||
|
LOG_DEBUG(Core, "Replacing instruction '{}' at: {}", ZydisMnemonicGetString(instruction.mnemonic),
|
||||||
|
fmt::ptr(code));
|
||||||
|
|
||||||
|
// Replace instruction with near jump to the trampoline.
|
||||||
|
static constexpr u32 NearJmpSize = 5;
|
||||||
|
ASSERT_MSG(instruction.length >= NearJmpSize, "Instruction {} with length {} is too short to replace at: {}",
|
||||||
|
ZydisMnemonicGetString(instruction.mnemonic), instruction.length, fmt::ptr(code));
|
||||||
|
|
||||||
|
auto patch = Xbyak::CodeGenerator(instruction.length, code);
|
||||||
|
patch.jmp(c.getCurr(), Xbyak::CodeGenerator::LabelType::T_NEAR);
|
||||||
|
patch.nop(instruction.length - NearJmpSize);
|
||||||
|
|
||||||
|
auto generator = InstructionGenerators.at(instruction.mnemonic);
|
||||||
|
generator(operands, c);
|
||||||
|
c.jmp(code + instruction.length); // Return to the following instruction.
|
||||||
|
}
|
||||||
|
|
||||||
|
code += instruction.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Loader
|
|
@ -0,0 +1,14 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Xbyak {
|
||||||
|
class CodeGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
void PatchInstructions(u64 segment_addr, u64 segment_size, Xbyak::CodeGenerator& c);
|
||||||
|
|
||||||
|
} // namespace Core
|
|
@ -1,7 +1,8 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <core/libraries/error_codes.h>
|
#include <thread>
|
||||||
|
#include "core/libraries/error_codes.h"
|
||||||
#include "event_flag_obj.h"
|
#include "event_flag_obj.h"
|
||||||
|
|
||||||
namespace Libraries::Kernel {
|
namespace Libraries::Kernel {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "core/libraries/kernel/event_queue.h"
|
#include "core/libraries/kernel/event_queue.h"
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <date/tz.h>
|
||||||
|
|
||||||
#include <boost/asio/io_context.hpp>
|
#include <boost/asio/io_context.hpp>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/polyfill_thread.h"
|
||||||
#include "common/singleton.h"
|
#include "common/singleton.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "core/file_format/psf.h"
|
#include "core/file_format/psf.h"
|
||||||
|
@ -56,7 +58,7 @@ static void KernelServiceThread(std::stop_token stoken) {
|
||||||
HLE_TRACE;
|
HLE_TRACE;
|
||||||
{
|
{
|
||||||
std::unique_lock lock{m_asio_req};
|
std::unique_lock lock{m_asio_req};
|
||||||
cv_asio_req.wait(lock, stoken, [] { return asio_requests != 0; });
|
Common::CondvarWait(cv_asio_req, lock, stoken, [] { return asio_requests != 0; });
|
||||||
}
|
}
|
||||||
if (stoken.stop_requested()) {
|
if (stoken.stop_requested()) {
|
||||||
break;
|
break;
|
||||||
|
@ -180,7 +182,7 @@ s64 PS4_SYSV_ABI ps4__write(int d, const void* buf, std::size_t nbytes) {
|
||||||
int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
|
int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
|
||||||
struct OrbisTimesec* st, unsigned long* dst_sec) {
|
struct OrbisTimesec* st, unsigned long* dst_sec) {
|
||||||
LOG_TRACE(Kernel, "Called");
|
LOG_TRACE(Kernel, "Called");
|
||||||
const auto* time_zone = std::chrono::current_zone();
|
const auto* time_zone = date::current_zone();
|
||||||
auto info = time_zone->get_info(std::chrono::system_clock::now());
|
auto info = time_zone->get_info(std::chrono::system_clock::now());
|
||||||
|
|
||||||
*local_time = info.offset.count() + info.save.count() * 60 + time;
|
*local_time = info.offset.count() + info.save.count() * 60 + time;
|
||||||
|
|
|
@ -74,7 +74,12 @@ s32 PS4_SYSV_ABI sceKernelAvailableDirectMemorySize(u64 searchStart, u64 searchE
|
||||||
LOG_WARNING(Kernel_Vmm, "called searchStart = {:#x}, searchEnd = {:#x}, alignment = {:#x}",
|
LOG_WARNING(Kernel_Vmm, "called searchStart = {:#x}, searchEnd = {:#x}, alignment = {:#x}",
|
||||||
searchStart, searchEnd, alignment);
|
searchStart, searchEnd, alignment);
|
||||||
auto* memory = Core::Memory::Instance();
|
auto* memory = Core::Memory::Instance();
|
||||||
return memory->DirectQueryAvailable(searchStart, searchEnd, alignment, physAddrOut, sizeOut);
|
|
||||||
|
PAddr physAddr;
|
||||||
|
s32 size = memory->DirectQueryAvailable(searchStart, searchEnd, alignment, &physAddr, sizeOut);
|
||||||
|
*physAddrOut = static_cast<u64>(physAddr);
|
||||||
|
|
||||||
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceKernelVirtualQuery(const void* addr, int flags, OrbisVirtualQueryInfo* info,
|
s32 PS4_SYSV_ABI sceKernelVirtualQuery(const void* addr, int flags, OrbisVirtualQueryInfo* info,
|
||||||
|
|
|
@ -848,6 +848,37 @@ int PS4_SYSV_ABI posix_pthread_mutexattr_setprotocol(ScePthreadMutexattr* attr,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK
|
||||||
|
static int pthread_mutex_timedlock(pthread_mutex_t* mutex, const struct timespec* abstime) {
|
||||||
|
int rc;
|
||||||
|
while ((rc = pthread_mutex_trylock(mutex)) == EBUSY) {
|
||||||
|
struct timespec curr_time;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &curr_time);
|
||||||
|
|
||||||
|
s64 remaining_ns = 0;
|
||||||
|
remaining_ns +=
|
||||||
|
(static_cast<s64>(abstime->tv_sec) - static_cast<s64>(curr_time.tv_sec)) * 1000000000L;
|
||||||
|
remaining_ns += static_cast<s64>(abstime->tv_nsec) - static_cast<s64>(curr_time.tv_nsec);
|
||||||
|
|
||||||
|
if (remaining_ns <= 0) {
|
||||||
|
return ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timespec sleep_time;
|
||||||
|
sleep_time.tv_sec = 0;
|
||||||
|
if (remaining_ns < 5000000L) {
|
||||||
|
sleep_time.tv_nsec = remaining_ns;
|
||||||
|
} else {
|
||||||
|
sleep_time.tv_nsec = 5000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
nanosleep(&sleep_time, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int PS4_SYSV_ABI scePthreadMutexTimedlock(ScePthreadMutex* mutex, u64 usec) {
|
int PS4_SYSV_ABI scePthreadMutexTimedlock(ScePthreadMutex* mutex, u64 usec) {
|
||||||
mutex = createMutex(mutex);
|
mutex = createMutex(mutex);
|
||||||
if (mutex == nullptr) {
|
if (mutex == nullptr) {
|
||||||
|
@ -1232,7 +1263,10 @@ int PS4_SYSV_ABI posix_pthread_create(ScePthread* thread, const ScePthreadAttr*
|
||||||
using Destructor = void (*)(void*);
|
using Destructor = void (*)(void*);
|
||||||
|
|
||||||
int PS4_SYSV_ABI posix_pthread_key_create(u32* key, Destructor func) {
|
int PS4_SYSV_ABI posix_pthread_key_create(u32* key, Destructor func) {
|
||||||
return pthread_key_create(key, func);
|
pthread_key_t thread_key;
|
||||||
|
int rc = pthread_key_create(&thread_key, func);
|
||||||
|
*key = static_cast<u32>(thread_key);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI posix_pthread_setspecific(int key, const void* value) {
|
int PS4_SYSV_ABI posix_pthread_setspecific(int key, const void* value) {
|
||||||
|
|
|
@ -12,7 +12,10 @@ int PS4_SYSV_ABI scePthreadKeyCreate(OrbisPthreadKey* key, PthreadKeyDestructor
|
||||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int result = pthread_key_create(key, nullptr);
|
pthread_key_t thread_key;
|
||||||
|
int result = pthread_key_create(&thread_key, nullptr);
|
||||||
|
*key = static_cast<OrbisPthreadKey>(thread_key);
|
||||||
|
|
||||||
if (destructor) {
|
if (destructor) {
|
||||||
auto thread = scePthreadSelf();
|
auto thread = scePthreadSelf();
|
||||||
thread->key_destructors.emplace_back(*key, destructor);
|
thread->key_destructors.emplace_back(*key, destructor);
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "common/ntapi.h"
|
#include "common/ntapi.h"
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
#include <sys/time.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -132,6 +132,11 @@ public:
|
||||||
return total_flexible_size - flexible_usage;
|
return total_flexible_size - flexible_usage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the offset of the mapped virtual memory base from where it usually would be mapped.
|
||||||
|
[[nodiscard]] u64 VirtualOffset() noexcept {
|
||||||
|
return impl.VirtualBase() - SYSTEM_MANAGED_MIN;
|
||||||
|
}
|
||||||
|
|
||||||
PAddr Allocate(PAddr search_start, PAddr search_end, size_t size, u64 alignment,
|
PAddr Allocate(PAddr search_start, PAddr search_end, size_t size, u64 alignment,
|
||||||
int memory_type);
|
int memory_type);
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "core/aerolib/aerolib.h"
|
#include "core/aerolib/aerolib.h"
|
||||||
|
#include "core/instruction_emulator.h"
|
||||||
#include "core/loader/dwarf.h"
|
#include "core/loader/dwarf.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
#include "core/module.h"
|
#include "core/module.h"
|
||||||
|
@ -84,8 +85,9 @@ void Module::LoadModuleToMemory(u32& max_tls_index) {
|
||||||
|
|
||||||
// Map module segments (and possible TLS trampolines)
|
// Map module segments (and possible TLS trampolines)
|
||||||
void** out_addr = reinterpret_cast<void**>(&base_virtual_addr);
|
void** out_addr = reinterpret_cast<void**>(&base_virtual_addr);
|
||||||
memory->MapMemory(out_addr, LoadAddress, aligned_base_size + TrampolineSize,
|
memory->MapMemory(out_addr, memory->VirtualOffset() + LoadAddress,
|
||||||
MemoryProt::CpuReadWrite, MemoryMapFlags::Fixed, VMAType::Code, name, true);
|
aligned_base_size + TrampolineSize, MemoryProt::CpuReadWrite,
|
||||||
|
MemoryMapFlags::Fixed, VMAType::Code, name, true);
|
||||||
LoadAddress += CODE_BASE_INCR * (1 + aligned_base_size / CODE_BASE_INCR);
|
LoadAddress += CODE_BASE_INCR * (1 + aligned_base_size / CODE_BASE_INCR);
|
||||||
|
|
||||||
// Initialize trampoline generator.
|
// Initialize trampoline generator.
|
||||||
|
@ -131,6 +133,7 @@ void Module::LoadModuleToMemory(u32& max_tls_index) {
|
||||||
add_segment(elf_pheader[i]);
|
add_segment(elf_pheader[i]);
|
||||||
if (elf_pheader[i].p_flags & PF_EXEC) {
|
if (elf_pheader[i].p_flags & PF_EXEC) {
|
||||||
PatchTLS(segment_addr, segment_file_size, c);
|
PatchTLS(segment_addr, segment_file_size, c);
|
||||||
|
PatchInstructions(segment_addr, segment_file_size, c);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#else
|
#elif !defined(__APPLE__)
|
||||||
#include <asm/prctl.h> /* Definition of ARCH_* constants */
|
#include <asm/prctl.h> /* Definition of ARCH_* constants */
|
||||||
#include <sys/syscall.h> /* Definition of SYS_* constants */
|
#include <sys/syscall.h> /* Definition of SYS_* constants */
|
||||||
#endif
|
#endif
|
||||||
|
@ -88,6 +88,51 @@ static void PatchFsAccess(u8* code, const TLSPattern& tls_pattern, Xbyak::CodeGe
|
||||||
c.jmp(code + total_size); // Return to the instruction right after the mov.
|
c.jmp(code + total_size); // Return to the instruction right after the mov.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
|
||||||
|
static pthread_key_t slot = 0;
|
||||||
|
static std::once_flag slot_alloc_flag;
|
||||||
|
|
||||||
|
static void AllocTcbKey() {
|
||||||
|
ASSERT(pthread_key_create(&slot, nullptr) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTcbBase(void* image_address) {
|
||||||
|
std::call_once(slot_alloc_flag, &AllocTcbKey);
|
||||||
|
ASSERT(pthread_setspecific(slot, image_address) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Tcb* GetTcbBase() {
|
||||||
|
std::call_once(slot_alloc_flag, &AllocTcbKey);
|
||||||
|
return reinterpret_cast<Tcb*>(pthread_getspecific(slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PatchFsAccess(u8* code, const TLSPattern& tls_pattern, Xbyak::CodeGenerator& c) {
|
||||||
|
using namespace Xbyak::util;
|
||||||
|
const auto total_size = tls_pattern.pattern_size + tls_pattern.imm_size;
|
||||||
|
|
||||||
|
// Allocate slot in the process if not done already.
|
||||||
|
std::call_once(slot_alloc_flag, &AllocTcbKey);
|
||||||
|
|
||||||
|
static constexpr u32 NearJmpSize = 5;
|
||||||
|
|
||||||
|
// Replace fs read with gs read.
|
||||||
|
auto patch = Xbyak::CodeGenerator(total_size, code);
|
||||||
|
patch.jmp(c.getCurr(), Xbyak::CodeGenerator::LabelType::T_NEAR);
|
||||||
|
patch.nop(total_size - NearJmpSize);
|
||||||
|
|
||||||
|
// Write the trampoline.
|
||||||
|
const auto target_reg = Xbyak::Reg64(tls_pattern.target_reg);
|
||||||
|
|
||||||
|
// The following logic is based on the Darwin implementation of _os_tsd_get_direct, used by pthread_getspecific
|
||||||
|
// https://github.com/apple/darwin-xnu/blob/main/libsyscall/os/tsd.h#L89-L96
|
||||||
|
c.putSeg(gs);
|
||||||
|
c.mov(target_reg, qword[reinterpret_cast<void*>(slot * sizeof(void*))]); // Load the slot data.
|
||||||
|
|
||||||
|
// Return to the instruction right after the mov.
|
||||||
|
c.jmp(code + total_size);
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static u32 slot = 0;
|
static u32 slot = 0;
|
||||||
|
@ -110,7 +155,6 @@ static void PatchFsAccess(u8* code, const TLSPattern& tls_pattern, Xbyak::CodeGe
|
||||||
|
|
||||||
// Replace fs read with gs read.
|
// Replace fs read with gs read.
|
||||||
auto patch = Xbyak::CodeGenerator(total_size, code);
|
auto patch = Xbyak::CodeGenerator(total_size, code);
|
||||||
const auto target_reg = Xbyak::Reg64(tls_pattern.target_reg);
|
|
||||||
patch.putSeg(gs);
|
patch.putSeg(gs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,11 +192,6 @@ void PatchTLS(u64 segment_addr, u64 segment_size, Xbyak::CodeGenerator& c) {
|
||||||
}
|
}
|
||||||
ASSERT(offset == 0);
|
ASSERT(offset == 0);
|
||||||
|
|
||||||
// Allocate slot in the process if not done already.
|
|
||||||
if (slot == 0) {
|
|
||||||
AllocTcbKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace bogus instruction prefix with nops if it exists.
|
// Replace bogus instruction prefix with nops if it exists.
|
||||||
if (std::memcmp(code - BadPrefix.size(), BadPrefix.data(), sizeof(BadPrefix)) == 0) {
|
if (std::memcmp(code - BadPrefix.size(), BadPrefix.data(), sizeof(BadPrefix)) == 0) {
|
||||||
auto patch = Xbyak::CodeGenerator(BadPrefix.size(), code - BadPrefix.size());
|
auto patch = Xbyak::CodeGenerator(BadPrefix.size(), code - BadPrefix.size());
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "common/logging/backend.h"
|
#include "common/logging/backend.h"
|
||||||
#include "common/ntapi.h"
|
#include "common/ntapi.h"
|
||||||
#include "common/path_util.h"
|
#include "common/path_util.h"
|
||||||
|
#include "common/polyfill_thread.h"
|
||||||
#include "common/singleton.h"
|
#include "common/singleton.h"
|
||||||
#include "common/version.h"
|
#include "common/version.h"
|
||||||
#include "core/file_sys/fs.h"
|
#include "core/file_sys/fs.h"
|
||||||
|
|
|
@ -12,6 +12,10 @@
|
||||||
#include "input/controller.h"
|
#include "input/controller.h"
|
||||||
#include "sdl_window.h"
|
#include "sdl_window.h"
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <SDL3/SDL_metal.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Frontend {
|
namespace Frontend {
|
||||||
|
|
||||||
WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_)
|
WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_)
|
||||||
|
@ -55,6 +59,9 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_
|
||||||
window_info.render_surface = SDL_GetProperty(SDL_GetWindowProperties(window),
|
window_info.render_surface = SDL_GetProperty(SDL_GetWindowProperties(window),
|
||||||
SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, NULL);
|
SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, NULL);
|
||||||
}
|
}
|
||||||
|
#elif defined(SDL_PLATFORM_MACOS)
|
||||||
|
window_info.type = WindowSystemType::Metal;
|
||||||
|
window_info.render_surface = SDL_Metal_GetLayer(SDL_Metal_CreateView(window));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ enum class WindowSystemType : u8 {
|
||||||
Windows,
|
Windows,
|
||||||
X11,
|
X11,
|
||||||
Wayland,
|
Wayland,
|
||||||
|
Metal,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WindowSystemInfo {
|
struct WindowSystemInfo {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <boost/container/static_vector.hpp>
|
#include <boost/container/static_vector.hpp>
|
||||||
#include "shader_recompiler/frontend/decode.h"
|
#include "shader_recompiler/frontend/decode.h"
|
||||||
#include "shader_recompiler/frontend/fetch_shader.h"
|
#include "shader_recompiler/frontend/fetch_shader.h"
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
|
#include "common/polyfill_thread.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "video_core/amdgpu/liverpool.h"
|
#include "video_core/amdgpu/liverpool.h"
|
||||||
#include "video_core/amdgpu/pm4_cmds.h"
|
#include "video_core/amdgpu/pm4_cmds.h"
|
||||||
|
@ -31,7 +32,7 @@ void Liverpool::Process(std::stop_token stoken) {
|
||||||
while (!stoken.stop_requested()) {
|
while (!stoken.stop_requested()) {
|
||||||
{
|
{
|
||||||
std::unique_lock lk{submit_mutex};
|
std::unique_lock lk{submit_mutex};
|
||||||
submit_cv.wait(lk, stoken, [this] { return num_submits != 0; });
|
Common::CondvarWait(submit_cv, lk, stoken, [this] { return num_submits != 0; });
|
||||||
}
|
}
|
||||||
if (stoken.stop_requested()) {
|
if (stoken.stop_requested()) {
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/bit_field.h"
|
#include "common/bit_field.h"
|
||||||
|
#include "common/polyfill_thread.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "video_core/amdgpu/pixel_format.h"
|
#include "video_core/amdgpu/pixel_format.h"
|
||||||
#include "video_core/amdgpu/resource.h"
|
#include "video_core/amdgpu/resource.h"
|
||||||
|
|
|
@ -460,7 +460,7 @@ struct PM4CmdWriteData {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void Address(T addr) {
|
void Address(T addr) {
|
||||||
addr64 = reinterpret_cast<u64>(addr);
|
addr64 = static_cast<u64>(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|
|
@ -292,10 +292,14 @@ bool Instance::CreateDevice() {
|
||||||
const bool has_host_time_domain =
|
const bool has_host_time_domain =
|
||||||
std::find(time_domains.cbegin(), time_domains.cend(),
|
std::find(time_domains.cbegin(), time_domains.cend(),
|
||||||
vk::TimeDomainEXT::eQueryPerformanceCounter) != time_domains.cend();
|
vk::TimeDomainEXT::eQueryPerformanceCounter) != time_domains.cend();
|
||||||
#else
|
#elif __linux__
|
||||||
const bool has_host_time_domain =
|
const bool has_host_time_domain =
|
||||||
std::find(time_domains.cbegin(), time_domains.cend(),
|
std::find(time_domains.cbegin(), time_domains.cend(),
|
||||||
vk::TimeDomainEXT::eClockMonotonicRaw) != time_domains.cend();
|
vk::TimeDomainEXT::eClockMonotonicRaw) != time_domains.cend();
|
||||||
|
#else
|
||||||
|
// Tracy limitation means only Windows and Linux can use host time domain.
|
||||||
|
// See https://github.com/shadps4-emu/tracy/blob/c6d779d78508514102fbe1b8eb28bda10d95bb2a/public/tracy/TracyVulkan.hpp#L384-L389
|
||||||
|
const bool has_host_time_domain = false;
|
||||||
#endif
|
#endif
|
||||||
if (has_host_time_domain) {
|
if (has_host_time_domain) {
|
||||||
static constexpr std::string_view context_name{"vk_rasterizer"};
|
static constexpr std::string_view context_name{"vk_rasterizer"};
|
||||||
|
|
|
@ -16,6 +16,12 @@ class WindowSDL;
|
||||||
|
|
||||||
VK_DEFINE_HANDLE(VmaAllocator)
|
VK_DEFINE_HANDLE(VmaAllocator)
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#define VULKAN_LIBRARY_NAME "libMoltenVK.dylib"
|
||||||
|
#else
|
||||||
|
#define VULKAN_LIBRARY_NAME
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
class Instance {
|
class Instance {
|
||||||
|
@ -206,7 +212,7 @@ private:
|
||||||
void CollectToolingInfo();
|
void CollectToolingInfo();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
vk::DynamicLoader dl;
|
vk::DynamicLoader dl{VULKAN_LIBRARY_NAME};
|
||||||
vk::UniqueInstance instance;
|
vk::UniqueInstance instance;
|
||||||
vk::PhysicalDevice physical_device;
|
vk::PhysicalDevice physical_device;
|
||||||
vk::UniqueDevice device;
|
vk::UniqueDevice device;
|
||||||
|
|
|
@ -99,6 +99,17 @@ vk::SurfaceKHR CreateSurface(vk::Instance instance, const Frontend::WindowSDL& e
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#elif defined(VK_USE_PLATFORM_METAL_EXT)
|
||||||
|
if (window_info.type == Frontend::WindowSystemType::Metal) {
|
||||||
|
const vk::MetalSurfaceCreateInfoEXT macos_ci = {
|
||||||
|
.pLayer = static_cast<const CAMetalLayer*>(window_info.render_surface),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (instance.createMetalSurfaceEXT(&macos_ci, nullptr, &surface) != vk::Result::eSuccess) {
|
||||||
|
LOG_CRITICAL(Render_Vulkan, "Failed to initialize MacOS surface");
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!surface) {
|
if (!surface) {
|
||||||
|
@ -135,6 +146,10 @@ std::vector<const char*> GetInstanceExtensions(Frontend::WindowSystemType window
|
||||||
case Frontend::WindowSystemType::Wayland:
|
case Frontend::WindowSystemType::Wayland:
|
||||||
extensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
|
extensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
|
||||||
break;
|
break;
|
||||||
|
#elif defined(VK_USE_PLATFORM_METAL_EXT)
|
||||||
|
case Frontend::WindowSystemType::Metal:
|
||||||
|
extensions.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME);
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform");
|
LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform");
|
||||||
|
|
|
@ -36,7 +36,14 @@ static TextureCache* g_texture_cache = nullptr;
|
||||||
void GuestFaultSignalHandler(int sig, siginfo_t* info, void* raw_context) {
|
void GuestFaultSignalHandler(int sig, siginfo_t* info, void* raw_context) {
|
||||||
ucontext_t* ctx = reinterpret_cast<ucontext_t*>(raw_context);
|
ucontext_t* ctx = reinterpret_cast<ucontext_t*>(raw_context);
|
||||||
const VAddr address = reinterpret_cast<VAddr>(info->si_addr);
|
const VAddr address = reinterpret_cast<VAddr>(info->si_addr);
|
||||||
if (ctx->uc_mcontext.gregs[REG_ERR] & 0x2) {
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
const u32 err = ctx->uc_mcontext->__es.__err;
|
||||||
|
#else
|
||||||
|
const greg_t err = ctx->uc_mcontext.gregs[REG_ERR];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (err & 0x2) {
|
||||||
g_texture_cache->OnCpuWrite(address);
|
g_texture_cache->OnCpuWrite(address);
|
||||||
} else {
|
} else {
|
||||||
// Read not supported!
|
// Read not supported!
|
||||||
|
@ -69,9 +76,16 @@ TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler&
|
||||||
tile_manager{instance, scheduler} {
|
tile_manager{instance, scheduler} {
|
||||||
|
|
||||||
#ifndef _WIN64
|
#ifndef _WIN64
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// Read-only memory write results in SIGBUS on Apple.
|
||||||
|
static constexpr int SignalType = SIGBUS;
|
||||||
|
#else
|
||||||
|
static constexpr int SignalType = SIGSEGV;
|
||||||
|
#endif
|
||||||
|
|
||||||
sigset_t signal_mask;
|
sigset_t signal_mask;
|
||||||
sigemptyset(&signal_mask);
|
sigemptyset(&signal_mask);
|
||||||
sigaddset(&signal_mask, SIGSEGV);
|
sigaddset(&signal_mask, SignalType);
|
||||||
|
|
||||||
using HandlerType = decltype(sigaction::sa_sigaction);
|
using HandlerType = decltype(sigaction::sa_sigaction);
|
||||||
|
|
||||||
|
@ -79,7 +93,7 @@ TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler&
|
||||||
guest_access_fault.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
guest_access_fault.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||||
guest_access_fault.sa_sigaction = &GuestFaultSignalHandler;
|
guest_access_fault.sa_sigaction = &GuestFaultSignalHandler;
|
||||||
guest_access_fault.sa_mask = signal_mask;
|
guest_access_fault.sa_mask = signal_mask;
|
||||||
sigaction(SIGSEGV, &guest_access_fault, nullptr);
|
sigaction(SignalType, &guest_access_fault, nullptr);
|
||||||
#else
|
#else
|
||||||
veh_handle = AddVectoredExceptionHandler(0, GuestFaultSignalHandler);
|
veh_handle = AddVectoredExceptionHandler(0, GuestFaultSignalHandler);
|
||||||
ASSERT_MSG(veh_handle, "Failed to register an exception handler");
|
ASSERT_MSG(veh_handle, "Failed to register an exception handler");
|
||||||
|
|
Loading…
Reference in New Issue