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
This commit is contained in:
Bill Hollings 2021-02-17 09:33:04 -05:00
parent 4a0a5cd0b4
commit 0b90a53d76
3 changed files with 63 additions and 42 deletions

View File

@ -1972,9 +1972,10 @@ MVKSampler::MVKSampler(MVKDevice* device, const VkSamplerCreateInfo* pCreateInfo
_requiresConstExprSampler = (pCreateInfo->compareEnable && !_device->_pMetalFeatures->depthSampleCompare) || _ycbcrConversion; _requiresConstExprSampler = (pCreateInfo->compareEnable && !_device->_pMetalFeatures->depthSampleCompare) || _ycbcrConversion;
@autoreleasepool { @autoreleasepool {
MTLSamplerDescriptor* mtlSampDesc = newMTLSamplerDescriptor(pCreateInfo); // temp retain auto mtlDev = getMTLDevice();
_mtlSamplerState = [getMTLDevice() newSamplerStateWithDescriptor: mtlSampDesc]; @synchronized (mtlDev) {
[mtlSampDesc release]; // temp release _mtlSamplerState = [mtlDev newSamplerStateWithDescriptor: [newMTLSamplerDescriptor(pCreateInfo) autorelease]];
}
} }
initConstExprSampler(pCreateInfo); initConstExprSampler(pCreateInfo);
@ -2060,5 +2061,7 @@ void MVKSampler::initConstExprSampler(const VkSamplerCreateInfo* pCreateInfo) {
} }
MVKSampler::~MVKSampler() { MVKSampler::~MVKSampler() {
[_mtlSamplerState release]; @synchronized (getMTLDevice()) {
[_mtlSamplerState release];
}
} }

View File

