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.