macOS: Fix blurry rendering on Retina displays by setting correct backing scale factor

This commit is contained in:
2026-06-04 10:21:47 +08:00
parent 9a665bffd2
commit 28ac9aee2b
3 changed files with 67 additions and 2 deletions
+8
View File
@@ -42,6 +42,9 @@ namespace Ryujinx.Common.Helper
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool bool_objc_msgSend(nint receiver, Selector selector, nint param);
[LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend")]
private static partial double double_objc_msgSend(nint receiver, Selector selector);
public readonly struct Object
{
public readonly nint ObjPtr;
@@ -105,6 +108,11 @@ namespace Ryujinx.Common.Helper
{
return bool_objc_msgSend(ObjPtr, selector, obj.ObjPtr);
}
public double GetDoubleFromMessage(Selector selector)
{
return double_objc_msgSend(ObjPtr, selector);
}
}
public readonly struct Selector
@@ -1,7 +1,9 @@
using Ryujinx.Common.Helper;
using Ryujinx.Common.Logging;
using System;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace Ryujinx.Common.SystemInterop
{
@@ -53,6 +55,10 @@ namespace Ryujinx.Common.SystemInterop
{
userDpiScale = GdiPlusHelper.GetDpiX(nint.Zero);
}
else if (OperatingSystem.IsMacOS())
{
userDpiScale = GetMacOSDpiScale();
}
else if (OperatingSystem.IsLinux())
{
string xdgSessionType = Environment.GetEnvironmentVariable("XDG_SESSION_TYPE")?.ToLower();
@@ -93,5 +99,27 @@ namespace Ryujinx.Common.SystemInterop
return Math.Min(userDpiScale / StandardDpiScale, MaxScaleFactor);
}
[SupportedOSPlatform("macos")]
private static double GetMacOSDpiScale()
{
try
{
ObjectiveC.Object screenClass = new("NSScreen");
ObjectiveC.Object mainScreen = screenClass.GetFromMessage("mainScreen");
if (mainScreen.ObjPtr != nint.Zero)
{
double backingScale = mainScreen.GetDoubleFromMessage("backingScaleFactor");
// Convert backing scale factor (e.g., 2.0) to equivalent DPI (e.g., 192).
return StandardDpiScale * backingScale;
}
}
catch (Exception e)
{
Logger.Warning?.Print(LogClass.Application, $"Couldn't determine monitor DPI on macOS: {e.Message}");
}
return StandardDpiScale;
}
}
}
+31 -2
View File
@@ -243,9 +243,18 @@ namespace Ryujinx.Ava.UI.Renderer
// Make its renderer our metal layer.
child.SendMessage("setWantsLayer:", 1);
child.SendMessage("setLayer:", metalLayer);
metalLayer.SendMessage("setContentsScale:", Program.DesktopScaleFactor);
// Ensure the scale factor is up to date.
// Query the actual backing scale factor from the screen.
// This ensures the CAMetalLayer drawable is created at the correct
// pixel resolution for Retina displays, preventing blurry rendering.
double backingScaleFactor = GetBackingScaleFactor();
metalLayer.SendMessage("setContentsScale:", backingScaleFactor);
// Update DesktopScaleFactor immediately so the rest of the app
// (including the bounds callback) uses the correct value.
Program.DesktopScaleFactor = backingScaleFactor;
// Ensure the scale factor is up to date on bounds changes.
_updateBoundsCallback = rect =>
{
metalLayer.SendMessage("setContentsScale:", Program.DesktopScaleFactor);
@@ -258,6 +267,26 @@ namespace Ryujinx.Ava.UI.Renderer
return new PlatformHandle(nsView, "NSView");
}
[SupportedOSPlatform("macos")]
private static double GetBackingScaleFactor()
{
try
{
ObjectiveC.Object screenClass = new("NSScreen");
ObjectiveC.Object mainScreen = screenClass.GetFromMessage("mainScreen");
if (mainScreen.ObjPtr != nint.Zero)
{
return mainScreen.GetDoubleFromMessage("backingScaleFactor");
}
}
catch
{
// Fallback to default scale if ObjC call fails.
}
return 2.0;
}
[SupportedOSPlatform("Linux")]
void DestroyLinux()
{