Enhancements and fixes to temporary MTLBuffer allocation.

Check allocations are larger than MTLBuffer alignment to avoid Metal errors.
Align returning safely with allocating safely, and change callers to use
MVKMTLBufferAllocation::returnToPool() globally, to ensure this match.
Fix use of const on returnToPool();
Move MVKCommandEncoder::copyToTempMTLBufferAllocation()
and support setting private and dedicated flags.
Align constructor parameter order on MVKMTLBufferAllocationPool
with that of MVKMTLBufferAllocator.
Move subclass overrides of MVKObjectPool::newObject() to protected access.
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommand.h b/MoltenVK/MoltenVK/Commands/MVKCommand.h
index 04e42a7..685625b 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommand.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommand.h
@@ -38,11 +38,11 @@
 	/** Returns the Vulkan API opaque object controlling this object. */
 	MVKVulkanAPIObject* getVulkanAPIObject() override { return nullptr; }
 
-	/** Returns a new command instance. */
-	T* newObject() override { return new T(); }
-
 	MVKCommandTypePool(bool isPooling = true) : MVKObjectPool<T>(isPooling) {}
 
+protected:
+	T* newObject() override { return new T(); }
+
 };
 
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
index 5b3d1b6..f2f40dd 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
@@ -372,6 +372,9 @@
     /** Get a temporary MTLBuffer that will be returned to a pool after the command buffer is finished. */
     const MVKMTLBufferAllocation* getTempMTLBuffer(NSUInteger length, bool isPrivate = false, bool isDedicated = false);
 
+	/** Copy the bytes to a temporary MTLBuffer that will be returned to a pool after the command buffer is finished. */
+	const MVKMTLBufferAllocation* copyToTempMTLBufferAllocation(const void* bytes, NSUInteger length, bool isPrivate = false, bool isDedicated = false);
+
     /** Returns the command encoding pool. */
     MVKCommandEncodingPool* getCommandEncodingPool();
 
@@ -470,7 +473,6 @@
     void finishQueries();
 	void setSubpass(MVKCommand* passCmd, VkSubpassContents subpassContents, uint32_t subpassIndex);
 	void clearRenderArea();
-    const MVKMTLBufferAllocation* copyToTempMTLBufferAllocation(const void* bytes, NSUInteger length);
     NSString* getMTLRenderCommandEncoderName();
 
 	VkSubpassContents _subpassContents;
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
index c90647b..8396ea3 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
@@ -647,15 +647,10 @@
     }
 }
 
+// Return the MTLBuffer allocation to the pool once the command buffer is done with it
 const MVKMTLBufferAllocation* MVKCommandEncoder::getTempMTLBuffer(NSUInteger length, bool isPrivate, bool isDedicated) {
     const MVKMTLBufferAllocation* mtlBuffAlloc = getCommandEncodingPool()->acquireMTLBufferAllocation(length, isPrivate, isDedicated);
-	MVKMTLBufferAllocationPool* pool = mtlBuffAlloc->getPool();
-
-    // Return the MTLBuffer allocation to the pool once the command buffer is done with it
-    [_mtlCmdBuffer addCompletedHandler: ^(id<MTLCommandBuffer> mcb) {
-        pool->returnObjectSafely((MVKMTLBufferAllocation*)mtlBuffAlloc);
-    }];
-
+    [_mtlCmdBuffer addCompletedHandler: ^(id<MTLCommandBuffer> mcb) { mtlBuffAlloc->returnToPool(); }];
     return mtlBuffAlloc;
 }
 
@@ -664,8 +659,8 @@
 }
 
 // Copies the specified bytes into a temporary allocation within a pooled MTLBuffer, and returns the MTLBuffer allocation.
