Use native texture buffers when available.

These were added in Metal 2.1.
diff --git a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
index 7365f17..27bac91 100644
--- a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
+++ b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
@@ -538,6 +538,7 @@
 	VkBool32 memoryBarriers;					/**< If true, full memory barriers within Metal render passes are supported. */
 	VkBool32 multisampleLayeredRendering;       /**< If true, layered rendering to multiple multi-sampled cube or texture array layers is supported. */
 	VkBool32 stencilFeedback;					/**< If true, fragment shaders that write to [[stencil]] outputs are supported. */
+	VkBool32 textureBuffers;					/**< If true, textures of type MTLTextureTypeBuffer are supported. */
 } MVKPhysicalDeviceMetalFeatures;
 
 /**
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm
index f56cd86..e60dab4 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm
@@ -162,16 +162,25 @@
 		lock_guard<mutex> lock(_lock);
 		if (_mtlTexture) { return _mtlTexture; }
 
-        MTLTextureDescriptor* mtlTexDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: _mtlPixelFormat
-                                                                                              width: _textureSize.width
-                                                                                             height: _textureSize.height
-                                                                                          mipmapped: NO];
-		id<MTLBuffer> mtlBuff = _buffer->getMTLBuffer();
-		mtlTexDesc.storageMode = mtlBuff.storageMode;
-        mtlTexDesc.cpuCacheMode = mtlBuff.cpuCacheMode;
-        mtlTexDesc.usage = MTLTextureUsageShaderRead;
+        MTLTextureUsage usage = MTLTextureUsageShaderRead;
         if ( mvkIsAnyFlagEnabled(_buffer->getUsage(), VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT) ) {
-            mtlTexDesc.usage |= MTLTextureUsageShaderWrite;
+            usage |= MTLTextureUsageShaderWrite;
+        }
+        id<MTLBuffer> mtlBuff = _buffer->getMTLBuffer();
+        MTLTextureDescriptor* mtlTexDesc;
+        if ( _device->_pMetalFeatures->textureBuffers ) {
+            mtlTexDesc = [MTLTextureDescriptor textureBufferDescriptorWithPixelFormat: _mtlPixelFormat
+                                                                                width: _textureSize.width
+                                                                      resourceOptions: (mtlBuff.cpuCacheMode << MTLResourceCPUCacheModeShift) | (mtlBuff.storageMode << MTLResourceStorageModeShift)
+                                                                                usage: usage];
+        } else {
+            mtlTexDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: _mtlPixelFormat
+                                                                            width: _textureSize.width
+                                                                           height: _textureSize.height
+                                                                        mipmapped: NO];
+            mtlTexDesc.storageMode = mtlBuff.storageMode;
+            mtlTexDesc.cpuCacheMode = mtlBuff.cpuCacheMode;
+            mtlTexDesc.usage = usage;
         }
 		_mtlTexture = [_buffer->getMTLBuffer() newTextureWithDescriptor: mtlTexDesc
 																 offset: _mtlBufferOffset
@@ -197,17 +206,24 @@
     if (byteCount == VK_WHOLE_SIZE) { byteCount = _buffer->getByteCount() - pCreateInfo->offset; }    // Remaining bytes in buffer
     size_t blockCount = byteCount / bytesPerBlock;
 
-	// But Metal requires the texture to be a 2D texture. Determine the number of 2D rows we need and their width.
-	// Multiple rows will automatically align with PoT max texture dimension, but need to align upwards if less than full single row.
-	size_t maxBlocksPerRow = _device->_pMetalFeatures->maxTextureDimension / fmtBlockSize.width;
-	size_t blocksPerRow = min(blockCount, maxBlocksPerRow);
-	_mtlBytesPerRow = mvkAlignByteOffset(blocksPerRow * bytesPerBlock, _device->getVkFormatTexelBufferAlignment(pCreateInfo->format, this));
+	if ( !_device->_pMetalFeatures->textureBuffers ) {
+		// But Metal requires the texture to be a 2D texture. Determine the number of 2D rows we need and their width.
+		// Multiple rows will automatically align with PoT max texture dimension, but need to align upwards if less than full single row.
+		size_t maxBlocksPerRow = _device->_pMetalFeatures->maxTextureDimension / fmtBlockSize.width;
+		size_t blocksPerRow = min(blockCount, maxBlocksPerRow);
+		_mtlBytesPerRow = mvkAlignByteOffset(blocksPerRow * bytesPerBlock, _device->getVkFormatTexelBufferAlignment(pCreateInfo->format, this));
 
-	size_t rowCount = blockCount / blocksPerRow;
-	if (blockCount % blocksPerRow) { rowCount++; }
+		size_t rowCount = blockCount / blocksPerRow;
+		if (blockCount % blocksPerRow) { rowCount++; }
 
-	_textureSize.width = uint32_t(blocksPerRow * fmtBlockSize.width);
-	_textureSize.height = uint32_t(rowCount * fmtBlockSize.height);
+		_textureSize.width = uint32_t(blocksPerRow * fmtBlockSize.width);
+		_textureSize.height = uint32_t(rowCount * fmtBlockSize.height);
+	} else {
+		// With native texture buffers we don't need to bother with any of that.
+		// We can just use a simple 1D texel array.
+		_textureSize.width = uint32_t(blockCount * fmtBlockSize.width);
+		_textureSize.height = 1;
+	}
 
     if ( !_device->_pMetalFeatures->texelBuffers ) {
         setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "Texel buffers are not supported on this device."));
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index 30c2a4a..4334aa2 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -737,6 +737,7 @@
 	if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v5] ) {
 		_metalFeatures.mslVersionEnum = MTLLanguageVersion2_1;
 		MVK_SET_FROM_ENV_OR_BUILD_BOOL(_metalFeatures.events, MVK_ALLOW_METAL_EVENTS);
+		_metalFeatures.textureBuffers = true;
 	}
 
 	if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v1] ) {
@@ -796,6 +797,7 @@
         _metalFeatures.multisampleArrayTextures = true;
 		MVK_SET_FROM_ENV_OR_BUILD_BOOL(_metalFeatures.events, MVK_ALLOW_METAL_EVENTS);
         _metalFeatures.memoryBarriers = true;
+        _metalFeatures.textureBuffers = true;
     }
 
 	if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily2_v1] ) {
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
index abbdc49..84fa930 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
@@ -1129,6 +1129,7 @@
 
     shaderContext.options.mslOptions.msl_version = _device->_pMetalFeatures->mslVersion;
     shaderContext.options.mslOptions.texel_buffer_texture_width = _device->_pMetalFeatures->maxTextureDimension;
+	shaderContext.options.mslOptions.texture_buffer_native = _device->_pMetalFeatures->textureBuffers;
 
     MVKPipelineLayout* layout = (MVKPipelineLayout*)pCreateInfo->layout;
     layout->populateShaderConverterContext(shaderContext);
@@ -1321,6 +1322,7 @@
     shaderContext.options.mslOptions.msl_version = _device->_pMetalFeatures->mslVersion;
     shaderContext.options.mslOptions.texel_buffer_texture_width = _device->_pMetalFeatures->maxTextureDimension;
 	shaderContext.options.mslOptions.swizzle_texture_samples = _fullImageViewSwizzle;
+	shaderContext.options.mslOptions.texture_buffer_native = _device->_pMetalFeatures->textureBuffers;
 
     MVKPipelineLayout* layout = (MVKPipelineLayout*)pCreateInfo->layout;
     layout->populateShaderConverterContext(shaderContext);