xamarin-macios/tests/monotouch-test/Metal/MTLDeviceTests.cs

419 строки
18 KiB
C#

#if MONOMAC || __IOS__
using System;
using System.IO;
using System.Runtime.InteropServices;
using CoreFoundation;
using Foundation;
using ObjCRuntime;
using Metal;
using NUnit.Framework;
namespace MonoTouchFixtures.Metal {
[Preserve (AllMembers = true)]
public class MTLDeviceTests {
[SetUp]
public void Setup ()
{
TestRuntime.AssertXcodeVersion (9, 0);
}
#if __MACOS__
[Test]
public void GetAllDevicesTest ()
{
NSObject refObj = new NSObject();
var devices = MTLDevice.GetAllDevices(ref refObj, (IMTLDevice device, NSString notifyName) => { });
// It's possible to run on a system that does not support metal,
// in which case we'll get an empty array of devices.
Assert.IsNotNull (devices, "MTLDevices.GetAllDevices not null");
Assert.DoesNotThrow (() => {
MTLDevice.RemoveObserver (refObj);
});
}
#endif
[Test]
public void SystemDefault ()
{
Assert.DoesNotThrow (() => { var obj = MTLDevice.SystemDefault; }, "No exception");
}
[DllImport (Constants.libcLibrary)]
static extern int getpagesize ();
[DllImport (Constants.libcLibrary)]
static extern IntPtr mmap (IntPtr start, nint length, int prot, int flags, int fd, nint offset);
static IntPtr AllocPageAligned (int pages, out int length)
{
length = pages * getpagesize ();
var rv = mmap (IntPtr.Zero, length, 0x1 /* PROT_READ */ | 0x2 /* */, 0x0002 /* MAP_PRIVATE */ | 0x1000 /* MAP_ANONYMOUS */, 0, 0);
return rv;
}
[DllImport (Constants.libcLibrary)]
static extern int munmap (IntPtr addr, nint size);
static void FreePageAligned (IntPtr ptr, int length)
{
munmap (ptr, length);
}
[Test]
public void ReturnReleaseTest ()
{
// This test tries to exercise all the Metal API that has a
// ReturnRelease attribute. To test that the attribute does the
// right thing: run the test app using instruments, run the test
// several times by tapping on it, and do a heap mark between each
// test. Then verify that there's at least one heap shot with 0
// memory increase, which means that nothing is leaking.
var device = MTLDevice.SystemDefault;
IntPtr buffer_mem;
int buffer_length;
bool freed;
byte [] buffer_bytes;
// Apple claims that "Indirect command buffers" are available with MTLGPUFamilyCommon2, but it crashes on at least one machine.
// Log what the current device supports, just to have it in the log.
foreach (MTLFeatureSet fs in Enum.GetValues (typeof (MTLFeatureSet))) {
Console.WriteLine ($"This device supports feature set: {fs}: {device.SupportsFeatureSet (fs)}");
}
if (TestRuntime.CheckXcodeVersion (11, 0)) {
foreach (MTLGpuFamily gf in Enum.GetValues (typeof (MTLGpuFamily))) {
Console.WriteLine ($"This device supports Gpu family: {gf}: {device.SupportsFamily (gf)}");
}
}
#if __MACOS__
string metal_code = File.ReadAllText (Path.Combine (NSBundle.MainBundle.ResourcePath, "metal-sample.metal"));
string metallib_path = Path.Combine (NSBundle.MainBundle.ResourcePath, "default.metallib");
string fragmentshader_path = Path.Combine (NSBundle.MainBundle.ResourcePath, "fragmentShader.metallib");
#else
string metal_code = File.ReadAllText (Path.Combine (NSBundle.MainBundle.BundlePath, "metal-sample.metal"));
string metallib_path = Path.Combine (NSBundle.MainBundle.BundlePath, "default.metallib");
string fragmentshader_path = Path.Combine (NSBundle.MainBundle.BundlePath, "fragmentShader.metallib");
if (Runtime.Arch == Arch.SIMULATOR)
Assert.Ignore ("Metal isn't available in the simulator");
#endif
using (var hd = new MTLHeapDescriptor ()) {
hd.CpuCacheMode = MTLCpuCacheMode.DefaultCache;
hd.StorageMode = MTLStorageMode.Private;
using (var txt = MTLTextureDescriptor.CreateTexture2DDescriptor (MTLPixelFormat.RGBA8Unorm, 40, 40, false)) {
var sa = device.GetHeapTextureSizeAndAlign (txt);
hd.Size = sa.Size;
using (var heap = device.CreateHeap (hd)) {
Assert.IsNotNull (heap, $"NonNullHeap");
}
}
}
using (var queue = device.CreateCommandQueue ()) {
Assert.IsNotNull (queue, "Queue: NonNull 1");
}
#if __MACOS__
if (TestRuntime.CheckXcodeVersion (10, 0) && device.SupportsFeatureSet (MTLFeatureSet.macOS_GPUFamily2_v1)) {
using (var descriptor = MTLTextureDescriptor.CreateTexture2DDescriptor (MTLPixelFormat.RGBA8Unorm, 64, 64, false)) {
descriptor.StorageMode = MTLStorageMode.Private;
using (var texture = device.CreateSharedTexture (descriptor)) {
Assert.IsNotNull (texture, "CreateSharedTexture (MTLTextureDescriptor): NonNull");
using (var handle = texture.CreateSharedTextureHandle ())
using (var shared = device.CreateSharedTexture (handle))
Assert.IsNotNull (texture, "CreateSharedTexture (MTLSharedTextureHandle): NonNull");
}
}
}
#endif
using (var queue = device.CreateCommandQueue (10)) {
Assert.IsNotNull (queue, "Queue: NonNull 2");
}
using (var buffer = device.CreateBuffer (1024, MTLResourceOptions.CpuCacheModeDefault)) {
Assert.IsNotNull (buffer, "CreateBuffer: NonNull 1");
}
buffer_mem = AllocPageAligned (1, out buffer_length);
using (var buffer = device.CreateBuffer (buffer_mem, (nuint)buffer_length, MTLResourceOptions.CpuCacheModeDefault)) {
Assert.IsNotNull (buffer, "CreateBuffer: NonNull 2");
}
FreePageAligned (buffer_mem, buffer_length);
buffer_bytes = new byte [getpagesize ()];
using (var buffer = device.CreateBuffer (buffer_bytes, MTLResourceOptions.CpuCacheModeDefault)) {
Assert.IsNotNull (buffer, "CreateBuffer: NonNull 3");
}
buffer_mem = AllocPageAligned (1, out buffer_length);
freed = false;
#if __MACOS__
var resourceOptions7 = MTLResourceOptions.StorageModeManaged;
#else
var resourceOptions7 = MTLResourceOptions.CpuCacheModeDefault;
#endif
using (var buffer = device.CreateBufferNoCopy (buffer_mem, (nuint)buffer_length, resourceOptions7, (pointer, length) => { FreePageAligned (pointer, (int)length); freed = true; })) {
Assert.IsNotNull (buffer, "CreateBufferNoCopy: NonNull 1");
}
Assert.IsTrue (freed, "CreateBufferNoCopy: Freed 1");
using (var descriptor = new MTLDepthStencilDescriptor ())
using (var dss = device.CreateDepthStencilState (descriptor)) {
Assert.IsNotNull (dss, "CreateDepthStencilState: NonNull 1");
}
using (var descriptor = MTLTextureDescriptor.CreateTexture2DDescriptor (MTLPixelFormat.RGBA8Unorm, 64, 64, false)) {
using (var texture = device.CreateTexture (descriptor))
Assert.NotNull (texture, "CreateTexture: NonNull 1");
using (var surface = new IOSurface.IOSurface (new IOSurface.IOSurfaceOptions {
Width = 64,
Height = 64,
BytesPerElement = 4,
})) {
using (var texture = device.CreateTexture (descriptor, surface, 0))
Assert.NotNull (texture, "CreateTexture: NonNull 2");
}
}
using (var descriptor = new MTLSamplerDescriptor ())
using (var sampler = device.CreateSamplerState (descriptor))
Assert.IsNotNull (sampler, "CreateSamplerState: NonNull 1");
using (var library = device.CreateDefaultLibrary ())
Assert.IsNotNull (library, "CreateDefaultLibrary: NonNull 1");
using (var library = device.CreateLibrary (metallib_path, out var error)) {
Assert.IsNotNull (library, "CreateLibrary: NonNull 1");
Assert.IsNull (error, "CreateLibrary: NonNull error 1");
}
using (var data = DispatchData.FromByteBuffer (File.ReadAllBytes (metallib_path)))
using (var library = device.CreateLibrary (data, out var error)) {
Assert.IsNotNull (library, "CreateLibrary: NonNull 2");
Assert.IsNull (error, "CreateLibrary: NonNull error 2");
}
using (var compile_options = new MTLCompileOptions ())
using (var library = device.CreateLibrary (metal_code, compile_options, out var error)) {
Assert.IsNotNull (library, "CreateLibrary: NonNull 3");
Assert.IsNull (error, "CreateLibrary: NonNull error 3");
}
using (var compile_options = new MTLCompileOptions ()) {
device.CreateLibrary (metal_code, compile_options, (library, error) => {
Assert.IsNotNull (library, "CreateLibrary: NonNull 4");
Assert.IsNull (error, "CreateLibrary: NonNull error 4");
});
}
using (var library = device.CreateDefaultLibrary (NSBundle.MainBundle, out var error)) {
Assert.IsNotNull (library, "CreateDefaultLibrary: NonNull 2");
Assert.IsNull (error, "CreateDefaultLibrary: NonNull error 2");
}
using (var descriptor = new MTLRenderPipelineDescriptor ())
using (var library = device.CreateDefaultLibrary ())
using (var func = library.CreateFunction ("vertexShader")) {
descriptor.VertexFunction = func;
descriptor.ColorAttachments [0].PixelFormat = MTLPixelFormat.BGRA8Unorm_sRGB;
using (var rps = device.CreateRenderPipelineState (descriptor, out var error)) {
Assert.IsNotNull (rps, "CreateRenderPipelineState: NonNull 1");
Assert.IsNull (error, "CreateRenderPipelineState: NonNull error 1");
}
}
using (var descriptor = new MTLRenderPipelineDescriptor ())
using (var library = device.CreateDefaultLibrary ())
using (var func = library.CreateFunction ("vertexShader")) {
descriptor.VertexFunction = func;
descriptor.ColorAttachments [0].PixelFormat = MTLPixelFormat.BGRA8Unorm_sRGB;
using (var rps = device.CreateRenderPipelineState (descriptor, MTLPipelineOption.BufferTypeInfo, out var reflection, out var error)) {
Assert.IsNotNull (rps, "CreateRenderPipelineState: NonNull 2");
Assert.IsNull (error, "CreateRenderPipelineState: NonNull error 2");
Assert.IsNotNull (reflection, "CreateRenderPipelineState: NonNull reflection 2");
}
}
using (var library = device.CreateDefaultLibrary ())
using (var func = library.CreateFunction ("grayscaleKernel"))
using (var cps = device.CreateComputePipelineState (func, MTLPipelineOption.ArgumentInfo, out var reflection, out var error)) {
Assert.IsNotNull (cps, "CreateComputePipelineState: NonNull 1");
Assert.IsNull (error, "CreateComputePipelineState: NonNull error 1");
Assert.IsNotNull (reflection, "CreateComputePipelineState: NonNull reflection 1");
}
using (var library = device.CreateDefaultLibrary ())
using (var func = library.CreateFunction ("grayscaleKernel"))
using (var cps = device.CreateComputePipelineState (func, out var error)) {
Assert.IsNotNull (cps, "CreateComputePipelineState: NonNull 2");
Assert.IsNull (error, "CreateComputePipelineState: NonNull error 2");
}
using (var descriptor = new MTLComputePipelineDescriptor ())
using (var library = device.CreateDefaultLibrary ())
using (var func = library.CreateFunction ("grayscaleKernel")) {
descriptor.ComputeFunction = func;
using (var cps = device.CreateComputePipelineState (descriptor, MTLPipelineOption.BufferTypeInfo, out var reflection, out var error)) {
Assert.IsNotNull (cps, "CreateComputePipelineState: NonNull 3");
Assert.IsNull (error, "CreateComputePipelineState: NonNull error 3");
Assert.IsNotNull (reflection, "CreateComputePipelineState: NonNull reflection 3");
}
}
using (var fence = device.CreateFence ()) {
Assert.IsNotNull (fence, "CreateFence 1: NonNull");
}
var url = "file://" + metallib_path;
url = url.Replace (" ", "%20"); // url encode!
using (var library = device.CreateLibrary (new NSUrl (url), out var error)) {
// Looks like creating a library with a url always fails: https://forums.developer.apple.com/thread/110416
Assert.IsNull (library, "CreateLibrary (NSUrl, NSError): Null");
Assert.IsNotNull (error, "CreateLibrary (NSUrl, NSError): NonNull error");
}
using (var library = device.CreateArgumentEncoder (new MTLArgumentDescriptor [] { new MTLArgumentDescriptor () { DataType = MTLDataType.Int } })) {
Assert.IsNotNull (library, "CreateArgumentEncoder (MTLArgumentDescriptor[]): NonNull");
}
// Apple's charts say that "Indirect command buffers" are supported with MTLGpuFamilyCommon2
var supportsIndirectCommandBuffers = TestRuntime.CheckXcodeVersion (11, 0) && device.SupportsFamily (MTLGpuFamily.Common2);
#if __MACOS__
// but something's not quite right somewhere, so on macOS verify that the device supports a bit more than what Apple says.
supportsIndirectCommandBuffers &= device.SupportsFeatureSet (MTLFeatureSet.macOS_GPUFamily2_v1);
#endif
if (supportsIndirectCommandBuffers) {
using (var descriptor = new MTLIndirectCommandBufferDescriptor ()) {
using (var library = device.CreateIndirectCommandBuffer (descriptor, 1, MTLResourceOptions.CpuCacheModeDefault)) {
Assert.IsNotNull (library, "CreateIndirectCommandBuffer: NonNull");
}
}
using (var evt = device.CreateEvent ()) {
Assert.IsNotNull (evt, "CreateEvent: NonNull");
}
using (var evt = device.CreateSharedEvent ()) {
Assert.IsNotNull (evt, "CreateSharedEvent: NonNull");
}
using (var evt1 = device.CreateSharedEvent ())
using (var evt_handle = evt1.CreateSharedEventHandle ())
using (var evt = device.CreateSharedEvent (evt_handle)) {
Assert.IsNotNull (evt, "CreateSharedEvent (MTLSharedEventHandle): NonNull");
}
}
using (var descriptor = new MTLRenderPipelineDescriptor ())
using (var library = device.CreateDefaultLibrary ())
using (var func = library.CreateFunction ("vertexShader")) {
descriptor.VertexFunction = func;
descriptor.ColorAttachments [0].PixelFormat = MTLPixelFormat.BGRA8Unorm_sRGB;
using (var rps = device.CreateRenderPipelineState (descriptor, MTLPipelineOption.ArgumentInfo, out var reflection, out var error)) {
Assert.IsNotNull (rps, "CreateRenderPipelineState (MTLTileRenderPipelineDescriptor, MTLPipelineOption, MTLRenderPipelineReflection, NSError): NonNull");
Assert.IsNull (error, "CreateRenderPipelineState (MTLTileRenderPipelineDescriptor, MTLPipelineOption, MTLRenderPipelineReflection, NSError: NonNull error");
Assert.IsNotNull (reflection, "CreateRenderPipelineState (MTLTileRenderPipelineDescriptor, MTLPipelineOption, MTLRenderPipelineReflection, NSError): NonNull reflection");
}
}
using (var buffer = device.CreateBuffer (1024, MTLResourceOptions.CpuCacheModeDefault))
using (var descriptor = new MTLTextureDescriptor ())
using (var texture = buffer.CreateTexture (descriptor, 0, 256)) {
Assert.IsNotNull (buffer, "MTLBuffer.CreateTexture (MTLTextureDescriptor, nuint, nuint): NonNull");
}
using (var descriptor = MTLTextureDescriptor.CreateTexture2DDescriptor (MTLPixelFormat.RGBA8Unorm, 64, 64, false))
using (var texture = device.CreateTexture (descriptor)) {
using (var view = texture.CreateTextureView (MTLPixelFormat.RGBA8Unorm)) {
Assert.IsNotNull (view, "MTLTexture.CreateTextureView (MTLPixelFormat): nonnull");
}
using (var view = texture.CreateTextureView (MTLPixelFormat.RGBA8Unorm, MTLTextureType.k2D, new NSRange (0, 1), new NSRange (0, 1))) {
Assert.IsNotNull (view, "MTLTexture.CreateTextureView (MTLPixelFormat, MTLTextureType, NSRange, NSRange): nonnull");
}
}
using (var library = device.CreateLibrary (fragmentshader_path, out var error))
using (var func = library.CreateFunction ("fragmentShader2")) {
using (var enc = func.CreateArgumentEncoder (0)) {
Assert.IsNotNull (enc, "MTLFunction.CreateArgumentEncoder (nuint): NonNull");
}
using (var enc = func.CreateArgumentEncoder (0, out var reflection)) {
Assert.IsNotNull (enc, "MTLFunction.CreateArgumentEncoder (nuint, MTLArgument): NonNull");
Assert.IsNotNull (reflection, "MTLFunction.CreateArgumentEncoder (nuint, MTLArgument): NonNull reflection");
}
}
using (var library = device.CreateDefaultLibrary ()) {
using (var func = library.CreateFunction ("grayscaleKernel")) {
Assert.IsNotNull (func, "CreateFunction (string): nonnull");
}
if (TestRuntime.CheckXcodeVersion (9, 0)) { // MTLFunctionConstantValues didn't have a default ctor until Xcode 9.
using (var constants = new MTLFunctionConstantValues ())
using (var func = library.CreateFunction ("grayscaleKernel", constants, out var error)) {
Assert.IsNotNull (func, "CreateFunction (string, MTLFunctionConstantValues, NSError): nonnull");
Assert.IsNull (error, "CreateFunction (string, MTLFunctionConstantValues, NSError): null error");
}
}
}
using (var hd = new MTLHeapDescriptor ()) {
hd.CpuCacheMode = MTLCpuCacheMode.DefaultCache;
hd.StorageMode = MTLStorageMode.Private;
using (var txt = MTLTextureDescriptor.CreateTexture2DDescriptor (MTLPixelFormat.RGBA8Unorm, 40, 40, false)) {
var sa = device.GetHeapTextureSizeAndAlign (txt);
hd.Size = sa.Size;
using (var heap = device.CreateHeap (hd))
using (var buffer = heap.CreateBuffer (1024, MTLResourceOptions.StorageModePrivate)) {
Assert.IsNotNull (buffer, "MTLHeap.CreateBuffer (nuint, MTLResourceOptions): nonnull");
}
}
}
using (var hd = new MTLHeapDescriptor ()) {
hd.CpuCacheMode = MTLCpuCacheMode.DefaultCache;
#if __MACOS__
hd.StorageMode = MTLStorageMode.Private;
#else
hd.StorageMode = MTLStorageMode.Shared;
#endif
using (var txt = MTLTextureDescriptor.CreateTexture2DDescriptor (MTLPixelFormat.RGBA8Unorm, 40, 40, false)) {
var sa = device.GetHeapTextureSizeAndAlign (txt);
hd.Size = sa.Size;
using (var heap = device.CreateHeap (hd)) {
#if __MACOS__
txt.StorageMode = MTLStorageMode.Private;
#endif
using (var texture = heap.CreateTexture (txt)) {
Assert.IsNotNull (texture, "MTLHeap.CreateTexture (MTLTextureDescriptor): nonnull");
}
}
}
}
using (var scope = MTLCaptureManager.Shared.CreateNewCaptureScope (device)) {
Assert.IsNotNull (scope, "MTLCaptureManager.CreateNewCaptureScope (MTLDevice): nonnull");
}
using (var queue = device.CreateCommandQueue ())
using (var scope = MTLCaptureManager.Shared.CreateNewCaptureScope (queue)) {
Assert.IsNotNull (scope, "MTLCaptureManager.CreateNewCaptureScope (MTLCommandQueue): nonnull");
}
TestRuntime.AssertXcodeVersion (10, 0);
using (var evt = device.CreateSharedEvent ())
using (var shared = evt.CreateSharedEventHandle ()) {
Assert.IsNotNull (shared, "MTLSharedEvent.CreateSharedEvent: NonNull");
}
}
}
}
#endif