-const MVKMTLBufferAllocation* MVKCommandEncoder::copyToTempMTLBufferAllocation(const void* bytes, NSUInteger length) {
-    const MVKMTLBufferAllocation* mtlBuffAlloc = getTempMTLBuffer(length);
+const MVKMTLBufferAllocation* MVKCommandEncoder::copyToTempMTLBufferAllocation(const void* bytes, NSUInteger length, bool isPrivate, bool isDedicated) {
+	const MVKMTLBufferAllocation* mtlBuffAlloc = getTempMTLBuffer(length, isPrivate, isDedicated);
     void* pBuffData = mtlBuffAlloc->getContents();
     mlock(pBuffData, length);
     memcpy(pBuffData, bytes, length);
diff --git a/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.h b/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.h
index c9bc944..e223225 100644
--- a/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.h
+++ b/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.h
@@ -51,7 +51,7 @@
     MVKMTLBufferAllocationPool* getPool() const { return _pool; }
 
 	/** Returns this object back to the pool that created it. */
-    void returnToPool();
+    void returnToPool() const;
 
 	/** Constructs this instance with the specified pool as its origin. */
     MVKMTLBufferAllocation(MVKMTLBufferAllocationPool* pool,
@@ -82,16 +82,18 @@
 	/** Returns the Vulkan API opaque object controlling this object. */
 	MVKVulkanAPIObject* getVulkanAPIObject() override { return _device->getVulkanAPIObject(); };
 
-    /** Returns a new MVKMTLBufferAllocation instance. */
-    MVKMTLBufferAllocation* newObject() override;
-
     /** Configures this instance to dispense MVKMTLBufferAllocation instances of the specified size. */
-    MVKMTLBufferAllocationPool(MVKDevice* device, NSUInteger allocationLength, MTLStorageMode mtlStorageMode, bool isDedicated);
+    MVKMTLBufferAllocationPool(MVKDevice* device, NSUInteger allocationLength, bool makeThreadSafe,
+							   bool isDedicated, MTLStorageMode mtlStorageMode);
 
     ~MVKMTLBufferAllocationPool() override;
 
 protected:
-    uint32_t calcMTLBufferAllocationCount();
+	friend class MVKMTLBufferAllocation;
+	
+	MVKMTLBufferAllocation* newObject() override;
+	void returnAllocation(MVKMTLBufferAllocation* ba) { _isThreadSafe ? returnObjectSafely(ba) : returnObject(ba); }
+	uint32_t calcMTLBufferAllocationCount();
     void addMTLBuffer();
 
     NSUInteger _nextOffset;
@@ -100,6 +102,7 @@
     MTLStorageMode _mtlStorageMode;
 	MVKSmallVector<id<MTLBuffer>, 64> _mtlBuffers;
     MVKDevice* _device;
+	bool _isThreadSafe;
 };
 
 
@@ -138,14 +141,15 @@
      * next power-of-two value that is at least as big as the specified maximum size.
 	 * If makeThreadSafe is true, a lock will be applied when an allocation is acquired.
      */
-    MVKMTLBufferAllocator(MVKDevice* device, NSUInteger maxRegionLength, bool makeThreadSafe = false, bool isDedicated = false, MTLStorageMode mtlStorageMode = MTLStorageModeShared);
+    MVKMTLBufferAllocator(MVKDevice* device, NSUInteger maxRegionLength, bool makeThreadSafe = false,
+						  bool isDedicated = false, MTLStorageMode mtlStorageMode = MTLStorageModeShared);
 
     ~MVKMTLBufferAllocator() override;
 
 protected:
 	MVKSmallVector<MVKMTLBufferAllocationPool*, 32> _regionPools;
     NSUInteger _maxAllocationLength;
-	bool _makeThreadSafe;
+	bool _isThreadSafe;
 
 };
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.mm b/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.mm
index 91fb2b1..ff93739 100644
--- a/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.mm
@@ -24,7 +24,7 @@
 
 MVKVulkanAPIObject* MVKMTLBufferAllocation::getVulkanAPIObject() { return _pool->getVulkanAPIObject(); };
 
-void MVKMTLBufferAllocation::returnToPool() { _pool->returnObjectSafely(this); }
+void MVKMTLBufferAllocation::returnToPool() const { _pool->returnAllocation((MVKMTLBufferAllocation*)this); }
 
 
 #pragma mark -
@@ -50,10 +50,11 @@
 }
 
 
