Merge pull request #662 from billhollings/master

Multiple fixes to image clear and texture memory (CTS)
diff --git a/MoltenVK/MoltenVK/API/mvk_datatypes.h b/MoltenVK/MoltenVK/API/mvk_datatypes.h
index b2c59ba..a3e9792 100644
--- a/MoltenVK/MoltenVK/API/mvk_datatypes.h
+++ b/MoltenVK/MoltenVK/API/mvk_datatypes.h
@@ -464,8 +464,8 @@
 /** Returns the Metal CPU cache mode corresponding to the specified Vulkan memory flags. */
 MTLCPUCacheMode mvkMTLCPUCacheModeFromVkMemoryPropertyFlags(VkMemoryPropertyFlags vkFlags);
 
-/** Returns the Metal resource option flags corresponding to the specified Vulkan memory flags. */
-MTLResourceOptions mvkMTLResourceOptionsFromVkMemoryPropertyFlags(VkMemoryPropertyFlags vkFlags);
+/** Returns the Metal resource option flags corresponding to the Metal storage mode and cache mode. */
+MTLResourceOptions mvkMTLResourceOptions(MTLStorageMode mtlStorageMode, MTLCPUCacheMode mtlCPUCacheMode);
 
 
 #ifdef __cplusplus
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
index 237c9c3..c7b9087 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
@@ -1055,8 +1055,15 @@
     for (uint32_t i = 0; i < rangeCount; i++) {
         _subresourceRanges.push_back(pRanges[i]);
     }
+
+	// Validate
+	if ( !_image->getSupportsAllFormatFeatures(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) ) {
+		setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdClearImage(): Format %s cannot be cleared on this device.", mvkVkFormatName(_image->getVkFormat())));
+	}
 }
 void MVKCmdClearImage::encode(MVKCommandEncoder* cmdEncoder) {
+	if (getConfigurationResult()) { return; }
+
 	id<MTLTexture> imgMTLTex = _image->getMTLTexture();
     if ( !imgMTLTex ) { return; }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm
index 18da46f..f56cd86 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm
@@ -45,6 +45,12 @@
 	pMemoryRequirements->size = getByteCount();
 	pMemoryRequirements->alignment = _byteAlignment;
 	pMemoryRequirements->memoryTypeBits = _device->getPhysicalDevice()->getAllMemoryTypes();
+#if MVK_MACOS
+	// Textures must not use shared memory
+	if (mvkIsAnyFlagEnabled(_usage, VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT)) {
+		mvkDisableFlag(pMemoryRequirements->memoryTypeBits, _device->getPhysicalDevice()->getHostCoherentMemoryTypes());
+	}
+#endif
 #if MVK_IOS
 	// Memoryless storage is not allowed for buffers
 	mvkDisableFlag(pMemoryRequirements->memoryTypeBits, _device->getPhysicalDevice()->getLazilyAllocatedMemoryTypes());
@@ -160,17 +166,9 @@
                                                                                               width: _textureSize.width
                                                                                              height: _textureSize.height
                                                                                           mipmapped: NO];
