From 65eed2fe33653ef1940782c38605a320be782e90 Mon Sep 17 00:00:00 2001 From: Bill Hollings Date: Wed, 17 Jul 2019 18:24:07 -0400 Subject: [PATCH] Reduce memory leaks when autorelease pools are not available. Remove use of autorelease. Rename getXXX() functions that returned autoreleased objects to newXXX() that don't. Calling functions now responsible for releasing returned objects from these functions. Move creation and tracking of texture->texture views for copying to MVKImage. MVKCmdCopyImage retrieve texture view from MVKImage. MVKMTLFunction responsible for releasing its MTLFunction. Add dev notes to README.md. --- MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h | 3 +- MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm | 16 +- .../Commands/MVKCommandResourceFactory.h | 14 +- .../Commands/MVKCommandResourceFactory.mm | 164 +++++++++++------- MoltenVK/MoltenVK/GPUObjects/MVKDevice.h | 13 +- MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm | 23 ++- MoltenVK/MoltenVK/GPUObjects/MVKImage.h | 9 +- MoltenVK/MoltenVK/GPUObjects/MVKImage.mm | 63 +++++-- MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h | 8 +- MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm | 76 ++++---- MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm | 6 +- .../MoltenVK/GPUObjects/MVKShaderModule.h | 18 +- .../MoltenVK/GPUObjects/MVKShaderModule.mm | 36 +++- MoltenVK/MoltenVK/Utility/MVKWatermark.mm | 20 ++- README.md | 13 ++ 15 files changed, 313 insertions(+), 169 deletions(-) diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h index bd7110a6..231924a4 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h +++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h @@ -67,8 +67,7 @@ protected: bool _isSrcCompressed; bool _isDstCompressed; bool _canCopyFormats; - bool _shouldUseTextureView; - bool _shouldUseTempBuffer; + bool _useTempBuffer; std::vector _imageCopyRegions; std::vector _srcTmpBuffImgCopies; std::vector _dstTmpBuffImgCopies; diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm index c7b90878..40c1fa18 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm +++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm @@ -77,8 +77,7 @@ void MVKCmdCopyImage::setContent(VkImage srcImage, 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::setContent(VkImage srcImage, } 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::addTempBufferImageCopyRegion(const VkImageCopy& region) { } void MVKCmdCopyImage::encode(MVKCommandEncoder* cmdEncoder) { - id 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 srcMTLTex = _srcImage->getMTLTexture(mapSrcMTLPixFmt); id 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 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 0749be9a..a14e2deb 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h +++ b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h @@ -357,8 +357,8 @@ public: */ id 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 @@ public: protected: void initMTLLibrary(); void initImageDeviceMemory(); - id getBlitFragFunction(MVKRPSKeyBlitImg& blitKey); - id getClearVertFunction(MVKRPSKeyClearAtt& attKey); - id getClearFragFunction(MVKRPSKeyClearAtt& attKey); + id newBlitFragFunction(MVKRPSKeyBlitImg& blitKey); + id newClearVertFunction(MVKRPSKeyClearAtt& attKey); + id newClearFragFunction(MVKRPSKeyClearAtt& attKey); NSString* getMTLFormatTypeString(MTLPixelFormat mtlPixFmt); - id getFunctionNamed(const char* funcName); + id newFunctionNamed(const char* funcName); id newMTLFunction(NSString* mslSrcCode, NSString* funcName); id newMTLRenderPipelineState(MTLRenderPipelineDescriptor* plDesc, MVKVulkanAPIDeviceObject* owner); - id newMTLComputePipelineState(id mtlFunction, + id newMTLComputePipelineState(const char* funcName, MVKVulkanAPIDeviceObject* owner); id _mtlLibrary; diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm index a7eb94c1..20b6f320 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm +++ b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm @@ -33,11 +33,13 @@ using namespace std; id MVKCommandResourceFactory::newCmdBlitImageMTLRenderPipelineState(MVKRPSKeyBlitImg& blitKey, MVKVulkanAPIDeviceObject* owner) { - MTLRenderPipelineDescriptor* plDesc = [[[MTLRenderPipelineDescriptor alloc] init] autorelease]; + id vtxFunc = newFunctionNamed("vtxCmdBlitImage"); // temp retain + id 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 @@ id MVKCommandResourceFactory::newCmdBlitImageMTLRenderPi vbDesc.stepRate = 1; vbDesc.stride = vtxStride; - return newMTLRenderPipelineState(plDesc, owner); + id rps = newMTLRenderPipelineState(plDesc, owner); + + [vtxFunc release]; // temp release + [fragFunc release]; // temp release + [plDesc release]; // temp release + + return rps; } id 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 @@ id MVKCommandResourceFactory::newCmdBlitImageMTLSamplerState(MT sDesc.normalizedCoordinates = YES; sDesc.minFilter = mtlFilter; sDesc.magFilter = mtlFilter; - return [getMTLDevice() newSamplerStateWithDescriptor: sDesc]; + + id ss = [getMTLDevice() newSamplerStateWithDescriptor: sDesc]; + + [sDesc release]; // temp release + + return ss; } id MVKCommandResourceFactory::newCmdClearMTLRenderPipelineState(MVKRPSKeyClearAtt& attKey, MVKVulkanAPIDeviceObject* owner) { - MTLRenderPipelineDescriptor* plDesc = [[[MTLRenderPipelineDescriptor alloc] init] autorelease]; + id vtxFunc = newClearVertFunction(attKey); // temp retain + id 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 @@ id MVKCommandResourceFactory::newCmdClearMTLRenderPipeli vbDesc.stepRate = 1; vbDesc.stride = vtxStride; - return newMTLRenderPipelineState(plDesc, owner); + id rps = newMTLRenderPipelineState(plDesc, owner); + + [vtxFunc release]; // temp release + [fragFunc release]; // temp release + [plDesc release]; // temp release + + return rps; } -id MVKCommandResourceFactory::getBlitFragFunction(MVKRPSKeyBlitImg& blitKey) { - id mtlFunc = nil; - - NSString* typeStr = getMTLFormatTypeString(blitKey.getSrcMTLPixelFormat()); - - bool isArrayType = blitKey.isSrcArrayType(); - NSString* arraySuffix = isArrayType ? @"_array" : @""; - NSString* sliceArg = isArrayType ? @", srcSlice" : @""; - +id 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 "]; [msl appendLineMVK: @"using namespace metal;"]; @@ -161,14 +180,13 @@ id MVKCommandResourceFactory::getBlitFragFunction(MVKRPSKeyBlitImg& [msl appendLineMVK]; [msl appendLineMVK: @"}"]; - mtlFunc = newMTLFunction(msl, funcName); // MVKLogDebug("\n%s", msl.UTF8String); + + return newMTLFunction(msl, funcName); } - return [mtlFunc autorelease]; } -id MVKCommandResourceFactory::getClearVertFunction(MVKRPSKeyClearAtt& attKey) { - id mtlFunc = nil; +id MVKCommandResourceFactory::newClearVertFunction(MVKRPSKeyClearAtt& attKey) { @autoreleasepool { NSMutableString* msl = [NSMutableString stringWithCapacity: (2 * KIBI) ]; [msl appendLineMVK: @"#include "]; @@ -197,14 +215,13 @@ id MVKCommandResourceFactory::getClearVertFunction(MVKRPSKeyClearAt [msl appendLineMVK: @" return varyings;"]; [msl appendLineMVK: @"}"]; - mtlFunc = newMTLFunction(msl, funcName); // MVKLogDebug("\n%s", msl.UTF8String); + + return newMTLFunction(msl, funcName); } - return [mtlFunc autorelease]; } -id MVKCommandResourceFactory::getClearFragFunction(MVKRPSKeyClearAtt& attKey) { - id mtlFunc = nil; +id MVKCommandResourceFactory::newClearFragFunction(MVKRPSKeyClearAtt& attKey) { @autoreleasepool { NSMutableString* msl = [NSMutableString stringWithCapacity: (2 * KIBI) ]; [msl appendLineMVK: @"#include "]; @@ -243,10 +260,10 @@ id MVKCommandResourceFactory::getClearFragFunction(MVKRPSKeyClearAt [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 @@ NSString* MVKCommandResourceFactory::getMTLFormatTypeString(MTLPixelFormat mtlPi id 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 @@ id MVKCommandResourceFactory::newMTLDepthStencilState(bool dsDesc.frontFaceStencil = sDesc; dsDesc.backFaceStencil = sDesc; + + [sDesc release]; // temp release } else { dsDesc.frontFaceStencil = nil; dsDesc.backFaceStencil = nil; } - return [getMTLDevice() newDepthStencilStateWithDescriptor: dsDesc]; + id dss = [getMTLDevice() newDepthStencilStateWithDescriptor: dsDesc]; + + [dsDesc release]; // temp release + + return dss; } id 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 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,68 +393,75 @@ MVKBuffer* MVKCommandResourceFactory::newMVKBuffer(MVKBufferDescriptorData& buff } id MVKCommandResourceFactory::newCmdCopyBufferBytesMTLComputePipelineState(MVKVulkanAPIDeviceObject* owner) { - return newMTLComputePipelineState(getFunctionNamed("cmdCopyBufferBytes"), owner); + return newMTLComputePipelineState("cmdCopyBufferBytes", owner); } id MVKCommandResourceFactory::newCmdFillBufferMTLComputePipelineState(MVKVulkanAPIDeviceObject* owner) { - return newMTLComputePipelineState(getFunctionNamed("cmdFillBuffer"), owner); + return newMTLComputePipelineState("cmdFillBuffer", owner); } id MVKCommandResourceFactory::newCmdCopyBufferToImage3DDecompressMTLComputePipelineState(bool needTempBuf, MVKVulkanAPIDeviceObject* owner) { - return newMTLComputePipelineState(getFunctionNamed(needTempBuf - ? "cmdCopyBufferToImage3DDecompressTempBufferDXTn" - : "cmdCopyBufferToImage3DDecompressDXTn"), owner); + return newMTLComputePipelineState(needTempBuf + ? "cmdCopyBufferToImage3DDecompressTempBufferDXTn" + : "cmdCopyBufferToImage3DDecompressDXTn", owner); } id MVKCommandResourceFactory::newCmdDrawIndirectConvertBuffersMTLComputePipelineState(bool indexed, MVKVulkanAPIDeviceObject* owner) { - return newMTLComputePipelineState(getFunctionNamed(indexed - ? "cmdDrawIndexedIndirectConvertBuffers" - : "cmdDrawIndirectConvertBuffers"), owner); + return newMTLComputePipelineState(indexed + ? "cmdDrawIndexedIndirectConvertBuffers" + : "cmdDrawIndirectConvertBuffers", owner); } id MVKCommandResourceFactory::newCmdDrawIndexedCopyIndexBufferMTLComputePipelineState(MTLIndexType type, MVKVulkanAPIDeviceObject* owner) { - return newMTLComputePipelineState(getFunctionNamed(type == MTLIndexTypeUInt16 - ? "cmdDrawIndexedCopyIndex16Buffer" - : "cmdDrawIndexedCopyIndex32Buffer"), owner); + return newMTLComputePipelineState(type == MTLIndexTypeUInt16 + ? "cmdDrawIndexedCopyIndex16Buffer" + : "cmdDrawIndexedCopyIndex32Buffer", owner); } id MVKCommandResourceFactory::newCmdCopyQueryPoolResultsMTLComputePipelineState(MVKVulkanAPIDeviceObject* owner) { - return newMTLComputePipelineState(getFunctionNamed("cmdCopyQueryPoolResultsToBuffer"), owner); + return newMTLComputePipelineState("cmdCopyQueryPoolResultsToBuffer", owner); } #pragma mark Support methods -id MVKCommandResourceFactory::getFunctionNamed(const char* funcName) { +// Returns the retained MTLFunction with the name. +// The caller is responsible for releasing the returned function object. +id MVKCommandResourceFactory::newFunctionNamed(const char* funcName) { uint64_t startTime = _device->getPerformanceTimestamp(); NSString* nsFuncName = [[NSString alloc] initWithUTF8String: funcName]; // temp retained - id mtlFunc = [[_mtlLibrary newFunctionWithName: nsFuncName] autorelease]; - [nsFuncName release]; // release temp NSStr + id mtlFunc = [_mtlLibrary newFunctionWithName: nsFuncName]; // retained + [nsFuncName release]; // temp release _device->addActivityPerformance(_device->_performanceStatistics.shaderCompilation.functionRetrieval, startTime); return mtlFunc; } id MVKCommandResourceFactory::newMTLFunction(NSString* mslSrcCode, NSString* funcName) { @autoreleasepool { + id mtlFunc = nil; NSError* err = nil; + uint64_t startTime = _device->getPerformanceTimestamp(); - id mtlLib = [[getMTLDevice() newLibraryWithSource: mslSrcCode - options: getDevice()->getMTLCompileOptions() - error: &err] autorelease]; + id 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 mtlFunc = [mtlLib newFunctionWithName: funcName]; - _device->addActivityPerformance(_device->_performanceStatistics.shaderCompilation.functionRetrieval, startTime); + [mtlLib release]; // temp release + return mtlFunc; } } @@ -436,11 +474,13 @@ id MVKCommandResourceFactory::newMTLRenderPipelineState( return rps; } -id MVKCommandResourceFactory::newMTLComputePipelineState(id mtlFunction, +id MVKCommandResourceFactory::newMTLComputePipelineState(const char* funcName, MVKVulkanAPIDeviceObject* owner) { + id mtlFunc = newFunctionNamed(funcName); // temp retain MVKComputePipelineCompiler* plc = new MVKComputePipelineCompiler(owner); - id cps = plc->newMTLComputePipelineState(mtlFunction); // retained + id 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 e22b7243..ee219031 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h @@ -282,13 +282,18 @@ public: #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 getMTLDevice() { return _mtlDevice; } /*** Replaces the underlying Metal device .*/ - inline void replaceMTLDevice(id mtlDevice) { [_mtlDevice autorelease]; _mtlDevice = [mtlDevice retain]; } + inline void replaceMTLDevice(id mtlDevice) { + if (mtlDevice != _mtlDevice) { + [_mtlDevice release]; + _mtlDevice = [mtlDevice retain]; + } + } #pragma mark Construction @@ -554,7 +559,7 @@ public: inline id 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 @@ protected: 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 @@ protected: MVKPhysicalDevice* _physicalDevice; MVKCommandResourceFactory* _commandResourceFactory; + MTLCompileOptions* _mtlCompileOptions; std::vector> _queuesByQueueFamilyIndex; std::vector _resources; std::mutex _rezLock; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm index a50cb46f..8586b290 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm @@ -1711,9 +1711,11 @@ void MVKPhysicalDevice::logGPUInfo() { #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 @@ void MVKDevice::getPerformanceStatistics(MVKPerformanceStatistics* pPerf) { #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 @@ MVKDevice::MVKDevice(MVKPhysicalDevice* physicalDevice, const VkDeviceCreateInfo _globalVisibilityResultMTLBuffer = nil; _globalVisibilityQueryCount = 0; + initMTLCompileOptions(); // Before command resource factory + _commandResourceFactory = new MVKCommandResourceFactory(this); initQueues(pCreateInfo); @@ -2451,12 +2449,19 @@ void MVKDevice::initQueues(const VkDeviceCreateInfo* pCreateInfo) { } } +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 0c5e3bf0..2c03080b 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h @@ -22,6 +22,7 @@ #include "MVKSync.h" #include "MVKVector.h" #include +#include #include #import @@ -154,6 +155,9 @@ public: /** Returns the Metal texture underlying this image. */ id getMTLTexture(); + /** Returns a Metal texture that interprets the pixels in the specified format. */ + id getMTLTexture(MTLPixelFormat mtlPixFmt); + /** * Sets this image to use the specified MTLTexture. * @@ -243,7 +247,7 @@ protected: virtual id 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 @@ protected: VkImageMemoryBarrier* pImageMemoryBarrier); std::vector _subresources; + std::unordered_map> _mtlTextureViews; VkExtent3D _extent; uint32_t _mipLevels; uint32_t _arrayLayers; @@ -376,7 +381,7 @@ public: protected: void propogateDebugName() override {} - MTLSamplerDescriptor* getMTLSamplerDescriptor(const VkSamplerCreateInfo* pCreateInfo); + MTLSamplerDescriptor* newMTLSamplerDescriptor(const VkSamplerCreateInfo* pCreateInfo); void initConstExprSampler(const VkSamplerCreateInfo* pCreateInfo); id _mtlSamplerState; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm index 39440a10..92049627 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm @@ -296,6 +296,22 @@ id MVKImage::getMTLTexture() { return _mtlTexture; } +id MVKImage::getMTLTexture(MTLPixelFormat mtlPixFmt) { + if (mtlPixFmt == _mtlPixelFormat) { return getMTLTexture(); } + + id mtlTex = _mtlTextureViews[mtlPixFmt]; + if ( !mtlTex ) { + // Lock and check again in case another thread has created the texture. + lock_guard lock(_lock); + mtlTex = _mtlTextureViews[mtlPixFmt]; + if ( !mtlTex ) { + mtlTex = [getMTLTexture() newTextureViewWithPixelFormat: mtlPixFmt]; // retained + _mtlTextureViews[mtlPixFmt] = mtlTex; + } + } + return mtlTex; +} + VkResult MVKImage::setMTLTexture(id mtlTexture) { lock_guard lock(_lock); resetMTLTexture(); @@ -325,15 +341,21 @@ VkResult MVKImage::setMTLTexture(id mtlTexture) { // 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 MVKImage::newMTLTexture() { - if (_ioSurface) { - return [getMTLDevice() newTextureWithDescriptor: getMTLTextureDescriptor() iosurface: _ioSurface plane: 0]; + id 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(). @@ -420,9 +442,10 @@ MTLTextureUsage MVKImage::getMTLTextureUsage() { 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 @@ -445,7 +468,7 @@ MTLTextureDescriptor* MVKImage::getMTLTextureDescriptor() { mtlTexDesc.storageModeMVK = getMTLStorageMode(); mtlTexDesc.cpuCacheMode = getMTLCPUCacheMode(); - return [mtlTexDesc autorelease]; + return mtlTexDesc; } MTLStorageMode MVKImage::getMTLStorageMode() { @@ -772,6 +795,7 @@ MVKImage::~MVKImage() { if (_deviceMemory) { _deviceMemory->removeImage(this); } resetMTLTexture(); resetIOSurface(); + for (auto elem : _mtlTextureViews) { [elem.second release]; } } @@ -1058,10 +1082,11 @@ bool MVKSampler::getConstexprSampler(mvk::MSLResourceBinding& resourceBinding) { 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); @@ -1099,12 +1124,16 @@ MTLSamplerDescriptor* MVKSampler::getMTLSamplerDescriptor(const VkSamplerCreateI } } #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/MVKPipeline.h b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h index 7b56702c..99bb888f 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h @@ -240,10 +240,10 @@ protected: void initMVKShaderConverterContext(SPIRVToMSLConversionConfiguration& _shaderContext, const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData); void addVertexInputToShaderConverterContext(SPIRVToMSLConversionConfiguration& shaderContext, const VkGraphicsPipelineCreateInfo* pCreateInfo); void addPrevStageOutputToShaderConverterContext(SPIRVToMSLConversionConfiguration& shaderContext, std::vector& 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& prevOutput); bool addTessEvalShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderContext, std::vector& prevOutput); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm index 84fa930a..c21340db 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm @@ -206,7 +206,7 @@ void MVKGraphicsPipeline::encode(MVKCommandEncoder* cmdEncoder, uint32_t stage) id 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 @@ void MVKGraphicsPipeline::encode(MVKCommandEncoder* cmdEncoder, uint32_t stage) plDesc.stageInputDescriptor.layouts[kMVKTessCtlInputBufferIndex].stepFunction = MTLStepFunctionThreadPositionInGridXIndexed; plState = getOrCompilePipeline(plDesc, _mtlTessControlStageIndex32State, compilerType); } + [plDesc release]; // temp release + if ( !_hasValidMTLPipelineStates ) { return; } id tessCtlEnc = cmdEncoder->getMTLComputeEncoder(kMVKCommandUseTessellationControl); @@ -426,10 +428,11 @@ void MVKGraphicsPipeline::initMTLRenderPipelineState(const VkGraphicsPipelineCre _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 @@ void MVKGraphicsPipeline::initMTLRenderPipelineState(const VkGraphicsPipelineCre 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 @@ MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLRenderPipelineDescriptor 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 @@ static VkFormat mvkFormatFromOutput(const SPIRVShaderOutput& output) { 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 vtxOutputs; std::string errorLog; @@ -634,11 +642,12 @@ MTLComputePipelineDescriptor* MVKGraphicsPipeline::getMTLTessControlStageDescrip 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 tcOutputs; std::string errorLog; @@ -775,11 +784,12 @@ bool MVKGraphicsPipeline::addVertexShaderToPipeline(MTLRenderPipelineDescriptor* addVertexInputToShaderConverterContext(shaderContext, pCreateInfo); MVKMTLFunction func = ((MVKShaderModule*)_pVertexSS->module)->getMTLFunction(&shaderContext, _pVertexSS->pSpecializationInfo, _pipelineCache); - if ( !func.mtlFunction ) { + id 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 @@ bool MVKGraphicsPipeline::addTessCtlShaderToPipeline(MTLComputePipelineDescripto addPrevStageOutputToShaderConverterContext(shaderContext, vtxOutputs); MVKMTLFunction func = ((MVKShaderModule*)_pTessCtlSS->module)->getMTLFunction(&shaderContext, _pTessCtlSS->pSpecializationInfo, _pipelineCache); - if ( !func.mtlFunction ) { + id 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 @@ bool MVKGraphicsPipeline::addTessEvalShaderToPipeline(MTLRenderPipelineDescripto addPrevStageOutputToShaderConverterContext(shaderContext, tcOutputs); MVKMTLFunction func = ((MVKShaderModule*)_pTessEvalSS->module)->getMTLFunction(&shaderContext, _pTessEvalSS->pSpecializationInfo, _pipelineCache); - if ( !func.mtlFunction ) { + id 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 @@ bool MVKGraphicsPipeline::addFragmentShaderToPipeline(MTLRenderPipelineDescripto shaderContext.options.mslOptions.capture_output_to_buffer = false; MVKMTLFunction func = ((MVKShaderModule*)_pFragmentSS->module)->getMTLFunction(&shaderContext, _pFragmentSS->pSpecializationInfo, _pipelineCache); - if ( !func.mtlFunction ) { + id 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 @@ MVKComputePipeline::MVKComputePipeline(MVKDevice* device, 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 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 @@ MVKComputePipeline::MVKComputePipeline(MVKDevice* device, 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 bdb60153..6a8647d2 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm @@ -65,13 +65,13 @@ void MVKQueue::propogateDebugName() { setLabelIfNotNil(_mtlQueue, _debugName); } #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 ff54bfbc..d9a8f843 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h @@ -39,14 +39,22 @@ using namespace mvk; #pragma mark MVKShaderLibrary /** A MTLFunction and corresponding result information resulting from a shader conversion. */ -typedef struct { - id mtlFunction; - const SPIRVToMSLConversionResults shaderConversionResults; +typedef struct MVKMTLFunction { + SPIRVToMSLConversionResults shaderConversionResults; MTLSize threadGroupSize; + inline id getMTLFunction() { return _mtlFunction; } + + MVKMTLFunction(id mtlFunc, const SPIRVToMSLConversionResults scRslts, MTLSize tgSize); + MVKMTLFunction(const MVKMTLFunction& other); + ~MVKMTLFunction(); + +private: + id _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 @@ public: 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 a243ffb2..aa0823fd 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 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 @@ -54,7 +69,7 @@ MVKMTLFunction MVKShaderLibrary::getMTLFunction(const VkSpecializationInfo* pSpe NSString* mtlFuncName = @(_shaderConversionResults.entryPoint.mtlFunctionName.c_str()); MVKDevice* mvkDev = _owner->getDevice(); uint64_t startTime = mvkDev->getPerformanceTimestamp(); - mtlFunc = [_mtlLibrary newFunctionWithName: mtlFuncName]; // retained + mtlFunc = [_mtlLibrary newFunctionWithName: mtlFuncName]; // temp retain mvkDev->addActivityPerformance(mvkDev->_performanceStatistics.shaderCompilation.functionRetrieval, startTime); if (mtlFunc) { @@ -84,9 +99,10 @@ MVKMTLFunction MVKShaderLibrary::getMTLFunction(const VkSpecializationInfo* pSpe // 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 + [mtlFunc release]; // temp release + mtlFunc = fs->newMTLFunction(_mtlLibrary, mtlFuncName, mtlFCVals); // temp retain fs->destroy(); - [mtlFCVals release]; // release temp + [mtlFCVals release]; // temp release } } } else { @@ -100,9 +116,13 @@ MVKMTLFunction MVKShaderLibrary::getMTLFunction(const VkSpecializationInfo* pSpe } auto& wgSize = _shaderConversionResults.entryPoint.workgroupSize; - return { [mtlFunc autorelease], _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. @@ -156,7 +176,7 @@ MVKShaderLibrary::MVKShaderLibrary(MVKVulkanAPIDeviceObject* owner, 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/Utility/MVKWatermark.mm b/MoltenVK/MoltenVK/Utility/MVKWatermark.mm index aa06d51b..cfc4b561 100644 --- a/MoltenVK/MoltenVK/Utility/MVKWatermark.mm +++ b/MoltenVK/MoltenVK/Utility/MVKWatermark.mm @@ -75,7 +75,7 @@ id MVKWatermark::mtlRenderPipelineState() { } id MVKWatermark::newRenderPipelineState() { - MTLRenderPipelineDescriptor* plDesc = [[MTLRenderPipelineDescriptor new] autorelease]; + MTLRenderPipelineDescriptor* plDesc = [MTLRenderPipelineDescriptor new]; // temp retained plDesc.label = _mtlName; plDesc.vertexFunction = _mtlFunctionVertex; @@ -128,7 +128,8 @@ id MVKWatermark::newRenderPipelineState() { NSError* err = nil; id 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,23 +297,26 @@ void MVKWatermark::initTexture(unsigned char* textureContent, 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; NSString* nsSrc = [[NSString alloc] initWithUTF8String: mslSourceCode]; // temp retained - id mtlLib = [[_mtlDevice newLibraryWithSource: nsSrc - options: nil - error: &err] autorelease]; - [nsSrc release]; // release temp string + id 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 72d7fd14..ab05ba1b 100644 --- a/README.md +++ b/README.md @@ -317,6 +317,19 @@ encumbrances. In submitting code to this repository, you are agreeing that the c 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.