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.
This commit is contained in:
Bill Hollings 2019-07-17 18:24:07 -04:00
parent 27a1c44f0a
commit 65eed2fe33
15 changed files with 313 additions and 169 deletions

View File

@ -67,8 +67,7 @@ protected:
bool _isSrcCompressed;
bool _isDstCompressed;
bool _canCopyFormats;
bool _shouldUseTextureView;
bool _shouldUseTempBuffer;
bool _useTempBuffer;
std::vector<VkImageCopy> _imageCopyRegions;
std::vector<VkBufferImageCopy> _srcTmpBuffImgCopies;
std::vector<VkBufferImageCopy> _dstTmpBuffImgCopies;

View File

@ -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<MTLTexture> srcMTLTex = _srcImage->getMTLTexture();
// Unless we need to use an intermediary buffer copy, map the source pixel format to the
// dest pixel format through a texture view on the source texture. If the source and dest
// pixel formats are the same, this will simply degenerate to the source texture itself.
MTLPixelFormat mapSrcMTLPixFmt = _useTempBuffer ? _srcMTLPixFmt : _dstMTLPixFmt;
id<MTLTexture> srcMTLTex = _srcImage->getMTLTexture(mapSrcMTLPixFmt);
id<MTLTexture> dstMTLTex = _dstImage->getMTLTexture();
if ( !srcMTLTex || !dstMTLTex ) { return; }
// If the pixel formats are different but mappable, use a texture view on the source texture
if (_shouldUseTextureView) {
srcMTLTex = [[srcMTLTex newTextureViewWithPixelFormat: _dstMTLPixFmt] autorelease];
}
id<MTLBlitCommandEncoder> mtlBlitEnc = cmdEncoder->getMTLBlitEncoder(_commandUse);
// If copies can be performed using direct texture-texture copying, do so

View File

