Merge pull request #1057 from cdavis5e/fix-1d-props

MVKDevice: Fix reported image format properties for 1D images as 2D.
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
index 5882132..227fe3e 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
@@ -144,8 +144,10 @@
     MVKImageMemoryBinding(MVKDevice* device, MVKImage* image, uint8_t planeIndex);
 
     MVKImage* _image;
+    id<MTLBuffer> _mtlTexelBuffer = nil;
+    NSUInteger _mtlTexelBufferOffset = 0;
     uint8_t _planeIndex;
-    bool _usesTexelBuffer;
+    bool _ownsTexelBuffer = false;
 };
 
 
@@ -368,6 +370,7 @@
 	bool _isAliasable;
 	bool _hasExtendedUsage;
 	bool _hasMutableFormat;
+	bool _isLinearForAtomics;
 };
 
 
@@ -588,6 +591,7 @@
 	 * This is a static function that can be used to validate image view formats prior to creating one.
 	 */
 	static VkResult validateSwizzledMTLPixelFormat(const VkImageViewCreateInfo* pCreateInfo,
+												   VkImageUsageFlags usage,
 												   MVKVulkanAPIObject* apiObject,
 												   bool hasNativeSwizzleSupport,
 												   bool hasShaderSwizzleSupport,
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index a50fb5a..d314f1e 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -51,10 +51,10 @@
                            newTextureWithDescriptor: mtlTexDesc
                            iosurface: _image->_ioSurface
                            plane: _planeIndex];
