Compare commits

..

1 Commits

Author SHA1 Message Date
Ty
eee71d54ef Darwin: Implement Mach exceptions ports instead of signals
Credit goes to the Julia language and Dolphin emulator for their free and open source implementations
2026-01-01 13:33:34 -05:00
8 changed files with 208 additions and 20 deletions

View File

@@ -11,6 +11,7 @@
#include "common/Threading.h"
#include "common/WindowInfo.h"
#include "common/HostSys.h"
#include "fmt/format.h"
#include <csignal>
#include <cstring>
@@ -19,12 +20,15 @@
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <thread>
#include <time.h>
#include <mach/mach_init.h>
#include <mach/mach_port.h>
#include <mach/mach_time.h>
#include <mach/mach_vm.h>
#include <mach/message.h>
#include <mach/task.h>
#include <mach/thread_state.h>
#include <mach/vm_map.h>
#include <mutex>
#include <ApplicationServices/ApplicationServices.h>
@@ -294,7 +298,7 @@ static CPUInfo CalcCPUInfo()
std::vector<DarwinMisc::CPUClass> classes = DarwinMisc::GetCPUClasses();
out.num_clusters = static_cast<u32>(classes.size());
out.num_big_cores = classes.empty() ? 0 : classes[0].num_physical;
out.num_threads = classes.empty() ? 0 : classes[0].num_logical;
out.num_threads = classes.empty() ? 0 : classes[0].num_logical;
out.num_small_cores = 0;
for (std::size_t i = 1; i < classes.size(); i++)
{
@@ -568,15 +572,206 @@ void HostSys::EndCodeWrite()
#endif // _M_ARM64
#define USE_MACH_EXCEPTION_PORTS
namespace PageFaultHandler
{
#ifdef USE_MACH_EXCEPTION_PORTS
static void SignalHandler(mach_port_t port);
#else
static void SignalHandler(int sig, siginfo_t* info, void* ctx);
#endif
static std::recursive_mutex s_exception_handler_mutex;
static bool s_in_exception_handler = false;
static bool s_installed = false;
} // namespace PageFaultHandler
#ifdef USE_MACH_EXCEPTION_PORTS
#if defined(_M_X86)
#define THREAD_STATE64_COUNT x86_THREAD_STATE64_COUNT
#define THREAD_STATE64 x86_THREAD_STATE64
#define thread_state64_t x86_thread_state64_t
#elif defined(_M_ARM64)
#define THREAD_STATE64_COUNT ARM_THREAD_STATE64_COUNT
#define THREAD_STATE64 ARM_THREAD_STATE64
#define thread_state64_t arm_thread_state64_t
#else
#error Unknown Darwin Platform
#endif
void PageFaultHandler::SignalHandler(mach_port_t port)
{
Threading::SetNameOfCurrentThread("Mach Exception Thread");
#pragma pack(4)
struct
{
mach_msg_header_t Head;
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
int64_t code[2];
int flavor;
mach_msg_type_number_t old_stateCnt;
natural_t old_state[THREAD_STATE64_COUNT];
mach_msg_trailer_t trailer;
} msg_in;
struct
{
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
int flavor;
mach_msg_type_number_t new_stateCnt;
natural_t new_state[THREAD_STATE64_COUNT];
} msg_out;
#pragma pack()
memset(&msg_in, 0xee, sizeof(msg_in));
memset(&msg_out, 0xee, sizeof(msg_out));
mach_msg_size_t send_size = 0;
mach_msg_option_t option = MACH_RCV_MSG;
while (true)
{
kern_return_t r;
if ((r = mach_msg_overwrite(&msg_out.Head, option, send_size, sizeof(msg_in), port,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL, &msg_in.Head, 0)))
{
pxFail(fmt::format("CRITICAL: mach_msg_overwrite: {:x}", r).c_str());
}
if (msg_in.Head.msgh_id == MACH_NOTIFY_NO_SENDERS)
{
// the other thread exited
mach_port_deallocate(mach_task_self(), port);
return;
}
if (msg_in.Head.msgh_id != 2406)
{
pxFailRel("unknown message received");
return;
}
if (msg_in.flavor != THREAD_STATE64)
{
pxFailRel(fmt::format("unknown flavour {}, expected {}", msg_in.flavor, THREAD_STATE64).c_str());
return;
}
s_exception_handler_mutex.lock();
thread_state64_t* state = (thread_state64_t*)msg_in.old_state;
HandlerResult result = HandlerResult::ExecuteNextHandler;
if (!s_in_exception_handler)
{
s_in_exception_handler = true;
#ifdef _M_ARM64
result = HandlePageFault(reinterpret_cast<void*>(state->__pc), reinterpret_cast<void*>(msg_in.code[1]), (msg_in.code[0] & 2) != 0);
#else
result = HandlePageFault(reinterpret_cast<void*>(state->__rip), reinterpret_cast<void*>(msg_in.code[1]), (msg_in.code[0] & 2) != 0);
#endif
s_in_exception_handler = false;
}
// Set up the reply.
msg_out.Head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(msg_in.Head.msgh_bits), 0);
msg_out.Head.msgh_remote_port = msg_in.Head.msgh_remote_port;
msg_out.Head.msgh_local_port = MACH_PORT_NULL;
msg_out.Head.msgh_id = msg_in.Head.msgh_id + 100;
msg_out.NDR = msg_in.NDR;
if (result != HandlerResult::ContinueExecution) // cooked
{
msg_out.RetCode = KERN_FAILURE;
msg_out.flavor = 0;
msg_out.new_stateCnt = 0;
// The crash handler on macOS or Linux doesn't use context passed to it
// Stubbing it here is fine
CrashHandler::CrashSignalHandler(-1, nullptr, nullptr);
pxFailRel("CrashSignalHandler returned when it should have terminated us!");
}
else
{
// Resumes execution right where we left off (re-executes instruction that caused the SIGSEGV)
msg_out.RetCode = KERN_SUCCESS;
msg_out.flavor = THREAD_STATE64;
msg_out.new_stateCnt = THREAD_STATE64_COUNT;
memcpy(msg_out.new_state, msg_in.old_state, THREAD_STATE64_COUNT * sizeof(natural_t));
}
msg_out.Head.msgh_size =
offsetof(__typeof__(msg_out), new_state) + msg_out.new_stateCnt * sizeof(natural_t);
send_size = msg_out.Head.msgh_size;
option |= MACH_SEND_MSG;
s_exception_handler_mutex.unlock();
}
}
bool PageFaultHandler::Install(Error* error)
{
exception_mask_t masks[EXC_TYPES_COUNT];
mach_port_t ports[EXC_TYPES_COUNT];
exception_behavior_t behaviors[EXC_TYPES_COUNT];
thread_state_flavor_t flavors[EXC_TYPES_COUNT];
mach_msg_type_number_t count = EXC_TYPES_COUNT;
kern_return_t r = task_get_exception_ports(mach_task_self(), EXC_MASK_ALL,
masks, &count, ports, behaviors, flavors);
mach_port_t port;
if ((r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port)))
{
pxFailRel(fmt::format("mach_port_allocate: {:x}", r).c_str());
return false;
}
std::thread sig_thread(PageFaultHandler::SignalHandler, port);
sig_thread.detach();
if ((r = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND)))
{
mach_port_deallocate(mach_task_self(), port);
pxFailRel(fmt::format("mach_port_insert_right: {:x}", r).c_str());
return false;
}
task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, MACH_PORT_NULL, EXCEPTION_DEFAULT, THREAD_STATE_NONE);
if ((r = thread_set_exception_ports(mach_thread_self(), EXC_MASK_BAD_ACCESS, port, EXCEPTION_STATE | MACH_EXCEPTION_CODES, THREAD_STATE64)))
{
mach_port_deallocate(mach_task_self(), port);
pxFailRel(fmt::format("thread_set_exception_ports: {:x}", r).c_str());
return false;
}
if ((r = mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, -1)))
{
mach_port_deallocate(mach_task_self(), port);
pxFailRel(fmt::format("mach_port_mod_refs: {:x}", r).c_str());
return false;
}
mach_port_t previous;
if ((r = mach_port_request_notification(mach_task_self(), port, MACH_NOTIFY_NO_SENDERS, 0, port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous)))
{
mach_port_deallocate(mach_task_self(), port);
pxFailRel(fmt::format("mach_port_mod_refs: {:x}", r).c_str());
return false;
}
s_installed = true;
return true;
}
#else
void PageFaultHandler::SignalHandler(int sig, siginfo_t* info, void* ctx)
{
#if defined(_M_X86)
@@ -644,3 +839,4 @@ bool PageFaultHandler::Install(Error* error)
s_installed = true;
return true;
}
#endif

