Vulkan memory coherence guarantees when HOST_VISIBLE without HOST_COHERENT
0 reputation · 11 Sept 2022, 09:50 UTC
0 reputation · 11 Sept 2022, 09:50 UTC
The Vulkan specification defines VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT as allowing CPU access to memory, but leaves cache coherency behavior implementation-defined when HOST_COHERENT is not set. This creates uncertainty for applications targeting multiple GPU vendors where memory types may or may not require explicit flush/invalidate operations.
Key Constraints
Relevant Documentation
According to the Vulkan 1.3 specification, memory barriers (VK_MEMORY_BARRIER) are required for correct host-device synchronization regardless of memory type selection. The VK_KHR_get_memory_requirements2 extension provides tools for runtime detection of memory properties.
Unresolved Behavior
Applications cannot reliably assume HOST_VISIBLE memory is coherent across different vendor implementations and driver versions without explicit runtime testing. This raises questions about portable memory management strategies.
33775 reputation · 11 Sept 2022, 18:08 UTC
Yes, applications must implement explicit vkFlushMappedMemoryRanges and vkInvalidateMappedMemoryRanges paths for any memory type that lacks VK_MEMORY_PROPERTY_HOST_COHERENT_BIT. The spec guarantees nothing about cache coherence for HOST_VISIBLE alone; omitting flush/invalidate on non-coherent memory is undefined behavior, even if it appears to work on your development GPU.
vkFlushMappedMemoryRanges before the device reads. Before host reads, call vkInvalidateMappedMemoryRanges after the device writes.VkPhysicalDeviceLimits::nonCoherentAtomSize (except ranges that reach the allocation end). Misaligned ranges are a validation error.vkGetPhysicalDeviceMemoryProperties once at startup.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT and propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT.nonCoherentAtomSize from VkPhysicalDeviceLimits.HOST_COHERENT type exists for your needs, prefer it — flush/invalidate become no-ops per spec.// At initialization
VkPhysicalDeviceMemoryProperties memProps;
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProps);
VkPhysicalDeviceProperties devProps;
vkGetPhysicalDeviceProperties(physicalDevice, &devProps);
const VkDeviceSize atomSize = devProps.limits.nonCoherentAtomSize;
// When choosing a memory type for host-visible buffers
bool needsExplicitFlush = false;
for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i) {
if ((typeFilter & (1u << i)) &&
(memProps.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) {
needsExplicitFlush = !(memProps.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
selectedTypeIndex = i;
break;
}
}
// At runtime, branch on the flag, not the vendor
if (needsExplicitFlush) {
VkMappedMemoryRange range = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
range.memory = allocation;
range.offset = alignDown(offset, atomSize);
range.size = alignUp(offset + size, atomSize) - range.offset;
vkFlushMappedMemoryRanges(device, 1, &range); // after host write
// or vkInvalidateMappedMemoryRanges(device, 1, &range); // before host read
}
HOST_COHERENT flag (queried at runtime) gives correct behavior everywhere with zero penalty on coherent platforms.nonCoherentAtomSize on each target device during QA.Which GPU architectures (vendor + generation) are in your minimum supported hardware set? This determines whether a HOST_COHERENT type is universally available for your buffer usage, or if you must ship the explicit path for some devices.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.