Merge pull request #666 from cdavis5e/swapchain-colorspace

Support the VK_EXT_swapchain_colorspace extension.
diff --git a/Docs/MoltenVK_Runtime_UserGuide.md b/Docs/MoltenVK_Runtime_UserGuide.md
index 9e9149d..4d891cc 100644
--- a/Docs/MoltenVK_Runtime_UserGuide.md
+++ b/Docs/MoltenVK_Runtime_UserGuide.md
@@ -247,6 +247,7 @@
 - `VK_KHR_surface`
 - `VK_KHR_swapchain`
 - `VK_KHR_swapchain_mutable_format`
+- `VK_KHR_uniform_buffer_standard_layout`
 - `VK_KHR_variable_pointers`
 - `VK_EXT_debug_marker`
 - `VK_EXT_debug_report`
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 2465667..4e8357b 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -93,6 +93,11 @@
                     f16Features->shaderInt8 = true;
                     break;
                 }
+                case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR: {
+                    auto* uboLayoutFeatures = (VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR*)next;
+                    uboLayoutFeatures->uniformBufferStandardLayout = true;
+                    break;
+                }
                 case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES: {
                     auto* varPtrFeatures = (VkPhysicalDeviceVariablePointerFeatures*)next;
                     varPtrFeatures->variablePointersStorageBuffer = true;
@@ -757,6 +762,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] ) {
@@ -816,6 +822,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);
diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.def b/MoltenVK/MoltenVK/Layers/MVKExtensions.def
index e167ced..6f8143d 100644
--- a/MoltenVK/MoltenVK/Layers/MVKExtensions.def
+++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.def
@@ -51,6 +51,7 @@
 MVK_EXTENSION(KHR_surface, KHR_SURFACE)
 MVK_EXTENSION(KHR_swapchain, KHR_SWAPCHAIN)
 MVK_EXTENSION(KHR_swapchain_mutable_format, KHR_SWAPCHAIN_MUTABLE_FORMAT)
+MVK_EXTENSION(KHR_uniform_buffer_standard_layout, KHR_UNIFORM_BUFFER_STANDARD_LAYOUT)
 MVK_EXTENSION(KHR_variable_pointers, KHR_VARIABLE_POINTERS)
 MVK_EXTENSION(EXT_debug_marker, EXT_DEBUG_MARKER)
 MVK_EXTENSION(EXT_debug_report, EXT_DEBUG_REPORT)