Merge pull request #1854 from billhollings/ext-external_memory_host

Add support for VK_EXT_external_memory_host extension.
This commit is contained in:
Bill Hollings 2023-03-20 21:51:04 -04:00 committed by GitHub
commit dd5ff2a9b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 121 additions and 22 deletions

View File

@ -162,3 +162,5 @@ uint64_t mvkGetAvailableMemorySize();
/** Returns the amount of memory currently used by this process. */
uint64_t mvkGetUsedMemorySize();
/** Returns the size of a page of host memory on this platform. */
uint64_t mvkGetHostMemoryPageSize();

View File

@ -136,3 +136,5 @@ uint64_t mvkGetUsedMemorySize() {
return 0;
}
uint64_t mvkGetHostMemoryPageSize() { return sysconf(_SC_PAGESIZE); }

View File

@ -356,6 +356,7 @@ In addition to core *Vulkan* functionality, **MoltenVK** also supports the foll
- `VK_EXT_descriptor_indexing` *(initial release limited to Metal Tier 1: 96/128 textures,
16 samplers, except macOS 11.0 (Big Sur) or later, or on older versions of macOS using
an Intel GPU, and if Metal argument buffers enabled in config)*
- `VK_EXT_external_memory_host`
- `VK_EXT_fragment_shader_interlock` *(requires Metal 2.0 and Raster Order Groups)*
- `VK_EXT_host_query_reset`
- `VK_EXT_image_robustness`

View File

@ -19,6 +19,7 @@ MoltenVK 1.2.3
Released TBA
- Add support for extensions:
- `VK_EXT_external_memory_host`
- `VK_EXT_pipeline_creation_cache_control`
- `VK_EXT_shader_atomic_float`
- `VK_EXT_surface_maintenance1`

View File

@ -1023,6 +1023,7 @@ typedef struct {
VkBool32 shaderBarycentricCoordinates; /**< If true, fragment shader barycentric coordinates are supported. */
MTLArgumentBuffersTier argumentBuffersTier; /**< The argument buffer tier available on this device, as a Metal enumeration. */
VkBool32 needsSampleDrefLodArrayWorkaround; /**< If true, sampling from arrayed depth images with explicit LoD is broken and needs a workaround. */
VkDeviceSize hostMemoryPageSize; /**< The size of a page of host memory on this platform. */
} MVKPhysicalDeviceMetalFeatures;
/** MoltenVK performance of a particular type of activity. */

View File

@ -444,6 +444,7 @@ protected:
uint32_t _hostCoherentMemoryTypes;
uint32_t _privateMemoryTypes;
uint32_t _lazilyAllocatedMemoryTypes;
VkExternalMemoryProperties _hostPointerExternalMemoryProperties;
VkExternalMemoryProperties _mtlBufferExternalMemoryProperties;
VkExternalMemoryProperties _mtlTextureExternalMemoryProperties;
};
@ -514,6 +515,11 @@ public:
/** Populates the device group peer memory features. */
void getPeerMemoryFeatures(uint32_t heapIndex, uint32_t localDevice, uint32_t remoteDevice, VkPeerMemoryFeatureFlags* pPeerMemoryFeatures);
/** Returns the properties of the host memory pointer. */
VkResult getMemoryHostPointerProperties(VkExternalMemoryHandleTypeFlagBits handleType,
const void* pHostPointer,
VkMemoryHostPointerPropertiesEXT* pMemHostPtrProps);
#pragma mark Object lifecycle

View File

