diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm index 7a0de21ae..d6687f069 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm +++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm @@ -681,6 +681,7 @@ - (void)setDepthBoundsTestAMD:(BOOL)enable minDepth:(float)minDepth maxDepth:(fl auto* dslBind = dsLayout->getBindingAt(dslBindIdx); if (dslBind->getApplyToStage(stage) && shaderBindingUsage.getBit(dslBindIdx)) { shouldBindArgBuffToStage = true; + if (getDevice()->hasResidencySet()) continue; uint32_t elemCnt = dslBind->getDescriptorCount(descSet->getVariableDescriptorCount()); for (uint32_t elemIdx = 0; elemIdx < elemCnt; elemIdx++) { uint32_t descIdx = dslBind->getDescriptorIndex(elemIdx); diff --git a/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.mm b/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.mm index 10ee00e38..54a816c9e 100644 --- a/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.mm +++ b/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.mm @@ -46,6 +46,7 @@ void MVKMTLBufferAllocationPool::addMTLBuffer() { MTLResourceOptions mbOpts = (_mtlStorageMode << MTLResourceStorageModeShift) | MTLResourceCPUCacheModeDefaultCache; _mtlBuffers.push_back({ [getMTLDevice() newBufferWithLength: _mtlBufferLength options: mbOpts], 0 }); + getDevice()->makeResident(_mtlBuffers.back().mtlBuffer); _nextOffset = 0; } @@ -106,6 +107,7 @@ MVKMTLBufferAllocationPool::~MVKMTLBufferAllocationPool() { for (uint32_t bufferIndex = 0; bufferIndex < _mtlBuffers.size(); ++bufferIndex) { + getDevice()->removeResidency(_mtlBuffers[bufferIndex].mtlBuffer); [_mtlBuffers[bufferIndex].mtlBuffer release]; } _mtlBuffers.clear(); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm index a7cd05595..82581be1b 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm @@ -185,6 +185,7 @@ _mtlBuffer = [_deviceMemory->getMTLHeap() newBufferWithLength: getByteCount() options: _deviceMemory->getMTLResourceOptions() offset: _deviceMemoryOffset]; // retained + getDevice()->makeResident(_mtlBuffer); propagateDebugName(); return _mtlBuffer; } else { @@ -202,6 +203,7 @@ _mtlBufferCache = [getMTLDevice() newBufferWithLength: getByteCount() options: MTLResourceStorageModeManaged]; // retained + getDevice()->makeResident(_mtlBufferCache); flushToDevice(_deviceMemoryOffset, _byteCount); } #endif @@ -268,8 +270,10 @@ void MVKBuffer::detachMemory() { if (_deviceMemory) { _deviceMemory->removeBuffer(this); } _deviceMemory = nullptr; + if (_mtlBuffer) getDevice()->removeResidency(_mtlBuffer); [_mtlBuffer release]; _mtlBuffer = nil; + if (_mtlBufferCache) getDevice()->removeResidency(_mtlBufferCache); [_mtlBufferCache release]; _mtlBufferCache = nil; } @@ -327,6 +331,7 @@ _mtlTexture = [mtlBuff newTextureWithDescriptor: mtlTexDesc offset: mtlBuffOffset bytesPerRow: _mtlBytesPerRow]; + getDevice()->makeResident(_mtlTexture); propagateDebugName(); } return _mtlTexture; @@ -390,6 +395,7 @@ // Potentially called twice, from destroy() and destructor, so ensure everything is nulled out. void MVKBufferView::detachMemory() { + if (_mtlTexture) getDevice()->removeResidency(_mtlTexture); [_mtlTexture release]; _mtlTexture = nil; } diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h index 252a96529..34b44d033 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h @@ -823,6 +823,41 @@ class MVKDevice : public MVKDispatchableVulkanAPIObject { /** Returns the Metal objects underpinning the Vulkan objects indicated in the pNext chain of pMetalObjectsInfo. */ void getMetalObjects(VkExportMetalObjectsInfoEXT* pMetalObjectsInfo); +#if !MVK_XCODE_16 + inline void makeResident(id allocation) {} +#else + inline void makeResident(id allocation) { + @synchronized(_residencySet) { + [_residencySet addAllocation: allocation]; + [_residencySet commit]; + } + } +#endif + +#if !MVK_XCODE_16 + inline void removeResidency(id allocation) {} +#else + inline void removeResidency(id allocation) { + @synchronized(_residencySet) { + [_residencySet removeAllocation:allocation]; + [_residencySet commit]; + } + } +#endif + + inline void addResidencySet(id queue) { +#if MVK_XCODE_16 + if (_residencySet) [queue addResidencySet:_residencySet]; +#endif + } + + inline bool hasResidencySet() { +#if MVK_XCODE_16 + return _residencySet != nil; +#else + return false; +#endif + } #pragma mark Construction @@ -912,6 +947,9 @@ class MVKDevice : public MVKDispatchableVulkanAPIObject { id _globalVisibilityResultMTLBuffer = nil; id _defaultMTLSamplerState = nil; id _dummyBlitMTLBuffer = nil; +#if MVK_XCODE_16 + id _residencySet = nil; +#endif uint32_t _globalVisibilityQueryCount = 0; int _capturePipeFileDesc = -1; bool _isPerformanceTracking = false; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm index 835051b55..79108afe6 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm @@ -5113,6 +5113,21 @@ static uint32_t mvkGetEntryProperty(io_registry_entry_t entry, CFStringRef prope // Create the command queues void MVKDevice::initQueues(const VkDeviceCreateInfo* pCreateInfo) { +#if MVK_XCODE_16 + MTLResidencySetDescriptor *setDescriptor; + setDescriptor = [MTLResidencySetDescriptor new]; + setDescriptor.label = @"Primary residency set"; + setDescriptor.initialCapacity = 256; + + NSError *error; + _residencySet = [_physicalDevice->getMTLDevice() newResidencySetWithDescriptor:setDescriptor + error:&error]; + if (error) { + reportMessage(MVK_CONFIG_LOG_LEVEL_ERROR, "Error allocating residency set: %s", error.description.UTF8String); + } + [setDescriptor release]; +#endif + auto qFams = _physicalDevice->getQueueFamilies(); uint32_t qrCnt = pCreateInfo->queueCreateInfoCount; for (uint32_t qrIdx = 0; qrIdx < qrCnt; qrIdx++) { @@ -5178,6 +5193,9 @@ static uint32_t mvkGetEntryProperty(io_registry_entry_t entry, CFStringRef prope for (auto &fences: _barrierFences) for (auto fence: fences) [fence release]; +#if MVK_XCODE_16 + [_residencySet release]; +#endif [_globalVisibilityResultMTLBuffer release]; [_defaultMTLSamplerState release]; [_dummyBlitMTLBuffer release]; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm index 7323691f6..8169ccde5 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm @@ -246,7 +246,7 @@ } if (!_mtlBuffer) { return false; } _pMemory = isMemoryHostAccessible() ? _mtlBuffer.contents : nullptr; - + getDevice()->makeResident(_mtlBuffer); propagateDebugName(); return true; @@ -425,6 +425,7 @@ auto imgCopies = _imageMemoryBindings; for (auto& img : imgCopies) { img->bindDeviceMemory(nullptr, 0); } + if (_mtlBuffer) getDevice()->removeResidency(_mtlBuffer); [_mtlBuffer release]; _mtlBuffer = nil; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm index 6bedb4a7b..3807f5b43 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm @@ -76,7 +76,7 @@ } [mtlTexDesc release]; // temp release - + _image->getDevice()->makeResident(_mtlTexture); propagateDebugName(); } return _mtlTexture; @@ -101,6 +101,7 @@ } void MVKImagePlane::releaseMTLTexture() { + if (_mtlTexture) _image->getDevice()->removeResidency(_mtlTexture); [_mtlTexture release]; _mtlTexture = nil; @@ -436,7 +437,8 @@ } if (!_mtlTexelBuffer) { return reportError(VK_ERROR_OUT_OF_DEVICE_MEMORY, "Could not create an MTLBuffer for an image that requires a buffer backing store. Images that can be used for atomic accesses must have a texel buffer backing them."); - } + } + getDevice()->makeResident(_mtlTexelBuffer); _mtlTexelBufferOffset = 0; _ownsTexelBuffer = true; } @@ -444,7 +446,6 @@ _mtlTexelBuffer = _deviceMemory->_mtlBuffer; _mtlTexelBufferOffset = getDeviceMemoryOffset(); } - flushToDevice(getDeviceMemoryOffset(), getByteCount()); return _deviceMemory->addImageMemoryBinding(this); } @@ -541,7 +542,10 @@ MVKImageMemoryBinding::~MVKImageMemoryBinding() { if (_deviceMemory) { _deviceMemory->removeImageMemoryBinding(this); } - if (_ownsTexelBuffer) { [_mtlTexelBuffer release]; } + if (_ownsTexelBuffer) { + if (_ownsTexelBuffer) _image->getDevice()->removeResidency(_mtlTexelBuffer); + [_mtlTexelBuffer release]; + } } diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm index 8fe78d30e..db363634d 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm @@ -339,6 +339,7 @@ // Retrieves and initializes the Metal command queue and Xcode GPU capture scopes void MVKQueue::initMTLCommandQueue() { _mtlQueue = _queueFamily->getMTLCommandQueue(_index); // not retained (cached in queue family) + _device->addResidencySet(_mtlQueue); _submissionCaptureScope = new MVKGPUCaptureScope(this); if (_queueFamily->getIndex() == getMVKConfig().defaultGPUCaptureScopeQueueFamilyIndex &&