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)
{