@ -1603,15 +1603,17 @@ bool MVKGraphicsPipeline::isRasterizationDisabled(const VkGraphicsPipelineCreate
} }
MVKGraphicsPipeline::~MVKGraphicsPipeline() { MVKGraphicsPipeline::~MVKGraphicsPipeline() {
[_mtlTessVertexStageDesc release]; @synchronized (getMTLDevice()) {
[_mtlTessVertexStageDesc release];
[_mtlTessVertexStageState release]; [_mtlTessVertexStageState release];
[_mtlTessVertexStageIndex16State release]; [_mtlTessVertexStageIndex16State release];
[_mtlTessVertexStageIndex32State release]; [_mtlTessVertexStageIndex32State release];
[_mtlTessControlStageState release]; [_mtlTessControlStageState release];
[_mtlPipelineState release]; [_mtlPipelineState release];
for (id<MTLFunction> func : _mtlTessVertexFunctions) { [func release]; } for (id<MTLFunction> func : _mtlTessVertexFunctions) { [func release]; }
}
} }
@ -1716,7 +1718,9 @@ MVKMTLFunction MVKComputePipeline::getMTLFunction(const VkComputePipelineCreateI
} }
MVKComputePipeline::~MVKComputePipeline() { MVKComputePipeline::~MVKComputePipeline() {
[_mtlPipelineState release]; @synchronized (getMTLDevice()) {
[_mtlPipelineState release];
}
} }
@ -2147,11 +2151,14 @@ id<MTLRenderPipelineState> MVKRenderPipelineCompiler::newMTLRenderPipelineState(
unique_lock<mutex> lock(_completionLock); unique_lock<mutex> lock(_completionLock);
compile(lock, ^{ compile(lock, ^{
[_owner->getMTLDevice() newRenderPipelineStateWithDescriptor: mtlRPLDesc auto mtlDev = _owner->getMTLDevice();
completionHandler: ^(id<MTLRenderPipelineState> ps, NSError* error) { @synchronized (mtlDev) {
bool isLate = compileComplete(ps, error); [mtlDev newRenderPipelineStateWithDescriptor: mtlRPLDesc
if (isLate) { destroy(); } completionHandler: ^(id<MTLRenderPipelineState> ps, NSError* error) {
}]; bool isLate = compileComplete(ps, error);
if (isLate) { destroy(); }
}];
}
}); });
return [_mtlRenderPipelineState retain]; return [_mtlRenderPipelineState retain];
@ -2178,11 +2185,14 @@ id<MTLComputePipelineState> MVKComputePipelineCompiler::newMTLComputePipelineSta
unique_lock<mutex> lock(_completionLock); unique_lock<mutex> lock(_completionLock);
compile(lock, ^{ compile(lock, ^{
[_owner->getMTLDevice() newComputePipelineStateWithFunction: mtlFunction auto mtlDev = _owner->getMTLDevice();
completionHandler: ^(id<MTLComputePipelineState> ps, NSError* error) { @synchronized (mtlDev) {
bool isLate = compileComplete(ps, error); [mtlDev newComputePipelineStateWithFunction: mtlFunction
if (isLate) { destroy(); } completionHandler: ^(id<MTLComputePipelineState> ps, NSError* error) {
}]; bool isLate = compileComplete(ps, error);
if (isLate) { destroy(); }
}];
}
}); });
return [_mtlComputePipelineState retain]; return [_mtlComputePipelineState retain];
@ -2192,12 +2202,15 @@ id<MTLComputePipelineState> MVKComputePipelineCompiler::newMTLComputePipelineSta
unique_lock<mutex> lock(_completionLock); unique_lock<mutex> lock(_completionLock);
compile(lock, ^{ compile(lock, ^{
[_owner->getMTLDevice() newComputePipelineStateWithDescriptor: plDesc auto mtlDev = _owner->getMTLDevice();
options: MTLPipelineOptionNone @synchronized (mtlDev) {
completionHandler: ^(id<MTLComputePipelineState> ps, MTLComputePipelineReflection*, NSError* error) { [mtlDev newComputePipelineStateWithDescriptor: plDesc
bool isLate = compileComplete(ps, error); options: MTLPipelineOptionNone
if (isLate) { destroy(); } completionHandler: ^(id<MTLComputePipelineState> ps, MTLComputePipelineReflection*, NSError* error) {
}]; bool isLate = compileComplete(ps, error);
if (isLate) { destroy(); }
}];
}
}); });
return [_mtlComputePipelineState retain]; return [_mtlComputePipelineState retain];

View File

@ -62,7 +62,7 @@ MVKMTLFunction MVKShaderLibrary::getMTLFunction(const VkSpecializationInfo* pSpe
if ( !_mtlLibrary ) { return MVKMTLFunctionNull; } if ( !_mtlLibrary ) { return MVKMTLFunctionNull; }
@synchronized (_mtlLibrary) { @synchronized (_owner->getMTLDevice()) {
@autoreleasepool { @autoreleasepool {
NSString* mtlFuncName = @(_shaderConversionResults.entryPoint.mtlFunctionName.c_str()); NSString* mtlFuncName = @(_shaderConversionResults.entryPoint.mtlFunctionName.c_str());
MVKDevice* mvkDev = _owner->getDevice(); MVKDevice* mvkDev = _owner->getDevice();
@ -423,13 +423,16 @@ id<MTLLibrary> MVKShaderLibraryCompiler::newMTLLibrary(NSString* mslSourceCode,
unique_lock<mutex> lock(_completionLock); unique_lock<mutex> lock(_completionLock);
compile(lock, ^{ compile(lock, ^{
[_owner->getMTLDevice() newLibraryWithSource: mslSourceCode auto mtlDev = _owner->getMTLDevice();
options: _owner->getDevice()->getMTLCompileOptions(shaderConversionResults.entryPoint.supportsFastMath, @synchronized (mtlDev) {
shaderConversionResults.isPositionInvariant) [mtlDev newLibraryWithSource: mslSourceCode
completionHandler: ^(id<MTLLibrary> mtlLib, NSError* error) { options: _owner->getDevice()->getMTLCompileOptions(shaderConversionResults.entryPoint.supportsFastMath,
bool isLate = compileComplete(mtlLib, error); shaderConversionResults.isPositionInvariant)
if (isLate) { destroy(); } completionHandler: ^(id<MTLLibrary> mtlLib, NSError* error) {
}]; bool isLate = compileComplete(mtlLib, error);
if (isLate) { destroy(); }
}];
}
}); });
return [_mtlLibrary retain]; return [_mtlLibrary retain];
@ -467,12 +470,14 @@ id<MTLFunction> MVKFunctionSpecializer::newMTLFunction(id<MTLLibrary> mtlLibrary
unique_lock<mutex> lock(_completionLock); unique_lock<mutex> lock(_completionLock);
compile(lock, ^{ compile(lock, ^{
[mtlLibrary newFunctionWithName: funcName @synchronized (_owner->getMTLDevice()) {
constantValues: constantValues [mtlLibrary newFunctionWithName: funcName
completionHandler: ^(id<MTLFunction> mtlFunc, NSError* error) { constantValues: constantValues
bool isLate = compileComplete(mtlFunc, error); completionHandler: ^(id<MTLFunction> mtlFunc, NSError* error) {
if (isLate) { destroy(); } bool isLate = compileComplete(mtlFunc, error);
}]; if (isLate) { destroy(); }
}];
}
}); });
return [_mtlFunction retain]; return [_mtlFunction retain];