-        } else if (memoryBinding->_usesTexelBuffer) {
-            _mtlTexture = [memoryBinding->_deviceMemory->getMTLBuffer()
+        } else if (memoryBinding->_mtlTexelBuffer) {
+            _mtlTexture = [memoryBinding->_mtlTexelBuffer
                            newTextureWithDescriptor: mtlTexDesc
-                           offset: memoryBinding->getDeviceMemoryOffset()
+                           offset: memoryBinding->_mtlTexelBufferOffset
                            bytesPerRow: _subresources[0].layout.rowPitch];
         } else if (memoryBinding->_deviceMemory->getMTLHeap() && !_image->getIsDepthStencil()) {
             // Metal support for depth/stencil from heaps is flaky
@@ -306,7 +306,7 @@
 
 	MVKImageMemoryBinding* memBind = getMemoryBinding();
 	bool needsSync = memBind->needsHostReadSync(srcStageMask, dstStageMask, barrier);
-	bool needsPull = (!memBind->_usesTexelBuffer &&
+	bool needsPull = ((!memBind->_mtlTexelBuffer || memBind->_ownsTexelBuffer) &&
 					  memBind->isMemoryHostCoherent() &&
 					  barrier.newLayout == VK_IMAGE_LAYOUT_GENERAL &&
 					  mvkIsAnyFlagEnabled(barrier.dstAccessMask, (VK_ACCESS_HOST_READ_BIT | VK_ACCESS_MEMORY_READ_BIT)));
@@ -375,9 +375,10 @@
         case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS: {
             auto* dedicatedReqs = (VkMemoryDedicatedRequirements*)next;
             bool writable = mvkIsAnyFlagEnabled(_image->_usage, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
+            bool canUseTexelBuffer = _device->_pMetalFeatures->texelBuffers && _image->_isLinear && !_image->getIsCompressed();
             dedicatedReqs->requiresDedicatedAllocation = _requiresDedicatedMemoryAllocation;
             dedicatedReqs->prefersDedicatedAllocation = (dedicatedReqs->requiresDedicatedAllocation ||
-                                                        (!_usesTexelBuffer && (writable || !_device->_pMetalFeatures->placementHeaps)));
+                                                        (!canUseTexelBuffer && (writable || !_device->_pMetalFeatures->placementHeaps)));
             break;
         }
         default:
@@ -392,11 +393,34 @@
     if (_deviceMemory) { _deviceMemory->removeImageMemoryBinding(this); }
     MVKResource::bindDeviceMemory(mvkMem, memOffset);
 
-    _usesTexelBuffer = _device->_pMetalFeatures->texelBuffers && _deviceMemory && _deviceMemory->_mtlBuffer; // Texel buffers available
-    _usesTexelBuffer = _usesTexelBuffer && (isMemoryHostAccessible() || _device->_pMetalFeatures->placementHeaps) && _image->_isLinear && !_image->getIsCompressed(); // Applicable memory layout
+    bool usesTexelBuffer = _device->_pMetalFeatures->texelBuffers && _deviceMemory; // Texel buffers available
+    usesTexelBuffer = usesTexelBuffer && (isMemoryHostAccessible() || _device->_pMetalFeatures->placementHeaps) && _image->_isLinear && !_image->getIsCompressed(); // Applicable memory layout
 
     // macOS before 10.15.5 cannot use shared memory for texel buffers.
-    _usesTexelBuffer = _usesTexelBuffer && (_device->_pMetalFeatures->sharedLinearTextures || !isMemoryHostCoherent());
+    usesTexelBuffer = usesTexelBuffer && (_device->_pMetalFeatures->sharedLinearTextures || !isMemoryHostCoherent());
+
+    if (_image->_isLinearForAtomics) {
+        if (usesTexelBuffer && _deviceMemory->ensureMTLBuffer()) {
+            _mtlTexelBuffer = _deviceMemory->_mtlBuffer;
+            _mtlTexelBufferOffset = getDeviceMemoryOffset();
+        } else {
+            // Create our own buffer for this.
+            if (_deviceMemory && _deviceMemory->_mtlHeap && _image->getMTLStorageMode() == _deviceMemory->_mtlStorageMode) {
+                _mtlTexelBuffer = [_deviceMemory->_mtlHeap newBufferWithLength: _byteCount options: _deviceMemory->getMTLResourceOptions() offset: getDeviceMemoryOffset()];
+                if (_image->_isAliasable) { [_mtlTexelBuffer makeAliasable]; }
+            } else {
+                _mtlTexelBuffer = [getMTLDevice() newBufferWithLength: _byteCount options: _image->getMTLStorageMode() << MTLResourceStorageModeShift];
+            }
+            if (!_mtlTexelBuffer) {
+                return reportError(VK_ERROR_OUT_OF_DEVICE_MEMORY, "Could not create an MTLBuffer for an image that requires a buffer backing store. Images that can be used for atomic accesses must have a texel buffer backing them.");
+            }
+            _mtlTexelBufferOffset = 0;
+            _ownsTexelBuffer = true;
+        }
+    } else if (usesTexelBuffer && _deviceMemory->_mtlBuffer) {
+        _mtlTexelBuffer = _deviceMemory->_mtlBuffer;
+        _mtlTexelBufferOffset = getDeviceMemoryOffset();
+    }
 
     flushToDevice(getDeviceMemoryOffset(), getByteCount());
     return _deviceMemory ? _deviceMemory->addImageMemoryBinding(this) : VK_SUCCESS;
@@ -420,6 +444,9 @@
     for(uint8_t planeIndex = beginPlaneIndex(); planeIndex < endPlaneIndex(); planeIndex++) {
         _image->_planes[planeIndex]->propagateDebugName();
     }
+    if (_ownsTexelBuffer) {
+        setLabelIfNotNil(_mtlTexelBuffer, _image->_debugName);
+    }
 }
 
 // Returns whether the specified image memory barrier requires a sync between this
@@ -437,7 +464,7 @@
 #endif
 }
 
-bool MVKImageMemoryBinding::shouldFlushHostMemory() { return isMemoryHostAccessible() && !_usesTexelBuffer; }
+bool MVKImageMemoryBinding::shouldFlushHostMemory() { return isMemoryHostAccessible() && (!_mtlTexelBuffer || _ownsTexelBuffer); }
 
 // Flushes the device memory at the specified memory range into the MTLTexture. Updates
 // all subresources that overlap the specified range and are in an updatable layout state.
@@ -489,14 +516,12 @@
     return (_image->_memoryBindings.size() > 1) ? _planeIndex : (uint8_t)_image->_memoryBindings.size();
 }
 
