Reorganize coherent texture flushing on memory map and unmap.
MVKDeviceMemory track original Vulkan memory coherency request, not just Metal coherency status. MVKDeviceMemory map() and unmap() only triggers host flush if coherent Vulkan memory was requested, not just when Metal memory is coherent, because this can be changed for dedicated image resources. Redesign MVKDeviceMemory flushToDevice() and pullFromDevice() to be consistent operation with each other. Don't trigger automatic texture flush during vkCmdCopyImage(), as it can cause an unexpected update from mapped memory, resulting in a race condition.
This commit is contained in:
parent
b74040a93d
commit
a2d8cf1710
@ -88,8 +88,6 @@ void MVKCmdCopyImage<N>::encode(MVKCommandEncoder* cmdEncoder, MVKCommandUse com
|
||||
VkBufferImageCopy vkDstCopies[copyCnt];
|
||||
size_t tmpBuffSize = 0;
|
||||
|
||||
_srcImage->flushToDevice(0, VK_WHOLE_SIZE);
|
||||
|
||||
for (uint32_t copyIdx = 0; copyIdx < copyCnt; copyIdx++) {
|
||||
auto& vkIC = _vkImageCopies[copyIdx];
|
||||
|
||||
|
@ -3616,7 +3616,7 @@ VkResult MVKDevice::invalidateMappedMemoryRanges(uint32_t memRangeCount, const V
|
||||
for (uint32_t i = 0; i < memRangeCount; i++) {
|
||||
const VkMappedMemoryRange* pMem = &pMemRanges[i];
|
||||
MVKDeviceMemory* mvkMem = (MVKDeviceMemory*)pMem->memory;
|
||||
VkResult r = mvkMem->pullFromDevice(pMem->offset, pMem->size, false, &mvkBlitEnc);
|
||||
VkResult r = mvkMem->pullFromDevice(pMem->offset, pMem->size, &mvkBlitEnc);
|
||||
if (rslt == VK_SUCCESS) { rslt = r; }
|
||||
}
|
||||
if (mvkBlitEnc.mtlBlitEncoder) { [mvkBlitEnc.mtlBlitEncoder endEncoding]; }
|
||||
|
@ -93,17 +93,11 @@ public:
|
||||
/** Returns whether this device memory is currently mapped to host memory. */
|
||||
bool isMapped() { return _mappedRange.size > 0; }
|
||||
|
||||
/**
|
||||
* If this memory is host-visible, the specified memory range is flushed to the device.
|
||||
* Normally, flushing will only occur if the device memory is non-coherent, but flushing
|
||||
* to coherent memory can be forced by setting evenIfCoherent to true.
|
||||
*/
|
||||
VkResult flushToDevice(VkDeviceSize offset, VkDeviceSize size, bool evenIfCoherent = false);
|
||||
/** If this memory is host-visible, the specified memory range is flushed to the device. */
|
||||
VkResult flushToDevice(VkDeviceSize offset, VkDeviceSize size);
|
||||
|
||||
/**
|
||||
* If this memory is host-visible, pulls the specified memory range from the device.
|
||||
* Normally, pulling will only occur if the device memory is non-coherent, but pulling
|
||||
* to coherent memory can be forced by setting evenIfCoherent to true.
|
||||
*
|
||||
* If pBlitEnc is not null, it points to a holder for a MTLBlitCommandEncoder and its
|
||||
* associated MTLCommandBuffer. If this instance has a MTLBuffer using managed memory,
|
||||
@ -114,7 +108,6 @@ public:
|
||||
*/
|
||||
VkResult pullFromDevice(VkDeviceSize offset,
|
||||
VkDeviceSize size,
|
||||
bool evenIfCoherent = false,
|
||||
MVKMTLBlitEncoder* pBlitEnc = nullptr);
|
||||
|
||||
|
||||
@ -172,8 +165,10 @@ protected:
|
||||
id<MTLHeap> _mtlHeap = nil;
|
||||
void* _pMemory = nullptr;
|
||||
void* _pHostMemory = nullptr;
|
||||
bool _isDedicated = false;
|
||||
VkMemoryPropertyFlags _vkMemProps;
|
||||
MTLStorageMode _mtlStorageMode;
|
||||
MTLCPUCacheMode _mtlCPUCacheMode;
|
||||
bool _isDedicated = false;
|
||||
|
||||
};
|
||||
|
||||
|
@ -55,8 +55,11 @@ VkResult MVKDeviceMemory::map(VkDeviceSize offset, VkDeviceSize size, VkMemoryMa
|
||||
|
||||
*ppData = (void*)((uintptr_t)_pMemory + offset);
|
||||
|
||||
// Coherent memory does not require flushing by app, so we must flush now, to handle any texture updates.
|
||||
pullFromDevice(offset, size, isMemoryHostCoherent());
|
||||
// Coherent memory does not require flushing by app, so we must flush now
|
||||
// to support Metal textures that actually reside in non-coherent memory.
|
||||
if (mvkIsAnyFlagEnabled(_vkMemProps, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
|
||||
pullFromDevice(offset, size);
|
||||
}
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
@ -68,54 +71,57 @@ void MVKDeviceMemory::unmap() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Coherent memory does not require flushing by app, so we must flush now.
|
||||
flushToDevice(_mappedRange.offset, _mappedRange.size, isMemoryHostCoherent());
|
||||
// Coherent memory does not require flushing by app, so we must flush now
|
||||
// to support Metal textures that actually reside in non-coherent memory.
|
||||
if (mvkIsAnyFlagEnabled(_vkMemProps, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
|
||||
flushToDevice(_mappedRange.offset, _mappedRange.size);
|
||||
}
|
||||
|
||||
_mappedRange.offset = 0;
|
||||
_mappedRange.size = 0;
|
||||
}
|
||||
|
||||
VkResult MVKDeviceMemory::flushToDevice(VkDeviceSize offset, VkDeviceSize size, bool evenIfCoherent) {
|
||||
// Coherent memory is flushed on unmap(), so it is only flushed if forced
|
||||
VkResult MVKDeviceMemory::flushToDevice(VkDeviceSize offset, VkDeviceSize size) {
|
||||
VkDeviceSize memSize = adjustMemorySize(size, offset);
|
||||
if (memSize > 0 && isMemoryHostAccessible() && (evenIfCoherent || !isMemoryHostCoherent()) ) {
|
||||
if (memSize == 0 || !isMemoryHostAccessible()) { return VK_SUCCESS; }
|
||||
|
||||
#if MVK_MACOS
|
||||
if (_mtlBuffer && _mtlStorageMode == MTLStorageModeManaged) {
|
||||
[_mtlBuffer didModifyRange: NSMakeRange(offset, memSize)];
|
||||
}
|
||||
if (_mtlBuffer && _mtlStorageMode == MTLStorageModeManaged) {
|
||||
[_mtlBuffer didModifyRange: NSMakeRange(offset, memSize)];
|
||||
}
|
||||
#endif
|
||||
|
||||
// If we have an MTLHeap object, there's no need to sync memory manually between images and the buffer.
|
||||
if (!_mtlHeap) {
|
||||
lock_guard<mutex> lock(_rezLock);
|
||||
for (auto& img : _imageMemoryBindings) { img->flushToDevice(offset, memSize); }
|
||||
for (auto& buf : _buffers) { buf->flushToDevice(offset, memSize); }
|
||||
}
|
||||
// If we have an MTLHeap object, there's no need to sync memory manually between resources and the buffer.
|
||||
if ( !_mtlHeap ) {
|
||||
lock_guard<mutex> lock(_rezLock);
|
||||
for (auto& img : _imageMemoryBindings) { img->flushToDevice(offset, memSize); }
|
||||
for (auto& buf : _buffers) { buf->flushToDevice(offset, memSize); }
|
||||
}
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
VkResult MVKDeviceMemory::pullFromDevice(VkDeviceSize offset,
|
||||
VkDeviceSize size,
|
||||
bool evenIfCoherent,
|
||||
MVKMTLBlitEncoder* pBlitEnc) {
|
||||
// Coherent memory is flushed on unmap(), so it is only flushed if forced
|
||||
VkDeviceSize memSize = adjustMemorySize(size, offset);
|
||||
if (memSize > 0 && isMemoryHostAccessible() && (evenIfCoherent || !isMemoryHostCoherent()) && !_mtlHeap) {
|
||||
if (memSize == 0 || !isMemoryHostAccessible()) { return VK_SUCCESS; }
|
||||
|
||||
#if MVK_MACOS
|
||||
if (pBlitEnc && _mtlBuffer && _mtlStorageMode == MTLStorageModeManaged) {
|
||||
if ( !pBlitEnc->mtlCmdBuffer) { pBlitEnc->mtlCmdBuffer = _device->getAnyQueue()->getMTLCommandBuffer(kMVKCommandUseInvalidateMappedMemoryRanges); }
|
||||
if ( !pBlitEnc->mtlBlitEncoder) { pBlitEnc->mtlBlitEncoder = [pBlitEnc->mtlCmdBuffer blitCommandEncoder]; }
|
||||
[pBlitEnc->mtlBlitEncoder synchronizeResource: _mtlBuffer];
|
||||
}
|
||||
#endif
|
||||
|
||||
// If we have an MTLHeap object, there's no need to sync memory manually between resources and the buffer.
|
||||
if ( !_mtlHeap ) {
|
||||
lock_guard<mutex> lock(_rezLock);
|
||||
for (auto& img : _imageMemoryBindings) { img->pullFromDevice(offset, memSize); }
|
||||
for (auto& buf : _buffers) { buf->pullFromDevice(offset, memSize); }
|
||||
|
||||
#if MVK_MACOS
|
||||
if (pBlitEnc && _mtlBuffer && _mtlStorageMode == MTLStorageModeManaged) {
|
||||
if ( !pBlitEnc->mtlCmdBuffer) { pBlitEnc->mtlCmdBuffer = _device->getAnyQueue()->getMTLCommandBuffer(kMVKCommandUseInvalidateMappedMemoryRanges); }
|
||||
if ( !pBlitEnc->mtlBlitEncoder) { pBlitEnc->mtlBlitEncoder = [pBlitEnc->mtlCmdBuffer blitCommandEncoder]; }
|
||||
[pBlitEnc->mtlBlitEncoder synchronizeResource: _mtlBuffer];
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
@ -271,9 +277,9 @@ MVKDeviceMemory::MVKDeviceMemory(MVKDevice* device,
|
||||
const VkMemoryAllocateInfo* pAllocateInfo,
|
||||
const VkAllocationCallbacks* pAllocator) : MVKVulkanAPIDeviceObject(device) {
|
||||
// Set Metal memory parameters
|
||||
VkMemoryPropertyFlags vkMemProps = _device->_pMemoryProperties->memoryTypes[pAllocateInfo->memoryTypeIndex].propertyFlags;
|
||||
_mtlStorageMode = mvkMTLStorageModeFromVkMemoryPropertyFlags(vkMemProps);
|
||||
_mtlCPUCacheMode = mvkMTLCPUCacheModeFromVkMemoryPropertyFlags(vkMemProps);
|
||||
_vkMemProps = _device->_pMemoryProperties->memoryTypes[pAllocateInfo->memoryTypeIndex].propertyFlags;
|
||||
_mtlStorageMode = mvkMTLStorageModeFromVkMemoryPropertyFlags(_vkMemProps);
|
||||
_mtlCPUCacheMode = mvkMTLCPUCacheModeFromVkMemoryPropertyFlags(_vkMemProps);
|
||||
|
||||
_allocationSize = pAllocateInfo->allocationSize;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user