Exemple #1
0
        public MTLSampler(ref SamplerDescription description, MTLGraphicsDevice gd)
        {
            MTLFormats.GetMinMagMipFilter(
                description.Filter,
                out MTLSamplerMinMagFilter min,
                out MTLSamplerMinMagFilter mag,
                out MTLSamplerMipFilter mip);

            MTLSamplerDescriptor mtlDesc = MTLSamplerDescriptor.New();

            mtlDesc.sAddressMode = MTLFormats.VdToMTLAddressMode(description.AddressModeU);
            mtlDesc.tAddressMode = MTLFormats.VdToMTLAddressMode(description.AddressModeV);
            mtlDesc.rAddressMode = MTLFormats.VdToMTLAddressMode(description.AddressModeW);
            mtlDesc.minFilter    = min;
            mtlDesc.magFilter    = mag;
            mtlDesc.mipFilter    = mip;
            if (gd.MetalFeatures.IsMacOS)
            {
                mtlDesc.borderColor = MTLFormats.VdToMTLBorderColor(description.BorderColor);
            }
            if (description.ComparisonKind != null)
            {
                mtlDesc.compareFunction = MTLFormats.VdToMTLCompareFunction(description.ComparisonKind.Value);
            }
            mtlDesc.lodMinClamp   = description.MinimumLod;
            mtlDesc.lodMaxClamp   = description.MaximumLod;
            mtlDesc.maxAnisotropy = (UIntPtr)(Math.Max(1, description.MaximumAnisotropy));
            DeviceSampler         = gd.Device.newSamplerStateWithDescriptor(mtlDesc);
            ObjectiveCRuntime.release(mtlDesc.NativePtr);
        }