@ -694,9 +694,16 @@ void MVKPhysicalDevice::getProperties(VkPhysicalDeviceProperties2* properties) {
portabilityProps->minVertexInputBindingStrideAlignment = (uint32_t)_metalFeatures.vertexStrideAlignment;
break;
}
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT: {
auto* divisorProps = (VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT*)next;
divisorProps->maxVertexAttribDivisor = kMVKUndefinedLargeUInt32;
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT: {
auto* extMemHostProps = (VkPhysicalDeviceExternalMemoryHostPropertiesEXT*)next;
extMemHostProps->minImportedHostPointerAlignment = _metalFeatures.hostMemoryPageSize;
break;
}
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT: {
// This isn't implemented yet, but when it is, it is expected that we'll wind up doing it manually.
auto* robustness2Props = (VkPhysicalDeviceRobustness2PropertiesEXT*)next;
robustness2Props->robustStorageBufferAccessSizeAlignment = 1;
robustness2Props->robustUniformBufferAccessSizeAlignment = 1;
break;
}
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT: {
@ -709,11 +716,9 @@ void MVKPhysicalDevice::getProperties(VkPhysicalDeviceProperties2* properties) {
sampLocnProps->variableSampleLocations = VK_FALSE;
break;
}
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT: {
// This isn't implemented yet, but when it is, it is expected that we'll wind up doing it manually.
auto* robustness2Props = (VkPhysicalDeviceRobustness2PropertiesEXT*)next;
robustness2Props->robustStorageBufferAccessSizeAlignment = 1;
robustness2Props->robustUniformBufferAccessSizeAlignment = 1;
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT: {
auto* divisorProps = (VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT*)next;
divisorProps->maxVertexAttribDivisor = kMVKUndefinedLargeUInt32;
break;
}
default:
@ -1055,15 +1060,25 @@ static VkExternalMemoryProperties _emptyExtMemProps = {};
VkExternalMemoryProperties& MVKPhysicalDevice::getExternalBufferProperties(VkExternalMemoryHandleTypeFlagBits handleType) {
switch (handleType) {
case VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR: return _mtlBufferExternalMemoryProperties;
default: return _emptyExtMemProps;
case VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT:
case VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_MAPPED_FOREIGN_MEMORY_BIT_EXT:
return _hostPointerExternalMemoryProperties;
case VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR:
return _mtlBufferExternalMemoryProperties;
default:
return _emptyExtMemProps;
}
}
VkExternalMemoryProperties& MVKPhysicalDevice::getExternalImageProperties(VkExternalMemoryHandleTypeFlagBits handleType) {
switch (handleType) {
case VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR: return _mtlTextureExternalMemoryProperties;
default: return _emptyExtMemProps;
case VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT:
case VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_MAPPED_FOREIGN_MEMORY_BIT_EXT:
return _hostPointerExternalMemoryProperties;
case VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR:
return _mtlTextureExternalMemoryProperties;
default:
return _emptyExtMemProps;
}
}
@ -1599,6 +1614,8 @@ void MVKPhysicalDevice::initMetalFeatures() {
// Start with all Metal features cleared
mvkClear(&_metalFeatures);
_metalFeatures.hostMemoryPageSize = mvkGetHostMemoryPageSize();
_metalFeatures.maxPerStageBufferCount = 31;
_metalFeatures.maxMTLBufferSize = (256 * MEBI);
_metalFeatures.dynamicMTLBufferSize = 0;
@ -2993,6 +3010,12 @@ uint32_t MVKPhysicalDevice::getMaxSamplerCount() {
void MVKPhysicalDevice::initExternalMemoryProperties() {
// Common
_hostPointerExternalMemoryProperties.externalMemoryFeatures = VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT;
_hostPointerExternalMemoryProperties.exportFromImportedHandleTypes = 0;
_hostPointerExternalMemoryProperties.compatibleHandleTypes = (VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT |
VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_MAPPED_FOREIGN_MEMORY_BIT_EXT);
// Buffers
_mtlBufferExternalMemoryProperties.externalMemoryFeatures = (VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT |
VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT);
@ -3469,6 +3492,23 @@ void MVKDevice::getPeerMemoryFeatures(uint32_t heapIndex, uint32_t localDevice,
*pPeerMemoryFeatures = VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT | VK_PEER_MEMORY_FEATURE_COPY_DST_BIT;
}
VkResult MVKDevice::getMemoryHostPointerProperties(VkExternalMemoryHandleTypeFlagBits handleType,
const void* pHostPointer,
VkMemoryHostPointerPropertiesEXT* pMemHostPtrProps) {
if (pMemHostPtrProps) {
switch (handleType) {
case VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT:
case VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_MAPPED_FOREIGN_MEMORY_BIT_EXT:
pMemHostPtrProps->memoryTypeBits = _physicalDevice->getHostVisibleMemoryTypes();
break;
default:
pMemHostPtrProps->memoryTypeBits = 0;
break;
}
}
return VK_SUCCESS;
}
#pragma mark Object lifecycle

View File

@ -170,6 +170,7 @@ protected:
MTLStorageMode _mtlStorageMode;
MTLCPUCacheMode _mtlCPUCacheMode;
bool _isDedicated = false;
bool _isHostMemImported = false;
};

View File

@ -181,6 +181,9 @@ bool MVKDeviceMemory::ensureMTLHeap() {
if (_mtlHeap) { return true; }
// Can't create a MTLHeap on a imported memory
if (_isHostMemImported) { return true; }
// Don't bother if we don't have placement heaps.
if (!getDevice()->_pMetalFeatures->placementHeaps) { return true; }
@ -233,7 +236,12 @@ bool MVKDeviceMemory::ensureMTLBuffer() {
}
[_mtlBuffer makeAliasable];
} else if (_pHostMemory) {
_mtlBuffer = [getMTLDevice() newBufferWithBytes: _pHostMemory length: memLen options: getMTLResourceOptions()]; // retained
auto rezOpts = getMTLResourceOptions();
if (_isHostMemImported) {
_mtlBuffer = [getMTLDevice() newBufferWithBytesNoCopy: _pHostMemory length: memLen options: rezOpts deallocator: nil]; // retained
} else {
_mtlBuffer = [getMTLDevice() newBufferWithBytes: _pHostMemory length: memLen options: rezOpts]; // retained
}
freeHostMemory();
} else {
_mtlBuffer = [getMTLDevice() newBufferWithLength: memLen options: getMTLResourceOptions()]; // retained
@ -264,7 +272,7 @@ bool MVKDeviceMemory::ensureHostMemory() {
}
void MVKDeviceMemory::freeHostMemory() {
free(_pHostMemory);
if ( !_isHostMemImported ) { free(_pHostMemory); }
_pHostMemory = nullptr;
}
@ -296,6 +304,23 @@ MVKDeviceMemory::MVKDeviceMemory(MVKDevice* device,
_isDedicated = dedicatedImage || dedicatedBuffer;
break;
}
case VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT: {
auto* pMemHostPtrInfo = (VkImportMemoryHostPointerInfoEXT*)next;
if (mvkIsAnyFlagEnabled(_vkMemPropFlags, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) {
switch (pMemHostPtrInfo->handleType) {
case VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT:
case VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_MAPPED_FOREIGN_MEMORY_BIT_EXT:
_pHostMemory = pMemHostPtrInfo->pHostPointer;
_isHostMemImported = true;
break;
default:
break;
}
} else {
setConfigurationResult(reportError(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR, "vkAllocateMemory(): Imported memory must be host-visible."));
}
break;
}
case VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO: {
auto* pExpMemInfo = (VkExportMemoryAllocateInfo*)next;
handleTypes = pExpMemInfo->handleTypes;
@ -333,7 +358,7 @@ MVKDeviceMemory::MVKDeviceMemory(MVKDevice* device,
#if MVK_MACOS
if (isMemoryHostCoherent() ) {
if (!((MVKImage*)dedicatedImage)->_isLinear) {
setConfigurationResult(reportError(VK_ERROR_OUT_OF_DEVICE_MEMORY, "Host-coherent VkDeviceMemory objects cannot be associated with optimal-tiling images."));
setConfigurationResult(reportError(VK_ERROR_OUT_OF_DEVICE_MEMORY, "vkAllocateMemory(): Host-coherent VkDeviceMemory objects cannot be associated with optimal-tiling images."));
} else {
if (!_device->_pMetalFeatures->sharedLinearTextures) {
// Need to use the managed mode for images.
@ -341,7 +366,7 @@ MVKDeviceMemory::MVKDeviceMemory(MVKDevice* device,
}
// Nonetheless, we need a buffer to be able to map the memory at will.
if (!ensureMTLBuffer() ) {
setConfigurationResult(reportError(VK_ERROR_OUT_OF_DEVICE_MEMORY, "Could not allocate a host-coherent VkDeviceMemory of size %llu bytes. The maximum memory-aligned size of a host-coherent VkDeviceMemory is %llu bytes.", _allocationSize, _device->_pMetalFeatures->maxMTLBufferSize));
setConfigurationResult(reportError(VK_ERROR_OUT_OF_DEVICE_MEMORY, "vkAllocateMemory(): Could not allocate a host-coherent VkDeviceMemory of size %llu bytes. The maximum memory-aligned size of a host-coherent VkDeviceMemory is %llu bytes.", _allocationSize, _device->_pMetalFeatures->maxMTLBufferSize));
}
}
}
@ -359,15 +384,16 @@ MVKDeviceMemory::MVKDeviceMemory(MVKDevice* device,
// If we can, create a MTLHeap. This should happen before creating the buffer, allowing us to map its contents.
if ( !_isDedicated ) {
if (!ensureMTLHeap()) {
setConfigurationResult(reportError(VK_ERROR_OUT_OF_DEVICE_MEMORY, "Could not allocate VkDeviceMemory of size %llu bytes.", _allocationSize));
setConfigurationResult(reportError(VK_ERROR_OUT_OF_DEVICE_MEMORY, "vkAllocateMemory(): Could not allocate VkDeviceMemory of size %llu bytes.", _allocationSize));
return;
}
}
// If memory needs to be coherent it must reside in an MTLBuffer, since an open-ended map() must work.
// If memory needs to be coherent it must reside in a MTLBuffer, since an open-ended map() must work.
// If memory was imported, a MTLBuffer must be created on it.
// Or if a MTLBuffer will be exported, ensure it exists.
if ((isMemoryHostCoherent() || willExportMTLBuffer) && !ensureMTLBuffer() ) {
setConfigurationResult(reportError(VK_ERROR_OUT_OF_DEVICE_MEMORY, "Could not allocate a host-coherent or exportable VkDeviceMemory of size %llu bytes. The maximum memory-aligned size of a host-coherent VkDeviceMemory is %llu bytes.", _allocationSize, _device->_pMetalFeatures->maxMTLBufferSize));
if ((isMemoryHostCoherent() || _isHostMemImported || willExportMTLBuffer) && !ensureMTLBuffer() ) {
setConfigurationResult(reportError(VK_ERROR_OUT_OF_DEVICE_MEMORY, "vkAllocateMemory(): Could not allocate a host-coherent or exportable VkDeviceMemory of size %llu bytes. The maximum memory-aligned size of a host-coherent VkDeviceMemory is %llu bytes.", _allocationSize, _device->_pMetalFeatures->maxMTLBufferSize));
}
}

View File

@ -721,6 +721,7 @@ void MVKInstance::initProcAddrs() {
ADD_DVC_EXT_ENTRY_POINT(vkCmdDebugMarkerBeginEXT, EXT_DEBUG_MARKER);
ADD_DVC_EXT_ENTRY_POINT(vkCmdDebugMarkerEndEXT, EXT_DEBUG_MARKER);
ADD_DVC_EXT_ENTRY_POINT(vkCmdDebugMarkerInsertEXT, EXT_DEBUG_MARKER);
ADD_DVC_EXT_ENTRY_POINT(vkGetMemoryHostPointerPropertiesEXT, EXT_EXTERNAL_MEMORY_HOST);
ADD_DVC_EXT_ENTRY_POINT(vkSetHdrMetadataEXT, EXT_HDR_METADATA);
ADD_DVC_EXT_ENTRY_POINT(vkExportMetalObjectsEXT, EXT_METAL_OBJECTS);
ADD_DVC_EXT_ENTRY_POINT(vkCreatePrivateDataSlotEXT, EXT_PRIVATE_DATA);

View File

@ -44,7 +44,7 @@
MVK_EXTENSION(KHR_16bit_storage, KHR_16BIT_STORAGE, DEVICE, 10.11, 8.0)
MVK_EXTENSION(KHR_8bit_storage, KHR_8BIT_STORAGE, DEVICE, 10.11, 8.0)
MVK_EXTENSION(KHR_bind_memory2, KHR_BIND_MEMORY_2, DEVICE, 10.11, 8.0)
MVK_EXTENSION(KHR_buffer_device_address, KHR_BUFFER_DEVICE_ADDRESS, DEVICE, 13.0, 16.0)
MVK_EXTENSION(KHR_buffer_device_address, KHR_BUFFER_DEVICE_ADDRESS, DEVICE, 13.0, 16.0)
MVK_EXTENSION(KHR_copy_commands2, KHR_COPY_COMMANDS_2, DEVICE, 10.11, 8.0)
MVK_EXTENSION(KHR_create_renderpass2, KHR_CREATE_RENDERPASS_2, DEVICE, 10.11, 8.0)
MVK_EXTENSION(KHR_dedicated_allocation, KHR_DEDICATED_ALLOCATION, DEVICE, 10.11, 8.0)
@ -91,11 +91,12 @@ MVK_EXTENSION(KHR_timeline_semaphore, KHR_TIMELINE_SEMAPHORE,
MVK_EXTENSION(KHR_uniform_buffer_standard_layout, KHR_UNIFORM_BUFFER_STANDARD_LAYOUT, DEVICE, 10.11, 8.0)
MVK_EXTENSION(KHR_variable_pointers, KHR_VARIABLE_POINTERS, DEVICE, 10.11, 8.0)
MVK_EXTENSION(KHR_vulkan_memory_model, KHR_VULKAN_MEMORY_MODEL, DEVICE, MVK_NA, MVK_NA)
MVK_EXTENSION(EXT_buffer_device_address, EXT_BUFFER_DEVICE_ADDRESS, DEVICE, 13.0, 16.0)
MVK_EXTENSION(EXT_buffer_device_address, EXT_BUFFER_DEVICE_ADDRESS, DEVICE, 13.0, 16.0)
MVK_EXTENSION(EXT_debug_marker, EXT_DEBUG_MARKER, DEVICE, 10.11, 8.0)
MVK_EXTENSION(EXT_debug_report, EXT_DEBUG_REPORT, INSTANCE, 10.11, 8.0)
MVK_EXTENSION(EXT_debug_utils, EXT_DEBUG_UTILS, INSTANCE, 10.11, 8.0)
MVK_EXTENSION(EXT_descriptor_indexing, EXT_DESCRIPTOR_INDEXING, DEVICE, 10.11, 8.0)
MVK_EXTENSION(EXT_external_memory_host, EXT_EXTERNAL_MEMORY_HOST, DEVICE, 10.11, 8.0)
MVK_EXTENSION(EXT_fragment_shader_interlock, EXT_FRAGMENT_SHADER_INTERLOCK, DEVICE, 10.13, 11.0)
MVK_EXTENSION(EXT_hdr_metadata, EXT_HDR_METADATA, DEVICE, 10.15, MVK_NA)
MVK_EXTENSION(EXT_host_query_reset, EXT_HOST_QUERY_RESET, DEVICE, 10.11, 8.0)

View File

@ -3228,6 +3228,23 @@ MVK_PUBLIC_VULKAN_SYMBOL void vkSubmitDebugUtilsMessageEXT(
}
#pragma mark -
#pragma mark VK_EXT_external_memory_host extension
MVK_PUBLIC_VULKAN_SYMBOL VkResult vkGetMemoryHostPointerPropertiesEXT(
VkDevice device,
VkExternalMemoryHandleTypeFlagBits handleType,
const void* pHostPointer,
VkMemoryHostPointerPropertiesEXT* pMemoryHostPointerProperties) {
MVKTraceVulkanCallStart();
MVKDevice* mvkDvc = MVKDevice::getMVKDevice(device);
VkResult rslt = mvkDvc->getMemoryHostPointerProperties(handleType, pHostPointer, pMemoryHostPointerProperties);
MVKTraceVulkanCallEnd();
return rslt;
}
#pragma mark -
#pragma mark VK_EXT_hdr_metadata extension