Reduce memory requirements for vkCmdPipelineBarrier().

Add MVKPipelineBarrier struct to consolidate barrier specs, reduce
memory use in each, and standardize barrier info into one collection.
MVKCmdPipelineBarrier uses templated collection sizes.
Add mvkPrintSizeOf() macro to simplify printing type sizes.
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.h b/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.h
index 336355d..4fef8b0 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.h
@@ -19,7 +19,6 @@
 #pragma once
 
 #include "MVKCommand.h"
-#include "MVKMTLResourceBindings.h"
 
 #import <Metal/Metal.h>
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.h b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.h
index 22471b7..5b07da5 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.h
@@ -19,6 +19,7 @@
 #pragma once
 
 #include "MVKCommand.h"
+#include "MVKMTLResourceBindings.h"
 #include "MVKSync.h"
 #include "MVKVector.h"
 
@@ -32,7 +33,11 @@
 #pragma mark -
 #pragma mark MVKCmdPipelineBarrier
 
-/** Represents an abstract Vulkan command to add a pipeline barrier. */
+/**
+ * Represents an abstract Vulkan command to add a pipeline barrier.
+ * Template class to balance vector pre-allocations between very common low counts and fewer larger counts.
+ */
+template <size_t N>
 class MVKCmdPipelineBarrier : public MVKCommand {
 
 public:
@@ -51,15 +56,19 @@
 
 protected:
 	MVKCommandTypePool<MVKCommand>* getTypePool(MVKCommandPool* cmdPool) override;
+	bool coversTextures();
 
 	VkPipelineStageFlags _srcStageMask;
 	VkPipelineStageFlags _dstStageMask;
 	VkDependencyFlags _dependencyFlags;
-	MVKVectorInline<VkMemoryBarrier, 4> _memoryBarriers;
-	MVKVectorInline<VkBufferMemoryBarrier, 4> _bufferMemoryBarriers;
-	MVKVectorInline<VkImageMemoryBarrier, 4> _imageMemoryBarriers;
+	MVKVectorInline<MVKPipelineBarrier, N> _barriers;
 };
 
+// Concrete template class implementations.
+typedef MVKCmdPipelineBarrier<1> MVKCmdPipelineBarrier1;
+typedef MVKCmdPipelineBarrier<4> MVKCmdPipelineBarrier4;
+typedef MVKCmdPipelineBarrier<32> MVKCmdPipelineBarrierMulti;
+
 
 #pragma mark -
 #pragma mark MVKCmdBindPipeline
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
index af83890..cac6f6d 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
@@ -30,100 +30,124 @@
 #pragma mark -
 #pragma mark MVKCmdPipelineBarrier
 