@ -357,8 +357,8 @@ public:
*/
id<MTLDepthStencilState> newMTLDepthStencilState(MVKMTLDepthStencilDescriptorData& dsData);
/** Returns an autoreleased MTLStencilDescriptor constructed from the stencil data. */
MTLStencilDescriptor* getMTLStencilDescriptor(MVKMTLStencilDescriptorData& sData);
/** Returns an retained MTLStencilDescriptor constructed from the stencil data. */
MTLStencilDescriptor* newMTLStencilDescriptor(MVKMTLStencilDescriptorData& sData);
/**
* Returns a new MVKImage configured with content held in Private storage.
@ -405,15 +405,15 @@ public:
protected:
void initMTLLibrary();
void initImageDeviceMemory();
id<MTLFunction> getBlitFragFunction(MVKRPSKeyBlitImg& blitKey);
id<MTLFunction> getClearVertFunction(MVKRPSKeyClearAtt& attKey);
id<MTLFunction> getClearFragFunction(MVKRPSKeyClearAtt& attKey);
id<MTLFunction> newBlitFragFunction(MVKRPSKeyBlitImg& blitKey);
id<MTLFunction> newClearVertFunction(MVKRPSKeyClearAtt& attKey);
id<MTLFunction> newClearFragFunction(MVKRPSKeyClearAtt& attKey);
NSString* getMTLFormatTypeString(MTLPixelFormat mtlPixFmt);
id<MTLFunction> getFunctionNamed(const char* funcName);
id<MTLFunction> newFunctionNamed(const char* funcName);
id<MTLFunction> newMTLFunction(NSString* mslSrcCode, NSString* funcName);
id<MTLRenderPipelineState> newMTLRenderPipelineState(MTLRenderPipelineDescriptor* plDesc,
MVKVulkanAPIDeviceObject* owner);
id<MTLComputePipelineState> newMTLComputePipelineState(id<MTLFunction> mtlFunction,
id<MTLComputePipelineState> newMTLComputePipelineState(const char* funcName,
MVKVulkanAPIDeviceObject* owner);
id<MTLLibrary> _mtlLibrary;

View File

@ -33,11 +33,13 @@ using namespace std;
id<MTLRenderPipelineState> MVKCommandResourceFactory::newCmdBlitImageMTLRenderPipelineState(MVKRPSKeyBlitImg& blitKey,
MVKVulkanAPIDeviceObject* owner) {
MTLRenderPipelineDescriptor* plDesc = [[[MTLRenderPipelineDescriptor alloc] init] autorelease];
id<MTLFunction> vtxFunc = newFunctionNamed("vtxCmdBlitImage"); // temp retain
id<MTLFunction> fragFunc = newBlitFragFunction(blitKey); // temp retain
MTLRenderPipelineDescriptor* plDesc = [MTLRenderPipelineDescriptor new]; // temp retain
plDesc.label = @"CmdBlitImage";
plDesc.vertexFunction = getFunctionNamed("vtxCmdBlitImage");
plDesc.fragmentFunction = getBlitFragFunction(blitKey);
plDesc.vertexFunction = vtxFunc;
plDesc.fragmentFunction = fragFunc;
plDesc.sampleCount = blitKey.dstSampleCount;
plDesc.colorAttachments[0].pixelFormat = blitKey.getDstMTLPixelFormat();
@ -71,12 +73,18 @@ id<MTLRenderPipelineState> MVKCommandResourceFactory::newCmdBlitImageMTLRenderPi
vbDesc.stepRate = 1;
vbDesc.stride = vtxStride;
return newMTLRenderPipelineState(plDesc, owner);
id<MTLRenderPipelineState> rps = newMTLRenderPipelineState(plDesc, owner);
[vtxFunc release]; // temp release
[fragFunc release]; // temp release
[plDesc release]; // temp release
return rps;
}
id<MTLSamplerState> MVKCommandResourceFactory::newCmdBlitImageMTLSamplerState(MTLSamplerMinMagFilter mtlFilter) {
MTLSamplerDescriptor* sDesc = [[[MTLSamplerDescriptor alloc] init] autorelease];
MTLSamplerDescriptor* sDesc = [MTLSamplerDescriptor new]; // temp retain
sDesc.rAddressMode = MTLSamplerAddressModeClampToZero;
sDesc.sAddressMode = MTLSamplerAddressModeClampToZero;
sDesc.tAddressMode = MTLSamplerAddressModeClampToZero;
@ -84,15 +92,22 @@ id<MTLSamplerState> MVKCommandResourceFactory::newCmdBlitImageMTLSamplerState(MT
sDesc.normalizedCoordinates = YES;
sDesc.minFilter = mtlFilter;
sDesc.magFilter = mtlFilter;
return [getMTLDevice() newSamplerStateWithDescriptor: sDesc];
id<MTLSamplerState> ss = [getMTLDevice() newSamplerStateWithDescriptor: sDesc];
[sDesc release]; // temp release
return ss;
}
id<MTLRenderPipelineState> MVKCommandResourceFactory::newCmdClearMTLRenderPipelineState(MVKRPSKeyClearAtt& attKey,
MVKVulkanAPIDeviceObject* owner) {
MTLRenderPipelineDescriptor* plDesc = [[[MTLRenderPipelineDescriptor alloc] init] autorelease];
id<MTLFunction> vtxFunc = newClearVertFunction(attKey); // temp retain
id<MTLFunction> fragFunc = newClearFragFunction(attKey); // temp retain
MTLRenderPipelineDescriptor* plDesc = [MTLRenderPipelineDescriptor new]; // temp retain
plDesc.label = @"CmdClearAttachments";
plDesc.vertexFunction = getClearVertFunction(attKey);
plDesc.fragmentFunction = getClearFragFunction(attKey);
plDesc.vertexFunction = vtxFunc;
plDesc.fragmentFunction = fragFunc;
plDesc.sampleCount = attKey.mtlSampleCount;
plDesc.inputPrimitiveTopologyMVK = MTLPrimitiveTopologyClassTriangle;
@ -127,19 +142,23 @@ id<MTLRenderPipelineState> MVKCommandResourceFactory::newCmdClearMTLRenderPipeli
vbDesc.stepRate = 1;
vbDesc.stride = vtxStride;
return newMTLRenderPipelineState(plDesc, owner);
id<MTLRenderPipelineState> rps = newMTLRenderPipelineState(plDesc, owner);
[vtxFunc release]; // temp release
[fragFunc release]; // temp release
[plDesc release]; // temp release
return rps;
}
id<MTLFunction> MVKCommandResourceFactory::getBlitFragFunction(MVKRPSKeyBlitImg& blitKey) {
id<MTLFunction> mtlFunc = nil;
id<MTLFunction> MVKCommandResourceFactory::newBlitFragFunction(MVKRPSKeyBlitImg& blitKey) {
@autoreleasepool {
NSString* typeStr = getMTLFormatTypeString(blitKey.getSrcMTLPixelFormat());
bool isArrayType = blitKey.isSrcArrayType();
NSString* arraySuffix = isArrayType ? @"_array" : @"";
NSString* sliceArg = isArrayType ? @", srcSlice" : @"";
@autoreleasepool {
NSMutableString* msl = [NSMutableString stringWithCapacity: (2 * KIBI) ];
[msl appendLineMVK: @"#include <metal_stdlib>"];
[msl appendLineMVK: @"using namespace metal;"];
@ -161,14 +180,13 @@ id<MTLFunction> MVKCommandResourceFactory::getBlitFragFunction(MVKRPSKeyBlitImg&
[msl appendLineMVK];
[msl appendLineMVK: @"}"];
mtlFunc = newMTLFunction(msl, funcName);
// MVKLogDebug("\n%s", msl.UTF8String);
return newMTLFunction(msl, funcName);
}
return [mtlFunc autorelease];
}
id<MTLFunction> MVKCommandResourceFactory::getClearVertFunction(MVKRPSKeyClearAtt& attKey) {
id<MTLFunction> mtlFunc = nil;
id<MTLFunction> MVKCommandResourceFactory::newClearVertFunction(MVKRPSKeyClearAtt& attKey) {
@autoreleasepool {
NSMutableString* msl = [NSMutableString stringWithCapacity: (2 * KIBI) ];
[msl appendLineMVK: @"#include <metal_stdlib>"];
@ -197,14 +215,13 @@ id<MTLFunction> 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<MTLFunction> MVKCommandResourceFactory::getClearFragFunction(MVKRPSKeyClearAtt& attKey) {
id<MTLFunction> mtlFunc = nil;
id<MTLFunction> MVKCommandResourceFactory::newClearFragFunction(MVKRPSKeyClearAtt& attKey) {
@autoreleasepool {
NSMutableString* msl = [NSMutableString stringWithCapacity: (2 * KIBI) ];
[msl appendLineMVK: @"#include <metal_stdlib>"];
@ -243,10 +260,10 @@ id<MTLFunction> 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<MTLDepthStencilState> MVKCommandResourceFactory::newMTLDepthStencilState(bool useDepth, bool useStencil) {
MTLDepthStencilDescriptor* dsDesc = [[[MTLDepthStencilDescriptor alloc] init] autorelease];
MTLDepthStencilDescriptor* dsDesc = [MTLDepthStencilDescriptor new]; // temp retain
dsDesc.depthCompareFunction = MTLCompareFunctionAlways;
dsDesc.depthWriteEnabled = useDepth;
if (useStencil) {
MTLStencilDescriptor* sDesc = [[[MTLStencilDescriptor alloc] init] autorelease];
MTLStencilDescriptor* sDesc = [MTLStencilDescriptor new]; // temp retain
sDesc.stencilCompareFunction = MTLCompareFunctionAlways;
sDesc.stencilFailureOperation = MTLStencilOperationReplace;
sDesc.depthFailureOperation = MTLStencilOperationReplace;
@ -278,28 +295,42 @@ id<MTLDepthStencilState> 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<MTLDepthStencilState> dss = [getMTLDevice() newDepthStencilStateWithDescriptor: dsDesc];
[dsDesc release]; // temp release
return dss;
}
id<MTLDepthStencilState> MVKCommandResourceFactory::newMTLDepthStencilState(MVKMTLDepthStencilDescriptorData& dsData) {
MTLDepthStencilDescriptor* dsDesc = [[[MTLDepthStencilDescriptor alloc] init] autorelease];
MTLStencilDescriptor* fsDesc = newMTLStencilDescriptor(dsData.frontFaceStencilData); // temp retain
MTLStencilDescriptor* bsDesc = newMTLStencilDescriptor(dsData.backFaceStencilData); // temp retain
MTLDepthStencilDescriptor* dsDesc = [MTLDepthStencilDescriptor new]; // temp retain
dsDesc.depthCompareFunction = (MTLCompareFunction)dsData.depthCompareFunction;
dsDesc.depthWriteEnabled = dsData.depthWriteEnabled;
dsDesc.frontFaceStencil = getMTLStencilDescriptor(dsData.frontFaceStencilData);
dsDesc.backFaceStencil = getMTLStencilDescriptor(dsData.backFaceStencilData);
dsDesc.frontFaceStencil = fsDesc;
dsDesc.backFaceStencil = bsDesc;
return [getMTLDevice() newDepthStencilStateWithDescriptor: dsDesc];
id<MTLDepthStencilState> dss = [getMTLDevice() newDepthStencilStateWithDescriptor: dsDesc];
[fsDesc release]; // temp release
[bsDesc release]; // temp release
[dsDesc release]; // temp release
return dss;
}
MTLStencilDescriptor* MVKCommandResourceFactory::getMTLStencilDescriptor(MVKMTLStencilDescriptorData& sData) {
MTLStencilDescriptor* MVKCommandResourceFactory::newMTLStencilDescriptor(MVKMTLStencilDescriptorData& sData) {
if ( !sData.enabled ) { return nil; }
MTLStencilDescriptor* sDesc = [[[MTLStencilDescriptor alloc] init] autorelease];
MTLStencilDescriptor* sDesc = [MTLStencilDescriptor new]; // retained
sDesc.stencilCompareFunction = (MTLCompareFunction)sData.stencilCompareFunction;
sDesc.stencilFailureOperation = (MTLStencilOperation)sData.stencilFailureOperation;
sDesc.depthFailureOperation = (MTLStencilOperation)sData.depthFailureOperation;
@ -362,68 +393,75 @@ MVKBuffer* MVKCommandResourceFactory::newMVKBuffer(MVKBufferDescriptorData& buff
}
id<MTLComputePipelineState> MVKCommandResourceFactory::newCmdCopyBufferBytesMTLComputePipelineState(MVKVulkanAPIDeviceObject* owner) {
return newMTLComputePipelineState(getFunctionNamed("cmdCopyBufferBytes"), owner);
return newMTLComputePipelineState("cmdCopyBufferBytes", owner);
}
id<MTLComputePipelineState> MVKCommandResourceFactory::newCmdFillBufferMTLComputePipelineState(MVKVulkanAPIDeviceObject* owner) {
return newMTLComputePipelineState(getFunctionNamed("cmdFillBuffer"), owner);
return newMTLComputePipelineState("cmdFillBuffer", owner);
}
id<MTLComputePipelineState> MVKCommandResourceFactory::newCmdCopyBufferToImage3DDecompressMTLComputePipelineState(bool needTempBuf,
MVKVulkanAPIDeviceObject* owner) {
return newMTLComputePipelineState(getFunctionNamed(needTempBuf
return newMTLComputePipelineState(needTempBuf
? "cmdCopyBufferToImage3DDecompressTempBufferDXTn"
: "cmdCopyBufferToImage3DDecompressDXTn"), owner);
: "cmdCopyBufferToImage3DDecompressDXTn", owner);
}
id<MTLComputePipelineState> MVKCommandResourceFactory::newCmdDrawIndirectConvertBuffersMTLComputePipelineState(bool indexed,
MVKVulkanAPIDeviceObject* owner) {
return newMTLComputePipelineState(getFunctionNamed(indexed
return newMTLComputePipelineState(indexed
? "cmdDrawIndexedIndirectConvertBuffers"
: "cmdDrawIndirectConvertBuffers"), owner);
: "cmdDrawIndirectConvertBuffers", owner);
}
id<MTLComputePipelineState> MVKCommandResourceFactory::newCmdDrawIndexedCopyIndexBufferMTLComputePipelineState(MTLIndexType type,
MVKVulkanAPIDeviceObject* owner) {
return newMTLComputePipelineState(getFunctionNamed(type == MTLIndexTypeUInt16
return newMTLComputePipelineState(type == MTLIndexTypeUInt16
? "cmdDrawIndexedCopyIndex16Buffer"
: "cmdDrawIndexedCopyIndex32Buffer"), owner);
: "cmdDrawIndexedCopyIndex32Buffer", owner);
}
id<MTLComputePipelineState> MVKCommandResourceFactory::newCmdCopyQueryPoolResultsMTLComputePipelineState(MVKVulkanAPIDeviceObject* owner) {
return newMTLComputePipelineState(getFunctionNamed("cmdCopyQueryPoolResultsToBuffer"), owner);
return newMTLComputePipelineState("cmdCopyQueryPoolResultsToBuffer", owner);
}
#pragma mark Support methods
id<MTLFunction> MVKCommandResourceFactory::getFunctionNamed(const char* funcName) {
// Returns the retained MTLFunction with the name.
// The caller is responsible for releasing the returned function object.
id<MTLFunction> MVKCommandResourceFactory::newFunctionNamed(const char* funcName) {
uint64_t startTime = _device->getPerformanceTimestamp();
NSString* nsFuncName = [[NSString alloc] initWithUTF8String: funcName]; // temp retained
id<MTLFunction> mtlFunc = [[_mtlLibrary newFunctionWithName: nsFuncName] autorelease];
[nsFuncName release]; // release temp NSStr
id<MTLFunction> mtlFunc = [_mtlLibrary newFunctionWithName: nsFuncName]; // retained
[nsFuncName release]; // temp release
_device->addActivityPerformance(_device->_performanceStatistics.shaderCompilation.functionRetrieval, startTime);
return mtlFunc;
}
id<MTLFunction> MVKCommandResourceFactory::newMTLFunction(NSString* mslSrcCode, NSString* funcName) {
@autoreleasepool {
id<MTLFunction> mtlFunc = nil;
NSError* err = nil;
uint64_t startTime = _device->getPerformanceTimestamp();
id<MTLLibrary> mtlLib = [[getMTLDevice() newLibraryWithSource: mslSrcCode
id<MTLLibrary> mtlLib = [getMTLDevice() newLibraryWithSource: mslSrcCode
options: getDevice()->getMTLCompileOptions()
error: &err] autorelease];
error: &err]; // temp retain
_device->addActivityPerformance(_device->_performanceStatistics.shaderCompilation.mslCompile, startTime);
if (err) {
reportError(VK_ERROR_INITIALIZATION_FAILED,
"Could not compile support shader from MSL source (Error code %li):\n%s\n%s",
(long)err.code, mslSrcCode.UTF8String, err.localizedDescription.UTF8String);
return nil;
} else {
startTime = _device->getPerformanceTimestamp();
mtlFunc = [mtlLib newFunctionWithName: funcName];
_device->addActivityPerformance(_device->_performanceStatistics.shaderCompilation.functionRetrieval, startTime);
}
startTime = _device->getPerformanceTimestamp();
id<MTLFunction> mtlFunc = [mtlLib newFunctionWithName: funcName];
_device->addActivityPerformance(_device->_performanceStatistics.shaderCompilation.functionRetrieval, startTime);
[mtlLib release]; // temp release
return mtlFunc;
}
}
@ -436,11 +474,13 @@ id<MTLRenderPipelineState> MVKCommandResourceFactory::newMTLRenderPipelineState(
return rps;
}
id<MTLComputePipelineState> MVKCommandResourceFactory::newMTLComputePipelineState(id<MTLFunction> mtlFunction,
id<MTLComputePipelineState> MVKCommandResourceFactory::newMTLComputePipelineState(const char* funcName,
MVKVulkanAPIDeviceObject* owner) {
id<MTLFunction> mtlFunc = newFunctionNamed(funcName); // temp retain
MVKComputePipelineCompiler* plc = new MVKComputePipelineCompiler(owner);
id<MTLComputePipelineState> cps = plc->newMTLComputePipelineState(mtlFunction); // retained
id<MTLComputePipelineState> cps = plc->newMTLComputePipelineState(mtlFunc); // retained
plc->destroy();
[mtlFunc release]; // temp release
return cps;
}

View File

@ -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<MTLDevice> getMTLDevice() { return _mtlDevice; }
/*** Replaces the underlying Metal device .*/
inline void replaceMTLDevice(id<MTLDevice> mtlDevice) { [_mtlDevice autorelease]; _mtlDevice = [mtlDevice retain]; }
inline void replaceMTLDevice(id<MTLDevice> mtlDevice) {
if (mtlDevice != _mtlDevice) {
[_mtlDevice release];
_mtlDevice = [mtlDevice retain];
}
}
#pragma mark Construction
@ -554,7 +559,7 @@ public:
inline id<MTLDevice> getMTLDevice() { return _physicalDevice->getMTLDevice(); }
/** Returns standard compilation options to be used when compiling MSL shaders. */
MTLCompileOptions* getMTLCompileOptions();
inline MTLCompileOptions* getMTLCompileOptions() { return _mtlCompileOptions; }
/** Returns the Metal vertex buffer index to use for the specified vertex attribute binding number. */
uint32_t getMetalBufferIndexForVertexAttributeBinding(uint32_t binding);
@ -657,6 +662,7 @@ 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<std::vector<MVKQueue*>> _queuesByQueueFamilyIndex;
std::vector<MVKResource*> _resources;
std::mutex _rezLock;