-#if MVK_MACOS
-        // Textures on Mac cannot use shared storage, so force managed.
-        if (_buffer->getMTLBuffer().storageMode == MTLStorageModeShared) {
-            mtlTexDesc.storageMode = MTLStorageModeManaged;
-        } else {
-            mtlTexDesc.storageMode = _buffer->getMTLBuffer().storageMode;
-        }
-#else
-        mtlTexDesc.storageMode = _buffer->getMTLBuffer().storageMode;
-#endif
-        mtlTexDesc.cpuCacheMode = _buffer->getMTLBuffer().cpuCacheMode;
+		id<MTLBuffer> mtlBuff = _buffer->getMTLBuffer();
+		mtlTexDesc.storageMode = mtlBuff.storageMode;
+        mtlTexDesc.cpuCacheMode = mtlBuff.cpuCacheMode;
         mtlTexDesc.usage = MTLTextureUsageShaderRead;
         if ( mvkIsAnyFlagEnabled(_buffer->getUsage(), VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT) ) {
             mtlTexDesc.usage |= MTLTextureUsageShaderWrite;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h
index 5c13988..c87b443 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h
@@ -101,7 +101,7 @@
 	inline MTLCPUCacheMode getMTLCPUCacheMode() { return _mtlCPUCacheMode; }
 
 	/** Returns the Metal reource options used by this memory allocation. */
-	inline MTLResourceOptions getMTLResourceOptions() { return _mtlResourceOptions; }
+	inline MTLResourceOptions getMTLResourceOptions() { return mvkMTLResourceOptions(_mtlStorageMode, _mtlCPUCacheMode); }
 
 
 #pragma mark Construction
@@ -139,7 +139,6 @@
 	void* _pHostMemory = nullptr;
 	bool _isMapped = false;
 	bool _isDedicated = false;
-	MTLResourceOptions _mtlResourceOptions;
 	MTLStorageMode _mtlStorageMode;
 	MTLCPUCacheMode _mtlCPUCacheMode;
 };
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm
index b179646..ee4aedf 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm
@@ -163,10 +163,10 @@
 
 	// If host memory was already allocated, it is copied into the new MTLBuffer, and then released.
 	if (_pHostMemory) {
-		_mtlBuffer = [getMTLDevice() newBufferWithBytes: _pHostMemory length: memLen options: _mtlResourceOptions];     // retained
+		_mtlBuffer = [getMTLDevice() newBufferWithBytes: _pHostMemory length: memLen options: getMTLResourceOptions()];     // retained
 		freeHostMemory();
 	} else {
-		_mtlBuffer = [getMTLDevice() newBufferWithLength: memLen options: _mtlResourceOptions];     // retained
+		_mtlBuffer = [getMTLDevice() newBufferWithLength: memLen options: getMTLResourceOptions()];     // retained
 	}
 	_pMemory = isMemoryHostAccessible() ? _mtlBuffer.contents : nullptr;
 
