Merge pull request #678 from cdavis5e/more-format-fixes
More format feature fixes.
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/Demos/LunarG-VulkanSamples/Cube/macOS/DemoViewController.m b/Demos/LunarG-VulkanSamples/Cube/macOS/DemoViewController.m
index 6018369..df24993 100644
--- a/Demos/LunarG-VulkanSamples/Cube/macOS/DemoViewController.m
+++ b/Demos/LunarG-VulkanSamples/Cube/macOS/DemoViewController.m
@@ -42,8 +42,12 @@
[super viewDidLoad];
self.view.wantsLayer = YES; // Back the view with a layer created by the makeBackingLayer method.
- const char* arg = "cube";
- demo_main(&demo, self.view.layer, 1, &arg);
+
+ uint32_t argc = 2;
+ const char* argv[argc];
+ argv[0] = "cube";
+ argv[1] = "--use_staging";
+ demo_main(&demo, self.view.layer, argc, argv);
CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
CVDisplayLinkSetOutputCallback(_displayLink, &DisplayLinkCallback, &demo);
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/MVKCmdTransfer.h b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h
index bd7110a..231924a 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h
@@ -67,8 +67,7 @@
bool _isSrcCompressed;
bool _isDstCompressed;
bool _canCopyFormats;
- bool _shouldUseTextureView;
- bool _shouldUseTempBuffer;
+ bool _useTempBuffer;
std::vector<VkImageCopy> _imageCopyRegions;
std::vector<VkBufferImageCopy> _srcTmpBuffImgCopies;
std::vector<VkBufferImageCopy> _dstTmpBuffImgCopies;
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
index c7b9087..40c1fa1 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
@@ -77,8 +77,7 @@
uint32_t dstBytesPerBlock = mvkMTLPixelFormatBytesPerBlock(_dstMTLPixFmt);
_canCopyFormats = (srcBytesPerBlock == dstBytesPerBlock) && (_srcSampleCount == _dstSampleCount);
- _shouldUseTextureView = (_srcMTLPixFmt != _dstMTLPixFmt) && !(_isSrcCompressed || _isDstCompressed); // Different formats and neither is compressed
- _shouldUseTempBuffer = (_srcMTLPixFmt != _dstMTLPixFmt) && (_isSrcCompressed || _isDstCompressed); // Different formats and at least one is compressed
+ _useTempBuffer = (_srcMTLPixFmt != _dstMTLPixFmt) && (_isSrcCompressed || _isDstCompressed); // Different formats and at least one is compressed
_commandUse = commandUse;
_tmpBuffSize = 0;
@@ -89,7 +88,7 @@
}
void MVKCmdCopyImage::addImageCopyRegion(const VkImageCopy& region) {
- if (_shouldUseTempBuffer) {
+ if (_useTempBuffer) {
addTempBufferImageCopyRegion(region); // Convert to image->buffer->image copies
} else {
_imageCopyRegions.push_back(region);
@@ -133,15 +132,14 @@
}
void MVKCmdCopyImage::encode(MVKCommandEncoder* cmdEncoder) {
- id<MTLTexture> srcMTLTex = _srcImage->getMTLTexture();
+ // Unless we need to use an intermediary buffer copy, map the source pixel format to the
+ // dest pixel format through a texture view on the source texture. If the source and dest
+ // pixel formats are the same, this will simply degenerate to the source texture itself.
+ MTLPixelFormat mapSrcMTLPixFmt = _useTempBuffer ? _srcMTLPixFmt : _dstMTLPixFmt;
+ id<MTLTexture> srcMTLTex = _srcImage->getMTLTexture(mapSrcMTLPixFmt);
id<MTLTexture> dstMTLTex = _dstImage->getMTLTexture();
if ( !srcMTLTex || !dstMTLTex ) { return; }
- // If the pixel formats are different but mappable, use a texture view on the source texture
- if (_shouldUseTextureView) {
- srcMTLTex = [[srcMTLTex newTextureViewWithPixelFormat: _dstMTLPixFmt] autorelease];
- }
-
id<MTLBlitCommandEncoder> mtlBlitEnc = cmdEncoder->getMTLBlitEncoder(_commandUse);
// If copies can be performed using direct texture-texture copying, do so
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h
index 0749be9..a14e2de 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h
@@ -357,8 +357,8 @@
*/
id<MTLDepthStencilState> newMTLDepthStencilState(MVKMTLDepthStencilDescriptorData& dsData);
- /** Returns an autoreleased MTLStencilDescriptor constructed from the stencil data. */
- MTLStencilDescriptor* getMTLStencilDescriptor(MVKMTLStencilDescriptorData& sData);
+ /** Returns an retained MTLStencilDescriptor constructed from the stencil data. */
+ MTLStencilDescriptor* newMTLStencilDescriptor(MVKMTLStencilDescriptorData& sData);
/**
* Returns a new MVKImage configured with content held in Private storage.
@@ -405,15 +405,15 @@
protected:
void initMTLLibrary();
void initImageDeviceMemory();
- id<MTLFunction> getBlitFragFunction(MVKRPSKeyBlitImg& blitKey);
- id<MTLFunction> getClearVertFunction(MVKRPSKeyClearAtt& attKey);
- id<MTLFunction> getClearFragFunction(MVKRPSKeyClearAtt& attKey);
+ id<MTLFunction> newBlitFragFunction(MVKRPSKeyBlitImg& blitKey);
+ id<MTLFunction> newClearVertFunction(MVKRPSKeyClearAtt& attKey);
+ id<MTLFunction> newClearFragFunction(MVKRPSKeyClearAtt& attKey);
NSString* getMTLFormatTypeString(MTLPixelFormat mtlPixFmt);
- id<MTLFunction> getFunctionNamed(const char* funcName);
+ id<MTLFunction> newFunctionNamed(const char* funcName);
id<MTLFunction> newMTLFunction(NSString* mslSrcCode, NSString* funcName);
id<MTLRenderPipelineState> newMTLRenderPipelineState(MTLRenderPipelineDescriptor* plDesc,
MVKVulkanAPIDeviceObject* owner);
- id<MTLComputePipelineState> newMTLComputePipelineState(id<MTLFunction> mtlFunction,
+ id<MTLComputePipelineState> newMTLComputePipelineState(const char* funcName,
MVKVulkanAPIDeviceObject* owner);
id<MTLLibrary> _mtlLibrary;
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm
index bf86b8f..20b6f32 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm
@@ -33,11 +33,13 @@
id<MTLRenderPipelineState> MVKCommandResourceFactory::newCmdBlitImageMTLRenderPipelineState(MVKRPSKeyBlitImg& blitKey,
MVKVulkanAPIDeviceObject* owner) {
- MTLRenderPipelineDescriptor* plDesc = [[[MTLRenderPipelineDescriptor alloc] init] autorelease];
+ id<MTLFunction> vtxFunc = newFunctionNamed("vtxCmdBlitImage"); // temp retain
+ id<MTLFunction> fragFunc = newBlitFragFunction(blitKey); // temp retain
+ MTLRenderPipelineDescriptor* plDesc = [MTLRenderPipelineDescriptor new]; // temp retain
plDesc.label = @"CmdBlitImage";
- plDesc.vertexFunction = getFunctionNamed("vtxCmdBlitImage");
- plDesc.fragmentFunction = getBlitFragFunction(blitKey);
+ plDesc.vertexFunction = vtxFunc;
+ plDesc.fragmentFunction = fragFunc;
plDesc.sampleCount = blitKey.dstSampleCount;
plDesc.colorAttachments[0].pixelFormat = blitKey.getDstMTLPixelFormat();
@@ -71,12 +73,18 @@
vbDesc.stepRate = 1;
vbDesc.stride = vtxStride;
- return newMTLRenderPipelineState(plDesc, owner);
+ id<MTLRenderPipelineState> rps = newMTLRenderPipelineState(plDesc, owner);
+
+ [vtxFunc release]; // temp release
+ [fragFunc release]; // temp release
+ [plDesc release]; // temp release
+
+ return rps;
}
id<MTLSamplerState> MVKCommandResourceFactory::newCmdBlitImageMTLSamplerState(MTLSamplerMinMagFilter mtlFilter) {
- MTLSamplerDescriptor* sDesc = [[[MTLSamplerDescriptor alloc] init] autorelease];
+ MTLSamplerDescriptor* sDesc = [MTLSamplerDescriptor new]; // temp retain
sDesc.rAddressMode = MTLSamplerAddressModeClampToZero;
sDesc.sAddressMode = MTLSamplerAddressModeClampToZero;
sDesc.tAddressMode = MTLSamplerAddressModeClampToZero;
@@ -84,15 +92,22 @@
sDesc.normalizedCoordinates = YES;
sDesc.minFilter = mtlFilter;
sDesc.magFilter = mtlFilter;
- return [getMTLDevice() newSamplerStateWithDescriptor: sDesc];
+
+ id<MTLSamplerState> ss = [getMTLDevice() newSamplerStateWithDescriptor: sDesc];
+
+ [sDesc release]; // temp release
+
+ return ss;
}
id<MTLRenderPipelineState> MVKCommandResourceFactory::newCmdClearMTLRenderPipelineState(MVKRPSKeyClearAtt& attKey,
MVKVulkanAPIDeviceObject* owner) {
- MTLRenderPipelineDescriptor* plDesc = [[[MTLRenderPipelineDescriptor alloc] init] autorelease];
+ id<MTLFunction> vtxFunc = newClearVertFunction(attKey); // temp retain
+ id<MTLFunction> fragFunc = newClearFragFunction(attKey); // temp retain
+ MTLRenderPipelineDescriptor* plDesc = [MTLRenderPipelineDescriptor new]; // temp retain
plDesc.label = @"CmdClearAttachments";
- plDesc.vertexFunction = getClearVertFunction(attKey);
- plDesc.fragmentFunction = getClearFragFunction(attKey);
+ plDesc.vertexFunction = vtxFunc;
+ plDesc.fragmentFunction = fragFunc;
plDesc.sampleCount = attKey.mtlSampleCount;
plDesc.inputPrimitiveTopologyMVK = MTLPrimitiveTopologyClassTriangle;
@@ -127,19 +142,23 @@
vbDesc.stepRate = 1;
vbDesc.stride = vtxStride;
- return newMTLRenderPipelineState(plDesc, owner);
+ id<MTLRenderPipelineState> rps = newMTLRenderPipelineState(plDesc, owner);
+
+ [vtxFunc release]; // temp release
+ [fragFunc release]; // temp release
+ [plDesc release]; // temp release
+
+ return rps;
}
-id<MTLFunction> MVKCommandResourceFactory::getBlitFragFunction(MVKRPSKeyBlitImg& blitKey) {
- id<MTLFunction> mtlFunc = nil;
-
- NSString* typeStr = getMTLFormatTypeString(blitKey.getSrcMTLPixelFormat());
-
- bool isArrayType = blitKey.isSrcArrayType();
- NSString* arraySuffix = isArrayType ? @"_array" : @"";
- NSString* sliceArg = isArrayType ? @", srcSlice" : @"";
-
+id<MTLFunction> MVKCommandResourceFactory::newBlitFragFunction(MVKRPSKeyBlitImg& blitKey) {
@autoreleasepool {
+ NSString* typeStr = getMTLFormatTypeString(blitKey.getSrcMTLPixelFormat());
+
+ bool isArrayType = blitKey.isSrcArrayType();
+ NSString* arraySuffix = isArrayType ? @"_array" : @"";
+ NSString* sliceArg = isArrayType ? @", srcSlice" : @"";
+
NSMutableString* msl = [NSMutableString stringWithCapacity: (2 * KIBI) ];
[msl appendLineMVK: @"#include <metal_stdlib>"];
[msl appendLineMVK: @"using namespace metal;"];
@@ -161,14 +180,13 @@
[msl appendLineMVK];
[msl appendLineMVK: @"}"];
- mtlFunc = newMTLFunction(msl, funcName);
// MVKLogDebug("\n%s", msl.UTF8String);
+
+ return newMTLFunction(msl, funcName);
}
- return [mtlFunc autorelease];
}
-id<MTLFunction> MVKCommandResourceFactory::getClearVertFunction(MVKRPSKeyClearAtt& attKey) {
- id<MTLFunction> mtlFunc = nil;
+id<MTLFunction> MVKCommandResourceFactory::newClearVertFunction(MVKRPSKeyClearAtt& attKey) {
@autoreleasepool {
NSMutableString* msl = [NSMutableString stringWithCapacity: (2 * KIBI) ];
[msl appendLineMVK: @"#include <metal_stdlib>"];
@@ -197,14 +215,13 @@
[msl appendLineMVK: @" return varyings;"];
[msl appendLineMVK: @"}"];
- mtlFunc = newMTLFunction(msl, funcName);
// MVKLogDebug("\n%s", msl.UTF8String);
+
+ return newMTLFunction(msl, funcName);
}
- return [mtlFunc autorelease];
}
-id<MTLFunction> MVKCommandResourceFactory::getClearFragFunction(MVKRPSKeyClearAtt& attKey) {
- id<MTLFunction> mtlFunc = nil;
+id<MTLFunction> MVKCommandResourceFactory::newClearFragFunction(MVKRPSKeyClearAtt& attKey) {
@autoreleasepool {
NSMutableString* msl = [NSMutableString stringWithCapacity: (2 * KIBI) ];
[msl appendLineMVK: @"#include <metal_stdlib>"];
@@ -243,10 +260,10 @@
[msl appendLineMVK: @" return ccOut;"];
[msl appendLineMVK: @"}"];
- mtlFunc = newMTLFunction(msl, funcName);
// MVKLogDebug("\n%s", msl.UTF8String);
+
+ return newMTLFunction(msl, funcName);
}
- return [mtlFunc autorelease];
}
NSString* MVKCommandResourceFactory::getMTLFormatTypeString(MTLPixelFormat mtlPixFmt) {
@@ -265,12 +282,12 @@
id<MTLDepthStencilState> MVKCommandResourceFactory::newMTLDepthStencilState(bool useDepth, bool useStencil) {
- MTLDepthStencilDescriptor* dsDesc = [[[MTLDepthStencilDescriptor alloc] init] autorelease];
+ MTLDepthStencilDescriptor* dsDesc = [MTLDepthStencilDescriptor new]; // temp retain
dsDesc.depthCompareFunction = MTLCompareFunctionAlways;
dsDesc.depthWriteEnabled = useDepth;
if (useStencil) {
- MTLStencilDescriptor* sDesc = [[[MTLStencilDescriptor alloc] init] autorelease];
+ MTLStencilDescriptor* sDesc = [MTLStencilDescriptor new]; // temp retain
sDesc.stencilCompareFunction = MTLCompareFunctionAlways;
sDesc.stencilFailureOperation = MTLStencilOperationReplace;
sDesc.depthFailureOperation = MTLStencilOperationReplace;
@@ -278,28 +295,42 @@
dsDesc.frontFaceStencil = sDesc;
dsDesc.backFaceStencil = sDesc;
+
+ [sDesc release]; // temp release
} else {
dsDesc.frontFaceStencil = nil;
dsDesc.backFaceStencil = nil;
}
- return [getMTLDevice() newDepthStencilStateWithDescriptor: dsDesc];
+ id<MTLDepthStencilState> dss = [getMTLDevice() newDepthStencilStateWithDescriptor: dsDesc];
+
+ [dsDesc release]; // temp release
+
+ return dss;
}
id<MTLDepthStencilState> MVKCommandResourceFactory::newMTLDepthStencilState(MVKMTLDepthStencilDescriptorData& dsData) {
- MTLDepthStencilDescriptor* dsDesc = [[[MTLDepthStencilDescriptor alloc] init] autorelease];
+ MTLStencilDescriptor* fsDesc = newMTLStencilDescriptor(dsData.frontFaceStencilData); // temp retain
+ MTLStencilDescriptor* bsDesc = newMTLStencilDescriptor(dsData.backFaceStencilData); // temp retain
+ MTLDepthStencilDescriptor* dsDesc = [MTLDepthStencilDescriptor new]; // temp retain
dsDesc.depthCompareFunction = (MTLCompareFunction)dsData.depthCompareFunction;
dsDesc.depthWriteEnabled = dsData.depthWriteEnabled;
- dsDesc.frontFaceStencil = getMTLStencilDescriptor(dsData.frontFaceStencilData);
- dsDesc.backFaceStencil = getMTLStencilDescriptor(dsData.backFaceStencilData);
+ dsDesc.frontFaceStencil = fsDesc;
+ dsDesc.backFaceStencil = bsDesc;
- return [getMTLDevice() newDepthStencilStateWithDescriptor: dsDesc];
+ id<MTLDepthStencilState> dss = [getMTLDevice() newDepthStencilStateWithDescriptor: dsDesc];
+
+ [fsDesc release]; // temp release
+ [bsDesc release]; // temp release
+ [dsDesc release]; // temp release
+
+ return dss;
}
-MTLStencilDescriptor* MVKCommandResourceFactory::getMTLStencilDescriptor(MVKMTLStencilDescriptorData& sData) {
+MTLStencilDescriptor* MVKCommandResourceFactory::newMTLStencilDescriptor(MVKMTLStencilDescriptorData& sData) {
if ( !sData.enabled ) { return nil; }
- MTLStencilDescriptor* sDesc = [[[MTLStencilDescriptor alloc] init] autorelease];
+ MTLStencilDescriptor* sDesc = [MTLStencilDescriptor new]; // retained
sDesc.stencilCompareFunction = (MTLCompareFunction)sData.stencilCompareFunction;
sDesc.stencilFailureOperation = (MTLStencilOperation)sData.stencilFailureOperation;
sDesc.depthFailureOperation = (MTLStencilOperation)sData.depthFailureOperation;
@@ -362,66 +393,75 @@
}
id<MTLComputePipelineState> MVKCommandResourceFactory::newCmdCopyBufferBytesMTLComputePipelineState(MVKVulkanAPIDeviceObject* owner) {
- return newMTLComputePipelineState(getFunctionNamed("cmdCopyBufferBytes"), owner);
+ return newMTLComputePipelineState("cmdCopyBufferBytes", owner);
}
id<MTLComputePipelineState> MVKCommandResourceFactory::newCmdFillBufferMTLComputePipelineState(MVKVulkanAPIDeviceObject* owner) {
- return newMTLComputePipelineState(getFunctionNamed("cmdFillBuffer"), owner);
+ return newMTLComputePipelineState("cmdFillBuffer", owner);
}
id<MTLComputePipelineState> MVKCommandResourceFactory::newCmdCopyBufferToImage3DDecompressMTLComputePipelineState(bool needTempBuf,
MVKVulkanAPIDeviceObject* owner) {
- return newMTLComputePipelineState(getFunctionNamed(needTempBuf
- ? "cmdCopyBufferToImage3DDecompressTempBufferDXTn"
- : "cmdCopyBufferToImage3DDecompressDXTn"), owner);
+ return newMTLComputePipelineState(needTempBuf
+ ? "cmdCopyBufferToImage3DDecompressTempBufferDXTn"
+ : "cmdCopyBufferToImage3DDecompressDXTn", owner);
}
id<MTLComputePipelineState> MVKCommandResourceFactory::newCmdDrawIndirectConvertBuffersMTLComputePipelineState(bool indexed,
MVKVulkanAPIDeviceObject* owner) {
- return newMTLComputePipelineState(getFunctionNamed(indexed
- ? "cmdDrawIndexedIndirectConvertBuffers"
- : "cmdDrawIndirectConvertBuffers"), owner);
+ return newMTLComputePipelineState(indexed
+ ? "cmdDrawIndexedIndirectConvertBuffers"
+ : "cmdDrawIndirectConvertBuffers", owner);
}
id<MTLComputePipelineState> MVKCommandResourceFactory::newCmdDrawIndexedCopyIndexBufferMTLComputePipelineState(MTLIndexType type,
MVKVulkanAPIDeviceObject* owner) {
- return newMTLComputePipelineState(getFunctionNamed(type == MTLIndexTypeUInt16
- ? "cmdDrawIndexedCopyIndex16Buffer"
- : "cmdDrawIndexedCopyIndex32Buffer"), owner);
+ return newMTLComputePipelineState(type == MTLIndexTypeUInt16
+ ? "cmdDrawIndexedCopyIndex16Buffer"
+ : "cmdDrawIndexedCopyIndex32Buffer", owner);
}
id<MTLComputePipelineState> MVKCommandResourceFactory::newCmdCopyQueryPoolResultsMTLComputePipelineState(MVKVulkanAPIDeviceObject* owner) {
- return newMTLComputePipelineState(getFunctionNamed("cmdCopyQueryPoolResultsToBuffer"), owner);
+ return newMTLComputePipelineState("cmdCopyQueryPoolResultsToBuffer", owner);
}
#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;
+// Returns the retained MTLFunction with the name.
+// The caller is responsible for releasing the returned function object.
+id<MTLFunction> MVKCommandResourceFactory::newFunctionNamed(const char* funcName) {
+ uint64_t startTime = _device->getPerformanceTimestamp();
+ NSString* nsFuncName = [[NSString alloc] initWithUTF8String: funcName]; // temp retained
+ id<MTLFunction> mtlFunc = [_mtlLibrary newFunctionWithName: nsFuncName]; // retained
+ [nsFuncName release]; // temp release
+ _device->addActivityPerformance(_device->_performanceStatistics.shaderCompilation.functionRetrieval, startTime);
+ return mtlFunc;
}
id<MTLFunction> MVKCommandResourceFactory::newMTLFunction(NSString* mslSrcCode, NSString* funcName) {
@autoreleasepool {
+ id<MTLFunction> mtlFunc = nil;
NSError* err = nil;
+
uint64_t startTime = _device->getPerformanceTimestamp();
- id<MTLLibrary> mtlLib = [[getMTLDevice() newLibraryWithSource: mslSrcCode
- options: getDevice()->getMTLCompileOptions()
- error: &err] autorelease];
+ id<MTLLibrary> mtlLib = [getMTLDevice() newLibraryWithSource: mslSrcCode
+ options: getDevice()->getMTLCompileOptions()
+ error: &err]; // temp retain
_device->addActivityPerformance(_device->_performanceStatistics.shaderCompilation.mslCompile, startTime);
+
if (err) {
reportError(VK_ERROR_INITIALIZATION_FAILED,
"Could not compile support shader from MSL source (Error code %li):\n%s\n%s",
(long)err.code, mslSrcCode.UTF8String, err.localizedDescription.UTF8String);
- return nil;
+ } else {
+ startTime = _device->getPerformanceTimestamp();
+ mtlFunc = [mtlLib newFunctionWithName: funcName];
+ _device->addActivityPerformance(_device->_performanceStatistics.shaderCompilation.functionRetrieval, startTime);
}
- startTime = _device->getPerformanceTimestamp();
- id<MTLFunction> mtlFunc = [mtlLib newFunctionWithName: funcName];
- _device->addActivityPerformance(_device->_performanceStatistics.shaderCompilation.functionRetrieval, startTime);
+ [mtlLib release]; // temp release
+
return mtlFunc;
}
}
@@ -434,11 +474,13 @@
return rps;
}
-id<MTLComputePipelineState> MVKCommandResourceFactory::newMTLComputePipelineState(id<MTLFunction> mtlFunction,
+id<MTLComputePipelineState> MVKCommandResourceFactory::newMTLComputePipelineState(const char* funcName,
MVKVulkanAPIDeviceObject* owner) {
+ id<MTLFunction> mtlFunc = newFunctionNamed(funcName); // temp retain
MVKComputePipelineCompiler* plc = new MVKComputePipelineCompiler(owner);
- id<MTLComputePipelineState> cps = plc->newMTLComputePipelineState(mtlFunction); // retained
+ id<MTLComputePipelineState> cps = plc->newMTLComputePipelineState(mtlFunc); // retained
plc->destroy();
+ [mtlFunc release]; // temp release
return cps;
}
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
index e22b724..ee21903 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
@@ -282,13 +282,18 @@
#pragma mark Metal
/** Populates the specified structure with the Metal-specific features of this device. */
- const MVKPhysicalDeviceMetalFeatures* getMetalFeatures() { return &_metalFeatures; }
+ inline const MVKPhysicalDeviceMetalFeatures* getMetalFeatures() { return &_metalFeatures; }
/** Returns the underlying Metal device. */
inline id<MTLDevice> getMTLDevice() { return _mtlDevice; }
/*** Replaces the underlying Metal device .*/
- inline void replaceMTLDevice(id<MTLDevice> mtlDevice) { [_mtlDevice autorelease]; _mtlDevice = [mtlDevice retain]; }
+ inline void replaceMTLDevice(id<MTLDevice> mtlDevice) {
+ if (mtlDevice != _mtlDevice) {
+ [_mtlDevice release];
+ _mtlDevice = [mtlDevice retain];
+ }
+ }
#pragma mark Construction
@@ -554,7 +559,7 @@
inline id<MTLDevice> getMTLDevice() { return _physicalDevice->getMTLDevice(); }
/** Returns standard compilation options to be used when compiling MSL shaders. */
- MTLCompileOptions* getMTLCompileOptions();
+ inline MTLCompileOptions* getMTLCompileOptions() { return _mtlCompileOptions; }
/** Returns the Metal vertex buffer index to use for the specified vertex attribute binding number. */
uint32_t getMetalBufferIndexForVertexAttributeBinding(uint32_t binding);
@@ -657,6 +662,7 @@
void initPerformanceTracking();
void initPhysicalDevice(MVKPhysicalDevice* physicalDevice);
void initQueues(const VkDeviceCreateInfo* pCreateInfo);
+ void initMTLCompileOptions();
void enableFeatures(const VkDeviceCreateInfo* pCreateInfo);
void enableFeatures(const VkBool32* pEnable, const VkBool32* pRequested, const VkBool32* pAvailable, uint32_t count);
void enableExtensions(const VkDeviceCreateInfo* pCreateInfo);
@@ -667,6 +673,7 @@
MVKPhysicalDevice* _physicalDevice;
MVKCommandResourceFactory* _commandResourceFactory;
+ MTLCompileOptions* _mtlCompileOptions;
std::vector<std::vector<MVKQueue*>> _queuesByQueueFamilyIndex;
std::vector<MVKResource*> _resources;
std::mutex _rezLock;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index a50cb46..8586b29 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -1711,9 +1711,11 @@
#endif
- MVKLogInfo(logMsg.c_str(), _properties.deviceName, devTypeStr.c_str(), _properties.vendorID, _properties.deviceID,
- [[[NSUUID alloc] initWithUUIDBytes: _properties.pipelineCacheUUID] autorelease].UUIDString.UTF8String,
+ NSUUID* nsUUID = [[NSUUID alloc] initWithUUIDBytes: _properties.pipelineCacheUUID]; // temp retain
+ MVKLogInfo(logMsg.c_str(), _properties.deviceName, devTypeStr.c_str(),
+ _properties.vendorID, _properties.deviceID, nsUUID.UUIDString.UTF8String,
SPIRVToMSLConversionOptions::printMSLVersion(_metalFeatures.mslVersion).c_str());
+ [nsUUID release]; // temp release
}
MVKPhysicalDevice::~MVKPhysicalDevice() {
@@ -2164,12 +2166,6 @@
#pragma mark Metal
-MTLCompileOptions* MVKDevice::getMTLCompileOptions() {
- MTLCompileOptions* opts = [[MTLCompileOptions new] autorelease];
- opts.languageVersion = _pMetalFeatures->mslVersionEnum;
- return opts;
-}
-
uint32_t MVKDevice::getMetalBufferIndexForVertexAttributeBinding(uint32_t binding) {
return ((_pMetalFeatures->maxPerStageBufferCount - 1) - binding);
}
@@ -2240,6 +2236,8 @@
_globalVisibilityResultMTLBuffer = nil;
_globalVisibilityQueryCount = 0;
+ initMTLCompileOptions(); // Before command resource factory
+
_commandResourceFactory = new MVKCommandResourceFactory(this);
initQueues(pCreateInfo);
@@ -2451,12 +2449,19 @@
}
}
+void MVKDevice::initMTLCompileOptions() {
+ _mtlCompileOptions = [MTLCompileOptions new]; // retained
+ _mtlCompileOptions.languageVersion = _pMetalFeatures->mslVersionEnum;
+}
+
MVKDevice::~MVKDevice() {
for (auto& queues : _queuesByQueueFamilyIndex) {
mvkDestroyContainerContents(queues);
}
- [_globalVisibilityResultMTLBuffer release];
_commandResourceFactory->destroy();
+
+ [_mtlCompileOptions release];
+ [_globalVisibilityResultMTLBuffer release];
}
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
index 0c5e3bf..2c03080 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
@@ -22,6 +22,7 @@
#include "MVKSync.h"
#include "MVKVector.h"
#include <MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h>
+#include <unordered_map>
#include <mutex>
#import <IOSurface/IOSurfaceRef.h>
@@ -154,6 +155,9 @@
/** Returns the Metal texture underlying this image. */
id<MTLTexture> getMTLTexture();
+ /** Returns a Metal texture that interprets the pixels in the specified format. */
+ id<MTLTexture> getMTLTexture(MTLPixelFormat mtlPixFmt);
+
/**
* Sets this image to use the specified MTLTexture.
*
@@ -243,7 +247,7 @@
virtual id<MTLTexture> newMTLTexture();
void resetMTLTexture();
void resetIOSurface();
- MTLTextureDescriptor* getMTLTextureDescriptor();
+ MTLTextureDescriptor* newMTLTextureDescriptor();
void updateMTLTextureContent(MVKImageSubresource& subresource, VkDeviceSize offset, VkDeviceSize size);
void getMTLTextureContent(MVKImageSubresource& subresource, VkDeviceSize offset, VkDeviceSize size);
bool shouldFlushHostMemory();
@@ -254,6 +258,7 @@
VkImageMemoryBarrier* pImageMemoryBarrier);
std::vector<MVKImageSubresource> _subresources;
+ std::unordered_map<NSUInteger, id<MTLTexture>> _mtlTextureViews;
VkExtent3D _extent;
uint32_t _mipLevels;
uint32_t _arrayLayers;
@@ -376,7 +381,7 @@
protected:
void propogateDebugName() override {}
- MTLSamplerDescriptor* getMTLSamplerDescriptor(const VkSamplerCreateInfo* pCreateInfo);
+ MTLSamplerDescriptor* newMTLSamplerDescriptor(const VkSamplerCreateInfo* pCreateInfo);
void initConstExprSampler(const VkSamplerCreateInfo* pCreateInfo);
id<MTLSamplerState> _mtlSamplerState;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index abda962..9204962 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -296,6 +296,22 @@
return _mtlTexture;
}
+id<MTLTexture> MVKImage::getMTLTexture(MTLPixelFormat mtlPixFmt) {
+ if (mtlPixFmt == _mtlPixelFormat) { return getMTLTexture(); }
+
+ id<MTLTexture> mtlTex = _mtlTextureViews[mtlPixFmt];
+ if ( !mtlTex ) {
+ // Lock and check again in case another thread has created the texture.
+ lock_guard<mutex> lock(_lock);
+ mtlTex = _mtlTextureViews[mtlPixFmt];
+ if ( !mtlTex ) {
+ mtlTex = [getMTLTexture() newTextureViewWithPixelFormat: mtlPixFmt]; // retained
+ _mtlTextureViews[mtlPixFmt] = mtlTex;
+ }
+ }
+ return mtlTex;
+}
+
VkResult MVKImage::setMTLTexture(id<MTLTexture> mtlTexture) {
lock_guard<mutex> lock(_lock);
resetMTLTexture();
@@ -325,15 +341,21 @@
// This implementation creates a new MTLTexture from a MTLTextureDescriptor and possible IOSurface.
// Subclasses may override this function to create the MTLTexture in a different manner.
id<MTLTexture> MVKImage::newMTLTexture() {
- if (_ioSurface) {
- return [getMTLDevice() newTextureWithDescriptor: getMTLTextureDescriptor() iosurface: _ioSurface plane: 0];
+ id<MTLTexture> mtlTex = nil;
+ MTLTextureDescriptor* mtlTexDesc = newMTLTextureDescriptor(); // temp retain
+
+ if (_ioSurface) {
+ mtlTex = [getMTLDevice() newTextureWithDescriptor: mtlTexDesc iosurface: _ioSurface plane: 0];
} else if (_usesTexelBuffer) {
- return [_deviceMemory->_mtlBuffer newTextureWithDescriptor: getMTLTextureDescriptor()
- offset: getDeviceMemoryOffset()
- bytesPerRow: _subresources[0].layout.rowPitch];
- } else {
- return [getMTLDevice() newTextureWithDescriptor: getMTLTextureDescriptor()];
- }
+ mtlTex = [_deviceMemory->_mtlBuffer newTextureWithDescriptor: mtlTexDesc
+ offset: getDeviceMemoryOffset()
+ bytesPerRow: _subresources[0].layout.rowPitch];
+ } else {
+ mtlTex = [getMTLDevice() newTextureWithDescriptor: mtlTexDesc];
+ }
+
+ [mtlTexDesc release]; // temp release
+ return mtlTex;
}
// Removes and releases the MTLTexture object, so that it can be lazily created by getMTLTexture().
@@ -372,14 +394,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
}
@@ -418,9 +442,10 @@
return usage;
}
-// Returns an autoreleased Metal texture descriptor constructed from the properties of this image.
-MTLTextureDescriptor* MVKImage::getMTLTextureDescriptor() {
- MTLTextureDescriptor* mtlTexDesc = [[MTLTextureDescriptor alloc] init];
+// Returns a Metal texture descriptor constructed from the properties of this image.
+// It is the caller's responsibility to release the returned descriptor object.
+MTLTextureDescriptor* MVKImage::newMTLTextureDescriptor() {
+ MTLTextureDescriptor* mtlTexDesc = [MTLTextureDescriptor new]; // retained
#if MVK_MACOS
if (_is3DCompressed) {
// Metal doesn't yet support 3D compressed textures, so we'll decompress
@@ -443,7 +468,7 @@
mtlTexDesc.storageModeMVK = getMTLStorageMode();
mtlTexDesc.cpuCacheMode = getMTLCPUCacheMode();
- return [mtlTexDesc autorelease];
+ return mtlTexDesc;
}
MTLStorageMode MVKImage::getMTLStorageMode() {
@@ -770,6 +795,7 @@
if (_deviceMemory) { _deviceMemory->removeImage(this); }
resetMTLTexture();
resetIOSurface();
+ for (auto elem : _mtlTextureViews) { [elem.second release]; }
}
@@ -1056,10 +1082,11 @@
return _requiresConstExprSampler;
}
-// Returns an autoreleased Metal sampler descriptor constructed from the properties of this image.
-MTLSamplerDescriptor* MVKSampler::getMTLSamplerDescriptor(const VkSamplerCreateInfo* pCreateInfo) {
+// Returns an Metal sampler descriptor constructed from the properties of this image.
+// It is the caller's responsibility to release the returned descriptor object.
+MTLSamplerDescriptor* MVKSampler::newMTLSamplerDescriptor(const VkSamplerCreateInfo* pCreateInfo) {
- MTLSamplerDescriptor* mtlSampDesc = [[MTLSamplerDescriptor alloc] init];
+ MTLSamplerDescriptor* mtlSampDesc = [MTLSamplerDescriptor new]; // retained
mtlSampDesc.sAddressMode = mvkMTLSamplerAddressModeFromVkSamplerAddressMode(pCreateInfo->addressModeU);
mtlSampDesc.tAddressMode = mvkMTLSamplerAddressModeFromVkSamplerAddressMode(pCreateInfo->addressModeV);
mtlSampDesc.rAddressMode = mvkMTLSamplerAddressModeFromVkSamplerAddressMode(pCreateInfo->addressModeW);
@@ -1097,12 +1124,16 @@
}
}
#endif
- return [mtlSampDesc autorelease];
+ return mtlSampDesc;
}
MVKSampler::MVKSampler(MVKDevice* device, const VkSamplerCreateInfo* pCreateInfo) : MVKVulkanAPIDeviceObject(device) {
_requiresConstExprSampler = pCreateInfo->compareEnable && !_device->_pMetalFeatures->depthSampleCompare;
- _mtlSamplerState = [getMTLDevice() newSamplerStateWithDescriptor: getMTLSamplerDescriptor(pCreateInfo)];
+
+ MTLSamplerDescriptor* mtlSampDesc = newMTLSamplerDescriptor(pCreateInfo); // temp retain
+ _mtlSamplerState = [getMTLDevice() newSamplerStateWithDescriptor: mtlSampDesc];
+ [mtlSampDesc release]; // temp release
+
initConstExprSampler(pCreateInfo);
}
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/MVKPipeline.h b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
index 7b56702..99bb888 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
@@ -240,10 +240,10 @@
void initMVKShaderConverterContext(SPIRVToMSLConversionConfiguration& _shaderContext, const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData);
void addVertexInputToShaderConverterContext(SPIRVToMSLConversionConfiguration& shaderContext, const VkGraphicsPipelineCreateInfo* pCreateInfo);
void addPrevStageOutputToShaderConverterContext(SPIRVToMSLConversionConfiguration& shaderContext, std::vector<SPIRVShaderOutput>& outputs);
- MTLRenderPipelineDescriptor* getMTLRenderPipelineDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData);
- MTLRenderPipelineDescriptor* getMTLTessVertexStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderContext);
- MTLComputePipelineDescriptor* getMTLTessControlStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderContext);
- MTLRenderPipelineDescriptor* getMTLTessRasterStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderContext);
+ MTLRenderPipelineDescriptor* newMTLRenderPipelineDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData);
+ MTLRenderPipelineDescriptor* newMTLTessVertexStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderContext);
+ MTLComputePipelineDescriptor* newMTLTessControlStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderContext);
+ MTLRenderPipelineDescriptor* newMTLTessRasterStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderContext);
bool addVertexShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderContext);
bool addTessCtlShaderToPipeline(MTLComputePipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderContext, std::vector<SPIRVShaderOutput>& prevOutput);
bool addTessEvalShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderContext, std::vector<SPIRVShaderOutput>& prevOutput);
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
index 84fa930..c21340d 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
@@ -206,7 +206,7 @@
id<MTLComputePipelineState> plState;
const char* compilerType = "Tessellation control stage pipeline";
const MVKIndexMTLBufferBinding& indexBuff = cmdEncoder->_graphicsResourcesState._mtlIndexBufferBinding;
- MTLComputePipelineDescriptor* plDesc = [[_mtlTessControlStageDesc copy] autorelease]; // Use a copy to be thread-safe.
+ MTLComputePipelineDescriptor* plDesc = [_mtlTessControlStageDesc copy]; // temp retain a copy to be thread-safe.
if (!indexBuff.mtlBuffer && getInputControlPointCount() >= getOutputControlPointCount()) {
plState = getOrCompilePipeline(plDesc, _mtlTessControlStageState, compilerType);
} else if (indexBuff.mtlIndexType == MTLIndexTypeUInt16) {
@@ -218,6 +218,8 @@
plDesc.stageInputDescriptor.layouts[kMVKTessCtlInputBufferIndex].stepFunction = MTLStepFunctionThreadPositionInGridXIndexed;
plState = getOrCompilePipeline(plDesc, _mtlTessControlStageIndex32State, compilerType);
}
+ [plDesc release]; // temp release
+
if ( !_hasValidMTLPipelineStates ) { return; }
id<MTLComputeCommandEncoder> tessCtlEnc = cmdEncoder->getMTLComputeEncoder(kMVKCommandUseTessellationControl);
@@ -426,10 +428,11 @@
_mtlPipelineState = nil;
_mtlTessControlStageDesc = nil;
if (!isTessellationPipeline()) {
- MTLRenderPipelineDescriptor* plDesc = getMTLRenderPipelineDescriptor(pCreateInfo, reflectData);
+ MTLRenderPipelineDescriptor* plDesc = newMTLRenderPipelineDescriptor(pCreateInfo, reflectData); // temp retain
if (plDesc) {
getOrCompilePipeline(plDesc, _mtlPipelineState);
}
+ [plDesc release]; // temp release
} else {
// In this case, we need to create three render pipelines. But, the way Metal handles
// index buffers for compute stage-in means we have to defer creation of stage 2 until
@@ -437,24 +440,27 @@
SPIRVToMSLConversionConfiguration shaderContext;
initMVKShaderConverterContext(shaderContext, pCreateInfo, reflectData);
- MTLRenderPipelineDescriptor* vtxPLDesc = getMTLTessVertexStageDescriptor(pCreateInfo, reflectData, shaderContext);
- _mtlTessControlStageDesc = getMTLTessControlStageDescriptor(pCreateInfo, reflectData, shaderContext); // retained
- MTLRenderPipelineDescriptor* rastPLDesc = getMTLTessRasterStageDescriptor(pCreateInfo, reflectData, shaderContext);
+ MTLRenderPipelineDescriptor* vtxPLDesc = newMTLTessVertexStageDescriptor(pCreateInfo, reflectData, shaderContext); // temp retain
+ _mtlTessControlStageDesc = newMTLTessControlStageDescriptor(pCreateInfo, reflectData, shaderContext); // retained
+ MTLRenderPipelineDescriptor* rastPLDesc = newMTLTessRasterStageDescriptor(pCreateInfo, reflectData, shaderContext); // temp retained
if (vtxPLDesc && _mtlTessControlStageDesc && rastPLDesc) {
if (getOrCompilePipeline(vtxPLDesc, _mtlTessVertexStageState)) {
getOrCompilePipeline(rastPLDesc, _mtlPipelineState);
}
}
+ [vtxPLDesc release]; // temp release
+ [rastPLDesc release]; // temp release
}
}
-// Returns a MTLRenderPipelineDescriptor constructed from this instance, or nil if an error occurs.
-MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLRenderPipelineDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo,
+// Returns a retained MTLRenderPipelineDescriptor constructed from this instance, or nil if an error occurs.
+// It is the responsibility of the caller to release the returned descriptor.
+MTLRenderPipelineDescriptor* MVKGraphicsPipeline::newMTLRenderPipelineDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo,
const SPIRVTessReflectionData& reflectData) {
SPIRVToMSLConversionConfiguration shaderContext;
initMVKShaderConverterContext(shaderContext, pCreateInfo, reflectData);
- MTLRenderPipelineDescriptor* plDesc = [[MTLRenderPipelineDescriptor new] autorelease];
+ MTLRenderPipelineDescriptor* plDesc = [MTLRenderPipelineDescriptor new]; // retained
// Add shader stages. Compile vertex shader before others just in case conversion changes anything...like rasterizaion disable.
if (!addVertexShaderToPipeline(plDesc, pCreateInfo, shaderContext)) { return nil; }
@@ -476,11 +482,12 @@
return plDesc;
}
-// Returns a MTLRenderPipelineDescriptor for the vertex stage of a tessellated draw constructed from this instance, or nil if an error occurs.
-MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLTessVertexStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo,
+// Returns a retained MTLRenderPipelineDescriptor for the vertex stage of a tessellated draw constructed from this instance, or nil if an error occurs.
+// It is the responsibility of the caller to release the returned descriptor.
+MTLRenderPipelineDescriptor* MVKGraphicsPipeline::newMTLTessVertexStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo,
const SPIRVTessReflectionData& reflectData,
SPIRVToMSLConversionConfiguration& shaderContext) {
- MTLRenderPipelineDescriptor* plDesc = [[MTLRenderPipelineDescriptor new] autorelease];
+ MTLRenderPipelineDescriptor* plDesc = [MTLRenderPipelineDescriptor new]; // retained
// Add shader stages.
if (!addVertexShaderToPipeline(plDesc, pCreateInfo, shaderContext)) { return nil; }
@@ -587,11 +594,12 @@
return VK_FORMAT_UNDEFINED;
}
-// Returns a MTLComputePipelineDescriptor for the tess. control stage of a tessellated draw constructed from this instance, or nil if an error occurs.
-MTLComputePipelineDescriptor* MVKGraphicsPipeline::getMTLTessControlStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo,
+// Returns a retained MTLComputePipelineDescriptor for the tess. control stage of a tessellated draw constructed from this instance, or nil if an error occurs.
+// It is the responsibility of the caller to release the returned descriptor.
+MTLComputePipelineDescriptor* MVKGraphicsPipeline::newMTLTessControlStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo,
const SPIRVTessReflectionData& reflectData,
SPIRVToMSLConversionConfiguration& shaderContext) {
- MTLComputePipelineDescriptor* plDesc = [MTLComputePipelineDescriptor new];
+ MTLComputePipelineDescriptor* plDesc = [MTLComputePipelineDescriptor new]; // retained
std::vector<SPIRVShaderOutput> vtxOutputs;
std::string errorLog;
@@ -634,11 +642,12 @@
return plDesc;
}
-// Returns a MTLRenderPipelineDescriptor for the last stage of a tessellated draw constructed from this instance, or nil if an error occurs.
-MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLTessRasterStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo,
+// Returns a retained MTLRenderPipelineDescriptor for the last stage of a tessellated draw constructed from this instance, or nil if an error occurs.
+// It is the responsibility of the caller to release the returned descriptor.
+MTLRenderPipelineDescriptor* MVKGraphicsPipeline::newMTLTessRasterStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo,
const SPIRVTessReflectionData& reflectData,
SPIRVToMSLConversionConfiguration& shaderContext) {
- MTLRenderPipelineDescriptor* plDesc = [[MTLRenderPipelineDescriptor new] autorelease];
+ MTLRenderPipelineDescriptor* plDesc = [MTLRenderPipelineDescriptor new]; // retained
std::vector<SPIRVShaderOutput> tcOutputs;
std::string errorLog;
@@ -775,11 +784,12 @@
addVertexInputToShaderConverterContext(shaderContext, pCreateInfo);
MVKMTLFunction func = ((MVKShaderModule*)_pVertexSS->module)->getMTLFunction(&shaderContext, _pVertexSS->pSpecializationInfo, _pipelineCache);
- if ( !func.mtlFunction ) {
+ id<MTLFunction> mtlFunc = func.getMTLFunction();
+ if ( !mtlFunc ) {
setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Vertex shader function could not be compiled into pipeline. See previous logged error."));
return false;
}
- plDesc.vertexFunction = func.mtlFunction;
+ plDesc.vertexFunction = mtlFunc;
auto& funcRslts = func.shaderConversionResults;
plDesc.rasterizationEnabled = !funcRslts.isRasterizationDisabled;
@@ -821,11 +831,12 @@
addPrevStageOutputToShaderConverterContext(shaderContext, vtxOutputs);
MVKMTLFunction func = ((MVKShaderModule*)_pTessCtlSS->module)->getMTLFunction(&shaderContext, _pTessCtlSS->pSpecializationInfo, _pipelineCache);
- if ( !func.mtlFunction ) {
+ id<MTLFunction> mtlFunc = func.getMTLFunction();
+ if ( !mtlFunc ) {
setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Tessellation control shader function could not be compiled into pipeline. See previous logged error."));
return false;
}
- plDesc.computeFunction = func.mtlFunction;
+ plDesc.computeFunction = mtlFunc;
auto& funcRslts = func.shaderConversionResults;
_needsTessCtlSwizzleBuffer = funcRslts.needsSwizzleBuffer;
@@ -870,12 +881,13 @@
addPrevStageOutputToShaderConverterContext(shaderContext, tcOutputs);
MVKMTLFunction func = ((MVKShaderModule*)_pTessEvalSS->module)->getMTLFunction(&shaderContext, _pTessEvalSS->pSpecializationInfo, _pipelineCache);
- if ( !func.mtlFunction ) {
+ id<MTLFunction> mtlFunc = func.getMTLFunction();
+ if ( !mtlFunc ) {
setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Tessellation evaluation shader function could not be compiled into pipeline. See previous logged error."));
return false;
}
// Yeah, you read that right. Tess. eval functions are a kind of vertex function in Metal.
- plDesc.vertexFunction = func.mtlFunction;
+ plDesc.vertexFunction = mtlFunc;
auto& funcRslts = func.shaderConversionResults;
plDesc.rasterizationEnabled = !funcRslts.isRasterizationDisabled;
@@ -902,11 +914,12 @@
shaderContext.options.mslOptions.capture_output_to_buffer = false;
MVKMTLFunction func = ((MVKShaderModule*)_pFragmentSS->module)->getMTLFunction(&shaderContext, _pFragmentSS->pSpecializationInfo, _pipelineCache);
- if ( !func.mtlFunction ) {
+ id<MTLFunction> mtlFunc = func.getMTLFunction();
+ if ( !mtlFunc ) {
setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Fragment shader function could not be compiled into pipeline. See previous logged error."));
return false;
}
- plDesc.fragmentFunction = func.mtlFunction;
+ plDesc.fragmentFunction = mtlFunc;
auto& funcRslts = func.shaderConversionResults;
_needsFragmentSwizzleBuffer = funcRslts.needsSwizzleBuffer;
@@ -1281,13 +1294,14 @@
const VkComputePipelineCreateInfo* pCreateInfo) :
MVKPipeline(device, pipelineCache, (MVKPipelineLayout*)pCreateInfo->layout, parent) {
- MVKMTLFunction shaderFunc = getMTLFunction(pCreateInfo);
- _mtlThreadgroupSize = shaderFunc.threadGroupSize;
+ MVKMTLFunction func = getMTLFunction(pCreateInfo);
+ _mtlThreadgroupSize = func.threadGroupSize;
_mtlPipelineState = nil;
- if (shaderFunc.mtlFunction) {
- MTLComputePipelineDescriptor* plDesc = [[MTLComputePipelineDescriptor new] autorelease];
- plDesc.computeFunction = shaderFunc.mtlFunction;
+ id<MTLFunction> mtlFunc = func.getMTLFunction();
+ if (mtlFunc) {
+ MTLComputePipelineDescriptor* plDesc = [MTLComputePipelineDescriptor new]; // temp retain
+ plDesc.computeFunction = mtlFunc;
// Metal does not allow the name of the pipeline to be changed after it has been created,
// and we need to create the Metal pipeline immediately to provide error feedback to app.
@@ -1297,6 +1311,8 @@
MVKComputePipelineCompiler* plc = new MVKComputePipelineCompiler(this);
_mtlPipelineState = plc->newMTLComputePipelineState(plDesc); // retained
plc->destroy();
+ [plDesc release]; // temp release
+
if ( !_mtlPipelineState ) { _hasValidMTLPipelineStates = false; }
} else {
setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Compute shader function could not be compiled into pipeline. See previous logged error."));
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm
index bdb6015..6a8647d 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm
@@ -65,13 +65,13 @@
#pragma mark Queue submissions
-// Execute the queue submission under an autorelease pool to ensure transient Metal objects are autoreleased.
+// Execute the queue submission under an autoreleasepool to ensure transient Metal objects are autoreleased.
// This is critical for apps that don't use standard OS autoreleasing runloop threading.
static inline void execute(MVKQueueSubmission* qSubmit) { @autoreleasepool { qSubmit->execute(); } }
// Executes the submmission, either immediately, or by dispatching to an execution queue.
-// Submissions to the execution queue are wrapped in a dedicated autorelease pool.
-// Relying on the dispatch queue to find time to drain the autorelease pool can
+// Submissions to the execution queue are wrapped in a dedicated autoreleasepool.
+// Relying on the dispatch queue to find time to drain the autoreleasepool can
// result in significant memory creep under heavy workloads.
VkResult MVKQueue::submit(MVKQueueSubmission* qSubmit) {
if ( !qSubmit ) { return VK_SUCCESS; } // Ignore nils
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
index ff54bfb..d9a8f84 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
@@ -39,14 +39,22 @@
#pragma mark MVKShaderLibrary
/** A MTLFunction and corresponding result information resulting from a shader conversion. */
-typedef struct {
- id<MTLFunction> mtlFunction;
- const SPIRVToMSLConversionResults shaderConversionResults;
+typedef struct MVKMTLFunction {
+ SPIRVToMSLConversionResults shaderConversionResults;
MTLSize threadGroupSize;
+ inline id<MTLFunction> getMTLFunction() { return _mtlFunction; }
+
+ MVKMTLFunction(id<MTLFunction> mtlFunc, const SPIRVToMSLConversionResults scRslts, MTLSize tgSize);
+ MVKMTLFunction(const MVKMTLFunction& other);
+ ~MVKMTLFunction();
+
+private:
+ id<MTLFunction> _mtlFunction;
+
} MVKMTLFunction;
/** A MVKMTLFunction indicating an invalid MTLFunction. The mtlFunction member is nil. */
-extern const MVKMTLFunction MVKMTLFunctionNull;
+const MVKMTLFunction MVKMTLFunctionNull(nil, SPIRVToMSLConversionResults(), MTLSizeMake(1, 1, 1));
/** Wraps a single MTLLibrary. */
class MVKShaderLibrary : public MVKBaseObject {
@@ -85,7 +93,7 @@
size_t mslCompiledCodeLength);
/** Copy constructor. */
- MVKShaderLibrary(MVKShaderLibrary& other);
+ MVKShaderLibrary(const MVKShaderLibrary& other);
~MVKShaderLibrary() override;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
index b80362b..aa0823f 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
@@ -26,7 +26,22 @@
using namespace std;
-const MVKMTLFunction MVKMTLFunctionNull = { nil, SPIRVToMSLConversionResults(), MTLSizeMake(1, 1, 1) };
+MVKMTLFunction::MVKMTLFunction(id<MTLFunction> mtlFunc, const SPIRVToMSLConversionResults scRslts, MTLSize tgSize) {
+ _mtlFunction = [mtlFunc retain]; // retained
+ shaderConversionResults = scRslts;
+ threadGroupSize = tgSize;
+}
+
+MVKMTLFunction::MVKMTLFunction(const MVKMTLFunction& other) {
+ _mtlFunction = [other._mtlFunction retain]; // retained
+ shaderConversionResults = other.shaderConversionResults;
+ threadGroupSize = other.threadGroupSize;
+}
+
+MVKMTLFunction::~MVKMTLFunction() {
+ [_mtlFunction release];
+}
+
#pragma mark -
#pragma mark MVKShaderLibrary
@@ -49,57 +64,65 @@
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]; // temp retain
+ 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 release]; // temp release
+ mtlFunc = fs->newMTLFunction(_mtlLibrary, mtlFuncName, mtlFCVals); // temp retain
+ fs->destroy();
+ [mtlFCVals release]; // temp release
+ }
+ }
+ } 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))};
+
+ MVKMTLFunction mvkMTLFunc(mtlFunc, _shaderConversionResults, MTLSizeMake(getWorkgroupDimensionSize(wgSize.width, pSpecializationInfo),
+ getWorkgroupDimensionSize(wgSize.height, pSpecializationInfo),
+ getWorkgroupDimensionSize(wgSize.depth, pSpecializationInfo)));
+ [mtlFunc release]; // temp release
+
+ return mvkMTLFunc;
}
// Returns the MTLFunctionConstant with the specified ID from the specified array of function constants.
@@ -124,7 +147,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;
@@ -149,7 +176,7 @@
mvkDev->addActivityPerformance(mvkDev->_performanceStatistics.shaderCompilation.mslLoad, startTime);
}
-MVKShaderLibrary::MVKShaderLibrary(MVKShaderLibrary& other) : _owner(other._owner) {
+MVKShaderLibrary::MVKShaderLibrary(const MVKShaderLibrary& other) : _owner(other._owner) {
_mtlLibrary = [other._mtlLibrary retain];
_shaderConversionResults = other._shaderConversionResults;
_msl = other._msl;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm
index e6f5ab0..ac3eabc 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..cfc4b56 100644
--- a/MoltenVK/MoltenVK/Utility/MVKWatermark.mm
+++ b/MoltenVK/MoltenVK/Utility/MVKWatermark.mm
@@ -75,7 +75,7 @@
}
id<MTLRenderPipelineState> MVKWatermark::newRenderPipelineState() {
- MTLRenderPipelineDescriptor* plDesc = [[MTLRenderPipelineDescriptor new] autorelease];
+ MTLRenderPipelineDescriptor* plDesc = [MTLRenderPipelineDescriptor new]; // temp retained
plDesc.label = _mtlName;
plDesc.vertexFunction = _mtlFunctionVertex;
@@ -128,7 +128,8 @@
NSError* err = nil;
id<MTLRenderPipelineState> rps = [_mtlDevice newRenderPipelineStateWithDescriptor: plDesc error: &err]; // retained
MVKAssert( !err, "Could not create watermark pipeline state (Error code %li)\n%s", (long)err.code, err.localizedDescription.UTF8String);
- return rps;
+ [plDesc release]; // temp released
+ return rps;
}
@@ -296,21 +297,26 @@
bytesPerRow: textureBytesPerRow
bytesPerImage: 0];
- MTLSamplerDescriptor* sampDesc = [[MTLSamplerDescriptor new] autorelease];
+ MTLSamplerDescriptor* sampDesc = [MTLSamplerDescriptor new]; // temp retained
sampDesc.minFilter = MTLSamplerMinMagFilterLinear;
- _mtlSamplerState = [_mtlDevice newSamplerStateWithDescriptor: sampDesc]; // retained
+ _mtlSamplerState = [_mtlDevice newSamplerStateWithDescriptor: sampDesc]; // retained
+ [sampDesc release]; // temp released
}
// Initialize the shader functions for rendering the watermark
void MVKWatermark::initShaders(const char* mslSourceCode) {
NSError* err = nil;
- id<MTLLibrary> mtlLib = [[_mtlDevice newLibraryWithSource: @(mslSourceCode)
- options: nil
- error: &err] autorelease];
+ NSString* nsSrc = [[NSString alloc] initWithUTF8String: mslSourceCode]; // temp retained
+ id<MTLLibrary> mtlLib = [_mtlDevice newLibraryWithSource: nsSrc
+ options: nil
+ error: &err]; // temp retained
MVKAssert( !err, "Could not compile watermark shaders (Error code %li):\n%s", (long)err.code, err.localizedDescription.UTF8String);
_mtlFunctionVertex = [mtlLib newFunctionWithName: @"watermarkVertex"]; // retained
_mtlFunctionFragment = [mtlLib newFunctionWithName: @"watermarkFragment"]; // retained
+
+ [nsSrc release]; // temp released
+ [mtlLib release]; // temp released
}
// Initialize the vertex buffers to use for rendering the watermark
diff --git a/README.md b/README.md
index 72d7fd1..ab05ba1 100644
--- a/README.md
+++ b/README.md
@@ -317,6 +317,19 @@
Property claims.
+### Memory Management
+
+*Metal*, and other *Objective-C* objects in *Apple's SDK* frameworks, use reference counting for memory management.
+When instantiating *Objective-C* objects, it is important that you do not rely on implied *autorelease pools* to do
+memory management for you. Because many *Vulkan* games and apps may be ported from other platforms, they will
+typically not include autorelease pools in their threading models.
+
+Avoid the use of the `autorelease` method, or any object creation methods that imply use of `autorelease`,
+(eg- `[NSString stringWithFormat: ]`, etc). Instead, favour object creation methods that return a retained object
+(eg- `[[NSString alloc] initWithFormat: ]`, etc), and manually track and release those objects. If you need to use
+autoreleased objects, wrap code blocks in an `@autoreleasepool {...}` block.
+
+
### Code Formatting
When contributing code, please honour the code formatting style found in existing **MoltenVK** source code.