diff --git a/src/Metal/MTLArgumentEncoder.cs b/src/Metal/MTLArgumentEncoder.cs index 59f465f185..301efa70ac 100644 --- a/src/Metal/MTLArgumentEncoder.cs +++ b/src/Metal/MTLArgumentEncoder.cs @@ -1,11 +1,40 @@ using System; +using System.Runtime.InteropServices; +using Foundation; +using ObjCRuntime; namespace Metal { + + public static partial class MTLArgumentEncoder_Extensions { +#if XAMCORE_4_0 + public static void SetBuffers (this IMTLArgumentEncoder encoder, IMTLBuffer[] buffers, nuint[] offsets, NSRange range) + { + if (buffers == null) + throw new ArgumentNullException (nameof (buffers)); + if (offsets == null) + throw new ArgumentNullException (nameof (offsets)); + + var bufferPtrArray = buffers.Length <= 1024 ? stackalloc IntPtr[buffers.Length] : new IntPtr [buffers.Length]; + // get all intptr from the array to pass to the lower level call + for (var i = 0; i < buffers.Length; i++) { + bufferPtrArray [i] = buffers [i].Handle; + } + + unsafe { + fixed (void* buffersPtr = bufferPtrArray) + fixed (void* offsetsPtr = offsets) { // can use fixed + encoder.SetBuffers ((IntPtr) buffersPtr, (IntPtr) offsetsPtr, range); + } + } + GC.KeepAlive (buffers) + } +#else public unsafe static void SetBuffers (this IMTLArgumentEncoder This, IMTLBuffer [] buffers, nint [] offsets, Foundation.NSRange range) { fixed (void* handle = offsets) This.SetBuffers (buffers, (IntPtr)handle, range); } +#endif } } diff --git a/src/Metal/MTLComputeCommandEncoder.cs b/src/Metal/MTLComputeCommandEncoder.cs new file mode 100644 index 0000000000..62da42bb9c --- /dev/null +++ b/src/Metal/MTLComputeCommandEncoder.cs @@ -0,0 +1,36 @@ +using System; +using System.Runtime.InteropServices; +using Foundation; +using ObjCRuntime; + +namespace Metal { + +#if XAMCORE_4_0 + + // add some extension methods to make the API of the protocol nicer + public static class IMTLComputeCommandEncoderExtensions { + + public static void SetBuffers (this IMTLComputeCommandEncoder table, IMTLBuffer[] buffers, nuint[] offsets, NSRange range) + { + if (buffers == null) + throw new ArgumentNullException (nameof (buffers)); + if (offsets == null) + throw new ArgumentNullException (nameof (offsets)); + + var bufferPtrArray = buffers.Length <= 1024 ? stackalloc IntPtr[buffers.Length] : new IntPtr [buffers.Length]; + // get all intptr from the array to pass to the lower level call + for (var i = 0; i < buffers.Length; i++) { + bufferPtrArray [i] = buffers [i].Handle; + } + + unsafe { + fixed (void* buffersPtr = bufferPtrArray) + fixed (void* offsetsPtr = offsets) { // can use fixed + table.SetBuffers ((IntPtr) buffersPtr, (IntPtr) offsetsPtr, range); + } + } + GC.KeepAlive (buffers) + } + } +#endif +} diff --git a/src/frameworks.sources b/src/frameworks.sources index c3e3ae5356..ba80d4d87e 100644 --- a/src/frameworks.sources +++ b/src/frameworks.sources @@ -1078,6 +1078,7 @@ METAL_SOURCES = \ Metal/MTLArgumentEncoder.cs \ Metal/MTLBlitPassSampleBufferAttachmentDescriptorArray.cs \ Metal/MTLCompat.cs \ + Metal/MTLComputeCommandEncoder.cs \ Metal/MTLComputePassSampleBufferAttachmentDescriptorArray.cs \ Metal/MTLDevice.cs \ Metal/MTLIntersectionFunctionTable.cs \ diff --git a/src/metal.cs b/src/metal.cs index 56d266740d..fa26e82e11 100644 --- a/src/metal.cs +++ b/src/metal.cs @@ -495,9 +495,16 @@ namespace Metal { [Export ("dispatchThreadgroupsWithIndirectBuffer:indirectBufferOffset:threadsPerThreadgroup:")] void DispatchThreadgroups (IMTLBuffer indirectBuffer, nuint indirectBufferOffset, MTLSize threadsPerThreadgroup); +#if XAMCORE_4_0 + [Abstract] + [Export ("setBuffers:offsets:withRange:")] + void SetBuffers (IntPtr buffers, IntPtr offsets, NSRange range); +#else [Abstract] [Export ("setBuffers:offsets:withRange:")] void SetBuffers (IMTLBuffer [] buffers, IntPtr offsets, NSRange range); +#endif + [Abstract] [Export ("setSamplerStates:lodMinClamps:lodMaxClamps:withRange:")] @@ -3911,9 +3918,15 @@ namespace Metal { [Export ("setBuffer:offset:atIndex:")] void SetBuffer ([NullAllowed] IMTLBuffer buffer, nuint offset, nuint index); +#if XAMCORE_4_0 + [Abstract] + [Export ("setBuffers:offsets:withRange:")] + void SetBuffers (IntPtr buffers, IntPtr offsets, NSRange range); +#else [Abstract] [Export ("setBuffers:offsets:withRange:")] void SetBuffers (IMTLBuffer[] buffers, IntPtr offsets, NSRange range); +#endif [Abstract] [Export ("setTexture:atIndex:")] diff --git a/tests/generator/BGenTests.cs b/tests/generator/BGenTests.cs index 2ac85260bf..0917f37cd2 100644 --- a/tests/generator/BGenTests.cs +++ b/tests/generator/BGenTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/tests/monotouch-test/Metal/MTLArgumentEncoderTest.cs b/tests/monotouch-test/Metal/MTLArgumentEncoderTest.cs new file mode 100644 index 0000000000..3b00bd0465 --- /dev/null +++ b/tests/monotouch-test/Metal/MTLArgumentEncoderTest.cs @@ -0,0 +1,69 @@ +#if !__WATCHOS__ + +using System; +using Foundation; +using Metal; + +using NUnit.Framework; + +namespace MonoTouchFixtures.Metal { + + [TestFixture] + public class MTLArgumentEncoderTest { + IMTLDevice device; + IMTLLibrary library; + IMTLFunction function; + IMTLArgumentEncoder encoder; + + [SetUp] + public void SetUp () + { + device = MTLDevice.SystemDefault; + // some older hardware won't have a default + if (device == null) + Assert.Inconclusive ("Metal is not supported"); + + library = device.CreateDefaultLibrary (); + if (library == null) // this happens on a simulator + Assert.Inconclusive ("Could not get the functions library for the device."); + + if (library.FunctionNames.Length == 0) + Assert.Inconclusive ("Could not get functions for the pipeline."); + + function = library.CreateFunction (library.FunctionNames [0]); + encoder = function.CreateArgumentEncoder (0); + } + + [TearDown] + public void TearDown () + { + library?.Dispose (); + library = null; + function?.Dispose (); + function = null; + encoder?.Dispose (); + encoder = null; + } + +#if XAMCORE_4_0 + [Test] + public void SetBuffers () + { + Assert.Throws (() => { + encoder.SetBuffers (null, new nuint [0], new NSRange ()); + }, "Null buffers should throw."); + + Assert.Throws (() => { + encoder.SetBuffers (new IMTLBuffer [0], null, new NSRange ()); + }, "Null offsets should throw."); + + // assert we do not crash or throw, we are testing the extension method + Assert.DoesNotThrow (() => { + encoder.SetBuffers (new IMTLBuffer [0], new nuint [0], new NSRange ()); + + }, "Should not throw"); + } +#endif + } +} +#endif diff --git a/tests/monotouch-test/Metal/MTLComputeCommandEncoderTest.cs b/tests/monotouch-test/Metal/MTLComputeCommandEncoderTest.cs new file mode 100644 index 0000000000..6d9156bfce --- /dev/null +++ b/tests/monotouch-test/Metal/MTLComputeCommandEncoderTest.cs @@ -0,0 +1,69 @@ +#if !__WATCHOS__ + +using System; +using Foundation; +using Metal; + +using NUnit.Framework; + +namespace MonoTouchFixtures.Metal { + + [TestFixture] + public class MTLComputeCommandEncoderTest { + IMTLDevice device; + IMTLCommandQueue commandQ; + IMTLCommandBuffer commandBuffer; + IMTLComputeCommandEncoder encoder; + + [SetUp] + public void SetUp () + { + device = MTLDevice.SystemDefault; + // some older hardware won't have a default + if (device == null) + Assert.Inconclusive ("Metal is not supported"); + + commandQ = device.CreateCommandQueue (); + if (commandQ == null) // this happens on a simulator + Assert.Inconclusive ("Could not get the functions library for the device."); + + commandBuffer = commandQ.CommandBuffer (); + if (commandBuffer == null) // happens on sim + Assert.Inconclusive ("Could not get the command buffer for the device."); + + encoder = commandBuffer.ComputeCommandEncoder; + } + + [TearDown] + public void TearDown () + { + commandQ?.Dispose (); + commandQ = null; + commandBuffer?.Dispose (); + commandBuffer = null; + encoder?.Dispose (); + encoder = null; + } + +#if XAMCORE_4_0 + [Test] + public void SetBuffers () + { + Assert.Throws (() => { + encoder.SetBuffers (null, new nuint [0], new NSRange ()); + }, "Null buffers should throw."); + + Assert.Throws (() => { + encoder.SetBuffers (new IMTLBuffer [0], null, new NSRange ()); + }, "Null offsets should throw."); + + // assert we do not crash or throw, we are testing the extension method + Assert.DoesNotThrow (() => { + encoder.SetBuffers (new IMTLBuffer [0], new nuint [0], new NSRange ()); + + }, "Should not throw"); + } +#endif + } +} +#endif