Beispiel #1
0
        public unsafe MTLShader(ref ShaderDescription description, MTLGraphicsDevice gd)
            : base(description.Stage)
        {
            _device = gd;

            if (description.ShaderBytes.Length > 4 &&
                description.ShaderBytes[0] == 0x4d &&
                description.ShaderBytes[1] == 0x54 &&
                description.ShaderBytes[2] == 0x4c &&
                description.ShaderBytes[3] == 0x42)
            {
                DispatchQueue queue = Dispatch.dispatch_get_global_queue(QualityOfServiceLevel.QOS_CLASS_USER_INTERACTIVE, 0);
                fixed(byte *shaderBytesPtr = description.ShaderBytes)
                {
                    DispatchData dispatchData = Dispatch.dispatch_data_create(
                        shaderBytesPtr,
                        (UIntPtr)description.ShaderBytes.Length,
                        queue,
                        IntPtr.Zero);

                    try
                    {
                        Library = gd.Device.newLibraryWithData(dispatchData);
                    }
                    finally
                    {
                        Dispatch.dispatch_release(dispatchData.NativePtr);
                    }
                }
            }
            else
            {
                string            source         = Encoding.UTF8.GetString(description.ShaderBytes);
                MTLCompileOptions compileOptions = MTLCompileOptions.New();
                Library = gd.Device.newLibraryWithSource(source, compileOptions);
                ObjectiveCRuntime.release(compileOptions);
            }

            Function = Library.newFunctionWithName(description.EntryPoint);
            if (Function.NativePtr == IntPtr.Zero)
            {
                throw new VeldridException(
                          $"Failed to create Metal {description.Stage} Shader. The given entry point \"{description.EntryPoint}\" was not found.");
            }

            if (Function.functionConstantsDictionary.count != UIntPtr.Zero)
            {
                // Need to create specialized MTLFunction.
                ObjectiveCRuntime.release(Function.NativePtr);
                MTLFunctionConstantValues constantValues = MTLFunctionConstantValues.New();
                Function = Library.newFunctionWithNameConstantValues(description.EntryPoint, constantValues);
                ObjectiveCRuntime.release(constantValues.NativePtr);

                Debug.Assert(Function.NativePtr != IntPtr.Zero, "Failed to create specialized MTLFunction");
            }
        }
Beispiel #2
0
        private unsafe MTLFunctionConstantValues CreateConstantValues(SpecializationConstant[] specializations)
        {
            MTLFunctionConstantValues ret = MTLFunctionConstantValues.New();

            if (specializations != null)
            {
                foreach (SpecializationConstant sc in specializations)
                {
                    MTLDataType mtlType = MTLFormats.VdVoMTLShaderConstantType(sc.Type);
                    ret.setConstantValuetypeatIndex(&sc.Data, mtlType, (UIntPtr)sc.ID);
                }
            }

            return(ret);
        }