-MVKImageMemoryBinding::MVKImageMemoryBinding(MVKDevice* device, MVKImage* image, uint8_t planeIndex) : MVKResource(device) {
-    _image = image;
-    _planeIndex = planeIndex;
-    _usesTexelBuffer = false;
+MVKImageMemoryBinding::MVKImageMemoryBinding(MVKDevice* device, MVKImage* image, uint8_t planeIndex) : MVKResource(device), _image(image), _planeIndex(planeIndex) {
 }
 
 MVKImageMemoryBinding::~MVKImageMemoryBinding() {
     if (_deviceMemory) { _deviceMemory->removeImageMemoryBinding(this); }
+    if (_ownsTexelBuffer) { [_mtlTexelBuffer release]; }
 }
 
 
@@ -809,6 +834,13 @@
 	_hasMutableFormat = mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT);
 	_hasExtendedUsage = mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_IMAGE_CREATE_EXTENDED_USAGE_BIT);
 
+    // If this is a storage image of format R32_UINT or R32_SINT, or MUTABLE_FORMAT is set
+    // and R32_UINT is in the set of possible view formats, then we must use a texel buffer,
+    // or image atomics won't work.
+    // TODO: Also add handling for VK_KHR_image_format_list here.
+    _isLinearForAtomics = _isLinear && mvkIsAnyFlagEnabled(_usage, VK_IMAGE_USAGE_STORAGE_BIT) &&
+                          ((_vkFormat == VK_FORMAT_R32_UINT || _vkFormat == VK_FORMAT_R32_SINT) ||
+                           (_hasMutableFormat && pixFmts->getViewClass(_vkFormat) == MVKMTLViewClass::Color32));
 	_is3DCompressed = (getImageType() == VK_IMAGE_TYPE_3D) && (pixFmts->getFormatType(pCreateInfo->format) == kMVKFormatCompressed) && !_device->_pMetalFeatures->native3DCompressedTextures;
 	_isDepthStencilAttachment = (mvkAreAllFlagsEnabled(pCreateInfo->usage, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ||
 								 mvkAreAllFlagsEnabled(pixFmts->getVkFormatProperties(pCreateInfo->format).optimalTilingFeatures, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT));
@@ -845,6 +877,16 @@
             memoryBinding->_byteCount += sizeAndAlign.size;
             memoryBinding->_byteAlignment = std::max(memoryBinding->_byteAlignment, (VkDeviceSize)sizeAndAlign.align);
             _isAliasable = mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_IMAGE_CREATE_ALIAS_BIT);