View File

@@ -1799,7 +1799,6 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
if (anisotropy > 1 && ssel.aniso)
{
sd.Filter = D3D11_FILTER_ANISOTROPIC;
sd.MaxLOD = (ssel.lodclamp || !ssel.UseMipmapFiltering()) ? 0.01f : FLT_MAX;
}
else
{
@@ -1818,13 +1817,13 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
(static_cast<u8>(ssel.IsMagFilterLinear()) << 1) |
static_cast<u8>(ssel.IsMinFilterLinear());
sd.Filter = filters[index];
sd.MaxLOD = (ssel.lodclamp || !ssel.UseMipmapFiltering()) ? 0.25f : FLT_MAX;
}
sd.AddressU = ssel.tau ? D3D11_TEXTURE_ADDRESS_WRAP : D3D11_TEXTURE_ADDRESS_CLAMP;
sd.AddressV = ssel.tav ? D3D11_TEXTURE_ADDRESS_WRAP : D3D11_TEXTURE_ADDRESS_CLAMP;
sd.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
sd.MinLOD = 0.0f;
sd.MaxLOD = (ssel.lodclamp || !ssel.UseMipmapFiltering()) ? 0.25f : FLT_MAX;
sd.MaxAnisotropy = std::clamp(anisotropy, 1, 16);
sd.ComparisonFunc = D3D11_COMPARISON_NEVER;

