Skip to content

Commit

Permalink
Merge pull request #6495 from frenzibyte/fix-macos-texture-release
Browse files Browse the repository at this point in the history
Fix incorrect release pattern in macOS/iOS texture loading code
  • Loading branch information
smoogipoo authored Jan 17, 2025
2 parents b27e4fb + 82272ae commit 8d2f620
Show file tree
Hide file tree
Showing 13 changed files with 72 additions and 51 deletions.
21 changes: 12 additions & 9 deletions osu.Framework.iOS/Graphics/Textures/IOSTextureLoaderStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,21 @@ public IOSTextureLoaderStore(IResourceStore<byte[]> store)

protected override unsafe Image<TPixel> ImageFromStream<TPixel>(Stream stream)
{
int length = (int)(stream.Length - stream.Position);
using var nativeData = NSMutableData.FromLength(length);
using (new NSAutoreleasePool())
{
int length = (int)(stream.Length - stream.Position);
var nativeData = NSMutableData.FromLength(length);

var bytesSpan = new Span<byte>(nativeData.MutableBytes.ToPointer(), length);
stream.ReadExactly(bytesSpan);
var bytesSpan = new Span<byte>(nativeData.MutableBytes.ToPointer(), length);
stream.ReadExactly(bytesSpan);

using var uiImage = UIImage.LoadFromData(nativeData);
if (uiImage == null)
throw new ArgumentException($"{nameof(Image)} could not be created from {nameof(stream)}.");
using var uiImage = UIImage.LoadFromData(nativeData);
if (uiImage == null)
throw new ArgumentException($"{nameof(Image)} could not be created from {nameof(stream)}.");

var cgImage = new Platform.Apple.Native.CGImage(uiImage.CGImage!.Handle);
return ImageFromCGImage<TPixel>(cgImage);
var cgImage = new Platform.Apple.Native.CGImage(uiImage.CGImage!.Handle);
return ImageFromCGImage<TPixel>(cgImage);
}
}
}
}
1 change: 0 additions & 1 deletion osu.Framework/Platform/Apple/Native/Class.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System;
using System.Runtime.InteropServices;
using osu.Framework.Platform.MacOS.Native;