+        } else if (_isLinearForAtomics && _device->_pMetalFeatures->placementHeaps) {
+            NSUInteger bufferLength = 0;
+            for (uint32_t mipLvl = 0; mipLvl < _mipLevels; mipLvl++) {
+                VkExtent3D mipExtent = getExtent3D(planeIndex, mipLvl);
+                bufferLength += getBytesPerLayer(planeIndex, mipLvl) * mipExtent.depth * _arrayLayers;
+            }
+            MTLSizeAndAlign sizeAndAlign = [_device->getMTLDevice() heapBufferSizeAndAlignWithLength: bufferLength options: MTLResourceStorageModePrivate];
+            memoryBinding->_byteCount += sizeAndAlign.size;
+            memoryBinding->_byteAlignment = std::max(std::max(memoryBinding->_byteAlignment, _rowByteAlignment), (VkDeviceSize)sizeAndAlign.align);
+            _isAliasable = mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_IMAGE_CREATE_ALIAS_BIT);
         } else {
             for (uint32_t mipLvl = 0; mipLvl < _mipLevels; mipLvl++) {
                 VkExtent3D mipExtent = getExtent3D(planeIndex, mipLvl);
@@ -1366,6 +1408,7 @@
 
     bool useSwizzle;
 	_imageView->setConfigurationResult(_imageView->validateSwizzledMTLPixelFormat(pCreateInfo,
+																				  _imageView->_usage,
 																				  _imageView,
 																				  _device->_pMetalFeatures->nativeTextureSwizzle,
 																				  _device->_pMVKConfig->fullImageViewSwizzle,
@@ -1506,6 +1549,7 @@
 }
 
 VkResult MVKImageView::validateSwizzledMTLPixelFormat(const VkImageViewCreateInfo* pCreateInfo,
+													  VkImageUsageFlags usage,
 													  MVKVulkanAPIObject* apiObject,
 													  bool hasNativeSwizzleSupport,
 													  bool hasShaderSwizzleSupport,
@@ -1520,7 +1564,8 @@
 	// If we have an identity swizzle, we're all good.
 	if (SWIZZLE_MATCHES(R, G, B, A)) {
 		// Change to stencil-only format if only stencil aspect is requested
-		if (pCreateInfo->subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) {
+		if (pCreateInfo->subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT &&
+			mvkIsAnyFlagEnabled(usage, (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT))) {
 			if (mtlPixFmt == MTLPixelFormatDepth32Float_Stencil8)
 				mtlPixFmt = MTLPixelFormatX32_Stencil8;
 #if MVK_MACOS
@@ -1577,7 +1622,8 @@
 
 		case MTLPixelFormatDepth32Float_Stencil8:
 			// If aspect mask looking only for stencil then change to stencil-only format even if shader swizzling is needed
-			if (pCreateInfo->subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) {
+			if (pCreateInfo->subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT &&
+				mvkIsAnyFlagEnabled(usage, (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT))) {
 				mtlPixFmt = MTLPixelFormatX32_Stencil8;
 				if (SWIZZLE_MATCHES(R, ANY, ANY, ANY)) {
 					return VK_SUCCESS;
@@ -1588,7 +1634,8 @@
 #if MVK_MACOS
 		case MTLPixelFormatDepth24Unorm_Stencil8:
 			// If aspect mask looking only for stencil then change to stencil-only format even if shader swizzling is needed
-			if (pCreateInfo->subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) {
+			if (pCreateInfo->subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT &&
+				mvkIsAnyFlagEnabled(usage, (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT))) {
 				mtlPixFmt = MTLPixelFormatX24_Stencil8;
 				if (SWIZZLE_MATCHES(R, ANY, ANY, ANY)) {
 					return VK_SUCCESS;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h
index 72f8279..fa576ab 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h
@@ -318,6 +318,9 @@
 	/** Returns the Metal format capabilities supported by the specified Metal format. */
 	MVKMTLFmtCaps getCapabilities(MTLPixelFormat mtlFormat, bool isExtended = false);
 
+	/** Returns the Metal view class of the specified Vulkan format. */
+	MVKMTLViewClass getViewClass(VkFormat vkFormat);
+
 	/** Returns the Metal view class of the specified Metal format. */
 	MVKMTLViewClass getViewClass(MTLPixelFormat mtlFormat);
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm
index e9f03ac..d937e7b 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm
@@ -352,6 +352,10 @@
     return caps;
 }
 
+MVKMTLViewClass MVKPixelFormats::getViewClass(VkFormat vkFormat) {
+    return getMTLPixelFormatDesc(getVkFormatDesc(vkFormat).mtlPixelFormat).mtlViewClass;
+}
+
 MVKMTLViewClass MVKPixelFormats::getViewClass(MTLPixelFormat mtlFormat) {
     return getMTLPixelFormatDesc(mtlFormat).mtlViewClass;
 }
@@ -545,8 +549,7 @@
 	if (mvkIsAnyFlagEnabled(vkImageUsageFlags, (VK_IMAGE_USAGE_TRANSFER_SRC_BIT | 		// May use temp view if transfer involves format change
 		 										VK_IMAGE_USAGE_SAMPLED_BIT |
 												VK_IMAGE_USAGE_STORAGE_BIT |
-												VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT |
-												VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) &&
+												VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT)) &&
 		isCombinedDepthStencilFmt && supportsStencilViews) {
 
 		mvkEnableFlags(mtlUsage, MTLTextureUsagePixelFormatView);