diff --git a/src/Core/Silk.NET.Core/Miscellaneous/Bool8.cs b/src/Core/Silk.NET.Core/Miscellaneous/Bool8.cs new file mode 100644 index 0000000000..225c4391b5 --- /dev/null +++ b/src/Core/Silk.NET.Core/Miscellaneous/Bool8.cs @@ -0,0 +1,187 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Silk.NET.Core +{ + /// + /// A 8-bit boolean. + /// + public readonly struct Bool8 : IEquatable, IEquatable, IEquatable + { + /// + /// Gets the 8-bit value for this boolean. + /// + public byte Value { get; } + + /// + /// Returns the hash code for this instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + /// + /// Compares the current to another . Returns true if they are equal. + /// + /// + /// True if is equal to the current . + public override bool Equals(object obj) => obj switch + { + Bool8 val => Equals(val), + bool val => Equals(val), + byte val => Equals(val), + _ => base.Equals(obj) + }; + + /// + /// Compares the current to another . Returns true if they are equal. + /// + /// The other value. + /// True if the current is equal to the provided value. + public bool Equals(Bool8 other) => Value == other.Value; + + /// + /// Compares the current to a . Returns true if they are equal. + /// + /// The other value. + /// True if the current is equal to the provided value. + public bool Equals(byte other) => Value == other; + + /// + /// Compares the current to a . Returns true if they are equal. + /// + /// The other value. + /// True if the current is equal to the provided value. + public bool Equals(bool other) => Value == (other ? 1U : 0U); + + #region Cast operators + /// + /// Creates a 8-bit boolean from the given byte. + /// + /// The byte value. + public Bool8(byte val) => Value = val; + + /// + /// Creates a 8-bit boolean from the given managed boolean. + /// + /// The boolean value. + public Bool8(bool val) => Value = val ? (byte) 1 : (byte) 0; + + /// + /// Converts this 8-bit boolean to a managed boolean. + /// + /// The 8-bit boolean. + /// The managed boolean. + public static implicit operator bool(Bool8 val) => val.Value == 1; + + /// + /// Converts this 8-bit boolean to a byte. + /// + /// The 8-bit boolean. + /// The byte. + public static implicit operator byte(Bool8 val) => val.Value; + + /// + /// Creates a 8-bit boolean from the given managed boolean. + /// + /// The boolean value. + public static implicit operator Bool8(bool val) => new Bool8(val); + + /// + /// Creates a 8-bit boolean from the given byte. + /// + /// The byte value. + public static implicit operator Bool8(byte val) => new Bool8(val); + #endregion + + #region Bool8 vs Bool8 equality + /// + /// Compares a to a for equality. + /// + /// The left-hand . + /// The right-hand . + /// + public static bool operator ==(Bool8 left, Bool8 right) => left.Value == right.Value; + + /// + /// Compares a to a for inequality. + /// + /// The left-hand . + /// The right-hand . + /// + public static bool operator !=(Bool8 left, Bool8 right) => left.Value != right.Value; + #endregion + + #region Bool8 vs bool equality + /// + /// Compares a to a for equality. + /// + /// The left-hand . + /// The right-hand . + /// + public static bool operator ==(Bool8 left, bool right) => left.Value == (right ? 1U : 0U); + + /// + /// Compares a to a for inequality. + /// + /// The left-hand . + /// The right-hand . + /// + public static bool operator !=(Bool8 left, bool right) => left.Value != (right ? 1U : 0U); + + /// + /// Compares a to a for equality. + /// + /// The left-hand . + /// The right-hand . + /// + public static bool operator ==(bool left, Bool8 right) => right.Value == (left ? 1U : 0U); + + /// + /// Compares a to a for equality. + /// + /// The left-hand . + /// The right-hand . + /// + public static bool operator !=(bool left, Bool8 right) => right.Value != (left ? 1U : 0U); + #endregion + + #region Bool8 vs byte equality + /// + /// Compares a to a for equality. + /// + /// The left-hand . + /// The right-hand . + /// + public static bool operator ==(Bool8 left, byte right) => left.Value == right; + + /// + /// Compares a to a for inequality. + /// + /// The left-hand . + /// The right-hand . + /// + public static bool operator !=(Bool8 left, byte right) => left.Value != right; + + /// + /// Compares a to a for equality. + /// + /// The left-hand . + /// The right-hand . + /// + public static bool operator ==(byte left, Bool8 right) => left == right.Value; + + /// + /// Compares a to a for inequality. + /// + /// The left-hand . + /// The right-hand . + /// + public static bool operator !=(byte left, Bool8 right) => left != right.Value; + #endregion + } +} diff --git a/src/WebGPU/Silk.NET.WebGPU/Platforms/MacOS/CAMetalLayer.cs b/src/WebGPU/Silk.NET.WebGPU/Platforms/MacOS/CAMetalLayer.cs new file mode 100644 index 0000000000..a56093e4f3 --- /dev/null +++ b/src/WebGPU/Silk.NET.WebGPU/Platforms/MacOS/CAMetalLayer.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Silk.NET.WebGPU.Platforms.MacOS; + +internal struct CAMetalLayer +{ + public readonly nint NativePtr; + + public CAMetalLayer(nint ptr) + { + NativePtr = ptr; + } + + public static CAMetalLayer New() + { + return s_class.AllocInit(); + } + + private static readonly ObjectiveCClass s_class = new(nameof(CAMetalLayer)); +} diff --git a/src/WebGPU/Silk.NET.WebGPU/Platforms/MacOS/NSView.cs b/src/WebGPU/Silk.NET.WebGPU/Platforms/MacOS/NSView.cs new file mode 100644 index 0000000000..7bc4f03f79 --- /dev/null +++ b/src/WebGPU/Silk.NET.WebGPU/Platforms/MacOS/NSView.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Silk.NET.Core; + +namespace Silk.NET.WebGPU.Platforms.MacOS; + +internal struct NSView +{ + public readonly nint NativePtr; + + public static implicit operator nint(NSView nsView) + { + return nsView.NativePtr; + } + + public NSView(nint ptr) + { + NativePtr = ptr; + } + + public Bool8 wantsLayer + { + get => ObjectiveCRuntime.bool8_objc_msgSend(NativePtr, "wantsLayer"); + set => ObjectiveCRuntime.objc_msgSend(NativePtr, "setWantsLayer:", value); + } + + public nint layer + { + get => ObjectiveCRuntime.ptr_objc_msgSend(NativePtr, "layer"); + set => ObjectiveCRuntime.ptr_objc_msgSend(NativePtr, "setLayer:", value); + } +} diff --git a/src/WebGPU/Silk.NET.WebGPU/Platforms/MacOS/NSWindow.cs b/src/WebGPU/Silk.NET.WebGPU/Platforms/MacOS/NSWindow.cs new file mode 100644 index 0000000000..9a16f5e9ae --- /dev/null +++ b/src/WebGPU/Silk.NET.WebGPU/Platforms/MacOS/NSWindow.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Silk.NET.WebGPU.Platforms.MacOS; + +internal class NSWindow +{ + public readonly nint NativePtr; + + public NSWindow(nint ptr) + { + NativePtr = ptr; + } + + public NSView contentView => ObjectiveCRuntime.objc_msgSend(NativePtr, "contentView"); +} diff --git a/src/WebGPU/Silk.NET.WebGPU/Platforms/MacOS/ObjectiveCClass.cs b/src/WebGPU/Silk.NET.WebGPU/Platforms/MacOS/ObjectiveCClass.cs new file mode 100644 index 0000000000..28276ce34d --- /dev/null +++ b/src/WebGPU/Silk.NET.WebGPU/Platforms/MacOS/ObjectiveCClass.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using Silk.NET.Core.Native; + +namespace Silk.NET.WebGPU.Platforms.MacOS; + +internal unsafe struct ObjectiveCClass +{ + public readonly nint NativePtr; + + public static implicit operator nint(ObjectiveCClass c) + { + return c.NativePtr; + } + + public ObjectiveCClass(string name) + { + var namePtr = SilkMarshal.StringToPtr(name); + NativePtr = ObjectiveCRuntime.objc_getClass(namePtr); + SilkMarshal.Free(namePtr); + } + + public T AllocInit() where T : struct + { + var value = ObjectiveCRuntime.ptr_objc_msgSend(NativePtr, "alloc"); + ObjectiveCRuntime.objc_msgSend(value, "init"); + return Unsafe.AsRef(&value); + } +} diff --git a/src/WebGPU/Silk.NET.WebGPU/Platforms/MacOS/ObjectiveCRuntime.cs b/src/WebGPU/Silk.NET.WebGPU/Platforms/MacOS/ObjectiveCRuntime.cs new file mode 100644 index 0000000000..04fb2845b9 --- /dev/null +++ b/src/WebGPU/Silk.NET.WebGPU/Platforms/MacOS/ObjectiveCRuntime.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Silk.NET.Core; + +namespace Silk.NET.WebGPU.Platforms.MacOS; + +internal static unsafe class ObjectiveCRuntime +{ + private const string ObjCLibrary = "/usr/lib/libobjc.A.dylib"; + + [DllImport(ObjCLibrary)] + public static extern nint sel_registerName(nint namePtr); + + [DllImport(ObjCLibrary)] + public static extern byte* sel_getName(nint selector); + + [DllImport(ObjCLibrary, EntryPoint = "objc_msgSend")] + public static extern Bool8 bool8_objc_msgSend(nint receiver, Selector selector); + + [DllImport(ObjCLibrary, EntryPoint = "objc_msgSend")] + public static extern nint ptr_objc_msgSend(nint receiver, Selector selector); + + [DllImport(ObjCLibrary, EntryPoint = "objc_msgSend")] + public static extern nint ptr_objc_msgSend(nint receiver, Selector selector, nint a); + + [DllImport(ObjCLibrary, EntryPoint = "objc_msgSend")] + public static extern void objc_msgSend(nint receiver, Selector selector, byte b); + + [DllImport(ObjCLibrary, EntryPoint = "objc_msgSend")] + public static extern void objc_msgSend(nint receiver, Selector selector); + + [DllImport(ObjCLibrary)] + public static extern nint objc_getClass(nint namePtr); + + public static T objc_msgSend(nint receiver, Selector selector) where T : struct + { + var value = ptr_objc_msgSend(receiver, selector); + return Unsafe.AsRef(&value); + } +} diff --git a/src/WebGPU/Silk.NET.WebGPU/Platforms/MacOS/Selector.cs b/src/WebGPU/Silk.NET.WebGPU/Platforms/MacOS/Selector.cs new file mode 100644 index 0000000000..306ae95d51 --- /dev/null +++ b/src/WebGPU/Silk.NET.WebGPU/Platforms/MacOS/Selector.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Silk.NET.Core.Native; + +namespace Silk.NET.WebGPU.Platforms.MacOS; + +internal struct Selector +{ + public readonly nint NativePtr; + + public Selector(nint ptr) + { + NativePtr = ptr; + } + + public Selector(string name) + { + var namePtr = SilkMarshal.StringToPtr(name); + NativePtr = ObjectiveCRuntime.sel_registerName(namePtr); + SilkMarshal.Free(namePtr); + } + + public static implicit operator Selector(string s) + { + return new Selector(s); + } +} diff --git a/src/WebGPU/Silk.NET.WebGPU/WebGPUSurface.cs b/src/WebGPU/Silk.NET.WebGPU/WebGPUSurface.cs index ffcb4e5666..9768c6b9d7 100644 --- a/src/WebGPU/Silk.NET.WebGPU/WebGPUSurface.cs +++ b/src/WebGPU/Silk.NET.WebGPU/WebGPUSurface.cs @@ -5,6 +5,7 @@ using System.Runtime.InteropServices; using Silk.NET.Core.Contexts; using Silk.NET.Core.Native; +using Silk.NET.WebGPU.Platforms.MacOS; namespace Silk.NET.WebGPU; @@ -55,9 +56,14 @@ public static class WebGPUSurface } else if (view.Native.Cocoa != null) { - throw new PlatformNotSupportedException("WebGPU on MacOS is not supported at this time!"); - + // Based on the Veldrid Metal bindings implementation: + // https://github.com/veldrid/veldrid/tree/master/src/Veldrid.MetalBindings var cocoa = view.Native.Cocoa.Value; + CAMetalLayer metalLayer = CAMetalLayer.New(); + NSWindow nsWindow = new(cocoa); + var contentView = nsWindow.contentView; + contentView.wantsLayer = true; + contentView.layer = metalLayer.NativePtr; var cocoaDescriptor = new SurfaceDescriptorFromMetalLayer { @@ -66,8 +72,10 @@ public static class WebGPUSurface Next = null, SType = SType.SurfaceDescriptorFromMetalLayer }, - Layer = null //TODO: Get the layer from the window + Layer = (void*) metalLayer.NativePtr }; + + descriptor.NextInChain = (ChainedStruct*) (&cocoaDescriptor); } else if (view.Native.Wayland != null) {