mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
Gate all macOS-only system APIs behind TARGET_OS_IPHONE checks across the shared core: ApplicationServices, IOKit, mouse APIs, AppKit, MetalFX, CDVD Darwin sources, USB, discord-rpc, cubeb CoreAudio HAL, and BSD networking headers in DEV9. Port TARGET_OS_IPHONE guards for DEV9 AdapterUtils with iOS fallbacks for sockaddr_dl, rt_msghdr, and sysctl. Add ARMSX2_ROOT to the include path for the frontend's common include style. Drop the global _M_ARM64 define that triggered fast_float MSVC intrin.h inclusion since __aarch64__ covers the core paths. Gate MTLFeatureSet_macOS_GPUFamily1_v1 behind !TARGET_OS_IPHONE. Fix missing unistd.h and gate pthread_jit_write_protect_np which is unavailable on iOS. Add stubs for DarwinMisc JIT diagnostics, Achievements stats and info APIs, Discord_Register, Host capture callbacks, and the host hotkeys map so the frontend links cleanly. Merge upstream master.
265 lines
10 KiB
Plaintext
265 lines
10 KiB
Plaintext
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
|
|
// SPDX-License-Identifier: GPL-3.0+
|
|
|
|
#include "GSMTLDeviceInfo.h"
|
|
#include "GS/GS.h"
|
|
#include "common/Console.h"
|
|
#include "common/Path.h"
|
|
|
|
#ifdef __APPLE__
|
|
|
|
static id<MTLLibrary> loadMainLibrary(id<MTLDevice> dev, NSString* name)
|
|
{
|
|
NSString* path = [[NSBundle mainBundle] pathForResource:name ofType:@"metallib"];
|
|
if (!path)
|
|
{
|
|
std::string ssname = std::string([name UTF8String]) + ".metallib";
|
|
std::string sspath = Path::Combine(EmuFolders::Resources, ssname);
|
|
path = [[NSString alloc] initWithBytes:sspath.data() length:sspath.length() encoding:NSUTF8StringEncoding];
|
|
}
|
|
return path ? [dev newLibraryWithFile:path error:nullptr] : nullptr;
|
|
}
|
|
|
|
static MRCOwned<id<MTLLibrary>> loadMainLibrary(id<MTLDevice> dev)
|
|
{
|
|
if (@available(macOS 11.0, iOS 14.0, *))
|
|
if (id<MTLLibrary> lib = loadMainLibrary(dev, @"Metal23"))
|
|
return MRCTransfer(lib);
|
|
if (@available(macOS 10.15, iOS 13.0, *))
|
|
if (id<MTLLibrary> lib = loadMainLibrary(dev, @"Metal22"))
|
|
return MRCTransfer(lib);
|
|
if (@available(macOS 10.14, iOS 12.0, *))
|
|
if (id<MTLLibrary> lib = loadMainLibrary(dev, @"Metal21"))
|
|
return MRCTransfer(lib);
|
|
if (id<MTLLibrary> lib = loadMainLibrary(dev, @"default"))
|
|
return MRCTransfer(lib);
|
|
return MRCTransfer([dev newDefaultLibrary]);
|
|
}
|
|
|
|
static GSMTLDevice::MetalVersion detectLibraryVersion(id<MTLLibrary> lib)
|
|
{
|
|
// These functions are defined in tfx.metal to indicate the metal version used to make the metallib
|
|
if (MRCTransfer([lib newFunctionWithName:@"metal_version_23"]))
|
|
return GSMTLDevice::MetalVersion::Metal23;
|
|
if (MRCTransfer([lib newFunctionWithName:@"metal_version_22"]))
|
|
return GSMTLDevice::MetalVersion::Metal22;
|
|
if (MRCTransfer([lib newFunctionWithName:@"metal_version_21"]))
|
|
return GSMTLDevice::MetalVersion::Metal21;
|
|
return GSMTLDevice::MetalVersion::Metal20;
|
|
}
|
|
|
|
static bool detectPrimIDSupport(id<MTLDevice> dev, id<MTLLibrary> lib)
|
|
{
|
|
// Nvidia Metal driver is missing primid support, yay
|
|
MRCOwned<MTLRenderPipelineDescriptor*> desc = MRCTransfer([MTLRenderPipelineDescriptor new]);
|
|
[desc setVertexFunction:MRCTransfer([lib newFunctionWithName:@"fs_triangle"])];
|
|
[desc setFragmentFunction:MRCTransfer([lib newFunctionWithName:@"primid_test"])];
|
|
[[desc colorAttachments][0] setPixelFormat:MTLPixelFormatR8Uint];
|
|
NSError* err;
|
|
[[dev newRenderPipelineStateWithDescriptor:desc error:&err] release];
|
|
return !err;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
enum class DetectionResult
|
|
{
|
|
HaswellOrNotIntel, ///< Everything works fine
|
|
Broadwell, ///< PrimID broken
|
|
Skylake, ///< PrimID broken, FBFetch supported
|
|
};
|
|
}
|
|
|
|
static DetectionResult detectIntelGPU(id<MTLDevice> dev, id<MTLLibrary> lib)
|
|
{
|
|
// Even though it's nowhere in the feature set tables, some Intel GPUs support fbfetch!
|
|
// Annoyingly, the Haswell compiler successfully makes a pipeline but actually miscompiles it and doesn't insert any fbfetch instructions
|
|
// The Broadwell compiler inserts the Skylake fbfetch instruction, but Broadwell doesn't support that. It seems to make the shader not do anything
|
|
// So we actually have to test the thing
|
|
// In addition, Broadwell+ has broken primid so we need to disable that.
|
|
// Conveniently we can use the same test to detect both (except on macOS < 11. All Broadwell machines support 11, so the answer to that is "upgrade")
|
|
// See https://github.com/tellowkrinkle/MetalBugReproduction/releases/tag/BrokenPrimID for details
|
|
|
|
// AMD compiler crashes and gets retried 3 times over multiple seconds trying to compile the pipeline
|
|
// We know this is only a possibility on Intel anyways
|
|
if (![[dev name] containsString:@"Intel"])
|
|
return DetectionResult::HaswellOrNotIntel;
|
|
auto pdesc = MRCTransfer([MTLRenderPipelineDescriptor new]);
|
|
[pdesc setVertexFunction:MRCTransfer([lib newFunctionWithName:@"fs_triangle"])];
|
|
[pdesc setFragmentFunction:MRCTransfer([lib newFunctionWithName:@"fbfetch_test"])];
|
|
[[pdesc colorAttachments][0] setPixelFormat:MTLPixelFormatRGBA8Unorm];
|
|
auto pipe = MRCTransfer([dev newRenderPipelineStateWithDescriptor:pdesc error:nil]);
|
|
if (!pipe)
|
|
return DetectionResult::HaswellOrNotIntel;
|
|
auto buf = MRCTransfer([dev newBufferWithLength:4 options:MTLResourceStorageModeShared]);
|
|
auto tdesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm width:1 height:1 mipmapped:false];
|
|
[tdesc setUsage:MTLTextureUsageRenderTarget];
|
|
auto tex = MRCTransfer([dev newTextureWithDescriptor:tdesc]);
|
|
auto q = MRCTransfer([dev newCommandQueue]);
|
|
u32 px = 0x11223344;
|
|
memcpy([buf contents], &px, 4);
|
|
id<MTLCommandBuffer> cmdbuf = [q commandBuffer];
|
|
id<MTLBlitCommandEncoder> upload = [cmdbuf blitCommandEncoder];
|
|
[upload copyFromBuffer:buf sourceOffset:0 sourceBytesPerRow:4 sourceBytesPerImage:4 sourceSize:MTLSizeMake(1, 1, 1) toTexture:tex destinationSlice:0 destinationLevel:0 destinationOrigin:MTLOriginMake(0, 0, 0)];
|
|
[upload endEncoding];
|
|
auto rpdesc = MRCTransfer([MTLRenderPassDescriptor new]);
|
|
[[rpdesc colorAttachments][0] setTexture:tex];
|
|
[[rpdesc colorAttachments][0] setLoadAction:MTLLoadActionLoad];
|
|
[[rpdesc colorAttachments][0] setStoreAction:MTLStoreActionStore];
|
|
id<MTLRenderCommandEncoder> renc = [cmdbuf renderCommandEncoderWithDescriptor:rpdesc];
|
|
[renc setRenderPipelineState:pipe];
|
|
[renc drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
|
|
[renc endEncoding];
|
|
id<MTLBlitCommandEncoder> download = [cmdbuf blitCommandEncoder];
|
|
[download copyFromTexture:tex sourceSlice:0 sourceLevel:0 sourceOrigin:MTLOriginMake(0, 0, 0) sourceSize:MTLSizeMake(1, 1, 1) toBuffer:buf destinationOffset:0 destinationBytesPerRow:4 destinationBytesPerImage:4];
|
|
[download endEncoding];
|
|
[cmdbuf commit];
|
|
[cmdbuf waitUntilCompleted];
|
|
u32 outpx;
|
|
memcpy(&outpx, [buf contents], 4);
|
|
// Proper fbfetch will double contents, Haswell will return black, and Broadwell will do nothing
|
|
if (outpx == 0x22446688)
|
|
return DetectionResult::Skylake;
|
|
else if (outpx == 0x11223344)
|
|
return DetectionResult::Broadwell;
|
|
else
|
|
return DetectionResult::HaswellOrNotIntel;
|
|
}
|
|
|
|
GSMTLDevice::GSMTLDevice(MRCOwned<id<MTLDevice>> dev)
|
|
{
|
|
if (!dev)
|
|
return;
|
|
shaders = loadMainLibrary(dev);
|
|
|
|
memset(&features, 0, sizeof(features));
|
|
|
|
if (char* env = getenv("MTL_UNIFIED_MEMORY"))
|
|
features.unified_memory = env[0] == '1' || env[0] == 'y' || env[0] == 'Y';
|
|
else if (@available(macOS 10.15, iOS 13.0, *))
|
|
features.unified_memory = [dev hasUnifiedMemory];
|
|
else
|
|
features.unified_memory = false;
|
|
|
|
if (@available(macOS 10.15, iOS 13.0, *))
|
|
if ([dev supportsFamily:MTLGPUFamilyMac2] || [dev supportsFamily:MTLGPUFamilyApple1])
|
|
features.texture_swizzle = true;
|
|
|
|
if (@available(macOS 11.0, iOS 13.0, *))
|
|
if ([dev supportsFamily:MTLGPUFamilyApple1])
|
|
features.framebuffer_fetch = features.memoryless_textures = true;
|
|
|
|
if (@available(macOS 10.15, iOS 13.0, *))
|
|
if ([dev supportsFamily:MTLGPUFamilyMac2] || [dev supportsFamily:MTLGPUFamilyApple1])
|
|
features.has_fast_half = true; // Approximate guess
|
|
|
|
features.shader_version = detectLibraryVersion(shaders);
|
|
if (features.framebuffer_fetch && features.shader_version < MetalVersion::Metal23)
|
|
{
|
|
Console.Warning("Metal: GPU supports framebuffer fetch but shader lib does not! Get an updated shader lib for better performance!");
|
|
features.framebuffer_fetch = false;
|
|
}
|
|
|
|
features.primid = features.shader_version >= MetalVersion::Metal22;
|
|
if (features.primid && !detectPrimIDSupport(dev, shaders))
|
|
features.primid = false;
|
|
|
|
features.depth_feedback = false;
|
|
|
|
features.rov = [dev areRasterOrderGroupsSupported];
|
|
features.rov_requires_r32 = [dev readWriteTextureSupport] < MTLReadWriteTextureTier2;
|
|
|
|
NSString* name = [dev name];
|
|
if ([name containsString:@"Intel"])
|
|
{
|
|
if (!features.framebuffer_fetch && features.shader_version >= MetalVersion::Metal23)
|
|
{
|
|
switch (detectIntelGPU(dev, shaders))
|
|
{
|
|
case DetectionResult::HaswellOrNotIntel:
|
|
// Older Intel GPUs seem to be fine with depth feedback
|
|
features.depth_feedback = true;
|
|
break;
|
|
case DetectionResult::Broadwell:
|
|
features.primid = false; // Broken
|
|
break;
|
|
case DetectionResult::Skylake:
|
|
features.primid = false; // Broken
|
|
features.framebuffer_fetch = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if ([name containsString:@"AMD"])
|
|
{
|
|
// RDNA+ seems to work fine with depth feedback
|
|
if (@available(macOS 13, iOS 16, *))
|
|
if ([dev supportsFamily:MTLGPUFamilyMetal3])
|
|
features.depth_feedback = true;
|
|
// ROV doesn't work on AMD GPUs with no render target
|
|
// Ideally the driver would insert a dummy texture for us (it does on Linux) but the Metal driver doesn't.
|
|
features.rov_requires_rt = true;
|
|
}
|
|
else if ([name containsString:@"NVIDIA"])
|
|
{
|
|
// macOS only supports Kepler, which seems to work fine with depth feedback
|
|
features.depth_feedback = true;
|
|
}
|
|
else if ([name containsString:@"Apple"])
|
|
{
|
|
// No special settings
|
|
}
|
|
else
|
|
{
|
|
Console.Warning("Unrecognized GPU vendor %s", [name UTF8String]);
|
|
}
|
|
|
|
if (features.framebuffer_fetch && GSConfig.DisableFramebufferFetch)
|
|
{
|
|
Console.Warning("Framebuffer fetch was found but is disabled. This will reduce performance.");
|
|
features.framebuffer_fetch = false;
|
|
}
|
|
|
|
if (char* env = getenv("MTL_SLOW_COLOR_COMPRESSION"))
|
|
features.slow_color_compression = env[0] == '1' || env[0] == 'y' || env[0] == 'Y';
|
|
else
|
|
features.slow_color_compression = [[dev name] containsString:@"AMD"] || [[dev name] isEqualToString:@"Intel HD Graphics 4000"];
|
|
|
|
if (char* env = getenv("MTL_ROV_WITH_RT"))
|
|
features.rov_requires_rt = env[0] == '1' || env[0] == 'y' || env[0] == 'Y';
|
|
|
|
features.max_texsize = GetMaxTextureSize(dev);
|
|
|
|
this->dev = std::move(dev);
|
|
}
|
|
|
|
u32 GSMTLDevice::GetMaxTextureSize(id<MTLDevice> dev)
|
|
{
|
|
if (@available(macOS 10.15, iOS 13.0, *))
|
|
{
|
|
MTLGPUFamily apple10 = static_cast<MTLGPUFamily>(1010); // Avoid relying on latest SDK, define ourselves
|
|
if ([dev supportsFamily:apple10])
|
|
return 32768;
|
|
if ([dev supportsFamily:MTLGPUFamilyApple3])
|
|
return 16384;
|
|
}
|
|
#if !TARGET_OS_IPHONE
|
|
if ([dev supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v1])
|
|
return 16384;
|
|
#endif
|
|
return 8192;
|
|
}
|
|
|
|
const char* to_string(GSMTLDevice::MetalVersion ver)
|
|
{
|
|
switch (ver)
|
|
{
|
|
case GSMTLDevice::MetalVersion::Metal20: return "Metal 2.0";
|
|
case GSMTLDevice::MetalVersion::Metal21: return "Metal 2.1";
|
|
case GSMTLDevice::MetalVersion::Metal22: return "Metal 2.2";
|
|
case GSMTLDevice::MetalVersion::Metal23: return "Metal 2.3";
|
|
}
|
|
}
|
|
|
|
#endif // __APPLE__
|