Exemple #2
0
        public AmtSampler(IMTLDevice mDevice, MgSamplerCreateInfo pCreateInfo)
        {
            if (pCreateInfo == null)
            {
                throw new ArgumentNullException(nameof(pCreateInfo));
            }

            var descriptor = new MTLSamplerDescriptor
            {
                SAddressMode          = TranslateAddressMode(pCreateInfo.AddressModeU),
                TAddressMode          = TranslateAddressMode(pCreateInfo.AddressModeV),
                RAddressMode          = TranslateAddressMode(pCreateInfo.AddressModeW),
                MinFilter             = TranslateMinFilter(pCreateInfo.MinFilter),
                MagFilter             = TranslateMagFilter(pCreateInfo.MagFilter),
                MipFilter             = TranslateMipFilter(pCreateInfo.MipmapMode),
                LodMinClamp           = pCreateInfo.MinLod,
                LodMaxClamp           = pCreateInfo.MaxLod,
                MaxAnisotropy         = (nuint)pCreateInfo.MaxAnisotropy,
                CompareFunction       = TranslateCompareFunction(pCreateInfo.CompareOp),
                NormalizedCoordinates = !pCreateInfo.UnnormalizedCoordinates,
            };

            Sampler = mDevice.CreateSamplerState(descriptor);
        }
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            // Set the view to use the default device
            device = MTLDevice.SystemDefault;

            if (device == null)
            {
                Console.WriteLine("Metal is not supported on this device");
                View = new NSView(View.Frame);
            }

            // Create a new command queue
            commandQueue = device.CreateCommandQueue();

            // Load all the shader files with a metal file extension in the project
            defaultLibrary = device.CreateDefaultLibrary();

            // Setup view
            view          = (MTKView)View;
            view.Delegate = this;
            view.Device   = device;

            view.SampleCount              = 1;
            view.DepthStencilPixelFormat  = MTLPixelFormat.Depth32Float_Stencil8;
            view.ColorPixelFormat         = MTLPixelFormat.BGRA8Unorm;
            view.PreferredFramesPerSecond = 60;
            view.ClearColor = new MTLClearColor(0.5f, 0.5f, 0.5f, 1.0f);

            // Load the vertex program into the library
            IMTLFunction vertexProgram = defaultLibrary.CreateFunction("quad_vertex");

            // Load the fragment program into the library
            IMTLFunction fragmentProgram = defaultLibrary.CreateFunction("quad_fragment");

            // Create a vertex descriptor from the MTKMesh
            MTLVertexDescriptor vertexDescriptor = new MTLVertexDescriptor();

            vertexDescriptor.Attributes[0].Format      = MTLVertexFormat.Float4;
            vertexDescriptor.Attributes[0].BufferIndex = 0;
            vertexDescriptor.Attributes[0].Offset      = 0;
            vertexDescriptor.Attributes[1].Format      = MTLVertexFormat.Float2;
            vertexDescriptor.Attributes[1].BufferIndex = 0;
            vertexDescriptor.Attributes[1].Offset      = 4 * sizeof(float);

            vertexDescriptor.Layouts[0].Stride = 6 * sizeof(float);

            vertexDescriptor.Layouts[0].StepRate     = 1;
            vertexDescriptor.Layouts[0].StepFunction = MTLVertexStepFunction.PerVertex;

            this.vertexBuffer = device.CreateBuffer(vertexData, MTLResourceOptions.CpuCacheModeDefault);// (MTLResourceOptions)0);

            this.mipmapping     = 0;
            this.constantBuffer = device.CreateBuffer(sizeof(float), MTLResourceOptions.CpuCacheModeDefault);
            SetConstantBuffer(this.mipmapping, constantBuffer);

            // Create a reusable pipeline state
            var pipelineStateDescriptor = new MTLRenderPipelineDescriptor
            {
                SampleCount                  = view.SampleCount,
                VertexFunction               = vertexProgram,
                FragmentFunction             = fragmentProgram,
                VertexDescriptor             = vertexDescriptor,
                DepthAttachmentPixelFormat   = view.DepthStencilPixelFormat,
                StencilAttachmentPixelFormat = view.DepthStencilPixelFormat
            };

            pipelineStateDescriptor.ColorAttachments[0].PixelFormat = view.ColorPixelFormat;

            NSError error;

            pipelineState = device.CreateRenderPipelineState(pipelineStateDescriptor, out error);
            if (pipelineState == null)
            {
                Console.WriteLine("Failed to created pipeline state, error {0}", error);
            }

            var depthStateDesc = new MTLDepthStencilDescriptor
            {
                DepthCompareFunction = MTLCompareFunction.Less,
                DepthWriteEnabled    = true
            };

            depthState = device.CreateDepthStencilState(depthStateDesc);

            // Texture KTX
            NSUrl            url = NSBundle.MainBundle.GetUrlForResource("crate", "ktx");
            MTKTextureLoader mTKTextureLoader = new MTKTextureLoader(device);

            this.texture = mTKTextureLoader.FromUrl(url, new MTKTextureLoaderOptions(), out error);

            Console.WriteLine("Failed to created pipeline state, error {0}", error);

            MTLSamplerDescriptor samplerDescriptor = new MTLSamplerDescriptor()
            {
                MinFilter    = MTLSamplerMinMagFilter.Linear,
                MagFilter    = MTLSamplerMinMagFilter.Linear,
                MipFilter    = MTLSamplerMipFilter.Linear,
                SAddressMode = MTLSamplerAddressMode.ClampToEdge,
                TAddressMode = MTLSamplerAddressMode.ClampToEdge,
            };

            this.sampler = device.CreateSamplerState(samplerDescriptor);

            this.clock = new System.Diagnostics.Stopwatch();
            this.clock.Start();
            this.time = 0;
        }