View File

@ -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];
}

View File

@ -22,6 +22,7 @@
#include "MVKSync.h"
#include "MVKVector.h"
#include <MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h>
#include <unordered_map>
#include <mutex>
#import <IOSurface/IOSurfaceRef.h>
@ -154,6 +155,9 @@ public:
/** Returns the Metal texture underlying this image. */
id<MTLTexture> getMTLTexture();
/** Returns a Metal texture that interprets the pixels in the specified format. */
id<MTLTexture> getMTLTexture(MTLPixelFormat mtlPixFmt);
/**
* Sets this image to use the specified MTLTexture.
*
@ -243,7 +247,7 @@ protected:
virtual id<MTLTexture> newMTLTexture();
void resetMTLTexture();
void resetIOSurface();
MTLTextureDescriptor* getMTLTextureDescriptor();
MTLTextureDescriptor* newMTLTextureDescriptor();
void updateMTLTextureContent(MVKImageSubresource& subresource, VkDeviceSize offset, VkDeviceSize size);
void getMTLTextureContent(MVKImageSubresource& subresource, VkDeviceSize offset, VkDeviceSize size);
bool shouldFlushHostMemory();
@ -254,6 +258,7 @@ protected:
VkImageMemoryBarrier* pImageMemoryBarrier);
std::vector<MVKImageSubresource> _subresources;
std::unordered_map<NSUInteger, id<MTLTexture>> _mtlTextureViews;
VkExtent3D _extent;
uint32_t _mipLevels;
uint32_t _arrayLayers;
@ -376,7 +381,7 @@ public:
protected:
void propogateDebugName() override {}
MTLSamplerDescriptor* getMTLSamplerDescriptor(const VkSamplerCreateInfo* pCreateInfo);
MTLSamplerDescriptor* newMTLSamplerDescriptor(const VkSamplerCreateInfo* pCreateInfo);
void initConstExprSampler(const VkSamplerCreateInfo* pCreateInfo);
id<MTLSamplerState> _mtlSamplerState;

View File

@ -296,6 +296,22 @@ id<MTLTexture> MVKImage::getMTLTexture() {
return _mtlTexture;
}
id<MTLTexture> MVKImage::getMTLTexture(MTLPixelFormat mtlPixFmt) {
if (mtlPixFmt == _mtlPixelFormat) { return getMTLTexture(); }
id<MTLTexture> mtlTex = _mtlTextureViews[mtlPixFmt];
if ( !mtlTex ) {
// Lock and check again in case another thread has created the texture.
lock_guard<mutex> lock(_lock);
mtlTex = _mtlTextureViews[mtlPixFmt];
if ( !mtlTex ) {
mtlTex = [getMTLTexture() newTextureViewWithPixelFormat: mtlPixFmt]; // retained
_mtlTextureViews[mtlPixFmt] = mtlTex;
}
}
return mtlTex;
}
VkResult MVKImage::setMTLTexture(id<MTLTexture> mtlTexture) {
lock_guard<mutex> lock(_lock);
resetMTLTexture();
@ -325,15 +341,21 @@ VkResult MVKImage::setMTLTexture(id<MTLTexture> 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<MTLTexture> MVKImage::newMTLTexture() {
id<MTLTexture> mtlTex = nil;
MTLTextureDescriptor* mtlTexDesc = newMTLTextureDescriptor(); // temp retain
if (_ioSurface) {
return [getMTLDevice() newTextureWithDescriptor: getMTLTextureDescriptor() iosurface: _ioSurface plane: 0];
mtlTex = [getMTLDevice() newTextureWithDescriptor: mtlTexDesc iosurface: _ioSurface plane: 0];
} else if (_usesTexelBuffer) {
return [_deviceMemory->_mtlBuffer newTextureWithDescriptor: getMTLTextureDescriptor()
mtlTex = [_deviceMemory->_mtlBuffer newTextureWithDescriptor: mtlTexDesc
offset: getDeviceMemoryOffset()
bytesPerRow: _subresources[0].layout.rowPitch];
} else {
return [getMTLDevice() newTextureWithDescriptor: getMTLTextureDescriptor()];
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);
}

View File

@ -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<SPIRVShaderOutput>& outputs);
MTLRenderPipelineDescriptor* getMTLRenderPipelineDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData);
MTLRenderPipelineDescriptor* getMTLTessVertexStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderContext);
MTLComputePipelineDescriptor* getMTLTessControlStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderContext);
MTLRenderPipelineDescriptor* getMTLTessRasterStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderContext);
MTLRenderPipelineDescriptor* newMTLRenderPipelineDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData);
MTLRenderPipelineDescriptor* newMTLTessVertexStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderContext);
MTLComputePipelineDescriptor* newMTLTessControlStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderContext);
MTLRenderPipelineDescriptor* newMTLTessRasterStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderContext);
bool addVertexShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderContext);
bool addTessCtlShaderToPipeline(MTLComputePipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderContext, std::vector<SPIRVShaderOutput>& prevOutput);
bool addTessEvalShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderContext, std::vector<SPIRVShaderOutput>& prevOutput);

View File

@ -206,7 +206,7 @@ void MVKGraphicsPipeline::encode(MVKCommandEncoder* cmdEncoder, uint32_t stage)
id<MTLComputePipelineState> plState;
const char* compilerType = "Tessellation control stage pipeline";
const MVKIndexMTLBufferBinding& indexBuff = cmdEncoder->_graphicsResourcesState._mtlIndexBufferBinding;
MTLComputePipelineDescriptor* plDesc = [[_mtlTessControlStageDesc copy] autorelease]; // Use a copy to be thread-safe.
MTLComputePipelineDescriptor* plDesc = [_mtlTessControlStageDesc copy]; // temp retain a copy to be thread-safe.
if (!indexBuff.mtlBuffer && getInputControlPointCount() >= getOutputControlPointCount()) {
plState = getOrCompilePipeline(plDesc, _mtlTessControlStageState, compilerType);
} else if (indexBuff.mtlIndexType == MTLIndexTypeUInt16) {
@ -218,6 +218,8 @@ 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<MTLComputeCommandEncoder> 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<SPIRVShaderOutput> 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<SPIRVShaderOutput> 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<MTLFunction> mtlFunc = func.getMTLFunction();
if ( !mtlFunc ) {
setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Vertex shader function could not be compiled into pipeline. See previous logged error."));
return false;
}
plDesc.vertexFunction = func.mtlFunction;
plDesc.vertexFunction = mtlFunc;
auto& funcRslts = func.shaderConversionResults;
plDesc.rasterizationEnabled = !funcRslts.isRasterizationDisabled;
@ -821,11 +831,12 @@ bool MVKGraphicsPipeline::addTessCtlShaderToPipeline(MTLComputePipelineDescripto
addPrevStageOutputToShaderConverterContext(shaderContext, vtxOutputs);
MVKMTLFunction func = ((MVKShaderModule*)_pTessCtlSS->module)->getMTLFunction(&shaderContext, _pTessCtlSS->pSpecializationInfo, _pipelineCache);
if ( !func.mtlFunction ) {
id<MTLFunction> mtlFunc = func.getMTLFunction();
if ( !mtlFunc ) {
setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Tessellation control shader function could not be compiled into pipeline. See previous logged error."));
return false;
}
plDesc.computeFunction = func.mtlFunction;
plDesc.computeFunction = mtlFunc;
auto& funcRslts = func.shaderConversionResults;
_needsTessCtlSwizzleBuffer = funcRslts.needsSwizzleBuffer;
@ -870,12 +881,13 @@ bool MVKGraphicsPipeline::addTessEvalShaderToPipeline(MTLRenderPipelineDescripto
addPrevStageOutputToShaderConverterContext(shaderContext, tcOutputs);
MVKMTLFunction func = ((MVKShaderModule*)_pTessEvalSS->module)->getMTLFunction(&shaderContext, _pTessEvalSS->pSpecializationInfo, _pipelineCache);
if ( !func.mtlFunction ) {
id<MTLFunction> mtlFunc = func.getMTLFunction();
if ( !mtlFunc ) {
setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Tessellation evaluation shader function could not be compiled into pipeline. See previous logged error."));
return false;
}
// Yeah, you read that right. Tess. eval functions are a kind of vertex function in Metal.
plDesc.vertexFunction = func.mtlFunction;
plDesc.vertexFunction = mtlFunc;
auto& funcRslts = func.shaderConversionResults;
plDesc.rasterizationEnabled = !funcRslts.isRasterizationDisabled;
@ -902,11 +914,12 @@ 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<MTLFunction> mtlFunc = func.getMTLFunction();
if ( !mtlFunc ) {
setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Fragment shader function could not be compiled into pipeline. See previous logged error."));
return false;
}
plDesc.fragmentFunction = func.mtlFunction;
plDesc.fragmentFunction = mtlFunc;
auto& funcRslts = func.shaderConversionResults;
_needsFragmentSwizzleBuffer = funcRslts.needsSwizzleBuffer;
@ -1281,13 +1294,14 @@ 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<MTLFunction> mtlFunc = func.getMTLFunction();
if (mtlFunc) {
MTLComputePipelineDescriptor* plDesc = [MTLComputePipelineDescriptor new]; // temp retain
plDesc.computeFunction = mtlFunc;
// Metal does not allow the name of the pipeline to be changed after it has been created,
// and we need to create the Metal pipeline immediately to provide error feedback to app.
@ -1297,6 +1311,8 @@ 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."));

View File

@ -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

View File

@ -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> mtlFunction;
const SPIRVToMSLConversionResults shaderConversionResults;
typedef struct MVKMTLFunction {
SPIRVToMSLConversionResults shaderConversionResults;
MTLSize threadGroupSize;
inline id<MTLFunction> getMTLFunction() { return _mtlFunction; }
MVKMTLFunction(id<MTLFunction> mtlFunc, const SPIRVToMSLConversionResults scRslts, MTLSize tgSize);
MVKMTLFunction(const MVKMTLFunction& other);
~MVKMTLFunction();
private:
id<MTLFunction> _mtlFunction;
} MVKMTLFunction;
/** A MVKMTLFunction indicating an invalid MTLFunction. The mtlFunction member is nil. */
extern const MVKMTLFunction MVKMTLFunctionNull;
const MVKMTLFunction MVKMTLFunctionNull(nil, SPIRVToMSLConversionResults(), MTLSizeMake(1, 1, 1));
/** Wraps a single MTLLibrary. */
class MVKShaderLibrary : public MVKBaseObject {
@ -85,7 +93,7 @@ public:
size_t mslCompiledCodeLength);
/** Copy constructor. */
MVKShaderLibrary(MVKShaderLibrary& other);
MVKShaderLibrary(const MVKShaderLibrary& other);
~MVKShaderLibrary() override;

