Add support for VK_KHR_external_memory extension.

Functions and functionality supported, but don't currently do anything
until Metal-friendly enumerations added to VkExternalMemoryHandleTypeFlagBits.
Updated What's New document.
This commit is contained in:
Bill Hollings 2020-05-06 19:43:21 -04:00
parent 2b3cf6f395
commit d343fed52e
11 changed files with 151 additions and 53 deletions

View File

@ -20,7 +20,10 @@ MoltenVK 1.0.42
Released TBD
- Add support for extensions:
- `VK_KHR_external_memory_capabilities`
- `VK_KHR_external_memory` (non-functional groundwork for future
Metal-resource Vulkan extension).
- `VK_KHR_external_memory_capabilities` (non-functional groundwork
for future Metal-resource Vulkan extension).
- Memory consumption improvements when handling Vulkan commands.
- Reinstate `VulkanSamples API-Samples` demo apps and add
`input_attachment` and `push_descriptors` demos.

View File

@ -92,6 +92,7 @@ protected:
bool shouldFlushHostMemory();
VkResult flushToDevice(VkDeviceSize offset, VkDeviceSize size);
VkResult pullFromDevice(VkDeviceSize offset, VkDeviceSize size);
void initExternalMemory(VkExternalMemoryHandleTypeFlags handleTypes);
VkBufferUsageFlags _usage;
bool _isHostCoherentTexelBuffer = false;

View File