Exemple #4
0
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            // Set the view to use the default device
            device = MTLDevice.SystemDefault;

            if (device == null)
            {
                Console.WriteLine("Metal is not supported on this device");
                View = new NSView(View.Frame);
            }

            // Create a new command queue
            commandQueue = device.CreateCommandQueue();

            // Load all the shader files with a metal file extension in the project
            defaultLibrary = device.CreateDefaultLibrary();

            // Setup view
            mtkView          = (MTKView)View;
            mtkView.Delegate = this;
            mtkView.Device   = device;

            mtkView.SampleCount              = 1;
            mtkView.DepthStencilPixelFormat  = MTLPixelFormat.Depth32Float_Stencil8;
            mtkView.ColorPixelFormat         = MTLPixelFormat.BGRA8Unorm;
            mtkView.PreferredFramesPerSecond = 60;
            mtkView.ClearColor = new MTLClearColor(0.5f, 0.5f, 0.5f, 1.0f);

            // Load the vertex program into the library
            IMTLFunction triangleVertexProgram = defaultLibrary.CreateFunction("triangle_vertex");
            IMTLFunction cubeVertexProgram     = defaultLibrary.CreateFunction("cube_vertex");

            // Load the fragment program into the library
            IMTLFunction triangleFragmentProgram = defaultLibrary.CreateFunction("triangle_fragment");
            IMTLFunction cubeFragmentProgram     = defaultLibrary.CreateFunction("cube_fragment");

            // Triangle vertex descriptor
            MTLVertexDescriptor triangleVertexDescriptor = new MTLVertexDescriptor();

            triangleVertexDescriptor.Attributes[0].Format      = MTLVertexFormat.Float4;
            triangleVertexDescriptor.Attributes[0].BufferIndex = 0;
            triangleVertexDescriptor.Attributes[0].Offset      = 0;
            triangleVertexDescriptor.Attributes[1].Format      = MTLVertexFormat.Float4;
            triangleVertexDescriptor.Attributes[1].BufferIndex = 0;
            triangleVertexDescriptor.Attributes[1].Offset      = 4 * sizeof(float);

            triangleVertexDescriptor.Layouts[0].Stride       = 8 * sizeof(float);
            triangleVertexDescriptor.Layouts[0].StepRate     = 1;
            triangleVertexDescriptor.Layouts[0].StepFunction = MTLVertexStepFunction.PerVertex;

            // Cube vertex descriptor
            MTLVertexDescriptor cubeVertexDescriptor = new MTLVertexDescriptor();

            cubeVertexDescriptor.Attributes[0].Format      = MTLVertexFormat.Float4;
            cubeVertexDescriptor.Attributes[0].BufferIndex = 0;
            cubeVertexDescriptor.Attributes[0].Offset      = 0;
            cubeVertexDescriptor.Attributes[1].Format      = MTLVertexFormat.Float2;
            cubeVertexDescriptor.Attributes[1].BufferIndex = 0;
            cubeVertexDescriptor.Attributes[1].Offset      = 4 * sizeof(float);

            cubeVertexDescriptor.Layouts[0].Stride       = 6 * sizeof(float);
            cubeVertexDescriptor.Layouts[0].StepRate     = 1;
            cubeVertexDescriptor.Layouts[0].StepFunction = MTLVertexStepFunction.PerVertex;

            // Create buffers
            triangleVertexBuffer = device.CreateBuffer(triangleVertexData, MTLResourceOptions.CpuCacheModeDefault);
            cubeVertexBuffer     = device.CreateBuffer(cubeVertexData, MTLResourceOptions.CpuCacheModeDefault);

            this.clock = new System.Diagnostics.Stopwatch();
            clock.Start();

            this.view = CreateLookAt(new Vector3(0, 0, 5), new Vector3(0, 0, 0), Vector3.UnitY);
            var aspect = (float)(View.Bounds.Size.Width / View.Bounds.Size.Height);

            this.proj = Matrix4.CreatePerspectiveFieldOfView((float)Math.PI / 4, aspect, 0.1f, 100);
            this.cubeParameters.WorldViewProjection = Matrix4.Identity;
            cubeConstantBuffer = device.CreateBuffer((uint)Marshal.SizeOf(this.cubeParameters), MTLResourceOptions.CpuCacheModeDefault);

            // Create Pipeline Descriptor
            var trianglePipelineStateDescriptor = new MTLRenderPipelineDescriptor
            {
                SampleCount                  = mtkView.SampleCount,
                VertexFunction               = triangleVertexProgram,
                FragmentFunction             = triangleFragmentProgram,
                VertexDescriptor             = triangleVertexDescriptor,
                DepthAttachmentPixelFormat   = mtkView.DepthStencilPixelFormat,
                StencilAttachmentPixelFormat = mtkView.DepthStencilPixelFormat,
            };

            var cubePipelineStateDescriptor = new MTLRenderPipelineDescriptor
            {
                SampleCount                  = mtkView.SampleCount,
                VertexFunction               = cubeVertexProgram,
                FragmentFunction             = cubeFragmentProgram,
                VertexDescriptor             = cubeVertexDescriptor,
                DepthAttachmentPixelFormat   = mtkView.DepthStencilPixelFormat,
                StencilAttachmentPixelFormat = mtkView.DepthStencilPixelFormat,
            };

            MTLRenderPipelineColorAttachmentDescriptor triangleRenderBufferAttachment = trianglePipelineStateDescriptor.ColorAttachments[0];

            triangleRenderBufferAttachment.PixelFormat = mtkView.ColorPixelFormat;

            MTLRenderPipelineColorAttachmentDescriptor cubeRenderBufferAttachment = cubePipelineStateDescriptor.ColorAttachments[0];

            cubeRenderBufferAttachment.PixelFormat = mtkView.ColorPixelFormat;

            NSError error;

            trianglePipelineState = device.CreateRenderPipelineState(trianglePipelineStateDescriptor, out error);
            if (trianglePipelineState == null)
            {
                Console.WriteLine("Failed to created pipeline state, error {0}", error);
            }

            cubePipelineState = device.CreateRenderPipelineState(cubePipelineStateDescriptor, out error);
            if (cubePipelineState == null)
            {
                Console.WriteLine("Failed to created pipeline state, error {0}", error);
            }

            var depthStencilDescriptor = new MTLDepthStencilDescriptor
            {
                DepthCompareFunction = MTLCompareFunction.Less,
                DepthWriteEnabled    = true
            };

            depthStencilState = device.CreateDepthStencilState(depthStencilDescriptor);

            // Texture
            NSImage          image            = NSImage.ImageNamed("crate.png");
            MTKTextureLoader mTKTextureLoader = new MTKTextureLoader(device);

            this.cubeTexture = mTKTextureLoader.FromCGImage(image.CGImage, new MTKTextureLoaderOptions(), out error);

            MTLSamplerDescriptor samplerDescriptor = new MTLSamplerDescriptor()
            {
                MinFilter    = MTLSamplerMinMagFilter.Linear,
                MagFilter    = MTLSamplerMinMagFilter.Linear,
                SAddressMode = MTLSamplerAddressMode.Repeat,
                TAddressMode = MTLSamplerAddressMode.Repeat,
            };

            this.sampler = device.CreateSamplerState(samplerDescriptor);

            // Create FrameBuffer texture
            IMTLTexture          drawableTexture   = mtkView.CurrentDrawable.Texture;
            MTLTextureDescriptor textureDescriptor = new MTLTextureDescriptor()
            {
                Width            = drawableTexture.Width,
                Height           = drawableTexture.Height,
                PixelFormat      = drawableTexture.PixelFormat,
                TextureType      = drawableTexture.TextureType,
                MipmapLevelCount = drawableTexture.MipmapLevelCount,
                ArrayLength      = drawableTexture.ArrayLength,
                SampleCount      = drawableTexture.SampleCount,
                Depth            = drawableTexture.Depth,
                CpuCacheMode     = drawableTexture.CpuCacheMode,
                Usage            = MTLTextureUsage.RenderTarget,
                StorageMode      = MTLStorageMode.Managed,
            };

            this.colorFrameBufferTexture = device.CreateTexture(textureDescriptor);
        }
