iOS: fix two warnings that were real bugs

MTLCopyAllDevices is iOS 18 only and we deploy to 17, so on 17 it's a null
weak symbol and GSDeviceMTL::Create crashes on it before ever reaching the
MTLCreateSystemDefaultDevice fallback. Guard both call sites with @available;
below 18 there's one GPU and nothing to enumerate.

CocoaTools::GetBundlePath was defined twice on iOS -- once in MacOSStubs.cpp
returning nullopt, once in HostImpls.mm returning std::string where the header
says std::optional<std::string>. Return types aren't mangled, so both are the
same symbol, the linker picked one, and DynamicLibrary.cpp read an
uninitialised has_value byte off the stack. HostImpls.mm has the real
implementation, so drop the stub and give it the declared signature.

CreateMetalLayer and DestroyMetalLayer were duplicated the same way.
CreateMetalLayer also returned void* against a bool declaration, which reads
back the low byte of a pointer -- fine until a layer lands on a 256-byte
boundary. Nothing calls it on iOS today (Vulkan is off) but it's the only
definition left, so make it match.
This commit is contained in:
J1coding
2026-07-26 13:48:45 +02:00
committed by Jeen
parent 7823d20d05
commit 80782e3781
3 changed files with 47 additions and 28 deletions
+29 -11
View File
@@ -28,17 +28,31 @@ GSDevice* MakeGSDeviceMTL()
return new GSDeviceMTL();
}
static GSAdapterInfo GetMetalAdapterInfo(id<MTLDevice> dev)
{
GSAdapterInfo ai;
ai.name = [[dev name] UTF8String];
ai.max_texture_size = GSMTLDevice::GetMaxTextureSize(dev);
ai.max_upscale_multiplier = GSGetMaxUpscaleMultiplier(ai.max_texture_size);
return ai;
}
std::vector<GSAdapterInfo> GetMetalAdapterList()
{ @autoreleasepool {
std::vector<GSAdapterInfo> list;
auto devs = MRCTransfer(MTLCopyAllDevices());
for (id<MTLDevice> dev in devs.Get())
// MTLCopyAllDevices only exists on iOS 18. We ship down to 17, where there's a
// single GPU and nothing to enumerate anyway.
if (@available(macOS 10.11, iOS 18.0, *))
{
GSAdapterInfo ai;
ai.name = [[dev name] UTF8String];
ai.max_texture_size = GSMTLDevice::GetMaxTextureSize(dev);
ai.max_upscale_multiplier = GSGetMaxUpscaleMultiplier(ai.max_texture_size);
list.push_back(std::move(ai));
auto devs = MRCTransfer(MTLCopyAllDevices());
for (id<MTLDevice> dev in devs.Get())
list.push_back(GetMetalAdapterInfo(dev));
}
else
{
auto dev = MRCTransfer(MTLCreateSystemDefaultDevice());
if (dev.Get())
list.push_back(GetMetalAdapterInfo(dev.Get()));
}
return list;
}}
@@ -1091,11 +1105,15 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
return false;
NSString* ns_adapter_name = [NSString stringWithUTF8String:GSConfig.Adapter.c_str()];
auto devs = MRCTransfer(MTLCopyAllDevices());
for (id<MTLDevice> dev in devs.Get())
// No device enumeration below iOS 18 — fall through to the default device.
if (@available(macOS 10.11, iOS 18.0, *))
{
if ([[dev name] isEqualToString:ns_adapter_name])
m_dev = GSMTLDevice(MRCRetain(dev));
auto devs = MRCTransfer(MTLCopyAllDevices());
for (id<MTLDevice> dev in devs.Get())
{
if ([[dev name] isEqualToString:ns_adapter_name])
m_dev = GSMTLDevice(MRCRetain(dev));
}
}
if (!m_dev.dev)
{
+4 -3
View File
@@ -132,6 +132,10 @@ void PCAPAdapter::reloadSettings() {}
// On iOS, CocoaTools.mm is excluded from the build. Provide stubs for the
// functions referenced by iOS-compiled core code (DynamicLibrary, WindowInfo,
// Pcsx2Config, etc.). iOS uses UIKit/Foundation, not Cocoa/AppKit.
//
// CreateMetalLayer, DestroyMetalLayer and GetBundlePath are deliberately absent:
// the app's HostImpls.mm implements them for real. Defining them here too gave the
// linker two of each and it picked one on its own.
#include "common/CocoaTools.h"
#include "common/WindowInfo.h"
#include <optional>
@@ -139,11 +143,8 @@ void PCAPAdapter::reloadSettings() {}
namespace CocoaTools
{
bool CreateMetalLayer(WindowInfo* wi) { return false; }
void DestroyMetalLayer(WindowInfo* wi) {}
std::optional<float> GetViewRefreshRate(const WindowInfo& wi) { return std::nullopt; }
void MarkHelpMenu(void* menu) {}
std::optional<std::string> GetBundlePath() { return std::nullopt; }
std::optional<std::string> GetNonTranslocatedBundlePath() { return std::nullopt; }
std::optional<std::string> MoveToTrash(std::string_view file) { return std::nullopt; }
bool DelayedLaunch(std::string_view file) { return false; }
+14 -14
View File
@@ -764,27 +764,27 @@ namespace FileSystem {
namespace CocoaTools {
void InhibitAppNap(const std::string&) {}
void UninhibitAppNap() {}
std::string GetBundlePath() { return [[NSBundle mainBundle].bundlePath UTF8String]; }
// Signature must match common/CocoaTools.h (not included here, it's mostly
// macOS-only). Return types aren't mangled, so a mismatch still links and
// callers read garbage off the stack.
std::optional<std::string> GetBundlePath() { return std::string([[NSBundle mainBundle].bundlePath UTF8String]); }
void* CreateMetalLayer(WindowInfo* wi) {
if (!Host::g_sdl_window) return nullptr;
// Return existing layer if we already have it
if (wi->surface_handle) {
return SDL_Metal_GetLayer((SDL_MetalView)wi->surface_handle);
}
bool CreateMetalLayer(WindowInfo* wi) {
if (!Host::g_sdl_window) return false;
// Already have one
if (wi->surface_handle) return true;
// Create the Metal view
SDL_MetalView view = SDL_Metal_CreateView(Host::g_sdl_window);
if (!view) {
Console.Error("SDL_Metal_CreateView failed: %s", SDL_GetError());
return nullptr;
return false;
}
void* layer = SDL_Metal_GetLayer(view);
wi->surface_handle = view; // Store view handle to destroy later
Console.WriteLn("Created Metal Layer: %p from View: %p", layer, view);
return layer;
Console.WriteLn("Created Metal Layer: %p from View: %p", SDL_Metal_GetLayer(view), view);
return true;
}
void DestroyMetalLayer(WindowInfo* wi) {