@ -66,8 +66,8 @@ VkResult MVKBuffer::getMemoryRequirements(const void*, VkMemoryRequirements2* pM
switch (next->sType) {
case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS: {
auto* dedicatedReqs = (VkMemoryDedicatedRequirements*)next;
dedicatedReqs->prefersDedicatedAllocation = VK_FALSE;
dedicatedReqs->requiresDedicatedAllocation = VK_FALSE;
dedicatedReqs->requiresDedicatedAllocation = _requiresDedicatedMemoryAllocation;
dedicatedReqs->prefersDedicatedAllocation = dedicatedReqs->requiresDedicatedAllocation;
break;
}
default:
@ -192,6 +192,28 @@ id<MTLBuffer> MVKBuffer::getMTLBuffer() {
MVKBuffer::MVKBuffer(MVKDevice* device, const VkBufferCreateInfo* pCreateInfo) : MVKResource(device), _usage(pCreateInfo->usage) {
_byteAlignment = _device->_pMetalFeatures->mtlBufferAlignment;
_byteCount = pCreateInfo->size;
for (const auto* next = (const VkBaseInStructure*)pCreateInfo->pNext; next; next = next->pNext) {
switch (next->sType) {
case VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO: {
auto* pExtMemInfo = (const VkExternalMemoryBufferCreateInfo*)next;
initExternalMemory(pExtMemInfo->handleTypes);
break;
}
default:
break;
}
}
}
void MVKBuffer::initExternalMemory(VkExternalMemoryHandleTypeFlags handleTypes) {
if (mvkIsOnlyAnyFlagEnabled(handleTypes, VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR)) {
_externalMemoryHandleTypes = handleTypes;
auto& xmProps = _device->getPhysicalDevice()->getExternalBufferProperties(VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR);
_requiresDedicatedMemoryAllocation = _requiresDedicatedMemoryAllocation || mvkIsAnyFlagEnabled(xmProps.externalMemoryFeatures, VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT);
} else {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateBuffer(): Only external memory handle type VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR is supported."));
}
}
MVKBuffer::~MVKBuffer() {

View File

@ -283,6 +283,12 @@ public:
/** Returns whether this is a unified memory device. */
bool getHasUnifiedMemory();
/** Returns the external memory properties supported for buffers for the handle type. */
VkExternalMemoryProperties& getExternalBufferProperties(VkExternalMemoryHandleTypeFlagBits handleType);
/** Returns the external memory properties supported for images for the handle type. */
VkExternalMemoryProperties& getExternalImageProperties(VkExternalMemoryHandleTypeFlagBits handleType);
#pragma mark Metal
@ -337,6 +343,7 @@ protected:
uint64_t getVRAMSize();
uint64_t getRecommendedMaxWorkingSetSize();
uint64_t getCurrentAllocatedSize();
void initExternalMemoryProperties();
void initExtensions();
MVKVector<MVKQueueFamily*>& getQueueFamilies();
void initPipelineCacheUUID();
@ -344,9 +351,6 @@ protected:
uint64_t getSpirvCrossRevision();
bool getImageViewIsSupported(const VkPhysicalDeviceImageFormatInfo2 *pImageFormatInfo);
void populate(VkPhysicalDeviceIDProperties* pDevIdProps);
void populate(VkExternalMemoryProperties& xmProps,
VkExternalMemoryHandleTypeFlagBits handleType,
VkExternalMemoryHandleTypeFlagBits supportedHandleType);
void logGPUInfo();
id<MTLDevice> _mtlDevice;
@ -364,6 +368,8 @@ protected:
uint32_t _hostCoherentMemoryTypes;
uint32_t _privateMemoryTypes;
uint32_t _lazilyAllocatedMemoryTypes;
VkExternalMemoryProperties _mtlBufferExternalMemoryProperties;
VkExternalMemoryProperties _mtlTextureExternalMemoryProperties;
};

View File

@ -419,10 +419,7 @@ VkResult MVKPhysicalDevice::getImageFormatProperties(const VkPhysicalDeviceImage
for (auto* nextProps = (VkBaseOutStructure*)pImageFormatProperties->pNext; nextProps; nextProps = nextProps->pNext) {
if (nextProps->sType == VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES) {
auto* pExtImgFmtProps = (VkExternalImageFormatProperties*)nextProps;
// TODO: This is an inoperable placeholder until VK_KHR_external_memory_metal is ratified and implemented
const VkExternalMemoryHandleTypeFlagBits VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR = VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM;
populate(pExtImgFmtProps->externalMemoryProperties, pExtImgFmtInfo->handleType, VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR);
pExtImgFmtProps->externalMemoryProperties = getExternalImageProperties(pExtImgFmtInfo->handleType);
}
}
break;
@ -482,24 +479,22 @@ bool MVKPhysicalDevice::getImageViewIsSupported(const VkPhysicalDeviceImageForma
void MVKPhysicalDevice::getExternalBufferProperties(const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo,
VkExternalBufferProperties* pExternalBufferProperties) {
// TODO: This is an inoperable placeholder until VK_KHR_external_memory_metal is ratified and implemented
const VkExternalMemoryHandleTypeFlagBits VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR = VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM;
populate(pExternalBufferProperties->externalMemoryProperties, pExternalBufferInfo->handleType, VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR);
pExternalBufferProperties->externalMemoryProperties = getExternalBufferProperties(pExternalBufferInfo->handleType);
}
// If the handleType is the supported handle type, populates xmProps accordingly, otherwise xmProps is cleared.
void MVKPhysicalDevice::populate(VkExternalMemoryProperties& xmProps,
VkExternalMemoryHandleTypeFlagBits handleType,
VkExternalMemoryHandleTypeFlagBits supportedHandleType) {
if (handleType == supportedHandleType) {
xmProps.externalMemoryFeatures = (VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT |
VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT |
VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT);
xmProps.exportFromImportedHandleTypes = supportedHandleType;
xmProps.compatibleHandleTypes = supportedHandleType;
} else {
mvkClear(&xmProps);
static VkExternalMemoryProperties _emptyExtMemProps = {};
VkExternalMemoryProperties& MVKPhysicalDevice::getExternalBufferProperties(VkExternalMemoryHandleTypeFlagBits handleType) {
switch (handleType) {
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;
}
}
@ -847,8 +842,9 @@ MVKPhysicalDevice::MVKPhysicalDevice(MVKInstance* mvkInstance, id<MTLDevice> mtl
initMetalFeatures(); // Call first.
initFeatures(); // Call second.
initProperties(); // Call third.
initMemoryProperties();
initExtensions();
initMemoryProperties();
initExternalMemoryProperties();
logGPUInfo();
}
@ -1973,6 +1969,23 @@ uint64_t MVKPhysicalDevice::getCurrentAllocatedSize() {
#endif
}
void MVKPhysicalDevice::initExternalMemoryProperties() {
// Buffers
_mtlBufferExternalMemoryProperties.externalMemoryFeatures = (VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT |
VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT |
VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT);
_mtlBufferExternalMemoryProperties.exportFromImportedHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR;
_mtlBufferExternalMemoryProperties.compatibleHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR;
// Images
_mtlTextureExternalMemoryProperties.externalMemoryFeatures = (VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT |
VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT |
VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT);
_mtlTextureExternalMemoryProperties.exportFromImportedHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR;
_mtlTextureExternalMemoryProperties.compatibleHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR;
}
void MVKPhysicalDevice::initExtensions() {
MVKExtensionList* pWritableExtns = (MVKExtensionList*)&_supportedExtensions;
pWritableExtns->disableAllButEnabledDeviceExtensions();

View File

@ -27,6 +27,10 @@
class MVKBuffer;
class MVKImage;
// TODO: These are inoperable placeholders until VK_KHR_external_memory_metal defines them properly
static const VkExternalMemoryHandleTypeFlagBits VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR = VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM;
static const VkExternalMemoryHandleTypeFlagBits VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR = VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM;
#pragma mark MVKDeviceMemory
@ -141,6 +145,7 @@ protected:
bool ensureHostMemory();
void freeHostMemory();
MVKResource* getDedicatedResource();
void initExternalMemory(VkExternalMemoryHandleTypeFlags handleTypes);
MVKVectorInline<MVKBuffer*, 4> _buffers;
MVKVectorInline<MVKImage*, 4> _images;

View File

@ -266,10 +266,7 @@ void MVKDeviceMemory::freeHostMemory() {
MVKResource* MVKDeviceMemory::getDedicatedResource() {
MVKAssert(_isDedicated, "This method should only be called on dedicated allocations!");
if (_buffers.empty())
return _images[0];
else
return _buffers[0];
return _buffers.empty() ? (MVKResource*)_images[0] : (MVKResource*)_buffers[0];
}
MVKDeviceMemory::MVKDeviceMemory(MVKDevice* device,
@ -284,22 +281,28 @@ MVKDeviceMemory::MVKDeviceMemory(MVKDevice* device,
VkImage dedicatedImage = VK_NULL_HANDLE;
VkBuffer dedicatedBuffer = VK_NULL_HANDLE;
auto* next = (VkStructureType*)pAllocateInfo->pNext;
while (next) {
switch (*next) {
case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO: {
auto* pDedicatedInfo = (VkMemoryDedicatedAllocateInfo*)next;
dedicatedImage = pDedicatedInfo->image;
dedicatedBuffer = pDedicatedInfo->buffer;
next = (VkStructureType*)pDedicatedInfo->pNext;
break;
}
default:
next = (VkStructureType*)((VkMemoryAllocateInfo*)next)->pNext;
break;
VkExternalMemoryHandleTypeFlags handleTypes = 0;
for (const auto* next = (const VkBaseInStructure*)pAllocateInfo->pNext; next; next = next->pNext) {
switch (next->sType) {
case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO: {
auto* pDedicatedInfo = (VkMemoryDedicatedAllocateInfo*)next;
dedicatedImage = pDedicatedInfo->image;
dedicatedBuffer = pDedicatedInfo->buffer;
_isDedicated = dedicatedImage || dedicatedBuffer;
break;
}
case VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO: {
auto* pExpMemInfo = (VkExportMemoryAllocateInfo*)next;
handleTypes = pExpMemInfo->handleTypes;
break;
}
default:
break;
}
}
initExternalMemory(handleTypes); // After setting _isDedicated
// "Dedicated" means this memory can only be used for this image or buffer.
if (dedicatedImage) {
#if MVK_MACOS
@ -316,14 +319,16 @@ MVKDeviceMemory::MVKDeviceMemory(MVKDevice* device,
}
}
#endif
_isDedicated = true;
_images.push_back((MVKImage*)dedicatedImage);
return;
}
// If we can, create a MTLHeap. This should happen before creating the buffer
// allowing us to map its contents.
if (!dedicatedImage && !dedicatedBuffer) {
if (dedicatedBuffer) {
_buffers.push_back((MVKBuffer*)dedicatedBuffer);
}
// 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));
return;
@ -334,10 +339,26 @@ MVKDeviceMemory::MVKDeviceMemory(MVKDevice* device,
if (isMemoryHostCoherent() && !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));
}
}
if (dedicatedBuffer) {
_isDedicated = true;
_buffers.push_back((MVKBuffer*)dedicatedBuffer);
void MVKDeviceMemory::initExternalMemory(VkExternalMemoryHandleTypeFlags handleTypes) {
if ( !handleTypes ) { return; }
if ( !mvkIsOnlyAnyFlagEnabled(handleTypes, VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR | VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR) ) {
setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "vkAllocateMemory(): Only external memory handle types VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR or VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR are supported."));
}
bool requiresDedicated = false;
if (mvkIsAnyFlagEnabled(handleTypes, VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR)) {
auto& xmProps = _device->getPhysicalDevice()->getExternalBufferProperties(VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR);
requiresDedicated = requiresDedicated || mvkIsAnyFlagEnabled(xmProps.externalMemoryFeatures, VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT);
}
if (mvkIsAnyFlagEnabled(handleTypes, VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR)) {
auto& xmProps = _device->getPhysicalDevice()->getExternalImageProperties(VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR);
requiresDedicated = requiresDedicated || mvkIsAnyFlagEnabled(xmProps.externalMemoryFeatures, VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT);
}
if (requiresDedicated && !_isDedicated) {
setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "vkAllocateMemory(): External memory requires a dedicated VkBuffer or VkImage."));
}
}

View File

@ -235,6 +235,7 @@ protected:
bool validateUseTexelBuffer();
void initSubresources(const VkImageCreateInfo* pCreateInfo);
void initSubresourceLayout(MVKImageSubresource& imgSubRez);
void initExternalMemory(VkExternalMemoryHandleTypeFlags handleTypes);
id<MTLTexture> newMTLTexture();
void releaseMTLTexture();
void releaseIOSurface();

View File

@ -182,8 +182,9 @@ VkResult MVKImage::getMemoryRequirements(const void*, VkMemoryRequirements2* pMe
case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS: {
auto* dedicatedReqs = (VkMemoryDedicatedRequirements*)next;
bool writable = mvkIsAnyFlagEnabled(_usage, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
dedicatedReqs->prefersDedicatedAllocation = !_usesTexelBuffer && (writable || !_device->_pMetalFeatures->placementHeaps);
dedicatedReqs->requiresDedicatedAllocation = VK_FALSE;
dedicatedReqs->requiresDedicatedAllocation = _requiresDedicatedMemoryAllocation;
dedicatedReqs->prefersDedicatedAllocation = (dedicatedReqs->requiresDedicatedAllocation ||
(!_usesTexelBuffer && (writable || !_device->_pMetalFeatures->placementHeaps)));
break;
}
default:
@ -615,6 +616,18 @@ MVKImage::MVKImage(MVKDevice* device, const VkImageCreateInfo* pCreateInfo) : MV
}
initSubresources(pCreateInfo);
for (const auto* next = (const VkBaseInStructure*)pCreateInfo->pNext; next; next = next->pNext) {
switch (next->sType) {
case VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO: {
auto* pExtMemInfo = (const VkExternalMemoryImageCreateInfo*)next;
initExternalMemory(pExtMemInfo->handleTypes);
break;
}
default:
break;
}
}
}
VkSampleCountFlagBits MVKImage::validateSamples(const VkImageCreateInfo* pCreateInfo, bool isAttachment) {
@ -784,6 +797,16 @@ void MVKImage::initSubresourceLayout(MVKImageSubresource& imgSubRez) {
layout.depthPitch = bytesPerLayerCurrLevel;
}
void MVKImage::initExternalMemory(VkExternalMemoryHandleTypeFlags handleTypes) {
if (mvkIsOnlyAnyFlagEnabled(handleTypes, VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR)) {
_externalMemoryHandleTypes = handleTypes;
auto& xmProps = _device->getPhysicalDevice()->getExternalImageProperties(VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR);
_requiresDedicatedMemoryAllocation = _requiresDedicatedMemoryAllocation || mvkIsAnyFlagEnabled(xmProps.externalMemoryFeatures, VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT);
} else {
setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage(): Only external memory handle type VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR is supported."));
}
}
MVKImage::~MVKImage() {
if (_deviceMemory) { _deviceMemory->removeImage(this); }
releaseMTLTexture();

View File

@ -88,4 +88,6 @@ protected:
VkDeviceSize _deviceMemoryOffset = 0;
VkDeviceSize _byteCount = 0;
VkDeviceSize _byteAlignment = 0;
VkExternalMemoryHandleTypeFlags _externalMemoryHandleTypes = 0;
bool _requiresDedicatedMemoryAllocation = false;
};

View File

@ -46,6 +46,7 @@ MVK_EXTENSION(KHR_dedicated_allocation, KHR_DEDICATED_ALLOCATION, DEVICE)
MVK_EXTENSION(KHR_descriptor_update_template, KHR_DESCRIPTOR_UPDATE_TEMPLATE, DEVICE)
MVK_EXTENSION(KHR_device_group, KHR_DEVICE_GROUP, DEVICE)
MVK_EXTENSION(KHR_device_group_creation, KHR_DEVICE_GROUP_CREATION, INSTANCE)
MVK_EXTENSION(KHR_external_memory, KHR_EXTERNAL_MEMORY, DEVICE)
MVK_EXTENSION(KHR_external_memory_capabilities, KHR_EXTERNAL_MEMORY_CAPABILITIES, INSTANCE)
MVK_EXTENSION(KHR_get_memory_requirements2, KHR_GET_MEMORY_REQUIREMENTS_2, DEVICE)
MVK_EXTENSION(KHR_get_physical_device_properties2, KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2, INSTANCE)