Exemple #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");
                }
        }
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            // Set the view to use the default device
            device = MTLDevice.SystemDefault;

            if (device == null)
            {
                Console.WriteLine("Metal is not supported on this device");
                View = new NSView(View.Frame);
            }

            // Create a new command queue
            commandQueue = device.CreateCommandQueue();

            // Load all the shader files with a metal file extension in the project
            defaultLibrary = device.CreateDefaultLibrary();

            // Setup view
            mtkView          = (MTKView)View;
            mtkView.Delegate = this;
            mtkView.Device   = device;

            mtkView.SampleCount              = 1;
            mtkView.DepthStencilPixelFormat  = MTLPixelFormat.Depth32Float_Stencil8;
            mtkView.ColorPixelFormat         = MTLPixelFormat.BGRA8Unorm;
            mtkView.PreferredFramesPerSecond = 60;
            mtkView.ClearColor = new MTLClearColor(0.5f, 0.5f, 0.5f, 1.0f);

            // Load the vertex program into the library
            IMTLFunction vertexProgram = defaultLibrary.CreateFunction("cube_vertex");

            // Load the fragment program into the library
            IMTLFunction fragmentProgram = defaultLibrary.CreateFunction("cube_fragment");

            // Create a vertex descriptor from the MTKMesh
            MTLVertexDescriptor vertexDescriptor = new MTLVertexDescriptor();

            vertexDescriptor.Attributes[0].Format      = MTLVertexFormat.Float4;
            vertexDescriptor.Attributes[0].BufferIndex = 0;
            vertexDescriptor.Attributes[0].Offset      = 0;
            vertexDescriptor.Attributes[1].Format      = MTLVertexFormat.Float3;
            vertexDescriptor.Attributes[1].BufferIndex = 0;
            vertexDescriptor.Attributes[1].Offset      = 4 * sizeof(float);
            vertexDescriptor.Attributes[2].Format      = MTLVertexFormat.Float2;
            vertexDescriptor.Attributes[2].BufferIndex = 0;
            vertexDescriptor.Attributes[2].Offset      = 7 * sizeof(float);

            vertexDescriptor.Layouts[0].Stride = 9 * sizeof(float);

            vertexDescriptor.Layouts[0].StepRate     = 1;
            vertexDescriptor.Layouts[0].StepFunction = MTLVertexStepFunction.PerVertex;

            // Primitive
            Torus(1.0f, 0.3f, 28, out List <PositionNormalTexture> vertexData, out List <ushort> indexData);

            vertexBuffer   = device.CreateBuffer(vertexData.ToArray(), MTLResourceOptions.CpuCacheModeDefault);// (MTLResourceOptions)0);
            indexDataArray = indexData.ToArray();
            indexBuffer    = device.CreateBuffer(indexDataArray, MTLResourceOptions.CpuCacheModeDefault);

            // Use clock
            this.clock = new System.Diagnostics.Stopwatch();
            clock.Start();

            Vector3 cameraPosition = new Vector3(0, 0, 1.5f);

            this.view = CreateLookAt(cameraPosition, new Vector3(0, 0, 0), Vector3.UnitY);
            var aspect = (float)(View.Bounds.Size.Width / View.Bounds.Size.Height);

            proj = Matrix4.CreatePerspectiveFieldOfView((float)Math.PI / 4, aspect, 0.1f, 100);


            // Constant Buffer
            this.param = new Parameters()
            {
                CameraPosition      = cameraPosition,
                WorldViewProjection = Matrix4.Identity,
                World = Matrix4.Identity,
                WorldInverseTranspose = Matrix4.Identity,
            };

            this.constantBuffer = device.CreateBuffer((uint)Marshal.SizeOf(this.param), MTLResourceOptions.CpuCacheModeDefault);

            // Create a reusable pipeline state
            var pipelineStateDescriptor = new MTLRenderPipelineDescriptor
            {
                SampleCount                  = mtkView.SampleCount,
                VertexFunction               = vertexProgram,
                FragmentFunction             = fragmentProgram,
                VertexDescriptor             = vertexDescriptor,
                DepthAttachmentPixelFormat   = mtkView.DepthStencilPixelFormat,
                StencilAttachmentPixelFormat = mtkView.DepthStencilPixelFormat,
            };

            MTLRenderPipelineColorAttachmentDescriptor renderBufferAttachment = pipelineStateDescriptor.ColorAttachments[0];

            renderBufferAttachment.PixelFormat = mtkView.ColorPixelFormat;

            NSError error;

            pipelineState = device.CreateRenderPipelineState(pipelineStateDescriptor, out error);
            if (pipelineState == null)
            {
                Console.WriteLine("Failed to created pipeline state, error {0}", error);
            }

            var depthStateDesc = new MTLDepthStencilDescriptor
            {
                DepthCompareFunction = MTLCompareFunction.Less,
                DepthWriteEnabled    = true
            };

            depthState = device.CreateDepthStencilState(depthStateDesc);


            MTKTextureLoader mTKTextureLoader = new MTKTextureLoader(device);

            // Texture
            NSUrl url = NSBundle.MainBundle.GetUrlForResource("cubemap2", "ktx");

            this.texture = mTKTextureLoader.FromUrl(url, new MTKTextureLoaderOptions(), out error);

            Console.WriteLine("Failed to created pipeline state, error {0}", error);

            MTLSamplerDescriptor samplerDescriptor = new MTLSamplerDescriptor()
            {
                MinFilter    = MTLSamplerMinMagFilter.Linear,
                MagFilter    = MTLSamplerMinMagFilter.Linear,
                MipFilter    = MTLSamplerMipFilter.Linear,
                SAddressMode = MTLSamplerAddressMode.ClampToEdge,
                TAddressMode = MTLSamplerAddressMode.ClampToEdge,
            };

            this.sampler = device.CreateSamplerState(samplerDescriptor);
        }
