Merge pull request #1382 from billhollings/coherent-texture-flushing

Reorganize coherent texture flushing on memory map and unmap.
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
index 7d1d379..1aa38f2 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
@@ -88,8 +88,6 @@
     VkBufferImageCopy vkDstCopies[copyCnt];
     size_t tmpBuffSize = 0;
 
-    _srcImage->flushToDevice(0, VK_WHOLE_SIZE);
-
     for (uint32_t copyIdx = 0; copyIdx < copyCnt; copyIdx++) {
         auto& vkIC = _vkImageCopies[copyIdx];
         
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index 6ca2971..a1bc0d0 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -3616,7 +3616,7 @@
 		for (uint32_t i = 0; i < memRangeCount; i++) {
 			const VkMappedMemoryRange* pMem = &pMemRanges[i];
 			MVKDeviceMemory* mvkMem = (MVKDeviceMemory*)pMem->memory;
-			VkResult r = mvkMem->pullFromDevice(pMem->offset, pMem->size, false, &mvkBlitEnc);
+			VkResult r = mvkMem->pullFromDevice(pMem->offset, pMem->size, &mvkBlitEnc);
 			if (rslt == VK_SUCCESS) { rslt = r; }
 		}
 		if (mvkBlitEnc.mtlBlitEncoder) { [mvkBlitEnc.mtlBlitEncoder endEncoding]; }
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h
index b08aa46..953e3b8 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h
@@ -93,17 +93,11 @@
 	/** Returns whether this device memory is currently mapped to host memory. */
 	bool isMapped() { return _mappedRange.size > 0; }
 
-	/**
-	 * If this memory is host-visible, the specified memory range is flushed to the device.
-	 * Normally, flushing will only occur if the device memory is non-coherent, but flushing
-	 * to coherent memory can be forced by setting evenIfCoherent to true.
-	 */
-	VkResult flushToDevice(VkDeviceSize offset, VkDeviceSize size, bool evenIfCoherent = false);
+	/** If this memory is host-visible, the specified memory range is flushed to the device. */
+	VkResult flushToDevice(VkDeviceSize offset, VkDeviceSize size);
 
 	/**
 	 * If this memory is host-visible, pulls the specified memory range from the device.
-	 * Normally, pulling will only occur if the device memory is non-coherent, but pulling
-	 * to coherent memory can be forced by setting evenIfCoherent to true.
 	 *
 	 * If pBlitEnc is not null, it points to a holder for a MTLBlitCommandEncoder and its
 	 * associated MTLCommandBuffer. If this instance has a MTLBuffer using managed memory,
@@ -114,7 +108,6 @@
 	 */
 	VkResult pullFromDevice(VkDeviceSize offset,
 							VkDeviceSize size,
-							bool evenIfCoherent = false,
 							MVKMTLBlitEncoder* pBlitEnc = nullptr);
 
 
@@ -172,8 +165,10 @@
 	id<MTLHeap> _mtlHeap = nil;
 	void* _pMemory = nullptr;
 	void* _pHostMemory = nullptr;
-	bool _isDedicated = false;
+	VkMemoryPropertyFlags _vkMemProps;
 	MTLStorageMode _mtlStorageMode;
 	MTLCPUCacheMode _mtlCPUCacheMode;
+	bool _isDedicated = false;
+
 };
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm
index eac900f..25686df 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm
@@ -55,8 +55,11 @@
 
 	*ppData = (void*)((uintptr_t)_pMemory + offset);
 
-	// Coherent memory does not require flushing by app, so we must flush now, to handle any texture updates.
-	pullFromDevice(offset, size, isMemoryHostCoherent());
+	// Coherent memory does not require flushing by app, so we must flush now
+	// to support Metal textures that actually reside in non-coherent memory.
+	if (mvkIsAnyFlagEnabled(_vkMemProps, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
+		pullFromDevice(offset, size);
+	}
 
 	return VK_SUCCESS;
 }
@@ -68,54 +71,57 @@
 		return;
 	}
 
-	// Coherent memory does not require flushing by app, so we must flush now.
-	flushToDevice(_mappedRange.offset, _mappedRange.size, isMemoryHostCoherent());
+	// Coherent memory does not require flushing by app, so we must flush now
+	// to support Metal textures that actually reside in non-coherent memory.
+	if (mvkIsAnyFlagEnabled(_vkMemProps, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
+		flushToDevice(_mappedRange.offset, _mappedRange.size);
+	}
 
 	_mappedRange.offset = 0;
 	_mappedRange.size = 0;
 }
 
