Merge pull request #596 from billhollings/master

Allow mvkMTLRenderStagesFromVkPipelineStageFlags() to map to all Vulkan stages, by indicating whether the pipeline barrier should come before or after the stages.
diff --git a/MoltenVK/MoltenVK/API/mvk_datatypes.h b/MoltenVK/MoltenVK/API/mvk_datatypes.h
index 41cbf7a..b2c59ba 100644
--- a/MoltenVK/MoltenVK/API/mvk_datatypes.h
+++ b/MoltenVK/MoltenVK/API/mvk_datatypes.h
@@ -402,8 +402,11 @@
 /** Returns the Metal MTLTessellationPartitionMode corresponding to the specified SPIR-V spv::ExecutionMode. */
 MTLTessellationPartitionMode mvkMTLTessellationPartitionModeFromSpvExecutionMode(uint32_t spvMode);
 
-/** Returns the combination of Metal MTLRenderStage bits corresponding to the specified Vulkan VkPiplineStageFlags. */
-MTLRenderStages mvkMTLRenderStagesFromVkPipelineStageFlags(VkPipelineStageFlags vkStages);
+/**
+ * Returns the combination of Metal MTLRenderStage bits corresponding to the specified Vulkan VkPiplineStageFlags,
+ * taking into consideration whether the barrier is to be placed before or after the specified pipeline stages.
+ */
+MTLRenderStages mvkMTLRenderStagesFromVkPipelineStageFlags(VkPipelineStageFlags vkStages, bool placeBarrierBefore);
 
 /** Returns the combination of Metal MTLBarrierScope bits corresponding to the specified Vulkan VkAccessFlags. */
 MTLBarrierScope mvkMTLBarrierScopeFromVkAccessFlags(VkAccessFlags vkAccess);
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
index 5ce849e..3931a70 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
@@ -65,10 +65,11 @@
 void MVKCmdPipelineBarrier::encode(MVKCommandEncoder* cmdEncoder) {
 
 #if MVK_MACOS
-    // Calls below invoke MTLBlitCommandEncoder so must apply this first
-	if ( getDevice()->_pMetalFeatures->memoryBarriers ) {
-		MTLRenderStages srcStages = mvkMTLRenderStagesFromVkPipelineStageFlags(_srcStageMask);
-		MTLRenderStages dstStages = mvkMTLRenderStagesFromVkPipelineStageFlags(_dstStageMask);
+    // Calls below invoke MTLBlitCommandEncoder so must apply this first.
+	// Check if pipeline barriers are available and we are in a renderpass.
+	if (getDevice()->_pMetalFeatures->memoryBarriers && cmdEncoder->_mtlRenderEncoder) {
+		MTLRenderStages srcStages = mvkMTLRenderStagesFromVkPipelineStageFlags(_srcStageMask, false);
+		MTLRenderStages dstStages = mvkMTLRenderStagesFromVkPipelineStageFlags(_dstStageMask, true);
 		for (auto& mb : _memoryBarriers) {
 			MTLBarrierScope scope = mvkMTLBarrierScopeFromVkAccessFlags(mb.dstAccessMask);
 			scope |= mvkMTLBarrierScopeFromVkAccessFlags(mb.srcAccessMask);
diff --git a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm
index 329fb6d..bc36b84 100644
--- a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm
+++ b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm
@@ -1318,15 +1318,28 @@
 	}
 }
 
-MVK_PUBLIC_SYMBOL MTLRenderStages mvkMTLRenderStagesFromVkPipelineStageFlags(VkPipelineStageFlags vkStages) {
-	MTLRenderStages mtlStages = MTLRenderStages(0);
-	if ( mvkIsAnyFlagEnabled(vkStages, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT | VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT) ) {
-		mtlStages |= MTLRenderStageVertex;
+MVK_PUBLIC_SYMBOL MTLRenderStages mvkMTLRenderStagesFromVkPipelineStageFlags(VkPipelineStageFlags vkStages,
+																			 bool placeBarrierBefore) {
+	// Although there are many combined render/compute/host stages in Vulkan, there are only two render
+	// stages in Metal. If the Vulkan stage did not map ONLY to a specific Metal render stage, then if the
+	// barrier is to be placed before the render stages, it should come before the vertex stage, otherwise
+	// if the barrier is to be placed after the render stages, it should come after the fragment stage.
+	if (placeBarrierBefore) {
+		bool placeBeforeFragment = mvkAreOnlyAnyFlagsEnabled(vkStages, (VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
+																		VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
+																		VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
+																		VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
+																		VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT));
+		return placeBeforeFragment ? MTLRenderStageFragment : MTLRenderStageVertex;
+	} else {
+		bool placeAfterVertex = mvkAreOnlyAnyFlagsEnabled(vkStages, (VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT |
+																	 VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT |
+																	 VK_PIPELINE_STAGE_VERTEX_INPUT_BIT |
+																	 VK_PIPELINE_STAGE_VERTEX_SHADER_BIT |
+																	 VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT |
+																	 VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT));
+		return placeAfterVertex ? MTLRenderStageVertex : MTLRenderStageFragment;
 	}
-	if ( mvkIsAnyFlagEnabled(vkStages, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT ) ) {
-		mtlStages |= MTLRenderStageFragment;
-	}
-	return mtlStages;
 }
 
 MVK_PUBLIC_SYMBOL MTLBarrierScope mvkMTLBarrierScopeFromVkAccessFlags(VkAccessFlags vkAccess) {