View File

@ -26,7 +26,22 @@
using namespace std;
const MVKMTLFunction MVKMTLFunctionNull = { nil, SPIRVToMSLConversionResults(), MTLSizeMake(1, 1, 1) };
MVKMTLFunction::MVKMTLFunction(id<MTLFunction> mtlFunc, const SPIRVToMSLConversionResults scRslts, MTLSize tgSize) {
_mtlFunction = [mtlFunc retain]; // retained
shaderConversionResults = scRslts;
threadGroupSize = tgSize;
}
MVKMTLFunction::MVKMTLFunction(const MVKMTLFunction& other) {
_mtlFunction = [other._mtlFunction retain]; // retained
shaderConversionResults = other.shaderConversionResults;
threadGroupSize = other.threadGroupSize;
}
MVKMTLFunction::~MVKMTLFunction() {
[_mtlFunction release];
}
#pragma mark -
#pragma mark MVKShaderLibrary
@ -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),
MVKMTLFunction mvkMTLFunc(mtlFunc, _shaderConversionResults, MTLSizeMake(getWorkgroupDimensionSize(wgSize.width, pSpecializationInfo),
getWorkgroupDimensionSize(wgSize.height, pSpecializationInfo),
getWorkgroupDimensionSize(wgSize.depth, 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;

View File

@ -75,7 +75,7 @@ id<MTLRenderPipelineState> MVKWatermark::mtlRenderPipelineState() {
}
id<MTLRenderPipelineState> MVKWatermark::newRenderPipelineState() {
MTLRenderPipelineDescriptor* plDesc = [[MTLRenderPipelineDescriptor new] autorelease];
MTLRenderPipelineDescriptor* plDesc = [MTLRenderPipelineDescriptor new]; // temp retained
plDesc.label = _mtlName;
plDesc.vertexFunction = _mtlFunctionVertex;
@ -128,6 +128,7 @@ id<MTLRenderPipelineState> MVKWatermark::newRenderPipelineState() {
NSError* err = nil;
id<MTLRenderPipelineState> rps = [_mtlDevice newRenderPipelineStateWithDescriptor: plDesc error: &err]; // retained
MVKAssert( !err, "Could not create watermark pipeline state (Error code %li)\n%s", (long)err.code, err.localizedDescription.UTF8String);
[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
[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<MTLLibrary> mtlLib = [[_mtlDevice newLibraryWithSource: nsSrc
id<MTLLibrary> mtlLib = [_mtlDevice newLibraryWithSource: nsSrc
options: nil
error: &err] autorelease];
[nsSrc release]; // release temp string
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

View File

@ -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.