-MVKMTLBufferAllocationPool::MVKMTLBufferAllocationPool(MVKDevice* device, NSUInteger allocationLength, MTLStorageMode mtlStorageMode, bool isDedicated)
-        : MVKObjectPool<MVKMTLBufferAllocation>(true) {
+MVKMTLBufferAllocationPool::MVKMTLBufferAllocationPool(MVKDevice* device, NSUInteger allocationLength, bool makeThreadSafe,
+													   bool isDedicated, MTLStorageMode mtlStorageMode) : MVKObjectPool<MVKMTLBufferAllocation>(true) {
     _device = device;
     _allocationLength = allocationLength;
+	_isThreadSafe = makeThreadSafe;
     _mtlBufferLength = _allocationLength * (isDedicated ? 1 : calcMTLBufferAllocationCount());
     _mtlStorageMode = mtlStorageMode;
     _nextOffset = _mtlBufferLength;     // Force a MTLBuffer to be added on first access
@@ -80,10 +81,13 @@
 const MVKMTLBufferAllocation* MVKMTLBufferAllocator::acquireMTLBufferRegion(NSUInteger length) {
 	MVKAssert(length <= _maxAllocationLength, "This MVKMTLBufferAllocator has been configured to dispense MVKMTLBufferRegions no larger than %lu bytes.", (unsigned long)_maxAllocationLength);
 
+	// Can't allocate a segment smaller than the minimum MTLBuffer alignment.
+	length = std::max<NSUInteger>(length, _device->_pMetalFeatures->mtlBufferAlignment);
+
     // Convert max length to the next power-of-two exponent to use as a lookup
     NSUInteger p2Exp = mvkPowerOfTwoExponent(length);
 	MVKMTLBufferAllocationPool* pRP = _regionPools[p2Exp];
-	const MVKMTLBufferAllocation* region = _makeThreadSafe ? pRP->acquireObjectSafely() : pRP->acquireObject();
+	const MVKMTLBufferAllocation* region = _isThreadSafe ? pRP->acquireObjectSafely() : pRP->acquireObject();
 	if (region) {
 		[region->_mtlBuffer setPurgeableState: MTLPurgeableStateVolatile];
 	}
@@ -91,8 +95,8 @@
 }
 
 MVKMTLBufferAllocator::MVKMTLBufferAllocator(MVKDevice* device, NSUInteger maxRegionLength, bool makeThreadSafe, bool isDedicated, MTLStorageMode mtlStorageMode) : MVKBaseDeviceObject(device) {
-    _maxAllocationLength = maxRegionLength;
-	_makeThreadSafe = makeThreadSafe;
+	_maxAllocationLength = std::max<NSUInteger>(maxRegionLength, _device->_pMetalFeatures->mtlBufferAlignment);
+	_isThreadSafe = makeThreadSafe;
 
     // Convert max length to the next power-of-two exponent
     NSUInteger maxP2Exp = mvkPowerOfTwoExponent(_maxAllocationLength);
@@ -101,7 +105,7 @@
     _regionPools.reserve(maxP2Exp + 1);
     NSUInteger allocLen = 1;
     for (uint32_t p2Exp = 0; p2Exp <= maxP2Exp; p2Exp++) {
-        _regionPools.push_back(new MVKMTLBufferAllocationPool(device, allocLen, mtlStorageMode, isDedicated));
+        _regionPools.push_back(new MVKMTLBufferAllocationPool(device, allocLen, makeThreadSafe, isDedicated, mtlStorageMode));
         allocLen <<= 1;
     }
 }
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
index b8b6dd3..adb1fbd 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
@@ -937,9 +937,6 @@
 	/** Returns the Vulkan API opaque object controlling this object. */
 	MVKVulkanAPIObject* getVulkanAPIObject() override { return _device; };
 
-	/** Returns a new instance. */
-	T* newObject() override { return new T(_device); }
-
 	/**
 	 * Configures this instance for the device, and either use pooling, or not, depending
 	 * on the value of isPooling, which defaults to true if not indicated explicitly.
@@ -947,6 +944,8 @@
 	MVKDeviceObjectPool(MVKDevice* device, bool isPooling = true) : MVKObjectPool<T>(isPooling), _device(device) {}
 
 protected:
+	T* newObject() override { return new T(_device); }
+
 	MVKDevice* _device;
 };