@@ -210,7 +210,6 @@
 								 const VkAllocationCallbacks* pAllocator) : MVKVulkanAPIDeviceObject(device) {
 	// Set Metal memory parameters
 	VkMemoryPropertyFlags vkMemProps = _device->_pMemoryProperties->memoryTypes[pAllocateInfo->memoryTypeIndex].propertyFlags;
-	_mtlResourceOptions = mvkMTLResourceOptionsFromVkMemoryPropertyFlags(vkMemProps);
 	_mtlStorageMode = mvkMTLStorageModeFromVkMemoryPropertyFlags(vkMemProps);
 	_mtlCPUCacheMode = mvkMTLCPUCacheModeFromVkMemoryPropertyFlags(vkMemProps);
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
index 7403d03..0c5e3bf 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
@@ -61,9 +61,21 @@
     /** Returns the Vulkan image format of this image. */
     VkFormat getVkFormat();
 
-	/** Returns whether this texture is compressed. */
+	/** Returns whether this image is compressed. */
 	bool getIsCompressed();
 
+	/**
+	 * Returns whether the format of this image supports ANY of the indicated feature flags,
+	 * taking into consideration whether this image is using linear or optimal tiling.
+	 */
+	bool getSupportsAnyFormatFeature(VkFormatFeatureFlags requiredFormatFeatureFlags);
+
+	/**
+	 * Returns whether the format of this image supports ALL of the indicated feature flags,
+	 * taking into consideration whether this image is using linear or optimal tiling.
+	 */
+	bool getSupportsAllFormatFeatures(VkFormatFeatureFlags requiredFormatFeatureFlags);
+
 	/** 
 	 * Returns the 3D extent of this image at the base mipmap level.
 	 * For 2D or cube images, the Z component will be 1.  
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index c08c036..de49bb3 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -45,6 +45,20 @@
 	return mvkFormatTypeFromMTLPixelFormat(_mtlPixelFormat) == kMVKFormatCompressed;
 }
 
+bool MVKImage::getSupportsAnyFormatFeature(VkFormatFeatureFlags requiredFormatFeatureFlags) {
+	VkFormatProperties props;
+	_device->getPhysicalDevice()->getFormatProperties(getVkFormat(), &props);
+	VkFormatFeatureFlags imageFeatureFlags = _isLinear ? props.linearTilingFeatures : props.optimalTilingFeatures;
+	return mvkIsAnyFlagEnabled(imageFeatureFlags, requiredFormatFeatureFlags);
+}
+
+bool MVKImage::getSupportsAllFormatFeatures(VkFormatFeatureFlags requiredFormatFeatureFlags) {
+	VkFormatProperties props;
+	_device->getPhysicalDevice()->getFormatProperties(getVkFormat(), &props);
+	VkFormatFeatureFlags imageFeatureFlags = _isLinear ? props.linearTilingFeatures : props.optimalTilingFeatures;
+	return mvkAreAllFlagsEnabled(imageFeatureFlags, requiredFormatFeatureFlags);
+}
+
 VkExtent3D MVKImage::getExtent3D(uint32_t mipLevel) {
 	return mvkMipmapLevelSizeFromBaseSize3D(_extent, mipLevel);
 }
@@ -161,9 +175,8 @@
 										   ? _device->getPhysicalDevice()->getPrivateMemoryTypes()
 										   : _device->getPhysicalDevice()->getAllMemoryTypes());
 #if MVK_MACOS
-	if (!_isLinear) {  // XXX Linear images must support host-coherent memory
-		mvkDisableFlag(pMemoryRequirements->memoryTypeBits, _device->getPhysicalDevice()->getHostCoherentMemoryTypes());
-	}
+	// Textures must not use shared memory
+	mvkDisableFlag(pMemoryRequirements->memoryTypeBits, _device->getPhysicalDevice()->getHostCoherentMemoryTypes());
 #endif
 #if MVK_IOS
 	// Only transient attachments may use memoryless storage
@@ -388,13 +401,10 @@
 		mvkDisableFlag(usage, MTLTextureUsagePixelFormatView);
 	}
 
-	// If this format doesn't support being blitted to, and the usage
-	// doesn't specify use as an attachment, turn off
-	// MTLTextureUsageRenderTarget.
-	VkFormatProperties props;
-	_device->getPhysicalDevice()->getFormatProperties(getVkFormat(), &props);
-	if (!mvkAreAllFlagsEnabled(_isLinear ? props.linearTilingFeatures : props.optimalTilingFeatures, VK_FORMAT_FEATURE_BLIT_DST_BIT) &&
-		!mvkIsAnyFlagEnabled(_usage, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT|VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
+	// If this format doesn't support being rendered to, disable MTLTextureUsageRenderTarget.
+	if ( !getSupportsAnyFormatFeature(VK_FORMAT_FEATURE_BLIT_DST_BIT |
+									  VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT |
+									  VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) ) {
 		mvkDisableFlag(usage, MTLTextureUsageRenderTarget);
 	}
 
diff --git a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm
index 4e4390e..b58a603 100644
--- a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm
+++ b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm
@@ -1395,44 +1395,9 @@
 	return MTLCPUCacheModeDefaultCache;
 }
 
-MVK_PUBLIC_SYMBOL MTLResourceOptions mvkMTLResourceOptionsFromVkMemoryPropertyFlags(VkMemoryPropertyFlags vkFlags) {
-	MTLResourceOptions mtlFlags = 0;
-
-	// First set the resource CPU cache mode
-	MTLCPUCacheMode mtlCPUMode = mvkMTLCPUCacheModeFromVkMemoryPropertyFlags(vkFlags);
-	switch (mtlCPUMode) {
-		case MTLCPUCacheModeDefaultCache:
-			mvkEnableFlag(mtlFlags, MTLResourceCPUCacheModeDefaultCache);
-			break;
-		case MTLCPUCacheModeWriteCombined:
-			mvkEnableFlag(mtlFlags, MTLResourceCPUCacheModeWriteCombined);
-			break;
-	}
-
-	// Then set the resource storage mode
-	MTLStorageMode mtlStgMode = mvkMTLStorageModeFromVkMemoryPropertyFlags(vkFlags);
-	switch (mtlStgMode) {
-		case MTLStorageModePrivate:
-			mvkEnableFlag(mtlFlags, MTLResourceStorageModePrivate);
-			break;
-		case MTLStorageModeShared:
-			mvkEnableFlag(mtlFlags, MTLResourceStorageModeShared);
-			break;
-#if MVK_MACOS
-		case MTLStorageModeManaged:
-			mvkEnableFlag(mtlFlags, MTLResourceStorageModeManaged);
-			break;
-#endif
-#if MVK_IOS
-		case MTLStorageModeMemoryless:
-			mvkEnableFlag(mtlFlags, MTLResourceStorageModeMemoryless);
-			break;
-#endif
-		default:		// Silence erroneous -Wswitch-enum warning on MTLResourceStorageModeManaged under iOS
-			break;
-	}
-
-	return mtlFlags;
+MVK_PUBLIC_SYMBOL MTLResourceOptions mvkMTLResourceOptions(MTLStorageMode mtlStorageMode,
+														   MTLCPUCacheMode mtlCPUCacheMode) {
+	return (mtlStorageMode << MTLResourceStorageModeShift) | (mtlCPUCacheMode << MTLResourceCPUCacheModeShift);
 }