Beispiel #3
0
        public MTLPipeline(ref GraphicsPipelineDescription description, MTLGraphicsDevice gd)
            : base(ref description)
        {
            PrimitiveType        = MTLFormats.VdToMTLPrimitiveTopology(description.PrimitiveTopology);
            ResourceLayouts      = new MTLResourceLayout[description.ResourceLayouts.Length];
            NonVertexBufferCount = 0;
            for (int i = 0; i < ResourceLayouts.Length; i++)
            {
                ResourceLayouts[i]    = Util.AssertSubtype <ResourceLayout, MTLResourceLayout>(description.ResourceLayouts[i]);
                NonVertexBufferCount += ResourceLayouts[i].BufferCount;
            }
            ResourceBindingModel = description.ResourceBindingModel ?? gd.ResourceBindingModel;

            CullMode           = MTLFormats.VdToMTLCullMode(description.RasterizerState.CullMode);
            FrontFace          = MTLFormats.VdVoMTLFrontFace(description.RasterizerState.FrontFace);
            FillMode           = MTLFormats.VdToMTLFillMode(description.RasterizerState.FillMode);
            ScissorTestEnabled = description.RasterizerState.ScissorTestEnabled;

            MTLRenderPipelineDescriptor mtlDesc = MTLRenderPipelineDescriptor.New();

            foreach (Shader shader in description.ShaderSet.Shaders)
            {
                MTLShader   mtlShader = Util.AssertSubtype <Shader, MTLShader>(shader);
                MTLFunction specializedFunction;

                if (mtlShader.HasFunctionConstants)
                {
                    // Need to create specialized MTLFunction.
                    MTLFunctionConstantValues constantValues = CreateConstantValues(description.ShaderSet.Specializations);
                    specializedFunction = mtlShader.Library.newFunctionWithNameConstantValues(mtlShader.EntryPoint, constantValues);
                    AddSpecializedFunction(specializedFunction);
                    ObjectiveCRuntime.release(constantValues.NativePtr);

                    Debug.Assert(specializedFunction.NativePtr != IntPtr.Zero, "Failed to create specialized MTLFunction");
                }
                else
                {
                    specializedFunction = mtlShader.Function;
                }

                if (shader.Stage == ShaderStages.Vertex)
                {
                    mtlDesc.vertexFunction = specializedFunction;
                }
                else if (shader.Stage == ShaderStages.Fragment)
                {
                    mtlDesc.fragmentFunction = specializedFunction;
                }
            }

            // Vertex layouts
            VertexLayoutDescription[] vdVertexLayouts  = description.ShaderSet.VertexLayouts;
            MTLVertexDescriptor       vertexDescriptor = mtlDesc.vertexDescriptor;

            for (uint i = 0; i < vdVertexLayouts.Length; i++)
            {
                uint layoutIndex = ResourceBindingModel == ResourceBindingModel.Improved
                    ? NonVertexBufferCount + i
                    : i;
                MTLVertexBufferLayoutDescriptor mtlLayout = vertexDescriptor.layouts[layoutIndex];
                mtlLayout.stride = (UIntPtr)vdVertexLayouts[i].Stride;
                uint stepRate = vdVertexLayouts[i].InstanceStepRate;
                mtlLayout.stepFunction = stepRate == 0 ? MTLVertexStepFunction.PerVertex : MTLVertexStepFunction.PerInstance;
                mtlLayout.stepRate     = (UIntPtr)Math.Max(1, stepRate);
            }

            uint element = 0;

            for (uint i = 0; i < vdVertexLayouts.Length; i++)
            {
                uint offset = 0;
                VertexLayoutDescription vdDesc = vdVertexLayouts[i];
                for (uint j = 0; j < vdDesc.Elements.Length; j++)
                {
                    VertexElementDescription     elementDesc  = vdDesc.Elements[j];
                    MTLVertexAttributeDescriptor mtlAttribute = vertexDescriptor.attributes[element];
                    mtlAttribute.bufferIndex = (UIntPtr)(ResourceBindingModel == ResourceBindingModel.Improved
                        ? NonVertexBufferCount + i
                        : i);
                    mtlAttribute.format = MTLFormats.VdToMTLVertexFormat(elementDesc.Format);
                    mtlAttribute.offset = elementDesc.Offset != 0 ? (UIntPtr)elementDesc.Offset : (UIntPtr)offset;
                    offset  += FormatHelpers.GetSizeInBytes(elementDesc.Format);
                    element += 1;
                }
            }

            VertexBufferCount = (uint)vdVertexLayouts.Length;

            // Outputs
            OutputDescription     outputs        = description.Outputs;
            BlendStateDescription blendStateDesc = description.BlendState;

            BlendColor = blendStateDesc.BlendFactor;

            if (outputs.SampleCount != TextureSampleCount.Count1)
            {
                mtlDesc.sampleCount = (UIntPtr)FormatHelpers.GetSampleCountUInt32(outputs.SampleCount);
            }

            if (outputs.DepthAttachment != null)
            {
                PixelFormat    depthFormat    = outputs.DepthAttachment.Value.Format;
                MTLPixelFormat mtlDepthFormat = MTLFormats.VdToMTLPixelFormat(depthFormat, true);
                mtlDesc.depthAttachmentPixelFormat = mtlDepthFormat;
                if ((FormatHelpers.IsStencilFormat(depthFormat)))
                {
                    HasStencil = true;
                    mtlDesc.stencilAttachmentPixelFormat = mtlDepthFormat;
                }
            }
            for (uint i = 0; i < outputs.ColorAttachments.Length; i++)
            {
                BlendAttachmentDescription attachmentBlendDesc       = blendStateDesc.AttachmentStates[i];
                MTLRenderPipelineColorAttachmentDescriptor colorDesc = mtlDesc.colorAttachments[i];
                colorDesc.pixelFormat                 = MTLFormats.VdToMTLPixelFormat(outputs.ColorAttachments[i].Format, false);
                colorDesc.blendingEnabled             = attachmentBlendDesc.BlendEnabled;
                colorDesc.alphaBlendOperation         = MTLFormats.VdToMTLBlendOp(attachmentBlendDesc.AlphaFunction);
                colorDesc.sourceAlphaBlendFactor      = MTLFormats.VdToMTLBlendFactor(attachmentBlendDesc.SourceAlphaFactor);
                colorDesc.destinationAlphaBlendFactor = MTLFormats.VdToMTLBlendFactor(attachmentBlendDesc.DestinationAlphaFactor);

                colorDesc.rgbBlendOperation         = MTLFormats.VdToMTLBlendOp(attachmentBlendDesc.ColorFunction);
                colorDesc.sourceRGBBlendFactor      = MTLFormats.VdToMTLBlendFactor(attachmentBlendDesc.SourceColorFactor);
                colorDesc.destinationRGBBlendFactor = MTLFormats.VdToMTLBlendFactor(attachmentBlendDesc.DestinationColorFactor);
            }

            RenderPipelineState = gd.Device.newRenderPipelineStateWithDescriptor(mtlDesc);
            ObjectiveCRuntime.release(mtlDesc.NativePtr);

            if (outputs.DepthAttachment != null)
            {
                MTLDepthStencilDescriptor depthDescriptor = MTLUtil.AllocInit <MTLDepthStencilDescriptor>(
                    nameof(MTLDepthStencilDescriptor));
                depthDescriptor.depthCompareFunction = MTLFormats.VdToMTLCompareFunction(
                    description.DepthStencilState.DepthComparison);
                depthDescriptor.depthWriteEnabled = description.DepthStencilState.DepthWriteEnabled;

                bool stencilEnabled = description.DepthStencilState.StencilTestEnabled;
                if (stencilEnabled)
                {
                    StencilReference = description.DepthStencilState.StencilReference;

                    StencilBehaviorDescription vdFrontDesc = description.DepthStencilState.StencilFront;
                    MTLStencilDescriptor       front       = MTLUtil.AllocInit <MTLStencilDescriptor>(nameof(MTLStencilDescriptor));
                    front.readMask                   = stencilEnabled ? description.DepthStencilState.StencilReadMask : 0u;
                    front.writeMask                  = stencilEnabled ? description.DepthStencilState.StencilWriteMask : 0u;
                    front.depthFailureOperation      = MTLFormats.VdToMTLStencilOperation(vdFrontDesc.DepthFail);
                    front.stencilFailureOperation    = MTLFormats.VdToMTLStencilOperation(vdFrontDesc.Fail);
                    front.depthStencilPassOperation  = MTLFormats.VdToMTLStencilOperation(vdFrontDesc.Pass);
                    front.stencilCompareFunction     = MTLFormats.VdToMTLCompareFunction(vdFrontDesc.Comparison);
                    depthDescriptor.frontFaceStencil = front;

                    StencilBehaviorDescription vdBackDesc = description.DepthStencilState.StencilBack;
                    MTLStencilDescriptor       back       = MTLUtil.AllocInit <MTLStencilDescriptor>(nameof(MTLStencilDescriptor));
                    back.readMask                   = stencilEnabled ? description.DepthStencilState.StencilReadMask : 0u;
                    back.writeMask                  = stencilEnabled ? description.DepthStencilState.StencilWriteMask : 0u;
                    back.depthFailureOperation      = MTLFormats.VdToMTLStencilOperation(vdBackDesc.DepthFail);
                    back.stencilFailureOperation    = MTLFormats.VdToMTLStencilOperation(vdBackDesc.Fail);
                    back.depthStencilPassOperation  = MTLFormats.VdToMTLStencilOperation(vdBackDesc.Pass);
                    back.stencilCompareFunction     = MTLFormats.VdToMTLCompareFunction(vdBackDesc.Comparison);
                    depthDescriptor.backFaceStencil = back;

                    ObjectiveCRuntime.release(front.NativePtr);
                    ObjectiveCRuntime.release(back.NativePtr);
                }

                DepthStencilState = gd.Device.newDepthStencilStateWithDescriptor(depthDescriptor);
                ObjectiveCRuntime.release(depthDescriptor.NativePtr);
            }

            DepthClipMode = description.DepthStencilState.DepthTestEnabled ? MTLDepthClipMode.Clip : MTLDepthClipMode.Clamp;
        }
