Browse Source
* [OSX] Implemented IOSurface/MTLSharedEvent interop APIs
* Bump Xcode?
* Xcode?
* APIDiff
* Use different XCode versions because of how awesome appium is
* A hack for crapium
* Replace SkiaMetalApi usages
* Update API suppressions
---------
Co-authored-by: Timothy Miller <innerlogic4321@gmail.com>
Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
(cherry picked from commit 22c4c630ce)
release/11.3.7
committed by
Julien Lebosquain
41 changed files with 1412 additions and 130 deletions
@ -0,0 +1,9 @@ |
|||||
|
// The only reason this file exists is Appium which limits our highest Xcode version to 15.2. Please, purge Appium from our codebase
|
||||
|
#ifndef crapium_h |
||||
|
#define crapium_h |
||||
|
#import <Foundation/Foundation.h> |
||||
|
@protocol MTLSharedEvent; |
||||
|
|
||||
|
API_AVAILABLE(macos(12)) |
||||
|
extern BOOL MtlSharedEventWaitUntilSignaledValueHack(id<MTLSharedEvent> ev, uint64_t value, uint64_t milliseconds); |
||||
|
#endif /* crapium_h */ |
||||
@ -0,0 +1,21 @@ |
|||||
|
// The only reason this file exists is Appium which limits our highest Xcode version to 15.2. Please, purge Appium from our codebase |
||||
|
#import <Foundation/Foundation.h> |
||||
|
#import "crapium.h" |
||||
|
@class MTLSharedEventHandle; |
||||
|
@protocol MTLSharedEvent; |
||||
|
@protocol MTLEvent; |
||||
|
|
||||
|
typedef void (^MTLSharedEventNotificationBlock)(id <MTLSharedEvent>, uint64_t value); |
||||
|
|
||||
|
API_AVAILABLE(macos(10.14), ios(12.0)) |
||||
|
@protocol MTLSharedEvent <MTLEvent> |
||||
|
// Synchronously wait for the signaledValue to be greater than or equal to 'value', with a timeout |
||||
|
// specified in milliseconds. Returns YES if the value was signaled before the timeout, otherwise NO. |
||||
|
- (BOOL)waitUntilSignaledValue:(uint64_t)value timeoutMS:(uint64_t)milliseconds API_AVAILABLE(macos(12.0), ios(15.0)); |
||||
|
@end |
||||
|
|
||||
|
API_AVAILABLE(macos(12)) |
||||
|
extern BOOL MtlSharedEventWaitUntilSignaledValueHack(id<MTLSharedEvent> ev, uint64_t value, uint64_t milliseconds) |
||||
|
{ |
||||
|
return [ev waitUntilSignaledValue:value timeoutMS:milliseconds]; |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
#include "common.h" |
||||
|
class MemHelper : public ComSingleObject<IAvnNativeObjectsMemoryManagement, &IID_IAvnNativeObjectsMemoryManagement> |
||||
|
{ |
||||
|
FORWARD_IUNKNOWN() |
||||
|
void RetainNSObject(void *object) override |
||||
|
{ |
||||
|
::RetainNSObject(object); |
||||
|
} |
||||
|
|
||||
|
void ReleaseNSObject(void *object) override |
||||
|
{ |
||||
|
::ReleaseNSObject(object); |
||||
|
} |
||||
|
|
||||
|
void RetainCFObject(void *object) override |
||||
|
{ |
||||
|
CFRetain(object); |
||||
|
} |
||||
|
|
||||
|
void ReleaseCFObject(void *object) override |
||||
|
{ |
||||
|
CFRelease(object); |
||||
|
} |
||||
|
|
||||
|
uint64_t GetRetainCountForNSObject(void *obj) override { |
||||
|
return ::GetRetainCountForNSObject(obj); |
||||
|
} |
||||
|
|
||||
|
int64_t GetRetainCountForCFObject(void *obj) override { |
||||
|
return CFGetRetainCount(obj); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
}; |
||||
|
|
||||
|
|
||||
|
extern IAvnNativeObjectsMemoryManagement* CreateMemoryManagementHelper() |
||||
|
{ |
||||
|
return new MemHelper(); |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
using System; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace GpuInterop; |
||||
|
|
||||
|
static class NativeMethods |
||||
|
{ |
||||
|
[Flags] |
||||
|
public enum IOSurfaceLockOptions : uint |
||||
|
{ |
||||
|
None = 0, |
||||
|
ReadOnly = 1 << 0, |
||||
|
AvoidSync = 1 << 1, |
||||
|
} |
||||
|
|
||||
|
[DllImport("/System/Library/Frameworks/IOSurface.framework/IOSurface")] |
||||
|
public static extern int IOSurfaceLock(IntPtr surface, IOSurfaceLockOptions options, IntPtr seed); |
||||
|
|
||||
|
[DllImport("/System/Library/Frameworks/IOSurface.framework/IOSurface")] |
||||
|
public static extern nint IOSurfaceGetWidth(IntPtr surface); |
||||
|
|
||||
|
[DllImport("/System/Library/Frameworks/IOSurface.framework/IOSurface")] |
||||
|
public static extern nint IOSurfaceGetHeight(IntPtr surface); |
||||
|
|
||||
|
[DllImport("/System/Library/Frameworks/IOSurface.framework/IOSurface")] |
||||
|
public static extern nint IOSurfaceGetBytesPerRow(IntPtr surface); |
||||
|
|
||||
|
[DllImport("/System/Library/Frameworks/IOSurface.framework/IOSurface")] |
||||
|
public static extern IntPtr IOSurfaceGetBaseAddress(IntPtr surface); |
||||
|
|
||||
|
[DllImport("/System/Library/Frameworks/IOSurface.framework/IOSurface")] |
||||
|
public static extern void IOSurfaceUnlock(IntPtr surface, IOSurfaceLockOptions options, IntPtr seed); |
||||
|
|
||||
|
[DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")] |
||||
|
public static extern IntPtr CFRetain(IntPtr cf); |
||||
|
|
||||
|
[DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")] |
||||
|
public static extern void CFRelease(IntPtr cf); |
||||
|
|
||||
|
[DllImport("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")] |
||||
|
public static extern nint CFGetRetainCount(IntPtr cf); |
||||
|
} |
||||
@ -0,0 +1,72 @@ |
|||||
|
using System; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using Avalonia.Platform; |
||||
|
using Silk.NET.Vulkan; |
||||
|
using Silk.NET.Vulkan.Extensions.EXT; |
||||
|
using SilkNetDemo; |
||||
|
|
||||
|
namespace GpuInterop.VulkanDemo; |
||||
|
|
||||
|
class VulkanTimelineSemaphore : IDisposable |
||||
|
{ |
||||
|
private VulkanContext _resources; |
||||
|
|
||||
|
public unsafe VulkanTimelineSemaphore(VulkanContext resources) |
||||
|
{ |
||||
|
_resources = resources; |
||||
|
var mtlEvent = new ExportMetalObjectCreateInfoEXT |
||||
|
{ |
||||
|
SType = StructureType.ExportMetalObjectCreateInfoExt, |
||||
|
ExportObjectType = ExportMetalObjectTypeFlagsEXT.SharedEventBitExt |
||||
|
}; |
||||
|
|
||||
|
var semaphoreTypeInfo = new SemaphoreTypeCreateInfoKHR() |
||||
|
{ |
||||
|
SType = StructureType.SemaphoreTypeCreateInfo, |
||||
|
SemaphoreType = SemaphoreType.Timeline, |
||||
|
PNext = &mtlEvent |
||||
|
}; |
||||
|
|
||||
|
var semaphoreCreateInfo = new SemaphoreCreateInfo |
||||
|
{ |
||||
|
SType = StructureType.SemaphoreCreateInfo, |
||||
|
PNext = &semaphoreTypeInfo, |
||||
|
}; |
||||
|
resources.Api.CreateSemaphore(resources.Device, semaphoreCreateInfo, null, out var semaphore).ThrowOnError(); |
||||
|
Handle = semaphore; |
||||
|
} |
||||
|
|
||||
|
public Semaphore Handle { get; } |
||||
|
public unsafe void Dispose() |
||||
|
{ |
||||
|
_resources.Api.DestroySemaphore(_resources.Device, Handle, null); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public unsafe IntPtr ExportSharedEvent() |
||||
|
{ |
||||
|
if (!_resources.Api.TryGetDeviceExtension<ExtMetalObjects>(_resources.Instance, _resources.Device, out var ext)) |
||||
|
throw new InvalidOperationException(); |
||||
|
var eventExport = new ExportMetalSharedEventInfoEXT() |
||||
|
{ |
||||
|
SType = StructureType.ExportMetalSharedEventInfoExt, |
||||
|
Semaphore = Handle, |
||||
|
}; |
||||
|
var export = new ExportMetalObjectsInfoEXT() |
||||
|
{ |
||||
|
SType = StructureType.ExportMetalObjectsInfoExt, |
||||
|
PNext = &eventExport |
||||
|
}; |
||||
|
ext.ExportMetalObjects(_resources.Device, ref export); |
||||
|
if (eventExport.MtlSharedEvent == IntPtr.Zero) |
||||
|
throw new Exception("Unable to export IOSurfaceRef"); |
||||
|
return eventExport.MtlSharedEvent; |
||||
|
} |
||||
|
public IPlatformHandle Export() |
||||
|
{ |
||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) |
||||
|
return new PlatformHandle(ExportSharedEvent(), |
||||
|
KnownPlatformGraphicsExternalSemaphoreHandleTypes.MetalSharedEvent); |
||||
|
throw new PlatformNotSupportedException(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Metadata; |
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Rendering.Composition; |
||||
|
|
||||
|
namespace Avalonia.Metal; |
||||
|
|
||||
|
[PrivateApi] |
||||
|
public interface IMetalExternalObjectsFeature |
||||
|
{ |
||||
|
IReadOnlyList<string> SupportedImageHandleTypes { get; } |
||||
|
IReadOnlyList<string> SupportedSemaphoreTypes { get; } |
||||
|
byte[]? DeviceLuid { get; } |
||||
|
CompositionGpuImportedImageSynchronizationCapabilities GetSynchronizationCapabilities(string imageHandleType); |
||||
|
IMetalExternalTexture ImportImage(IPlatformHandle handle, PlatformGraphicsExternalImageProperties properties); |
||||
|
IMetalSharedEvent ImportSharedEvent(IPlatformHandle handle); |
||||
|
|
||||
|
void SubmitWait(IMetalSharedEvent @event, ulong waitForValue); |
||||
|
void SubmitSignal(IMetalSharedEvent @event, ulong signalValue); |
||||
|
} |
||||
|
|
||||
|
[PrivateApi] |
||||
|
public interface IMetalExternalTexture : IDisposable |
||||
|
{ |
||||
|
int Width { get; } |
||||
|
int Height { get; } |
||||
|
int Samples { get; } |
||||
|
IntPtr Handle { get; } |
||||
|
} |
||||
|
|
||||
|
[PrivateApi] |
||||
|
public interface IMetalSharedEvent : IDisposable |
||||
|
{ |
||||
|
IntPtr Handle { get; } |
||||
|
} |
||||
@ -0,0 +1,55 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Native.Interop; |
||||
|
using Avalonia.Platform; |
||||
|
|
||||
|
namespace Avalonia.Native; |
||||
|
|
||||
|
class GpuHandleWrapFeature : IExternalObjectsHandleWrapRenderInterfaceContextFeature |
||||
|
{ |
||||
|
private readonly IAvnNativeObjectsMemoryManagement _helper; |
||||
|
|
||||
|
public GpuHandleWrapFeature(IAvaloniaNativeFactory factory) |
||||
|
{ |
||||
|
_helper = factory.CreateMemoryManagementHelper(); |
||||
|
} |
||||
|
public IExternalObjectsWrappedGpuHandle? WrapImageHandleOnAnyThread(IPlatformHandle handle, PlatformGraphicsExternalImageProperties properties) |
||||
|
{ |
||||
|
if (handle.HandleDescriptor == KnownPlatformGraphicsExternalImageHandleTypes.IOSurfaceRef) |
||||
|
{ |
||||
|
_helper.RetainCFObject(handle.Handle); |
||||
|
return new CFObjectWrapper(_helper, handle.Handle, handle.HandleDescriptor); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public IExternalObjectsWrappedGpuHandle? WrapSemaphoreHandleOnAnyThread(IPlatformHandle handle) |
||||
|
{ |
||||
|
if (handle.HandleDescriptor == KnownPlatformGraphicsExternalSemaphoreHandleTypes.MetalSharedEvent) |
||||
|
{ |
||||
|
_helper.RetainNSObject(handle.Handle); |
||||
|
return new NSObjectWrapper(_helper, handle.Handle, handle.HandleDescriptor); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
class NSObjectWrapper(IAvnNativeObjectsMemoryManagement helper, IntPtr handle, string descriptor) : IExternalObjectsWrappedGpuHandle |
||||
|
{ |
||||
|
public void Dispose() => helper.ReleaseNSObject(handle); |
||||
|
|
||||
|
public IntPtr Handle => handle; |
||||
|
public string HandleDescriptor => descriptor; |
||||
|
} |
||||
|
|
||||
|
class CFObjectWrapper(IAvnNativeObjectsMemoryManagement helper, IntPtr handle, string descriptor) : IExternalObjectsWrappedGpuHandle |
||||
|
{ |
||||
|
public void Dispose() |
||||
|
{ |
||||
|
helper.ReleaseCFObject(handle); |
||||
|
} |
||||
|
|
||||
|
public IntPtr Handle => handle; |
||||
|
public string HandleDescriptor => descriptor; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,75 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Metal; |
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Rendering.Composition; |
||||
|
using SkiaSharp; |
||||
|
|
||||
|
namespace Avalonia.Skia.Metal; |
||||
|
|
||||
|
class SkiaMetalExternalObjectsFeature(SkiaMetalGpu gpu, IMetalExternalObjectsFeature inner) : IExternalObjectsRenderInterfaceContextFeature |
||||
|
{ |
||||
|
public IReadOnlyList<string> SupportedImageHandleTypes => inner.SupportedImageHandleTypes; |
||||
|
|
||||
|
public IReadOnlyList<string> SupportedSemaphoreTypes => inner.SupportedSemaphoreTypes; |
||||
|
|
||||
|
class ImportedSemaphore(IMetalSharedEvent ev) : IPlatformRenderInterfaceImportedSemaphore |
||||
|
{ |
||||
|
public IMetalSharedEvent Event => ev; |
||||
|
public void Dispose() => ev.Dispose(); |
||||
|
} |
||||
|
|
||||
|
class ImportedImage(SkiaMetalGpu gpu, IMetalExternalObjectsFeature feature, IMetalExternalTexture texture, |
||||
|
SKColorType colorType, bool topLeftOrigin) : IPlatformRenderInterfaceImportedImage |
||||
|
{ |
||||
|
public void Dispose() => texture.Dispose(); |
||||
|
|
||||
|
public IBitmapImpl SnapshotWithKeyedMutex(uint acquireIndex, uint releaseIndex) => throw new System.NotSupportedException(); |
||||
|
|
||||
|
public IBitmapImpl SnapshotWithSemaphores(IPlatformRenderInterfaceImportedSemaphore waitForSemaphore, |
||||
|
IPlatformRenderInterfaceImportedSemaphore signalSemaphore) => |
||||
|
throw new System.NotSupportedException(); |
||||
|
|
||||
|
public IBitmapImpl SnapshotWithTimelineSemaphores(IPlatformRenderInterfaceImportedSemaphore waitForSemaphore, |
||||
|
ulong waitForValue, IPlatformRenderInterfaceImportedSemaphore signalSemaphore, ulong signalValue) |
||||
|
{ |
||||
|
gpu.GrContext.Flush(true, false); |
||||
|
feature.SubmitWait(((ImportedSemaphore)waitForSemaphore).Event, waitForValue); |
||||
|
using var backendTarget = gpu.SkiaMetalApi.CreateBackendRenderTarget(texture.Width, texture.Height, texture.Samples, texture.Handle); |
||||
|
|
||||
|
using var surface = SKSurface.Create(gpu.GrContext, backendTarget, |
||||
|
topLeftOrigin ? GRSurfaceOrigin.TopLeft : GRSurfaceOrigin.BottomLeft, |
||||
|
colorType); |
||||
|
var rv = new ImmutableBitmap(surface.Snapshot()); |
||||
|
gpu.GrContext.Flush(); |
||||
|
feature.SubmitSignal(((ImportedSemaphore)signalSemaphore).Event, signalValue); |
||||
|
return rv; |
||||
|
} |
||||
|
|
||||
|
public IBitmapImpl SnapshotWithAutomaticSync() => throw new System.NotSupportedException(); |
||||
|
} |
||||
|
|
||||
|
public IPlatformRenderInterfaceImportedImage ImportImage(IPlatformHandle handle, |
||||
|
PlatformGraphicsExternalImageProperties properties) |
||||
|
{ |
||||
|
var format = properties.Format switch |
||||
|
{ |
||||
|
PlatformGraphicsExternalImageFormat.R8G8B8A8UNorm => SKColorType.Rgba8888, |
||||
|
PlatformGraphicsExternalImageFormat.B8G8R8A8UNorm => SKColorType.Bgra8888, |
||||
|
_ => throw new NotSupportedException("Pixel format is not supported") |
||||
|
}; |
||||
|
|
||||
|
return new ImportedImage(gpu, inner, inner.ImportImage(handle, properties), format, |
||||
|
properties.TopLeftOrigin); |
||||
|
} |
||||
|
|
||||
|
public IPlatformRenderInterfaceImportedImage ImportImage(ICompositionImportableSharedGpuContextImage image) => throw new System.NotSupportedException(); |
||||
|
|
||||
|
public IPlatformRenderInterfaceImportedSemaphore ImportSemaphore(IPlatformHandle handle) => new ImportedSemaphore(inner.ImportSharedEvent(handle)); |
||||
|
|
||||
|
public CompositionGpuImportedImageSynchronizationCapabilities GetSynchronizationCapabilities(string imageHandleType) |
||||
|
=> inner.GetSynchronizationCapabilities(imageHandleType); |
||||
|
|
||||
|
public byte[]? DeviceUuid { get; } = null; |
||||
|
public byte[]? DeviceLuid => inner.DeviceLuid; |
||||
|
} |
||||
Loading…
Reference in new issue