Exemple #7
0
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            // Set the view to use the default device
            device = MTLDevice.SystemDefault;

            if (device == null)
            {
                Console.WriteLine("Metal is not supported on this device");
                View = new NSView(View.Frame);
            }

            // Create a new command queue
            commandQueue = device.CreateCommandQueue();

            // Load all the shader files with a metal file extension in the project
            defaultLibrary = device.CreateDefaultLibrary();

            // Setup view
            mtkView          = (MTKView)View;
            mtkView.Delegate = this;
            mtkView.Device   = device;

            mtkView.SampleCount              = 1;
            mtkView.DepthStencilPixelFormat  = MTLPixelFormat.Depth32Float_Stencil8;
            mtkView.ColorPixelFormat         = MTLPixelFormat.BGRA8Unorm;
            mtkView.PreferredFramesPerSecond = 60;
            mtkView.ClearColor = new MTLClearColor(0.5f, 0.5f, 0.5f, 1.0f);

            // Load the vertex program into the library
            IMTLFunction vertexProgram = defaultLibrary.CreateFunction("mesh_vertex");

            // Load the fragment program into the library
            IMTLFunction fragmentProgram = defaultLibrary.CreateFunction("mesh_fragment");

            // Generate meshes
            MTKMeshBufferAllocator mtkBufferAllocator = new MTKMeshBufferAllocator(device);
            NSUrl     url       = NSBundle.MainBundle.GetUrlForResource("Fighter", "obj");
            MDLAsset  mdlAsset  = new MDLAsset(url, new MDLVertexDescriptor(), mtkBufferAllocator);
            MDLObject mdlObject = mdlAsset.GetObject(0);
            MDLMesh   mdlMesh   = mdlObject as MDLMesh;

            NSError error;

            objMesh = new MTKMesh(mdlMesh, device, out error);

            // Create a vertex descriptor from the MTKMesh
            MTLVertexDescriptor vertexDescriptor = MTLVertexDescriptor.FromModelIO(objMesh.VertexDescriptor);

            vertexDescriptor.Layouts[0].StepRate     = 1;
            vertexDescriptor.Layouts[0].StepFunction = MTLVertexStepFunction.PerVertex;

            this.clock = new System.Diagnostics.Stopwatch();
            clock.Start();

            this.view = CreateLookAt(new Vector3(0, 1, 2), new Vector3(0, 0, 0), Vector3.UnitY);
            var aspect = (float)(View.Bounds.Size.Width / View.Bounds.Size.Height);

            this.proj = Matrix4.CreatePerspectiveFieldOfView((float)Math.PI / 4, aspect, 0.1f, 100);

            this.constantBuffer = device.CreateBuffer((uint)Marshal.SizeOf(this.param), MTLResourceOptions.CpuCacheModeDefault);

            // Create a reusable pipeline state
            var pipelineStateDescriptor = new MTLRenderPipelineDescriptor
            {
                SampleCount                  = mtkView.SampleCount,
                VertexFunction               = vertexProgram,
                FragmentFunction             = fragmentProgram,
                VertexDescriptor             = vertexDescriptor,
                DepthAttachmentPixelFormat   = mtkView.DepthStencilPixelFormat,
                StencilAttachmentPixelFormat = mtkView.DepthStencilPixelFormat
            };

            pipelineStateDescriptor.ColorAttachments[0].PixelFormat = mtkView.ColorPixelFormat;

            pipelineState = device.CreateRenderPipelineState(pipelineStateDescriptor, out error);
            if (pipelineState == null)
            {
                Console.WriteLine("Failed to created pipeline state, error {0}", error);
            }

            var depthStateDesc = new MTLDepthStencilDescriptor
            {
                DepthCompareFunction = MTLCompareFunction.Less,
                DepthWriteEnabled    = true
            };

            depthState = device.CreateDepthStencilState(depthStateDesc);

            NSImage          image            = NSImage.ImageNamed("Fighter_Diffuse.jpg");
            MTKTextureLoader mTKTextureLoader = new MTKTextureLoader(device);

            this.texture = mTKTextureLoader.FromCGImage(image.CGImage, new MTKTextureLoaderOptions(), out error);

            MTLSamplerDescriptor samplerDescriptor = new MTLSamplerDescriptor()
            {
                MinFilter    = MTLSamplerMinMagFilter.Linear,
                MagFilter    = MTLSamplerMinMagFilter.Linear,
                SAddressMode = MTLSamplerAddressMode.ClampToEdge,
                TAddressMode = MTLSamplerAddressMode.ClampToEdge,
            };

            this.sampler = device.CreateSamplerState(samplerDescriptor);
        }
