Fixes for CTS multithread tests.

MTLDevice retains an internal object web that is used during the creation of
new objects, such as pipeline states, libraries, functions, and samplers.
Simultaneously creating and destroying objects of these types can trigger
race conditions on the internal MTLDevice content.

Wrap the following in @synchronized (mtlDevice) {...}:
- MTLRenderPipelineState creation and destruction
- MTLComputePipelineState creation and destruction
- MTLLibrary creation
- MTLFunction creation and specialization
- MTLSampler creation and destruction
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index 595ea8a..65a9524 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -1972,9 +1972,10 @@
 	_requiresConstExprSampler = (pCreateInfo->compareEnable && !_device->_pMetalFeatures->depthSampleCompare) || _ycbcrConversion;
 
 	@autoreleasepool {
-		MTLSamplerDescriptor* mtlSampDesc = newMTLSamplerDescriptor(pCreateInfo);	// temp retain
-		_mtlSamplerState = [getMTLDevice() newSamplerStateWithDescriptor: mtlSampDesc];
-		[mtlSampDesc release];														// temp release
+		auto mtlDev = getMTLDevice();
+		@synchronized (mtlDev) {
+			_mtlSamplerState = [mtlDev newSamplerStateWithDescriptor: [newMTLSamplerDescriptor(pCreateInfo) autorelease]];
+		}
 	}
 
 	initConstExprSampler(pCreateInfo);
@@ -2060,5 +2061,7 @@
 }
 
 MVKSampler::~MVKSampler() {
-	[_mtlSamplerState release];
+	@synchronized (getMTLDevice()) {
+		[_mtlSamplerState release];
+	}
 }
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
index 873729e..54c105d 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
@@ -1603,15 +1603,17 @@
 }
 
 MVKGraphicsPipeline::~MVKGraphicsPipeline() {
-	[_mtlTessVertexStageDesc release];
+	@synchronized (getMTLDevice()) {
+		[_mtlTessVertexStageDesc release];
 
-	[_mtlTessVertexStageState release];
-	[_mtlTessVertexStageIndex16State release];
-	[_mtlTessVertexStageIndex32State release];
-	[_mtlTessControlStageState release];
-	[_mtlPipelineState release];
+		[_mtlTessVertexStageState release];
+		[_mtlTessVertexStageIndex16State release];
+		[_mtlTessVertexStageIndex32State release];
+		[_mtlTessControlStageState release];
+		[_mtlPipelineState release];
 
-	for (id<MTLFunction> func : _mtlTessVertexFunctions) { [func release]; }
+		for (id<MTLFunction> func : _mtlTessVertexFunctions) { [func release]; }
+	}
 }
 
 
@@ -1716,7 +1718,9 @@
 }
 
 MVKComputePipeline::~MVKComputePipeline() {
-    [_mtlPipelineState release];
+	@synchronized (getMTLDevice()) {
+		[_mtlPipelineState release];
+	}
 }
 
 
@@ -2147,11 +2151,14 @@
 	unique_lock<mutex> lock(_completionLock);
 
 	compile(lock, ^{
-		[_owner->getMTLDevice() newRenderPipelineStateWithDescriptor: mtlRPLDesc
-												   completionHandler: ^(id<MTLRenderPipelineState> ps, NSError* error) {
-													   bool isLate = compileComplete(ps, error);
-													   if (isLate) { destroy(); }
-												   }];
+		auto mtlDev = _owner->getMTLDevice();
+		@synchronized (mtlDev) {
+			[mtlDev newRenderPipelineStateWithDescriptor: mtlRPLDesc
+									   completionHandler: ^(id<MTLRenderPipelineState> ps, NSError* error) {
+				bool isLate = compileComplete(ps, error);
+				if (isLate) { destroy(); }
+			}];
+		}
 	});
 
 	return [_mtlRenderPipelineState retain];