View File

@@ -2246,7 +2246,6 @@ bool GSDevice12::GetSampler(D3D12DescriptorHandle* cpu_handle, GSHWDrawConfig::S
if (anisotropy > 1 && ss.aniso)
{
sd.Filter = D3D12_FILTER_ANISOTROPIC;
sd.MaxLOD = (ss.lodclamp || !ss.UseMipmapFiltering()) ? 0.01f : FLT_MAX;
}
else
{
@@ -2264,13 +2263,13 @@ bool GSDevice12::GetSampler(D3D12DescriptorHandle* cpu_handle, GSHWDrawConfig::S
const u8 index = (static_cast<u8>(ss.IsMipFilterLinear()) << 2) |
(static_cast<u8>(ss.IsMagFilterLinear()) << 1) | static_cast<u8>(ss.IsMinFilterLinear());
sd.Filter = filters[index];
sd.MaxLOD = (ss.lodclamp || !ss.UseMipmapFiltering()) ? 0.25f : FLT_MAX;
}
sd.AddressU = ss.tau ? D3D12_TEXTURE_ADDRESS_MODE_WRAP : D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
sd.AddressV = ss.tav ? D3D12_TEXTURE_ADDRESS_MODE_WRAP : D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
sd.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
sd.MinLOD = 0.0f;
sd.MaxLOD = (ss.lodclamp || !ss.UseMipmapFiltering()) ? 0.25f : FLT_MAX;
sd.MaxAnisotropy = std::clamp<u8>(GSConfig.MaxAnisotropy, 1, 16);
sd.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;

View File

@@ -6783,11 +6783,11 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
m_conf.sampler.biln = bilinear;
// Aniso filtering doesn't work with textureLod so use texture (automatic_lod) instead.
// Enable aniso only for triangles. Sprites are flat so aniso is likely useless (it would save perf for others primitives).
// Also make sure there isn't flat shading.
const bool anisotropic = m_vt.m_primclass == GS_TRIANGLE_CLASS && !trilinear_manual && !m_vt.m_eq.z;
const bool anisotropic = m_vt.m_primclass == GS_TRIANGLE_CLASS && !trilinear_manual;
m_conf.sampler.aniso = anisotropic;
m_conf.sampler.triln = trilinear;
m_conf.ps.automatic_lod = anisotropic;
if (anisotropic && !trilinear_manual)
m_conf.ps.automatic_lod = 1;
}
// clamp to base level if we're not providing or generating mipmaps

View File

@@ -5826,9 +5826,6 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
src->m_lod = *lod;
}
if (GSConfig.MaxAnisotropy > 1)
tlevels = std::max(2, tlevels);
bool hack = false;
bool channel_shuffle = dst && (TEX0.PSM == PSMT8) && (GSRendererHW::GetInstance()->TestChannelShuffle(dst));
@@ -6783,8 +6780,7 @@ GSTextureCache::HashCacheEntry* GSTextureCache::LookupHashCache(const GIFRegTEX0
const int tw = region.HasX() ? region.GetWidth() : (1 << TEX0.TW);
const int th = region.HasY() ? region.GetHeight() : (1 << TEX0.TH);
const int tlevels = lod ? (GSConfig.HWMipmap ? std::min(lod->y - lod->x + 1, GSDevice::GetMipmapLevelsForSize(tw, th)) : -1) : 1;
const int aniso_tlevels = (GSConfig.MaxAnisotropy > 1) ? std::max(2, tlevels) : tlevels;
GSTexture* tex = g_gs_device->CreateTexture(tw, th, aniso_tlevels, paltex ? GSTexture::Format::UNorm8 : GSTexture::Format::Color);
GSTexture* tex = g_gs_device->CreateTexture(tw, th, tlevels, paltex ? GSTexture::Format::UNorm8 : GSTexture::Format::Color);
if (!tex)
{
// out of video memory if we hit here

View File

@@ -839,8 +839,7 @@ static MRCOwned<id<MTLSamplerState>> CreateSampler(id<MTLDevice> dev, GSHWDrawCo
[sdesc setMaxAnisotropy:GSConfig.MaxAnisotropy && sel.aniso ? GSConfig.MaxAnisotropy : 1];
bool clampLOD = sel.lodclamp || !sel.UseMipmapFiltering();
const char* clampdesc = clampLOD ? " LODClamp" : "";
float clampValue = GSConfig.MaxAnisotropy > 1 && sel.aniso ? 0.01f : 0.25f;
[sdesc setLodMaxClamp:clampLOD ? clampValue : FLT_MAX];
[sdesc setLodMaxClamp:clampLOD ? 0.25f : FLT_MAX];
[sdesc setLabel:[NSString stringWithFormat:@"%s%s %s%s%s", taudesc, tavdesc, magname, minname, clampdesc]];
MRCOwned<id<MTLSamplerState>> ret = MRCTransfer([dev newSamplerStateWithDescriptor:sdesc]);

View File

@@ -1186,9 +1186,8 @@ GLuint GSDeviceOGL::CreateSampler(PSSamplerSelector sel)
glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, sel.IsMinFilterLinear() ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_NEAREST);
}
const bool aniso = GSConfig.MaxAnisotropy > 1 && sel.aniso;
glSamplerParameterf(sampler, GL_TEXTURE_MIN_LOD, -1000.0f);
glSamplerParameterf(sampler, GL_TEXTURE_MAX_LOD, sel.lodclamp ? (aniso ? 0.01f : 0.25f) : 1000.0f);
glSamplerParameterf(sampler, GL_TEXTURE_MAX_LOD, sel.lodclamp ? 0.25f : 1000.0f);
if (sel.tau)
glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, GL_REPEAT);
@@ -1201,9 +1200,9 @@ GLuint GSDeviceOGL::CreateSampler(PSSamplerSelector sel)
glSamplerParameteri(sampler, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
if (aniso)
const int anisotropy = GSConfig.MaxAnisotropy;
if (anisotropy > 1 && sel.aniso)
{
const int anisotropy = GSConfig.MaxAnisotropy;
if (GLAD_GL_ARB_texture_filter_anisotropic)
glSamplerParameterf(sampler, GL_TEXTURE_MAX_ANISOTROPY, static_cast<float>(anisotropy));
else if (GLAD_GL_EXT_texture_filter_anisotropic)

View File

@@ -3581,7 +3581,7 @@ VkSampler GSDeviceVK::GetSampler(GSHWDrawConfig::SamplerSelector ss)
VK_FALSE, // compare enable
VK_COMPARE_OP_ALWAYS, // compare op
0.0f, // min lod
(ss.lodclamp || !ss.UseMipmapFiltering()) ? (aniso ? 0.01f : 0.25f) : VK_LOD_CLAMP_NONE, // max lod
(ss.lodclamp || !ss.UseMipmapFiltering()) ? 0.25f : VK_LOD_CLAMP_NONE, // max lod
VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, // border
VK_FALSE // unnormalized coordinates
};