Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Source/Core/Common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ if (APPLE)
PRIVATE
${APPKIT_LIBRARY}
${COREFOUNDATION_LIBRARY}
${FOUNDATION_LIBRARY}
${IOK_LIBRARY}
)
elseif(WIN32)
Expand Down Expand Up @@ -239,6 +240,7 @@ elseif(WIN32)
)
elseif(APPLE)
target_sources(common PRIVATE
CommonFuncsObjC.mm
Logging/ConsoleListenerNix.cpp
MemArenaDarwin.cpp
)
Expand Down
17 changes: 17 additions & 0 deletions Source/Core/Common/CommonFuncs.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
#endif
#include <string>

#ifdef __APPLE__
#include "Common/CommonTypes.h"
#endif

#ifndef _WIN32

// go to debugger mode
Expand Down Expand Up @@ -61,4 +65,17 @@ std::string GetWin32ErrorString(unsigned long error_code);
// Obtains a full path to the specified module.
std::optional<std::wstring> GetModuleName(void* hInstance);
#endif

#ifdef __APPLE__
struct MacOSVersion // NSOperatingSystemVersion
{
s64 major; // NSInteger majorVersion
s64 minor; // NSInteger minorVersion
s64 patch; // NSInteger patchVersion
};

// Helper function to get the current macOS version, which is easy to do with
// from Objective-C code, but a little harder from C++.
MacOSVersion GetMacOSVersion();
#endif
} // namespace Common
15 changes: 15 additions & 0 deletions Source/Core/Common/CommonFuncsObjC.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2026 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "Common/CommonFuncs.h"

#include <Foundation/Foundation.h>

namespace Common
{
MacOSVersion GetMacOSVersion()
{
const NSOperatingSystemVersion ver = [[NSProcessInfo processInfo] operatingSystemVersion];
return {ver.majorVersion, ver.minorVersion, ver.patchVersion};
}
} // namespace Common
36 changes: 8 additions & 28 deletions Source/Core/Core/DolphinAnalytics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@
#if defined(_WIN32)
#include <Windows.h>
#include "Common/WindowsRegistry.h"
#elif defined(__APPLE__)
#include <objc/message.h>
#endif

#if defined(ANDROID)
#include <functional>
#endif

#if defined(__APPLE__)
#include "Common/CommonFuncs.h"
#endif

#include "Common/Analytics.h"
#include "Common/CPUDetect.h"
#include "Common/CommonTypes.h"
Expand Down Expand Up @@ -300,32 +302,10 @@ void DolphinAnalytics::MakeBaseBuilder()
#elif defined(__APPLE__)
builder.AddData("os-type", "osx");

// id processInfo = [NSProcessInfo processInfo]
id processInfo = reinterpret_cast<id (*)(Class, SEL)>(objc_msgSend)(
objc_getClass("NSProcessInfo"), sel_getUid("processInfo"));
if (processInfo)
{
struct OSVersion // NSOperatingSystemVersion
{
s64 major_version; // NSInteger majorVersion
s64 minor_version; // NSInteger minorVersion
s64 patch_version; // NSInteger patchVersion
};
// Under arm64, we need to call objc_msgSend to receive a struct.
// On x86_64, we need to explicitly call objc_msgSend_stret for a struct.
#ifdef _M_ARM_64
#define msgSend objc_msgSend
#else
#define msgSend objc_msgSend_stret
#endif
// NSOperatingSystemVersion version = [processInfo operatingSystemVersion]
OSVersion version = reinterpret_cast<OSVersion (*)(id, SEL)>(msgSend)(
processInfo, sel_getUid("operatingSystemVersion"));
#undef msgSend
builder.AddData("osx-ver-major", version.major_version);
builder.AddData("osx-ver-minor", version.minor_version);
builder.AddData("osx-ver-bugfix", version.patch_version);
}
Common::MacOSVersion version = Common::GetMacOSVersion();
builder.AddData("osx-ver-major", version.major);
builder.AddData("osx-ver-minor", version.minor);
builder.AddData("osx-ver-bugfix", version.patch);
#elif defined(__linux__)
builder.AddData("os-type", "linux");
#elif defined(__FreeBSD__)
Expand Down
4 changes: 1 addition & 3 deletions Source/Core/VideoBackends/Metal/MTLUtil.mm
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,7 @@ fragment float4 is_helper_test() {
vendor = DriverDetails::VENDOR_INTEL;
else if (name.find("Apple") != std::string::npos)
vendor = DriverDetails::VENDOR_APPLE;
const NSOperatingSystemVersion cocoa_ver = [[NSProcessInfo processInfo] operatingSystemVersion];
double version = cocoa_ver.majorVersion * 100 + cocoa_ver.minorVersion;
DriverDetails::Init(DriverDetails::API_METAL, vendor, DriverDetails::DRIVER_APPLE, version,
DriverDetails::Init(DriverDetails::API_METAL, vendor, DriverDetails::DRIVER_APPLE, 0.0,
DriverDetails::Family::UNKNOWN, std::move(name));

#if TARGET_OS_OSX
Expand Down
21 changes: 9 additions & 12 deletions Source/Core/VideoBackends/Vulkan/VulkanContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "Common/Assert.h"
#include "Common/Contains.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"

#include "VideoCommon/DriverDetails.h"
#include "VideoCommon/VideoCommon.h"
Expand Down Expand Up @@ -450,7 +451,6 @@ void VulkanContext::PopulateBackendInfo(BackendInfo* backend_info)
backend_info->bSupportsBPTCTextures = false; // Dependent on features.
backend_info->bSupportsLogicOp = false; // Dependent on features.
backend_info->bSupportsLargePoints = false; // Dependent on features.
backend_info->bSupportsFramebufferFetch = false; // Dependent on OS and features.
backend_info->bSupportsCoarseDerivatives = true; // Assumed support.
backend_info->bSupportsTextureQueryLevels = true; // Assumed support.
backend_info->bSupportsLodBiasInSampler = false; // Dependent on OS.
Expand Down Expand Up @@ -510,17 +510,6 @@ void VulkanContext::PopulateBackendInfoFeatures(BackendInfo* backend_info, VkPhy
backend_info->bSupportsLargePoints =
info.largePoints && info.pointSizeRange[0] <= 1.0f && info.pointSizeRange[1] >= 16;

std::string device_name = info.deviceName;
u32 vendor_id = info.vendorID;
bool is_moltenvk = info.driverID == VK_DRIVER_ID_MOLTENVK;

// Only Apple family GPUs support framebuffer fetch.
// We currently use a hacked MoltenVK to implement this, so don't attempt outside of MVK
if (is_moltenvk && (vendor_id == 0x106B || device_name.find("Apple") != std::string::npos))
{
backend_info->bSupportsFramebufferFetch = true;
}

// Our usage of primitive restart appears to be broken on AMD's binary drivers.
// Seems to be fine on GCN Gen 1-2, unconfirmed on GCN Gen 3, causes driver resets on GCN Gen 4.
if (DriverDetails::HasBug(DriverDetails::BUG_PRIMITIVE_RESTART))
Expand All @@ -534,6 +523,14 @@ void VulkanContext::PopulateBackendInfoFeatures(BackendInfo* backend_info, VkPhy
// Dynamic sampler indexing locks up Intel GPUs on MoltenVK/Metal
if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DYNAMIC_SAMPLER_INDEXING))
backend_info->bSupportsDynamicSamplerIndexing = false;

if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DISCARD_WITH_EARLY_Z))
{
PanicAlertFmtT(
"You are attempting to use the Vulkan backend on an unsupported operating system. "
"To prevent visual glitches and artifacts, please use the Metal backend or update "
"to macOS Sonoma 14 or newer.");
}
}