Exemple #8
0
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            // Set the view to use the default device
            device = MTLDevice.SystemDefault;

            if (device == null)
            {
                Console.WriteLine("Metal is not supported on this device");
                View = new NSView(View.Frame);
            }

            // Create a new command queue
            commandQueue = device.CreateCommandQueue();

            // Load all the shader files with a metal file extension in the project
            defaultLibrary = device.CreateDefaultLibrary();

            // Setup view
            mtkView          = (MTKView)View;
            mtkView.Delegate = this;
            mtkView.Device   = device;

            mtkView.SampleCount              = 1;
            mtkView.DepthStencilPixelFormat  = MTLPixelFormat.Depth32Float_Stencil8;
            mtkView.ColorPixelFormat         = MTLPixelFormat.BGRA8Unorm;
            mtkView.PreferredFramesPerSecond = 60;
            mtkView.ClearColor = new MTLClearColor(0.5f, 0.5f, 0.5f, 1.0f);

            // Load the vertex program into the library
            IMTLFunction vertexProgram = defaultLibrary.CreateFunction("cube_vertex");

            // Load the fragment program into the library
            IMTLFunction fragmentProgram = defaultLibrary.CreateFunction("cube_fragment");

            // Create a vertex descriptor from the MTKMesh
            MTLVertexDescriptor vertexDescriptor = new MTLVertexDescriptor();

            vertexDescriptor.Attributes[0].Format      = MTLVertexFormat.Float4;
            vertexDescriptor.Attributes[0].BufferIndex = 0;
            vertexDescriptor.Attributes[0].Offset      = 0;
            vertexDescriptor.Attributes[1].Format      = MTLVertexFormat.Float4;
            vertexDescriptor.Attributes[1].BufferIndex = 0;
            vertexDescriptor.Attributes[1].Offset      = 4 * sizeof(float);
            vertexDescriptor.Attributes[2].Format      = MTLVertexFormat.Float2;
            vertexDescriptor.Attributes[2].BufferIndex = 0;
            vertexDescriptor.Attributes[2].Offset      = 8 * sizeof(float);

            vertexDescriptor.Layouts[0].Stride = 10 * sizeof(float);

            vertexDescriptor.Layouts[0].StepRate     = 1;
            vertexDescriptor.Layouts[0].StepFunction = MTLVertexStepFunction.PerVertex;

            vertexBuffer = device.CreateBuffer(vertexData, MTLResourceOptions.CpuCacheModeDefault);// (MTLResourceOptions)0);

            this.clock = new System.Diagnostics.Stopwatch();
            clock.Start();

            this.view = CreateLookAt(new Vector3(0, 0, 5), new Vector3(0, 0, 0), Vector3.UnitY);
            var aspect = (float)(View.Bounds.Size.Width / View.Bounds.Size.Height);

            proj = Matrix4.CreatePerspectiveFieldOfView((float)Math.PI / 4, aspect, 0.1f, 100);

            constantBuffer = device.CreateBuffer(64, MTLResourceOptions.CpuCacheModeDefault);

            // Create a reusable pipeline state
            var pipelineStateDescriptor = new MTLRenderPipelineDescriptor
            {
                SampleCount                  = mtkView.SampleCount,
                VertexFunction               = vertexProgram,
                FragmentFunction             = fragmentProgram,
                VertexDescriptor             = vertexDescriptor,
                DepthAttachmentPixelFormat   = mtkView.DepthStencilPixelFormat,
                StencilAttachmentPixelFormat = mtkView.DepthStencilPixelFormat,
            };

            MTLRenderPipelineColorAttachmentDescriptor renderBufferAttachment = pipelineStateDescriptor.ColorAttachments[0];

            renderBufferAttachment.PixelFormat = mtkView.ColorPixelFormat;

            NSError error;

            pipelineState = device.CreateRenderPipelineState(pipelineStateDescriptor, out error);
            if (pipelineState == null)
            {
                Console.WriteLine("Failed to created pipeline state, error {0}", error);
            }

            var depthStateDesc = new MTLDepthStencilDescriptor
            {
                DepthCompareFunction = MTLCompareFunction.Less,
                DepthWriteEnabled    = true
            };

            depthState = device.CreateDepthStencilState(depthStateDesc);

            // Texture
            NSImage          image            = NSImage.ImageNamed("crate.png");
            MTKTextureLoader mTKTextureLoader = new MTKTextureLoader(device);

            this.texture = mTKTextureLoader.FromCGImage(image.CGImage, new MTKTextureLoaderOptions(), out error);

            MTLSamplerDescriptor samplerDescriptor = new MTLSamplerDescriptor()
            {
                MinFilter    = MTLSamplerMinMagFilter.Linear,
                MagFilter    = MTLSamplerMinMagFilter.Linear,
                SAddressMode = MTLSamplerAddressMode.Repeat,
                TAddressMode = MTLSamplerAddressMode.Repeat,
            };

            this.sampler = device.CreateSamplerState(samplerDescriptor);
        }