Remove exposure to leakable instances of NSString, NSArray & NSDictionary.
Reduce creation of autoreleased instances of NSString, NSArray & NSDictionary.
Ensure remaining are covered by deliberate autorelease pools.
diff --git a/Common/MVKOSExtensions.mm b/Common/MVKOSExtensions.mm
index 7759e48..39ef03b 100644
--- a/Common/MVKOSExtensions.mm
+++ b/Common/MVKOSExtensions.mm
@@ -76,10 +76,12 @@
#pragma mark Process environment
string mvkGetEnvVar(string varName, bool* pWasFound) {
- NSDictionary* env = [[NSProcessInfo processInfo] environment];
- NSString* envStr = env[@(varName.c_str())];
- if (pWasFound) { *pWasFound = envStr != nil; }
- return envStr ? envStr.UTF8String : "";
+ @autoreleasepool {
+ NSDictionary*nsEnv = [[NSProcessInfo processInfo] environment];
+ NSString* envStr = nsEnv[@(varName.c_str())];
+ if (pWasFound) { *pWasFound = envStr != nil; }
+ return envStr ? envStr.UTF8String : "";
+ }
}
int64_t mvkGetEnvVarInt64(string varName, bool* pWasFound) {
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index 22a63de..d763dc3 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -31,9 +31,12 @@
- Fix pipeline cache lookups.
- Fix race condition between swapchain image destruction and presentation completion callback.
- Set Metal texture usage to allow texture copy via view.
+- Fix memory leak in debug marker and debug utils labelling.
+- Reduce use of autoreleased Obj-C objects, and ensure those remaining are
+ covered by deliberate autorelease pools.
- `vkCmdCopyImage()` support copying between compressed and uncompressed formats
and validate that formats are compatible for copying.
-- `vkCmdBufferImageCopy()` fix crash when setting bytes per image in non-arrayed images.
+- `vkCmdBufferImageCopy()` fix crash when setting bytes per image in non-arrayed images.
- Document that the functions in `vk_mvk_moltenvk.h` cannot be used with objects
retrieved through the *Vulkan SDK Loader and Layers* framework.
- Update `VK_MVK_MOLTENVK_SPEC_VERSION` to 21.
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm
index bf86b8f..a7eb94c 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm
@@ -398,10 +398,12 @@
#pragma mark Support methods
id<MTLFunction> MVKCommandResourceFactory::getFunctionNamed(const char* funcName) {
- uint64_t startTime = _device->getPerformanceTimestamp();
- id<MTLFunction> mtlFunc = [[_mtlLibrary newFunctionWithName: @(funcName)] autorelease];
- _device->addActivityPerformance(_device->_performanceStatistics.shaderCompilation.functionRetrieval, startTime);
- return mtlFunc;
+ uint64_t startTime = _device->getPerformanceTimestamp();
+ NSString* nsFuncName = [[NSString alloc] initWithUTF8String: funcName]; // temp retained
+ id<MTLFunction> mtlFunc = [[_mtlLibrary newFunctionWithName: nsFuncName] autorelease];
+ [nsFuncName release]; // release temp NSStr
+ _device->addActivityPerformance(_device->_performanceStatistics.shaderCompilation.functionRetrieval, startTime);
+ return mtlFunc;
}
id<MTLFunction> MVKCommandResourceFactory::newMTLFunction(NSString* mslSrcCode, NSString* funcName) {
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index de49bb3..7c7185c 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -372,14 +372,16 @@
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- _ioSurface = IOSurfaceCreate((CFDictionaryRef)@{
- (id)kIOSurfaceWidth: @(_extent.width),
- (id)kIOSurfaceHeight: @(_extent.height),
- (id)kIOSurfaceBytesPerElement: @(mvkMTLPixelFormatBytesPerBlock(_mtlPixelFormat)),
- (id)kIOSurfaceElementWidth: @(mvkMTLPixelFormatBlockTexelSize(_mtlPixelFormat).width),
- (id)kIOSurfaceElementHeight: @(mvkMTLPixelFormatBlockTexelSize(_mtlPixelFormat).height),
- (id)kIOSurfaceIsGlobal: @(true), // Deprecated but needed for interprocess transfers
- });
+ @autoreleasepool {
+ _ioSurface = IOSurfaceCreate((CFDictionaryRef)@{
+ (id)kIOSurfaceWidth: @(_extent.width),
+ (id)kIOSurfaceHeight: @(_extent.height),
+ (id)kIOSurfaceBytesPerElement: @(mvkMTLPixelFormatBytesPerBlock(_mtlPixelFormat)),
+ (id)kIOSurfaceElementWidth: @(mvkMTLPixelFormatBlockTexelSize(_mtlPixelFormat).width),
+ (id)kIOSurfaceElementHeight: @(mvkMTLPixelFormatBlockTexelSize(_mtlPixelFormat).height),
+ (id)kIOSurfaceIsGlobal: @(true), // Deprecated but needed for interprocess transfers
+ });
+ }
#pragma clang diagnostic pop
}
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
index aae5cf4..88c41af 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
@@ -252,28 +252,28 @@
#pragma mark Object Creation
-// Returns an autoreleased array containing the MTLDevices available on this system,
-// sorted according to power, with higher power GPU's at the front of the array.
-// This ensures that a lazy app that simply grabs the first GPU will get a high-power
-// one by default. If the MVK_CONFIG_FORCE_LOW_POWER_GPU env var or build setting is set,
-// the returned array will only include low-power devices.
-// If Metal is not supported, ensure we return an empty array.
-static NSArray<id<MTLDevice>>* getAvailableMTLDevices() {
+// Returns a new array containing the MTLDevices available on this system, sorted according to power,
+// with higher power GPU's at the front of the array. This ensures that a lazy app that simply
+// grabs the first GPU will get a high-power one by default. If the MVK_CONFIG_FORCE_LOW_POWER_GPU
+// env var or build setting is set, the returned array will only include low-power devices.
+// It is the caller's responsibility to release the array when not required anymore.
+// If Metal is not supported, returns an empty array.
+static NSArray<id<MTLDevice>>* newAvailableMTLDevicesArray() {
+ NSMutableArray* mtlDevs = [NSMutableArray new];
+
#if MVK_MACOS
- NSArray* mtlDevs = [MTLCopyAllDevices() autorelease];
- if ( !mtlDevs ) { return @[]; }
+ NSArray* rawMTLDevs = MTLCopyAllDevices(); // temp retain
+ if (rawMTLDevs) {
+ bool forceLowPower = MVK_CONFIG_FORCE_LOW_POWER_GPU;
+ MVK_SET_FROM_ENV_OR_BUILD_BOOL(forceLowPower, MVK_CONFIG_FORCE_LOW_POWER_GPU);
- bool forceLowPower = MVK_CONFIG_FORCE_LOW_POWER_GPU;
- MVK_SET_FROM_ENV_OR_BUILD_BOOL(forceLowPower, MVK_CONFIG_FORCE_LOW_POWER_GPU);
-
- if (forceLowPower) {
- NSMutableArray* lpDevs = [[NSMutableArray new] autorelease];
- for (id<MTLDevice> md in mtlDevs) {
- if (md.isLowPower) { [lpDevs addObject: md]; }
+ // Populate the array of appropriate MTLDevices
+ for (id<MTLDevice> md in rawMTLDevs) {
+ if ( !forceLowPower || md.isLowPower ) { [mtlDevs addObject: md]; }
}
- return lpDevs;
- } else {
- return [mtlDevs sortedArrayUsingComparator: ^(id<MTLDevice> md1, id<MTLDevice> md2) {
+
+ // Sort by power
+ [mtlDevs sortUsingComparator: ^(id<MTLDevice> md1, id<MTLDevice> md2) {
BOOL md1IsLP = md1.isLowPower;
BOOL md2IsLP = md2.isLowPower;
@@ -290,14 +290,18 @@
return md2IsLP ? NSOrderedAscending : NSOrderedDescending;
}];
- }
+ }
+ [rawMTLDevs release]; // release temp
#endif // MVK_MACOS
#if MVK_IOS
- id<MTLDevice> mtlDev = MTLCreateSystemDefaultDevice();
- return mtlDev ? [NSArray arrayWithObject: mtlDev] : @[];
+ id<MTLDevice> md = MTLCreateSystemDefaultDevice();
+ if (md) { [mtlDevs addObject: md]; }
+ [md release];
#endif // MVK_IOS
+
+ return mtlDevs; // retained
}
MVKInstance::MVKInstance(const VkInstanceCreateInfo* pCreateInfo) : _enabledExtensions(this) {
@@ -326,11 +330,13 @@
}
// Populate the array of physical GPU devices
- NSArray<id<MTLDevice>>* mtlDevices = getAvailableMTLDevices();
+ NSArray<id<MTLDevice>>* mtlDevices = newAvailableMTLDevicesArray(); // temp retain
_physicalDevices.reserve(mtlDevices.count);
for (id<MTLDevice> mtlDev in mtlDevices) {
_physicalDevices.push_back(new MVKPhysicalDevice(this, mtlDev));
}
+ [mtlDevices release]; // release temp
+
if (_physicalDevices.empty()) {
setConfigurationResult(reportError(VK_ERROR_INCOMPATIBLE_DRIVER, "Vulkan is not supported on this device. MoltenVK requires Metal, which is not available on this device."));
}
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
index b80362b..a243ffb 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
@@ -49,57 +49,60 @@
if ( !_mtlLibrary ) { return MVKMTLFunctionNull; }
- NSString* mtlFuncName = @(_shaderConversionResults.entryPoint.mtlFunctionName.c_str());
- MVKDevice* mvkDev = _owner->getDevice();
- uint64_t startTime = mvkDev->getPerformanceTimestamp();
- id<MTLFunction> mtlFunc = [[_mtlLibrary newFunctionWithName: mtlFuncName] autorelease];
- mvkDev->addActivityPerformance(mvkDev->_performanceStatistics.shaderCompilation.functionRetrieval, startTime);
+ id<MTLFunction> mtlFunc = nil;
+ @autoreleasepool {
+ NSString* mtlFuncName = @(_shaderConversionResults.entryPoint.mtlFunctionName.c_str());
+ MVKDevice* mvkDev = _owner->getDevice();
+ uint64_t startTime = mvkDev->getPerformanceTimestamp();
+ mtlFunc = [_mtlLibrary newFunctionWithName: mtlFuncName]; // retained
+ mvkDev->addActivityPerformance(mvkDev->_performanceStatistics.shaderCompilation.functionRetrieval, startTime);
- if (mtlFunc) {
- // If the Metal device supports shader specialization, and the Metal function expects to be
- // specialized, populate Metal function constant values from the Vulkan specialization info,
- // and compiled a specialized Metal function, otherwise simply use the unspecialized Metal function.
- if (mvkDev->_pMetalFeatures->shaderSpecialization) {
- NSArray<MTLFunctionConstant*>* mtlFCs = mtlFunc.functionConstantsDictionary.allValues;
- if (mtlFCs.count) {
- // The Metal shader contains function constants and expects to be specialized
- // Populate the Metal function constant values from the Vulkan specialization info.
- MTLFunctionConstantValues* mtlFCVals = [[MTLFunctionConstantValues new] autorelease];
- if (pSpecializationInfo) {
- // Iterate through the provided Vulkan specialization entries, and populate the
- // Metal function constant value that matches the Vulkan specialization constantID.
- for (uint32_t specIdx = 0; specIdx < pSpecializationInfo->mapEntryCount; specIdx++) {
- const VkSpecializationMapEntry* pMapEntry = &pSpecializationInfo->pMapEntries[specIdx];
- NSUInteger mtlFCIndex = pMapEntry->constantID;
- MTLFunctionConstant* mtlFC = getFunctionConstant(mtlFCs, mtlFCIndex);
- if (mtlFC) {
- [mtlFCVals setConstantValue: &(((char*)pSpecializationInfo->pData)[pMapEntry->offset])
- type: mtlFC.type
- atIndex: mtlFCIndex];
- }
- }
- }
+ if (mtlFunc) {
+ // If the Metal device supports shader specialization, and the Metal function expects to be
+ // specialized, populate Metal function constant values from the Vulkan specialization info,
+ // and compiled a specialized Metal function, otherwise simply use the unspecialized Metal function.
+ if (mvkDev->_pMetalFeatures->shaderSpecialization) {
+ NSArray<MTLFunctionConstant*>* mtlFCs = mtlFunc.functionConstantsDictionary.allValues;
+ if (mtlFCs.count) {
+ // The Metal shader contains function constants and expects to be specialized
+ // Populate the Metal function constant values from the Vulkan specialization info.
+ MTLFunctionConstantValues* mtlFCVals = [MTLFunctionConstantValues new]; // temp retain
+ if (pSpecializationInfo) {
+ // Iterate through the provided Vulkan specialization entries, and populate the
+ // Metal function constant value that matches the Vulkan specialization constantID.
+ for (uint32_t specIdx = 0; specIdx < pSpecializationInfo->mapEntryCount; specIdx++) {
+ const VkSpecializationMapEntry* pMapEntry = &pSpecializationInfo->pMapEntries[specIdx];
+ NSUInteger mtlFCIndex = pMapEntry->constantID;
+ MTLFunctionConstant* mtlFC = getFunctionConstant(mtlFCs, mtlFCIndex);
+ if (mtlFC) {
+ [mtlFCVals setConstantValue: &(((char*)pSpecializationInfo->pData)[pMapEntry->offset])
+ type: mtlFC.type
+ atIndex: mtlFCIndex];
+ }
+ }
+ }
- // Compile the specialized Metal function, and use it instead of the unspecialized Metal function.
- MVKFunctionSpecializer* fs = new MVKFunctionSpecializer(_owner);
- mtlFunc = [fs->newMTLFunction(_mtlLibrary, mtlFuncName, mtlFCVals) autorelease];
- fs->destroy();
- }
- }
- } else {
- reportError(VK_ERROR_INVALID_SHADER_NV, "Shader module does not contain an entry point named '%s'.", mtlFuncName.UTF8String);
- }
+ // Compile the specialized Metal function, and use it instead of the unspecialized Metal function.
+ MVKFunctionSpecializer* fs = new MVKFunctionSpecializer(_owner);
+ mtlFunc = fs->newMTLFunction(_mtlLibrary, mtlFuncName, mtlFCVals); // retained
+ fs->destroy();
+ [mtlFCVals release]; // release temp
+ }
+ }
+ } else {
+ reportError(VK_ERROR_INVALID_SHADER_NV, "Shader module does not contain an entry point named '%s'.", mtlFuncName.UTF8String);
+ }
- // Set the debug name. First try name of shader module, otherwise try name of owner.
- NSString* dbName = shaderModule-> getDebugName();
- if ( !dbName ) { dbName = _owner-> getDebugName(); }
- setLabelIfNotNil(mtlFunc, dbName);
-
+ // Set the debug name. First try name of shader module, otherwise try name of owner.
+ NSString* dbName = shaderModule-> getDebugName();
+ if ( !dbName ) { dbName = _owner-> getDebugName(); }
+ setLabelIfNotNil(mtlFunc, dbName);
+ }
auto& wgSize = _shaderConversionResults.entryPoint.workgroupSize;
- return { mtlFunc, _shaderConversionResults, MTLSizeMake(getWorkgroupDimensionSize(wgSize.width, pSpecializationInfo),
- getWorkgroupDimensionSize(wgSize.height, pSpecializationInfo),
- getWorkgroupDimensionSize(wgSize.depth, pSpecializationInfo))};
+ return { [mtlFunc autorelease], _shaderConversionResults, MTLSizeMake(getWorkgroupDimensionSize(wgSize.width, pSpecializationInfo),
+ getWorkgroupDimensionSize(wgSize.height, pSpecializationInfo),
+ getWorkgroupDimensionSize(wgSize.depth, pSpecializationInfo))};
}
// Returns the MTLFunctionConstant with the specified ID from the specified array of function constants.
@@ -124,7 +127,11 @@
const string& mslSourceCode,
const SPIRVToMSLConversionResults& shaderConversionResults) : _owner(owner) {
MVKShaderLibraryCompiler* slc = new MVKShaderLibraryCompiler(_owner);
- _mtlLibrary = slc->newMTLLibrary(@(mslSourceCode.c_str())); // retained
+
+ NSString* nsSrc = [[NSString alloc] initWithUTF8String: mslSourceCode.c_str()]; // temp retained
+ _mtlLibrary = slc->newMTLLibrary(nsSrc); // retained
+ [nsSrc release]; // release temp string
+
slc->destroy();
_shaderConversionResults = shaderConversionResults;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm
index 992f1cb..c45c8a6 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm
@@ -38,7 +38,9 @@
if (_debugName) {
size_t imgCnt = _surfaceImages.size();
for (size_t imgIdx = 0; imgIdx < imgCnt; imgIdx++) {
- _surfaceImages[imgIdx]->setDebugName([NSString stringWithFormat: @"%@(%lu)", _debugName, imgIdx].UTF8String);
+ NSString* nsName = [[NSString alloc] initWithFormat: @"%@(%lu)", _debugName, imgIdx]; // temp retain
+ _surfaceImages[imgIdx]->setDebugName(nsName.UTF8String);
+ [nsName release]; // release temp string
}
}
}
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm
index 171df2a..f0a1b1c 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm
@@ -215,8 +215,10 @@
_blocker.wait_for(lock, nanoTimeout, [this]{ return _isCompileDone; });
if ( !_isCompileDone ) {
- NSString* errDesc = [NSString stringWithFormat: @"Timeout after %.3f milliseconds. Likely internal Metal compiler error", (double)nanoTimeout.count() / 1e6];
- _compileError = [[NSError alloc] initWithDomain: @"MoltenVK" code: 1 userInfo: @{NSLocalizedDescriptionKey : errDesc}]; // retained
+ @autoreleasepool {
+ NSString* errDesc = [NSString stringWithFormat: @"Timeout after %.3f milliseconds. Likely internal Metal compiler error", (double)nanoTimeout.count() / 1e6];
+ _compileError = [[NSError alloc] initWithDomain: @"MoltenVK" code: 1 userInfo: @{NSLocalizedDescriptionKey : errDesc}]; // retained
+ }
}
if (_compileError) { handleError(); }
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.mm b/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.mm
index 6a390b1..109844d 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.mm
@@ -39,7 +39,7 @@
VkResult MVKVulkanAPIObject::setDebugName(const char* pObjectName) {
if (pObjectName) {
[_debugName release];
- _debugName = [[NSString stringWithUTF8String: pObjectName] retain]; // retained
+ _debugName = [[NSString alloc] initWithUTF8String: pObjectName]; // retained
propogateDebugName();
}
return VK_SUCCESS;
diff --git a/MoltenVK/MoltenVK/OS/MVKGPUCapture.mm b/MoltenVK/MoltenVK/OS/MVKGPUCapture.mm
index 156a16e..4ae0de9 100644
--- a/MoltenVK/MoltenVK/OS/MVKGPUCapture.mm
+++ b/MoltenVK/MoltenVK/OS/MVKGPUCapture.mm
@@ -59,8 +59,10 @@
MVKGPUCaptureScope::MVKGPUCaptureScope(MVKQueue* mvkQueue, const char* purpose) : _queue(mvkQueue) {
_mtlQueue = [_queue->getMTLCommandQueue() retain]; // retained
if (mvkOSVersion() >= kMinOSVersionMTLCaptureScope) {
+ NSString* nsQLbl = [[NSString alloc] initWithUTF8String: (_queue->getName() + "-" + purpose).c_str()]; // temp retained
_mtlCaptureScope = [[MTLCaptureManager sharedCaptureManager] newCaptureScopeWithCommandQueue: _mtlQueue]; // retained
- _mtlCaptureScope.label = @((_queue->getName() + "-" + purpose).c_str());
+ _mtlCaptureScope.label = nsQLbl;
+ [nsQLbl release]; // release temp
}
}
diff --git a/MoltenVK/MoltenVK/Utility/MVKWatermark.mm b/MoltenVK/MoltenVK/Utility/MVKWatermark.mm
index db7740c..aa06d51 100644
--- a/MoltenVK/MoltenVK/Utility/MVKWatermark.mm
+++ b/MoltenVK/MoltenVK/Utility/MVKWatermark.mm
@@ -304,9 +304,11 @@
// Initialize the shader functions for rendering the watermark
void MVKWatermark::initShaders(const char* mslSourceCode) {
NSError* err = nil;
- id<MTLLibrary> mtlLib = [[_mtlDevice newLibraryWithSource: @(mslSourceCode)
+ NSString* nsSrc = [[NSString alloc] initWithUTF8String: mslSourceCode]; // temp retained
+ id<MTLLibrary> mtlLib = [[_mtlDevice newLibraryWithSource: nsSrc
options: nil
error: &err] autorelease];
+ [nsSrc release]; // release temp string
MVKAssert( !err, "Could not compile watermark shaders (Error code %li):\n%s", (long)err.code, err.localizedDescription.UTF8String);
_mtlFunctionVertex = [mtlLib newFunctionWithName: @"watermarkVertex"]; // retained