-VkResult MVKCmdPipelineBarrier::setContent(MVKCommandBuffer* cmdBuff,
-										   VkPipelineStageFlags srcStageMask,
-										   VkPipelineStageFlags dstStageMask,
-										   VkDependencyFlags dependencyFlags,
-										   uint32_t memoryBarrierCount,
-										   const VkMemoryBarrier* pMemoryBarriers,
-										   uint32_t bufferMemoryBarrierCount,
-										   const VkBufferMemoryBarrier* pBufferMemoryBarriers,
-										   uint32_t imageMemoryBarrierCount,
-										   const VkImageMemoryBarrier* pImageMemoryBarriers) {
+template <size_t N>
+VkResult MVKCmdPipelineBarrier<N>::setContent(MVKCommandBuffer* cmdBuff,
+											  VkPipelineStageFlags srcStageMask,
+											  VkPipelineStageFlags dstStageMask,
+											  VkDependencyFlags dependencyFlags,
+											  uint32_t memoryBarrierCount,
+											  const VkMemoryBarrier* pMemoryBarriers,
+											  uint32_t bufferMemoryBarrierCount,
+											  const VkBufferMemoryBarrier* pBufferMemoryBarriers,
+											  uint32_t imageMemoryBarrierCount,
+											  const VkImageMemoryBarrier* pImageMemoryBarriers) {
 	_srcStageMask = srcStageMask;
 	_dstStageMask = dstStageMask;
 	_dependencyFlags = dependencyFlags;
 
-	_memoryBarriers.clear();	// Clear for reuse
-	_memoryBarriers.reserve(memoryBarrierCount);
+	_barriers.clear();	// Clear for reuse
+	_barriers.reserve(memoryBarrierCount + bufferMemoryBarrierCount + imageMemoryBarrierCount);
+
 	for (uint32_t i = 0; i < memoryBarrierCount; i++) {
-		_memoryBarriers.push_back(pMemoryBarriers[i]);
+		_barriers.emplace_back(pMemoryBarriers[i]);
 	}
-
-	_bufferMemoryBarriers.clear();	// Clear for reuse
-	_bufferMemoryBarriers.reserve(bufferMemoryBarrierCount);
 	for (uint32_t i = 0; i < bufferMemoryBarrierCount; i++) {
-		_bufferMemoryBarriers.push_back(pBufferMemoryBarriers[i]);
+		_barriers.emplace_back(pBufferMemoryBarriers[i]);
 	}
-
-	_imageMemoryBarriers.clear();	// Clear for reuse
-	_imageMemoryBarriers.reserve(imageMemoryBarrierCount);
 	for (uint32_t i = 0; i < imageMemoryBarrierCount; i++) {
-		_imageMemoryBarriers.push_back(pImageMemoryBarriers[i]);
+		_barriers.emplace_back(pImageMemoryBarriers[i]);
 	}
 
 	return VK_SUCCESS;
 }
 
-void MVKCmdPipelineBarrier::encode(MVKCommandEncoder* cmdEncoder) {
+template <size_t N>
+void MVKCmdPipelineBarrier<N>::encode(MVKCommandEncoder* cmdEncoder) {
 
 #if MVK_MACOS
-    // Calls below invoke MTLBlitCommandEncoder so must apply this first.
+	// Calls below invoke MTLBlitCommandEncoder so must apply this first.
 	// Check if pipeline barriers are available and we are in a renderpass.
 	if (cmdEncoder->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);
-			[cmdEncoder->_mtlRenderEncoder memoryBarrierWithScope: scope
-													  afterStages: srcStages
-													 beforeStages: dstStages];
+
+		id<MTLResource> resources[_barriers.size()];
+		uint32_t rezCnt = 0;
+
+		for (auto& b : _barriers) {
+			switch (b.type) {
+				case MVKPipelineBarrier::Memory: {
+					MTLBarrierScope scope = (mvkMTLBarrierScopeFromVkAccessFlags(b.srcAccessMask) |
+											 mvkMTLBarrierScopeFromVkAccessFlags(b.dstAccessMask));
+					[cmdEncoder->_mtlRenderEncoder memoryBarrierWithScope: scope
+															  afterStages: srcStages
+															 beforeStages: dstStages];
+					break;
+				}
+
+				case MVKPipelineBarrier::Buffer:
+					resources[rezCnt++] = b.mvkBuffer->getMTLBuffer();
+					break;
+
+				case MVKPipelineBarrier::Image:
+					resources[rezCnt++] = b.mvkImage->getMTLTexture();
+					break;
+
+				default:
+					break;
+			}
 		}
-		MVKVectorInline<id<MTLResource>, 16> resources;
-		resources.reserve(_bufferMemoryBarriers.size() + _imageMemoryBarriers.size());
-		for (auto& mb : _bufferMemoryBarriers) {
-			auto* mvkBuff = (MVKBuffer*)mb.buffer;
-			resources.push_back(mvkBuff->getMTLBuffer());
-		}
-		for (auto& mb : _imageMemoryBarriers) {
-			auto* mvkImg = (MVKImage*)mb.image;
-			resources.push_back(mvkImg->getMTLTexture());
-		}
-		if ( !resources.empty() ) {
-			[cmdEncoder->_mtlRenderEncoder memoryBarrierWithResources: resources.data()
-																count: resources.size()
+
+		if (rezCnt) {
+			[cmdEncoder->_mtlRenderEncoder memoryBarrierWithResources: resources
+																count: rezCnt
 														  afterStages: srcStages
 														 beforeStages: dstStages];
 		}
 	} else {
-		if ( !(_memoryBarriers.empty() && _imageMemoryBarriers.empty()) ) {
-			[cmdEncoder->_mtlRenderEncoder textureBarrier];
-		}
+		if (coversTextures()) { [cmdEncoder->_mtlRenderEncoder textureBarrier]; }
 	}
 #endif
 
 	MVKDevice* mvkDvc = cmdEncoder->getDevice();
-    MVKCommandUse cmdUse = kMVKCommandUsePipelineBarrier;
+	MVKCommandUse cmdUse = kMVKCommandUsePipelineBarrier;
 
-	// Apply global memory barriers
-    for (auto& mb : _memoryBarriers) {
-        mvkDvc->applyMemoryBarrier(_srcStageMask, _dstStageMask, &mb, cmdEncoder, cmdUse);
-    }
+	for (auto& b : _barriers) {
+		switch (b.type) {
+			case MVKPipelineBarrier::Memory:
+				mvkDvc->applyMemoryBarrier(_srcStageMask, _dstStageMask, b, cmdEncoder, cmdUse);
+				break;
 
-    // Apply specific buffer barriers
-    for (auto& mb : _bufferMemoryBarriers) {
-        MVKBuffer* mvkBuff = (MVKBuffer*)mb.buffer;
-        mvkBuff->applyBufferMemoryBarrier(_srcStageMask, _dstStageMask, &mb, cmdEncoder, cmdUse);
-    }
+			case MVKPipelineBarrier::Buffer:
+				b.mvkBuffer->applyBufferMemoryBarrier(_srcStageMask, _dstStageMask, b, cmdEncoder, cmdUse);
+				break;
 
-    // Apply specific image barriers
-    for (auto& mb : _imageMemoryBarriers) {
-        MVKImage* mvkImg = (MVKImage*)mb.image;
-        mvkImg->applyImageMemoryBarrier(_srcStageMask, _dstStageMask, &mb, cmdEncoder, cmdUse);
-    }
+			case MVKPipelineBarrier::Image:
+				b.mvkImage->applyImageMemoryBarrier(_srcStageMask, _dstStageMask, b, cmdEncoder, cmdUse);
+				break;
+
+			default:
+				break;
+		}
+	}
 }
 
+template <size_t N>
+bool MVKCmdPipelineBarrier<N>::coversTextures() {
+	for (auto& b : _barriers) {
+		switch (b.type) {
+			case MVKPipelineBarrier::Memory:	return true;
+			case MVKPipelineBarrier::Image: 	return true;
+			default: 							break;
+		}
+	}
+	return false;
+}
+
+template class MVKCmdPipelineBarrier<1>;
+template class MVKCmdPipelineBarrier<4>;
+template class MVKCmdPipelineBarrier<32>;
+
 
 #pragma mark -
 #pragma mark MVKCmdBindPipeline
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandTypePools.def b/MoltenVK/MoltenVK/Commands/MVKCommandTypePools.def
index 9db85ec..a6087b4 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandTypePools.def
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandTypePools.def
@@ -51,7 +51,7 @@
 	MVK_TMPLT_DECL MVK_CMD_TYPE_POOL(cmdType ##Multi)
 
 
-MVK_CMD_TYPE_POOL(PipelineBarrier)
+MVK_CMD_TYPE_POOLS_FROM_TWO_THRESHOLDS(PipelineBarrier, 1, 4)
 MVK_CMD_TYPE_POOL(BindPipeline)
 MVK_CMD_TYPE_POOLS_FROM_TWO_THRESHOLDS(BeginRenderPass, 1, 2)
 MVK_CMD_TYPE_POOL(NextSubpass)
diff --git a/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h b/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h
index 1d789a2..a530288 100644
--- a/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h
+++ b/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h
@@ -18,8 +18,16 @@
 
 #pragma once
 
+#include "mvk_vulkan.h"
+
 #import <Metal/Metal.h>
 
+
+class MVKResource;
+class MVKBuffer;
+class MVKImage;
+
+
 /** Describes a MTLTexture resource binding. */
 typedef struct {
     union { id<MTLTexture> mtlTexture = nil; id<MTLTexture> mtlResource; }; // aliases
@@ -38,7 +46,7 @@
 /** Describes a MTLBuffer resource binding. */
 typedef struct {
     union { id<MTLBuffer> mtlBuffer = nil; id<MTLBuffer> mtlResource; const void* mtlBytes; }; // aliases
-    NSUInteger offset = 0;
+    VkDeviceSize offset = 0;
     uint32_t size = 0;
 	uint16_t index = 0;
     bool isDirty = true;
@@ -48,7 +56,78 @@
 /** Describes a MTLBuffer resource binding as used for an index buffer. */
 typedef struct {
     union { id<MTLBuffer> mtlBuffer = nil; id<MTLBuffer> mtlResource; }; // aliases
-    NSUInteger offset = 0;
-    uint8_t mtlIndexType;		// MTLIndexType
+    VkDeviceSize offset = 0;
+    uint8_t mtlIndexType = 0;		// MTLIndexType
     bool isDirty = true;
 } MVKIndexMTLBufferBinding;
+
+/** Concise and consistent structure for holding pipeline barrier info. */
+typedef struct MVKPipelineBarrier {
+
+	typedef enum : uint8_t {
+		None,
+		Memory,
+		Buffer,
+		Image,
+	} MVKPipelineBarrierType;
+
+	union { MVKBuffer* mvkBuffer = nullptr; MVKImage* mvkImage; MVKResource* mvkResource; };
+	union {
+		struct {
+			VkDeviceSize offset = 0;
+			VkDeviceSize size = 0;
+		};
+		struct {
+			VkImageLayout newLayout;
+			VkImageAspectFlags aspectMask;
+			uint16_t baseArrayLayer;
+			uint16_t layerCount;
+			uint8_t baseMipLevel;
+			uint8_t levelCount;
+		};
+	};
+	VkAccessFlags srcAccessMask = 0;
+	VkAccessFlags dstAccessMask = 0;
+	uint8_t srcQueueFamilyIndex = 0;
+	uint8_t dstQueueFamilyIndex = 0;
+
+	MVKPipelineBarrierType type = None;
+
+	bool isMemoryBarrier() { return type == Memory; }
+	bool isBufferBarrier() { return type == Buffer; }
+	bool isImageBarrier() { return type == Image; }
+
+	MVKPipelineBarrier(const VkMemoryBarrier& vkBarrier) :
+		type(Memory),
+		srcAccessMask(vkBarrier.srcAccessMask),
+		dstAccessMask(vkBarrier.dstAccessMask)
+		{}
+
+	MVKPipelineBarrier(const VkBufferMemoryBarrier& vkBarrier) :
+		type(Buffer),
+		srcAccessMask(vkBarrier.srcAccessMask),
+		dstAccessMask(vkBarrier.dstAccessMask),
+		srcQueueFamilyIndex(vkBarrier.srcQueueFamilyIndex),
+		dstQueueFamilyIndex(vkBarrier.dstQueueFamilyIndex),
+		mvkBuffer((MVKBuffer*)vkBarrier.buffer),
+		offset(vkBarrier.offset),
+		size(vkBarrier.size)
+		{}
+
+	MVKPipelineBarrier(const VkImageMemoryBarrier& vkBarrier) :
+		type(Image),
+		srcAccessMask(vkBarrier.srcAccessMask),
+		dstAccessMask(vkBarrier.dstAccessMask),
+		newLayout(vkBarrier.newLayout),
+		srcQueueFamilyIndex(vkBarrier.srcQueueFamilyIndex),
+		dstQueueFamilyIndex(vkBarrier.dstQueueFamilyIndex),
+		mvkImage((MVKImage*)vkBarrier.image),
+		aspectMask(vkBarrier.subresourceRange.aspectMask),
+		baseMipLevel(vkBarrier.subresourceRange.baseMipLevel),
+		levelCount(vkBarrier.subresourceRange.levelCount),
+		baseArrayLayer(vkBarrier.subresourceRange.baseArrayLayer),
+		layerCount(vkBarrier.subresourceRange.layerCount)
+		{}
+
+} MVKPipelineBarrier;
+
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h
index 3f88415..74d06f3 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h
@@ -52,18 +52,18 @@
 	VkResult bindDeviceMemory2(const VkBindBufferMemoryInfo* pBindInfo);
 
 	/** Applies the specified global memory barrier. */
-    void applyMemoryBarrier(VkPipelineStageFlags srcStageMask,
-                            VkPipelineStageFlags dstStageMask,
-                            VkMemoryBarrier* pMemoryBarrier,
-                            MVKCommandEncoder* cmdEncoder,
-                            MVKCommandUse cmdUse) override;
+	void applyMemoryBarrier(VkPipelineStageFlags srcStageMask,
+							VkPipelineStageFlags dstStageMask,
+							MVKPipelineBarrier& barrier,
+							MVKCommandEncoder* cmdEncoder,
+							MVKCommandUse cmdUse) override;
 
 	/** Applies the specified buffer memory barrier. */
-    void applyBufferMemoryBarrier(VkPipelineStageFlags srcStageMask,
-                                  VkPipelineStageFlags dstStageMask,
-                                  VkBufferMemoryBarrier* pBufferMemoryBarrier,
-                                  MVKCommandEncoder* cmdEncoder,
-                                  MVKCommandUse cmdUse);
+	void applyBufferMemoryBarrier(VkPipelineStageFlags srcStageMask,
+								  VkPipelineStageFlags dstStageMask,
+								  MVKPipelineBarrier& barrier,
+								  MVKCommandEncoder* cmdEncoder,
+								  MVKCommandUse cmdUse);
 
     /** Returns the intended usage of this buffer. */
     VkBufferUsageFlags getUsage() const { return _usage; }
@@ -91,7 +91,7 @@
 	void propogateDebugName() override;
 	bool needsHostReadSync(VkPipelineStageFlags srcStageMask,
 						   VkPipelineStageFlags dstStageMask,
-						   VkBufferMemoryBarrier* pBufferMemoryBarrier);
+						   MVKPipelineBarrier& barrier);
 	bool shouldFlushHostMemory();
 	VkResult flushToDevice(VkDeviceSize offset, VkDeviceSize size);
 	VkResult pullFromDevice(VkDeviceSize offset, VkDeviceSize size);
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm
index 8c55b3b..09244fa 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm
@@ -99,11 +99,11 @@
 
 void MVKBuffer::applyMemoryBarrier(VkPipelineStageFlags srcStageMask,
 								   VkPipelineStageFlags dstStageMask,
-								   VkMemoryBarrier* pMemoryBarrier,
+								   MVKPipelineBarrier& barrier,
                                    MVKCommandEncoder* cmdEncoder,
                                    MVKCommandUse cmdUse) {
 #if MVK_MACOS
-	if ( needsHostReadSync(srcStageMask, dstStageMask, pMemoryBarrier) ) {
+	if ( needsHostReadSync(srcStageMask, dstStageMask, barrier) ) {
 		[cmdEncoder->getMTLBlitEncoder(cmdUse) synchronizeResource: getMTLBuffer()];
 	}
 #endif
@@ -111,16 +111,31 @@
 
 void MVKBuffer::applyBufferMemoryBarrier(VkPipelineStageFlags srcStageMask,
 										 VkPipelineStageFlags dstStageMask,
-										 VkBufferMemoryBarrier* pBufferMemoryBarrier,
+										 MVKPipelineBarrier& barrier,
                                          MVKCommandEncoder* cmdEncoder,
                                          MVKCommandUse cmdUse) {
 #if MVK_MACOS
-	if ( needsHostReadSync(srcStageMask, dstStageMask, pBufferMemoryBarrier) ) {
+	if ( needsHostReadSync(srcStageMask, dstStageMask, barrier) ) {
 		[cmdEncoder->getMTLBlitEncoder(cmdUse) synchronizeResource: getMTLBuffer()];
 	}
 #endif
 }
 
+// Returns whether the specified buffer memory barrier requires a sync between this
+// buffer and host memory for the purpose of the host reading texture memory.
+bool MVKBuffer::needsHostReadSync(VkPipelineStageFlags srcStageMask,
+								  VkPipelineStageFlags dstStageMask,
+								  MVKPipelineBarrier& barrier) {
+#if MVK_MACOS
+	return (mvkIsAnyFlagEnabled(dstStageMask, (VK_PIPELINE_STAGE_HOST_BIT)) &&
+			mvkIsAnyFlagEnabled(barrier.dstAccessMask, (VK_ACCESS_HOST_READ_BIT)) &&
+			isMemoryHostAccessible() && (!isMemoryHostCoherent() || _isHostCoherentTexelBuffer));
+#endif
+#if MVK_IOS
+	return false;
+#endif
+}
+
 #if MVK_MACOS
 bool MVKBuffer::shouldFlushHostMemory() { return _isHostCoherentTexelBuffer; }
 #endif
@@ -146,21 +161,6 @@
 	return VK_SUCCESS;
 }
 
-// Returns whether the specified buffer memory barrier requires a sync between this
-// buffer and host memory for the purpose of the host reading texture memory.
-bool MVKBuffer::needsHostReadSync(VkPipelineStageFlags srcStageMask,
-								  VkPipelineStageFlags dstStageMask,
-								  VkBufferMemoryBarrier* pBufferMemoryBarrier) {
-#if MVK_IOS
-	return false;
-#endif
-#if MVK_MACOS
-	return (mvkIsAnyFlagEnabled(dstStageMask, (VK_PIPELINE_STAGE_HOST_BIT)) &&
-			mvkIsAnyFlagEnabled(pBufferMemoryBarrier->dstAccessMask, (VK_ACCESS_HOST_READ_BIT)) &&
-			isMemoryHostAccessible() && (!isMemoryHostCoherent() || _isHostCoherentTexelBuffer));
-#endif
-}
-
 
 #pragma mark Metal
 
@@ -176,8 +176,7 @@
 #if MVK_MACOS
 		} else if (_isHostCoherentTexelBuffer) {
 			// According to the Vulkan spec, buffers, like linear images, can always use host-coherent memory.
-                        // But texel buffers on Mac cannot use shared memory. So we need to use host-cached
-                        // memory here.
+			// But texel buffers on Mac cannot use shared memory. So we need to use host-cached memory here.
 			_mtlBuffer = [_device->getMTLDevice() newBufferWithLength: getByteCount()
 															  options: MTLResourceStorageModeManaged];	// retained
 			propogateDebugName();
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
index f5e1f4d..d23ad13 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
@@ -20,6 +20,7 @@
 
 #include "MVKFoundation.h"
 #include "MVKVulkanAPIObject.h"
+#include "MVKMTLResourceBindings.h"
 #include "MVKLayers.h"
 #include "MVKObjectPool.h"
 #include "MVKVector.h"
@@ -561,9 +562,9 @@
 	/** Applies the specified global memory barrier to all resource issued by this device. */
 	void applyMemoryBarrier(VkPipelineStageFlags srcStageMask,
 							VkPipelineStageFlags dstStageMask,
-							VkMemoryBarrier* pMemoryBarrier,
-                            MVKCommandEncoder* cmdEncoder,
-                            MVKCommandUse cmdUse);
+							MVKPipelineBarrier& barrier,
+							MVKCommandEncoder* cmdEncoder,
+							MVKCommandUse cmdUse);
 
     /**
 	 * If performance is being tracked, returns a monotonic timestamp value for use performance timestamping.
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index 540811e..bde8c19 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -2529,14 +2529,14 @@
 
 void MVKDevice::applyMemoryBarrier(VkPipelineStageFlags srcStageMask,
 								   VkPipelineStageFlags dstStageMask,
-								   VkMemoryBarrier* pMemoryBarrier,
-                                   MVKCommandEncoder* cmdEncoder,
-                                   MVKCommandUse cmdUse) {
+								   MVKPipelineBarrier& barrier,
+								   MVKCommandEncoder* cmdEncoder,
+								   MVKCommandUse cmdUse) {
 	if (!mvkIsAnyFlagEnabled(dstStageMask, VK_PIPELINE_STAGE_HOST_BIT) ||
-		!mvkIsAnyFlagEnabled(pMemoryBarrier->dstAccessMask, VK_ACCESS_HOST_READ_BIT) ) { return; }
+		!mvkIsAnyFlagEnabled(barrier.dstAccessMask, VK_ACCESS_HOST_READ_BIT) ) { return; }
 	lock_guard<mutex> lock(_rezLock);
-    for (auto& rez : _resources) {
-		rez->applyMemoryBarrier(srcStageMask, dstStageMask, pMemoryBarrier, cmdEncoder, cmdUse);
+	for (auto& rez : _resources) {
+		rez->applyMemoryBarrier(srcStageMask, dstStageMask, barrier, cmdEncoder, cmdUse);
 	}
 }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
index 8bbfa05..0fc5ad7 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
@@ -134,18 +134,18 @@
 	virtual VkResult bindDeviceMemory2(const VkBindImageMemoryInfo* pBindInfo);
 
 	/** Applies the specified global memory barrier. */
-    void applyMemoryBarrier(VkPipelineStageFlags srcStageMask,
-                            VkPipelineStageFlags dstStageMask,
-                            VkMemoryBarrier* pMemoryBarrier,
-                            MVKCommandEncoder* cmdEncoder,
-                            MVKCommandUse cmdUse) override;
+	void applyMemoryBarrier(VkPipelineStageFlags srcStageMask,
+							VkPipelineStageFlags dstStageMask,
+							MVKPipelineBarrier& barrier,
+							MVKCommandEncoder* cmdEncoder,
+							MVKCommandUse cmdUse) override;
 
 	/** Applies the specified image memory barrier. */
-    void applyImageMemoryBarrier(VkPipelineStageFlags srcStageMask,
-                                 VkPipelineStageFlags dstStageMask,
-                                 VkImageMemoryBarrier* pImageMemoryBarrier,
-                                 MVKCommandEncoder* cmdEncoder,
-                                 MVKCommandUse cmdUse);
+	void applyImageMemoryBarrier(VkPipelineStageFlags srcStageMask,
+								 VkPipelineStageFlags dstStageMask,
+								 MVKPipelineBarrier& barrier,
+								 MVKCommandEncoder* cmdEncoder,
+								 MVKCommandUse cmdUse);
 
 #pragma mark Metal
 
@@ -250,7 +250,7 @@
 	VkResult pullFromDevice(VkDeviceSize offset, VkDeviceSize size);
 	bool needsHostReadSync(VkPipelineStageFlags srcStageMask,
 						   VkPipelineStageFlags dstStageMask,
-						   VkImageMemoryBarrier* pImageMemoryBarrier);
+						   MVKPipelineBarrier& barrier);
 
 	MVKVectorInline<MVKImageSubresource, 1> _subresources;
 	std::unordered_map<NSUInteger, id<MTLTexture>> _mtlTextureViews;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index a90913b..68663fa 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -83,11 +83,11 @@
 
 void MVKImage::applyMemoryBarrier(VkPipelineStageFlags srcStageMask,
 								  VkPipelineStageFlags dstStageMask,
-								  VkMemoryBarrier* pMemoryBarrier,
-                                  MVKCommandEncoder* cmdEncoder,
-                                  MVKCommandUse cmdUse) {
+								  MVKPipelineBarrier& barrier,
+								  MVKCommandEncoder* cmdEncoder,
+								  MVKCommandUse cmdUse) {
 #if MVK_MACOS
-	if ( needsHostReadSync(srcStageMask, dstStageMask, pMemoryBarrier) ) {
+	if ( needsHostReadSync(srcStageMask, dstStageMask, barrier) ) {
 		[cmdEncoder->getMTLBlitEncoder(cmdUse) synchronizeResource: getMTLTexture()];
 	}
 #endif
@@ -95,27 +95,24 @@
 
 void MVKImage::applyImageMemoryBarrier(VkPipelineStageFlags srcStageMask,
 									   VkPipelineStageFlags dstStageMask,
-									   VkImageMemoryBarrier* pImageMemoryBarrier,
-                                       MVKCommandEncoder* cmdEncoder,
-                                       MVKCommandUse cmdUse) {
-	const VkImageSubresourceRange& srRange = pImageMemoryBarrier->subresourceRange;
+									   MVKPipelineBarrier& barrier,
+									   MVKCommandEncoder* cmdEncoder,
+									   MVKCommandUse cmdUse) {
 
 	// Extract the mipmap levels that are to be updated
-	uint32_t mipLvlStart = srRange.baseMipLevel;
-	uint32_t mipLvlCnt = srRange.levelCount;
-	uint32_t mipLvlEnd = (mipLvlCnt == VK_REMAINING_MIP_LEVELS
+	uint32_t mipLvlStart = barrier.baseMipLevel;
+	uint32_t mipLvlEnd = (barrier.levelCount == (uint8_t)VK_REMAINING_MIP_LEVELS
 						  ? getMipLevelCount()
-						  : (mipLvlStart + mipLvlCnt));
+						  : (mipLvlStart + barrier.levelCount));
 
 	// Extract the cube or array layers (slices) that are to be updated
-	uint32_t layerStart = srRange.baseArrayLayer;
-	uint32_t layerCnt = srRange.layerCount;
-	uint32_t layerEnd = (layerCnt == VK_REMAINING_ARRAY_LAYERS
+	uint32_t layerStart = barrier.baseArrayLayer;
+	uint32_t layerEnd = (barrier.layerCount == (uint16_t)VK_REMAINING_ARRAY_LAYERS
 						 ? getLayerCount()
-						 : (layerStart + layerCnt));
+						 : (layerStart + barrier.layerCount));
 
 #if MVK_MACOS
-	bool needsSync = needsHostReadSync(srcStageMask, dstStageMask, pImageMemoryBarrier);
+	bool needsSync = needsHostReadSync(srcStageMask, dstStageMask, barrier);
 	id<MTLTexture> mtlTex = needsSync ? getMTLTexture() : nil;
 	id<MTLBlitCommandEncoder> mtlBlitEncoder = needsSync ? cmdEncoder->getMTLBlitEncoder(cmdUse) : nil;
 #endif
@@ -124,7 +121,7 @@
 	for (uint32_t mipLvl = mipLvlStart; mipLvl < mipLvlEnd; mipLvl++) {
 		for (uint32_t layer = layerStart; layer < layerEnd; layer++) {
 			MVKImageSubresource* pImgRez = getSubresource(mipLvl, layer);
-			if (pImgRez) { pImgRez->layoutState = pImageMemoryBarrier->newLayout; }
+			if (pImgRez) { pImgRez->layoutState = barrier.newLayout; }
 #if MVK_MACOS
 			if (needsSync) { [mtlBlitEncoder synchronizeTexture: mtlTex slice: layer level: mipLvl]; }
 #endif
@@ -136,15 +133,15 @@
 // texture and host memory for the purpose of the host reading texture memory.
 bool MVKImage::needsHostReadSync(VkPipelineStageFlags srcStageMask,
 								 VkPipelineStageFlags dstStageMask,
-								 VkImageMemoryBarrier* pImageMemoryBarrier) {
+								 MVKPipelineBarrier& barrier) {
+#if MVK_MACOS
+	return ((barrier.newLayout == VK_IMAGE_LAYOUT_GENERAL) &&
+			mvkIsAnyFlagEnabled(barrier.dstAccessMask, (VK_ACCESS_HOST_READ_BIT | VK_ACCESS_MEMORY_READ_BIT)) &&
+			isMemoryHostAccessible() && !isMemoryHostCoherent());
+#endif
 #if MVK_IOS
 	return false;
 #endif
-#if MVK_MACOS
-	return ((pImageMemoryBarrier->newLayout == VK_IMAGE_LAYOUT_GENERAL) &&
-			mvkIsAnyFlagEnabled(pImageMemoryBarrier->dstAccessMask, (VK_ACCESS_HOST_READ_BIT | VK_ACCESS_MEMORY_READ_BIT)) &&
-			isMemoryHostAccessible() && !isMemoryHostCoherent());
-#endif
 }
 
 // Returns a pointer to the internal subresource for the specified MIP level layer.
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKResource.h b/MoltenVK/MoltenVK/GPUObjects/MVKResource.h
index fb6f7ee..ca3ccfa 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKResource.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKResource.h
@@ -20,6 +20,7 @@
 
 #include "MVKDevice.h"
 #include "MVKDeviceMemory.h"
+#include "MVKMTLResourceBindings.h"
 
 class MVKCommandEncoder;
 
@@ -67,9 +68,9 @@
 	/** Applies the specified global memory barrier. */
 	virtual void applyMemoryBarrier(VkPipelineStageFlags srcStageMask,
 									VkPipelineStageFlags dstStageMask,
-									VkMemoryBarrier* pMemoryBarrier,
-                                    MVKCommandEncoder* cmdEncoder,
-                                    MVKCommandUse cmdUse) = 0;
+									MVKPipelineBarrier& barrier,
+									MVKCommandEncoder* cmdEncoder,
+									MVKCommandUse cmdUse) = 0;
 
 	
 #pragma mark Construction
diff --git a/MoltenVK/MoltenVK/Utility/MVKFoundation.h b/MoltenVK/MoltenVK/Utility/MVKFoundation.h
index c2c17f3..f626ebb 100644
--- a/MoltenVK/MoltenVK/Utility/MVKFoundation.h
+++ b/MoltenVK/MoltenVK/Utility/MVKFoundation.h
@@ -321,6 +321,9 @@
 			mvkVKComponentSwizzlesMatch(cm1.a, cm2.a, VK_COMPONENT_SWIZZLE_A));
 }
 
+/** Print the size of the type. */
+#define mvkPrintSizeOf(type)    printf("Size of " #type " is %lu.\n", sizeof(type))
+
 
 #pragma mark -
 #pragma mark Template functions
diff --git a/MoltenVK/MoltenVK/Vulkan/vulkan.mm b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
index 26b0329..90b79ec 100644
--- a/MoltenVK/MoltenVK/Vulkan/vulkan.mm
+++ b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
@@ -1739,10 +1739,11 @@
 	const VkImageMemoryBarrier*                 pImageMemoryBarriers) {
 
 	MVKTraceVulkanCallStart();
-	MVKAddCmd(PipelineBarrier, commandBuffer, srcStageMask, dstStageMask, dependencyFlags,
-			  memoryBarrierCount, pMemoryBarriers,
-			  bufferMemoryBarrierCount, pBufferMemoryBarriers,
-			  imageMemoryBarrierCount, pImageMemoryBarriers);
+	uint32_t barrierCount = memoryBarrierCount + bufferMemoryBarrierCount + imageMemoryBarrierCount;
+	MVKAddCmdFromTwoThresholds(PipelineBarrier, barrierCount, 1, 4, commandBuffer, srcStageMask, dstStageMask, dependencyFlags,
+							   memoryBarrierCount, pMemoryBarriers,
+							   bufferMemoryBarrierCount, pBufferMemoryBarriers,
+							   imageMemoryBarrierCount, pImageMemoryBarriers);
 	MVKTraceVulkanCallEnd();
 }