void VulkanContext::PopulateBackendInfoMultisampleModes(BackendInfo* backend_info,
Expand Down
18 changes: 16 additions & 2 deletions Source/Core/VideoCommon/DriverDetails.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

#include "Core/DolphinAnalytics.h"

#ifdef __APPLE__
#include "Common/CommonFuncs.h"
#endif

namespace DriverDetails
{
struct BugInfo
Expand Down Expand Up @@ -148,9 +152,9 @@ constexpr BugInfo m_known_bugs[] = {
{API_VULKAN, OS_ALL, VENDOR_QUALCOMM, DRIVER_QUALCOMM, Family::UNKNOWN, BUG_PRIMITIVE_RESTART,
-1.0, -1.0, true},
{API_VULKAN, OS_OSX, VENDOR_APPLE, DRIVER_PORTABILITY, Family::UNKNOWN,
BUG_BROKEN_DISCARD_WITH_EARLY_Z, -1.0, -1.0, true},
BUG_BROKEN_DISCARD_WITH_EARLY_Z, 1100, 1400, true},
{API_METAL, OS_OSX, VENDOR_APPLE, DRIVER_APPLE, Family::UNKNOWN,
BUG_BROKEN_DISCARD_WITH_EARLY_Z, -1.0, -1.0, true},
BUG_BROKEN_DISCARD_WITH_EARLY_Z, 1100, 1400, true},
{API_VULKAN, OS_OSX, VENDOR_INTEL, DRIVER_PORTABILITY, Family::UNKNOWN,
BUG_BROKEN_DYNAMIC_SAMPLER_INDEXING, -1.0, -1.0, true},
{API_METAL, OS_OSX, VENDOR_INTEL, DRIVER_APPLE, Family::UNKNOWN,
Expand Down Expand Up @@ -198,6 +202,16 @@ void Init(API api, Vendor vendor, Driver driver, const double version, const Fam
}
}

#ifdef __APPLE__
// The Metal graphics drivers are part of macOS, so we use the current macOS version as the
// driver version for Metal and Vulkan on Metal.
if (api == API_METAL || (api == API_VULKAN && driver == DRIVER_PORTABILITY))
{
Common::MacOSVersion mac_version = Common::GetMacOSVersion();
m_version = mac_version.major * 100 + mac_version.minor;
}
#endif

// Clear bug list, as the API may have changed
m_bugs.clear();

Expand Down
3 changes: 2 additions & 1 deletion Source/Core/VideoCommon/UberShaderPixel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1005,7 +1005,8 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,

out.Write(" // Alpha Test\n");

if (early_depth && DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DISCARD_WITH_EARLY_Z))
if (early_depth && DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DISCARD_WITH_EARLY_Z) &&
host_config.backend_shader_framebuffer_fetch)
{
// Instead of using discard, fetch the framebuffer's color value and use it as the output
// for this fragment.
Expand Down