@@ -2178,11 +2185,14 @@
 	unique_lock<mutex> lock(_completionLock);
 
 	compile(lock, ^{
-		[_owner->getMTLDevice() newComputePipelineStateWithFunction: mtlFunction
-												  completionHandler: ^(id<MTLComputePipelineState> ps, NSError* error) {
-													  bool isLate = compileComplete(ps, error);
-													  if (isLate) { destroy(); }
-												  }];
+		auto mtlDev = _owner->getMTLDevice();
+		@synchronized (mtlDev) {
+			[mtlDev newComputePipelineStateWithFunction: mtlFunction
+									  completionHandler: ^(id<MTLComputePipelineState> ps, NSError* error) {
+				bool isLate = compileComplete(ps, error);
+				if (isLate) { destroy(); }
+			}];
+		}
 	});
 
 	return [_mtlComputePipelineState retain];
@@ -2192,12 +2202,15 @@
 	unique_lock<mutex> lock(_completionLock);
 
 	compile(lock, ^{
-		[_owner->getMTLDevice() newComputePipelineStateWithDescriptor: plDesc
-															  options: MTLPipelineOptionNone
-													completionHandler: ^(id<MTLComputePipelineState> ps, MTLComputePipelineReflection*, NSError* error) {
-														bool isLate = compileComplete(ps, error);
-														if (isLate) { destroy(); }
-													}];
+		auto mtlDev = _owner->getMTLDevice();
+		@synchronized (mtlDev) {
+			[mtlDev newComputePipelineStateWithDescriptor: plDesc
+												  options: MTLPipelineOptionNone
+										completionHandler: ^(id<MTLComputePipelineState> ps, MTLComputePipelineReflection*, NSError* error) {
+				bool isLate = compileComplete(ps, error);
+				if (isLate) { destroy(); }
+			}];
+		}
 	});
 
 	return [_mtlComputePipelineState retain];
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
index c4bc0cc..0c0b3ed 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
@@ -62,7 +62,7 @@
 
 	if ( !_mtlLibrary ) { return MVKMTLFunctionNull; }
 
-	@synchronized (_mtlLibrary) {
+	@synchronized (_owner->getMTLDevice()) {
 		@autoreleasepool {
 			NSString* mtlFuncName = @(_shaderConversionResults.entryPoint.mtlFunctionName.c_str());
 			MVKDevice* mvkDev = _owner->getDevice();
@@ -423,13 +423,16 @@
 	unique_lock<mutex> lock(_completionLock);
 
 	compile(lock, ^{
-		[_owner->getMTLDevice() newLibraryWithSource: mslSourceCode
-											 options: _owner->getDevice()->getMTLCompileOptions(shaderConversionResults.entryPoint.supportsFastMath,
-																								shaderConversionResults.isPositionInvariant)
-								   completionHandler: ^(id<MTLLibrary> mtlLib, NSError* error) {
-									   bool isLate = compileComplete(mtlLib, error);
-									   if (isLate) { destroy(); }
-								   }];
+		auto mtlDev = _owner->getMTLDevice();
+		@synchronized (mtlDev) {
+			[mtlDev newLibraryWithSource: mslSourceCode
+								 options: _owner->getDevice()->getMTLCompileOptions(shaderConversionResults.entryPoint.supportsFastMath,
+																					shaderConversionResults.isPositionInvariant)
+					   completionHandler: ^(id<MTLLibrary> mtlLib, NSError* error) {
+				bool isLate = compileComplete(mtlLib, error);
+				if (isLate) { destroy(); }
+			}];
+		}
 	});
 
 	return [_mtlLibrary retain];
@@ -467,12 +470,14 @@
 	unique_lock<mutex> lock(_completionLock);
 
 	compile(lock, ^{
-		[mtlLibrary newFunctionWithName: funcName
-						 constantValues: constantValues
-					  completionHandler: ^(id<MTLFunction> mtlFunc, NSError* error) {
-						  bool isLate = compileComplete(mtlFunc, error);
-						  if (isLate) { destroy(); }
-					  }];
+		@synchronized (_owner->getMTLDevice()) {
+			[mtlLibrary newFunctionWithName: funcName
+							 constantValues: constantValues
+						  completionHandler: ^(id<MTLFunction> mtlFunc, NSError* error) {
+				bool isLate = compileComplete(mtlFunc, error);
+				if (isLate) { destroy(); }
+			}];
+		}
 	});
 
 	return [_mtlFunction retain];