-VkResult MVKDeviceMemory::flushToDevice(VkDeviceSize offset, VkDeviceSize size, bool evenIfCoherent) {
-	// Coherent memory is flushed on unmap(), so it is only flushed if forced
+VkResult MVKDeviceMemory::flushToDevice(VkDeviceSize offset, VkDeviceSize size) {
 	VkDeviceSize memSize = adjustMemorySize(size, offset);
-	if (memSize > 0 && isMemoryHostAccessible() && (evenIfCoherent || !isMemoryHostCoherent()) ) {
+	if (memSize == 0 || !isMemoryHostAccessible()) { return VK_SUCCESS; }
 
 #if MVK_MACOS
-		if (_mtlBuffer && _mtlStorageMode == MTLStorageModeManaged) {
-			[_mtlBuffer didModifyRange: NSMakeRange(offset, memSize)];
-		}
+	if (_mtlBuffer && _mtlStorageMode == MTLStorageModeManaged) {
+		[_mtlBuffer didModifyRange: NSMakeRange(offset, memSize)];
+	}
 #endif
 
-		// If we have an MTLHeap object, there's no need to sync memory manually between images and the buffer.
-		if (!_mtlHeap) {
-			lock_guard<mutex> lock(_rezLock);
-			for (auto& img : _imageMemoryBindings) { img->flushToDevice(offset, memSize); }
-			for (auto& buf : _buffers) { buf->flushToDevice(offset, memSize); }
-		}
+	// If we have an MTLHeap object, there's no need to sync memory manually between resources and the buffer.
+	if ( !_mtlHeap ) {
+		lock_guard<mutex> lock(_rezLock);
+		for (auto& img : _imageMemoryBindings) { img->flushToDevice(offset, memSize); }
+		for (auto& buf : _buffers) { buf->flushToDevice(offset, memSize); }
 	}
+
 	return VK_SUCCESS;
 }
 
 VkResult MVKDeviceMemory::pullFromDevice(VkDeviceSize offset,
 										 VkDeviceSize size,
-										 bool evenIfCoherent,
 										 MVKMTLBlitEncoder* pBlitEnc) {
-	// Coherent memory is flushed on unmap(), so it is only flushed if forced
     VkDeviceSize memSize = adjustMemorySize(size, offset);
-	if (memSize > 0 && isMemoryHostAccessible() && (evenIfCoherent || !isMemoryHostCoherent()) && !_mtlHeap) {
+	if (memSize == 0 || !isMemoryHostAccessible()) { return VK_SUCCESS; }
+
+#if MVK_MACOS
+	if (pBlitEnc && _mtlBuffer && _mtlStorageMode == MTLStorageModeManaged) {
+		if ( !pBlitEnc->mtlCmdBuffer) { pBlitEnc->mtlCmdBuffer = _device->getAnyQueue()->getMTLCommandBuffer(kMVKCommandUseInvalidateMappedMemoryRanges); }
+		if ( !pBlitEnc->mtlBlitEncoder) { pBlitEnc->mtlBlitEncoder = [pBlitEnc->mtlCmdBuffer blitCommandEncoder]; }
+		[pBlitEnc->mtlBlitEncoder synchronizeResource: _mtlBuffer];
+	}
+#endif
+
+	// If we have an MTLHeap object, there's no need to sync memory manually between resources and the buffer.
+	if ( !_mtlHeap ) {
 		lock_guard<mutex> lock(_rezLock);
         for (auto& img : _imageMemoryBindings) { img->pullFromDevice(offset, memSize); }
         for (auto& buf : _buffers) { buf->pullFromDevice(offset, memSize); }
-
-#if MVK_MACOS
-		if (pBlitEnc && _mtlBuffer && _mtlStorageMode == MTLStorageModeManaged) {
-			if ( !pBlitEnc->mtlCmdBuffer) { pBlitEnc->mtlCmdBuffer = _device->getAnyQueue()->getMTLCommandBuffer(kMVKCommandUseInvalidateMappedMemoryRanges); }
-			if ( !pBlitEnc->mtlBlitEncoder) { pBlitEnc->mtlBlitEncoder = [pBlitEnc->mtlCmdBuffer blitCommandEncoder]; }
-			[pBlitEnc->mtlBlitEncoder synchronizeResource: _mtlBuffer];
-		}
-#endif
-
 	}
+
 	return VK_SUCCESS;
 }
 
@@ -271,9 +277,9 @@
 								 const VkMemoryAllocateInfo* pAllocateInfo,
 								 const VkAllocationCallbacks* pAllocator) : MVKVulkanAPIDeviceObject(device) {
 	// Set Metal memory parameters
-	VkMemoryPropertyFlags vkMemProps = _device->_pMemoryProperties->memoryTypes[pAllocateInfo->memoryTypeIndex].propertyFlags;
-	_mtlStorageMode = mvkMTLStorageModeFromVkMemoryPropertyFlags(vkMemProps);
-	_mtlCPUCacheMode = mvkMTLCPUCacheModeFromVkMemoryPropertyFlags(vkMemProps);
+	_vkMemProps = _device->_pMemoryProperties->memoryTypes[pAllocateInfo->memoryTypeIndex].propertyFlags;
+	_mtlStorageMode = mvkMTLStorageModeFromVkMemoryPropertyFlags(_vkMemProps);
+	_mtlCPUCacheMode = mvkMTLCPUCacheModeFromVkMemoryPropertyFlags(_vkMemProps);
 
 	_allocationSize = pAllocateInfo->allocationSize;