namespace osu.Framework.Platform.Apple.Native
{
Expand Down
1 change: 0 additions & 1 deletion osu.Framework/Platform/Apple/Native/NSArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.

using System;
using osu.Framework.Platform.MacOS.Native;

namespace osu.Framework.Platform.Apple.Native
{
Expand Down
37 changes: 37 additions & 0 deletions osu.Framework/Platform/Apple/Native/NSAutoreleasePool.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;

namespace osu.Framework.Platform.Apple.Native
{
internal readonly struct NSAutoreleasePool : IDisposable
{
internal IntPtr Handle { get; }

internal NSAutoreleasePool(IntPtr handle)
{
Handle = handle;
}

private static readonly IntPtr class_pointer = Class.Get("NSAutoreleasePool");
private static readonly IntPtr sel_alloc = Selector.Get("alloc");
private static readonly IntPtr sel_init = Selector.Get("init");
private static readonly IntPtr sel_drain = Selector.Get("drain");

public static NSAutoreleasePool Init()
{
var pool = alloc();
Interop.SendIntPtr(pool.Handle, sel_init);
return pool;
}

private static NSAutoreleasePool alloc() => new NSAutoreleasePool(Interop.SendIntPtr(class_pointer, sel_alloc));

public void Dispose()
{
if (Handle != IntPtr.Zero)
Interop.SendIntPtr(Handle, sel_drain);
}
}
}
12 changes: 1 addition & 11 deletions osu.Framework/Platform/Apple/Native/NSData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@

using System;
using System.Runtime.InteropServices;
using osu.Framework.Platform.MacOS.Native;

namespace osu.Framework.Platform.Apple.Native
{
internal readonly struct NSData : IDisposable
internal readonly struct NSData
{
internal IntPtr Handle { get; }

private static readonly IntPtr class_pointer = Class.Get("NSData");
private static readonly IntPtr sel_release = Selector.Get("release");
private static readonly IntPtr sel_data_with_bytes = Selector.Get("dataWithBytes:length:");
private static readonly IntPtr sel_bytes = Selector.Get("bytes");
private static readonly IntPtr sel_length = Selector.Get("length");
Expand All @@ -34,14 +32,6 @@ internal byte[] ToBytes()
return bytes;
}

internal void Release() => Interop.SendVoid(Handle, sel_release);

public void Dispose()
{
if (Handle != IntPtr.Zero)
Release();
}

internal static unsafe NSData FromBytes(ReadOnlySpan<byte> bytes)
{
fixed (byte* ptr = bytes)
Expand Down
12 changes: 1 addition & 11 deletions osu.Framework/Platform/Apple/Native/NSMutableData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@
// See the LICENCE file in the repository root for full licence text.

using System;
using osu.Framework.Platform.MacOS.Native;

namespace osu.Framework.Platform.Apple.Native
{
internal readonly struct NSMutableData : IDisposable
internal readonly struct NSMutableData
{
internal IntPtr Handle { get; }

private static readonly IntPtr class_pointer = Class.Get("NSMutableData");
private static readonly IntPtr sel_release = Selector.Get("release");
private static readonly IntPtr sel_data_with_length = Selector.Get("dataWithLength:");
private static readonly IntPtr sel_mutable_bytes = Selector.Get("mutableBytes");

Expand All @@ -22,14 +20,6 @@ internal NSMutableData(IntPtr handle)

internal unsafe byte* MutableBytes => (byte*)Interop.SendIntPtr(Handle, sel_mutable_bytes);

internal void Release() => Interop.SendVoid(Handle, sel_release);

public void Dispose()
{
if (Handle != IntPtr.Zero)
Release();
}

internal static NSMutableData FromLength(int length)
{
IntPtr handle = Interop.SendIntPtr(class_pointer, sel_data_with_length, length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.

using System;
using osu.Framework.Platform.MacOS.Native;

namespace osu.Framework.Platform.Apple.Native
{
Expand Down
1 change: 0 additions & 1 deletion osu.Framework/Platform/Apple/Native/NSString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System;
using System.Runtime.InteropServices;
using osu.Framework.Platform.MacOS.Native;

namespace osu.Framework.Platform.Apple.Native
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@

using System;
using System.Runtime.InteropServices;
using osu.Framework.Platform.Apple.Native;

namespace osu.Framework.Platform.MacOS.Native
namespace osu.Framework.Platform.Apple.Native
{
internal static partial class Selector
{
Expand Down
9 changes: 6 additions & 3 deletions osu.Framework/Platform/MacOS/MacOSClipboard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@ public override bool SetImage(Image image)
using var stream = new MemoryStream();
image.SaveAsTiff(stream);

using var nsData = NSData.FromBytes(stream.ToArray());
using var nsImage = NSImage.LoadFromData(nsData);
return setToPasteboard(nsImage.Handle);
using (NSAutoreleasePool.Init())
{
var nsData = NSData.FromBytes(stream.ToArray());
using var nsImage = NSImage.LoadFromData(nsData);
return setToPasteboard(nsImage.Handle);
}
}

private IntPtr getFromPasteboard(IntPtr @class)
Expand Down
21 changes: 12 additions & 9 deletions osu.Framework/Platform/MacOS/MacOSTextureLoaderStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,21 @@ public MacOSTextureLoaderStore(IResourceStore<byte[]> store)

protected override unsafe Image<TPixel> ImageFromStream<TPixel>(Stream stream)
{
int length = (int)(stream.Length - stream.Position);
using var nativeData = NSMutableData.FromLength(length);
using (NSAutoreleasePool.Init())
{
int length = (int)(stream.Length - stream.Position);
var nativeData = NSMutableData.FromLength(length);

var bytesSpan = new Span<byte>(nativeData.MutableBytes, length);
stream.ReadExactly(bytesSpan);
var bytesSpan = new Span<byte>(nativeData.MutableBytes, length);
stream.ReadExactly(bytesSpan);

using var nsImage = NSImage.LoadFromData(nativeData);
if (nsImage.Handle == IntPtr.Zero)
throw new ArgumentException($"{nameof(Image)} could not be created from {nameof(stream)}.");
using var nsImage = NSImage.LoadFromData(nativeData);
if (nsImage.Handle == IntPtr.Zero)
throw new ArgumentException($"{nameof(Image)} could not be created from {nameof(stream)}.");

var cgImage = nsImage.CGImage;
return ImageFromCGImage<TPixel>(cgImage);
var cgImage = nsImage.CGImage;
return ImageFromCGImage<TPixel>(cgImage);
}
}
}
}
2 changes: 1 addition & 1 deletion osu.Framework/Platform/MacOS/SDL2MacOSWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

using System;
using osu.Framework.Platform.Apple.Native;
using osu.Framework.Platform.MacOS.Native;
using osu.Framework.Platform.SDL2;
using osuTK;
using Selector = osu.Framework.Platform.Apple.Native.Selector;

namespace osu.Framework.Platform.MacOS
{
Expand Down
2 changes: 1 addition & 1 deletion osu.Framework/Platform/MacOS/SDL3MacOSWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

using System;
using osu.Framework.Platform.Apple.Native;
using osu.Framework.Platform.MacOS.Native;
using osu.Framework.Platform.SDL3;
using osuTK;
using Selector = osu.Framework.Platform.Apple.Native.Selector;

namespace osu.Framework.Platform.MacOS
{
Expand Down

0 comments on commit 8d2f620

Please sign in to comment.