Beispiel #4
0
        public MTLPipeline(ref ComputePipelineDescription description, MTLGraphicsDevice gd)
            : base(ref description)
        {
            IsComputePipeline = true;
            ResourceLayouts   = new MTLResourceLayout[description.ResourceLayouts.Length];
            for (int i = 0; i < ResourceLayouts.Length; i++)
            {
                ResourceLayouts[i] = Util.AssertSubtype <ResourceLayout, MTLResourceLayout>(description.ResourceLayouts[i]);
            }

            ThreadsPerThreadgroup = new MTLSize(
                description.ThreadGroupSizeX,
                description.ThreadGroupSizeY,
                description.ThreadGroupSizeZ);

            MTLComputePipelineDescriptor mtlDesc = MTLUtil.AllocInit <MTLComputePipelineDescriptor>(
                nameof(MTLComputePipelineDescriptor));
            MTLShader   mtlShader = Util.AssertSubtype <Shader, MTLShader>(description.ComputeShader);
            MTLFunction specializedFunction;

            if (mtlShader.HasFunctionConstants)
            {
                // Need to create specialized MTLFunction.
                MTLFunctionConstantValues constantValues = CreateConstantValues(description.Specializations);
                specializedFunction = mtlShader.Library.newFunctionWithNameConstantValues(mtlShader.EntryPoint, constantValues);
                AddSpecializedFunction(specializedFunction);
                ObjectiveCRuntime.release(constantValues.NativePtr);

                Debug.Assert(specializedFunction.NativePtr != IntPtr.Zero, "Failed to create specialized MTLFunction");
            }
            else
            {
                specializedFunction = mtlShader.Function;
            }

            mtlDesc.computeFunction = specializedFunction;
            MTLPipelineBufferDescriptorArray buffers = mtlDesc.buffers;
            uint bufferIndex = 0;

            foreach (MTLResourceLayout layout in ResourceLayouts)
            {
                foreach (ResourceKind kind in layout.ResourceKinds)
                {
                    if (kind == ResourceKind.UniformBuffer ||
                        kind == ResourceKind.StructuredBufferReadOnly)
                    {
                        MTLPipelineBufferDescriptor bufferDesc = buffers[bufferIndex];
                        bufferDesc.mutability = MTLMutability.Immutable;
                        bufferIndex          += 1;
                    }
                    else if (kind == ResourceKind.StructuredBufferReadWrite)
                    {
                        MTLPipelineBufferDescriptor bufferDesc = buffers[bufferIndex];
                        bufferDesc.mutability = MTLMutability.Mutable;
                        bufferIndex          += 1;
                    }
                }
            }

            ComputePipelineState = gd.Device.newComputePipelineStateWithDescriptor(mtlDesc);

            ObjectiveCRuntime.release(mtlDesc.NativePtr);
        }
Beispiel #5
0
        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;

            // some older hardware won't have a default
            if (device == null)
            {
                Assert.Inconclusive("Metal is not supported");
            }

            // 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)}");
                }
            }


            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");

#if !__MACOS__ && !__MACCATALYST__
            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)) {
#if NET
                // Looks like creating a library with a url always fails: https://forums.developer.apple.com/thread/110416
                Assert.IsNotNull(library, "CreateLibrary (NSUrl, NSError): Null");
                Assert.IsNull(error, "CreateLibrary (NSUrl, NSError): NonNull error");
#else
                // 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");
#endif
            }

            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)) {
                Assert.IsNull(error, "MTLFunction.CreateArgumentEncoder: library creation failure");
                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__ || __MACCATALYST__
                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__ || __MACCATALYST__
                        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");
                }
        }