From 4893f78b29f838f8badff672255f3837fe1ff682 Mon Sep 17 00:00:00 2001 From: Bill Hollings Date: Mon, 15 May 2023 14:32:22 -0400 Subject: [PATCH] Identify each unsupported device feature flag that the app attempts to be enable. - Make MVKDevice::enableFeatures() functions into templates to pass struct type. - Add mvkGetAddressOfFirstMember() to retrieve the address of the first member of a struct, taking into consideration whether the struct has a Vulkan pNext member. - Add mvk::getTypeName() and mvk::getOrdinalSuffix() string functions. --- Common/MVKStrings.h | 22 +++++ Docs/Whats_New.md | 1 + MoltenVK/MoltenVK/GPUObjects/MVKDevice.h | 4 +- MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm | 105 +++++++++++++-------- MoltenVK/MoltenVK/Utility/MVKBaseObject.mm | 10 +- MoltenVK/MoltenVK/Utility/MVKFoundation.h | 38 ++++++++ 6 files changed, 133 insertions(+), 47 deletions(-) diff --git a/Common/MVKStrings.h b/Common/MVKStrings.h index ec78d9ad..d3ce5316 100644 --- a/Common/MVKStrings.h +++ b/Common/MVKStrings.h @@ -21,6 +21,8 @@ #include #include +#include +#include namespace mvk { @@ -59,6 +61,26 @@ namespace mvk { return varName; } + /** Returns a string containing the ordinal suffix for a numeric value.*/ + inline const char* getOrdinalSuffix(int64_t val) { + static const char* suffixes[] = {"th", "st", "nd", "rd"}; + auto ord = val % 100; + if (ord > 10 && ord < 20) { return suffixes[0]; } // All teens end in th. + ord = ord % 10; + if (ord > 3) { return suffixes[0]; } // 4-9 end in th. + return suffixes[ord]; + } + + /** Returns the name of a C++ type. */ + template + inline std::string getTypeName(const T* pObj) { + int status; + char* demangledName = abi::__cxa_demangle(typeid(*pObj).name(), 0, 0, &status); + std::string tName = demangledName; + free(demangledName); + return tName; + } + #pragma mark - #pragma mark Streams diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md index 947cd308..5e0c8460 100644 --- a/Docs/Whats_New.md +++ b/Docs/Whats_New.md @@ -30,6 +30,7 @@ Released TBD - Add `MVK_ENABLE_EXPLICIT_LOD_WORKAROUND` environment variable to selectively disable recent fixes to handling LOD for arrayed depth images in shaders, on Apple Silicon, when those fixes cause regression in rendering behavior. +- Identify each unsupported device feature flag that the app attempts to be enable. - For correctness, set `VkPhysicalDeviceLimits::lineWidthGranularity` to `1`. - Improve GitHub CI production of binary artifacts on submission and release. diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h index db484823..e1f7e310 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h @@ -886,8 +886,8 @@ protected: void initQueues(const VkDeviceCreateInfo* pCreateInfo); void reservePrivateData(const VkDeviceCreateInfo* pCreateInfo); void enableFeatures(const VkDeviceCreateInfo* pCreateInfo); - void enableFeatures(VkBaseInStructure* pEnabled, const VkBaseInStructure* pRequested, const VkBaseInStructure* pAvailable, uint32_t count); - void enableFeatures(VkBool32* pEnabledBools, const VkBool32* pRequestedBools, const VkBool32* pAvailableBools, uint32_t count); + template void enableFeatures(S* pEnabled, const S* pRequested, const S* pAvailable, uint32_t count); + template void enableFeatures(S* pRequested, VkBool32* pEnabledBools, const VkBool32* pRequestedBools, const VkBool32* pAvailableBools, uint32_t count); void enableExtensions(const VkDeviceCreateInfo* pCreateInfo); const char* getActivityPerformanceDescription(MVKPerformanceTracker& activity, MVKPerformanceStatistics& perfStats); void logActivityPerformance(MVKPerformanceTracker& activity, MVKPerformanceStatistics& perfStats, bool isInline = false); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm index 8ad6fd87..ba0c3310 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm @@ -32,6 +32,7 @@ #include "MVKCommandPool.h" #include "MVKFoundation.h" #include "MVKCodec.h" +#include "MVKStrings.h" #include #import "CAMetalLayer+MoltenVK.h" @@ -4590,7 +4591,8 @@ void MVKDevice::enableFeatures(const VkDeviceCreateInfo* pCreateInfo) { //Enable device features based on requested and available features, // including extended features that are requested in the pNext chain. if (pCreateInfo->pEnabledFeatures) { - enableFeatures(&_enabledFeatures.robustBufferAccess, + enableFeatures(pCreateInfo->pEnabledFeatures, + &_enabledFeatures.robustBufferAccess, &pCreateInfo->pEnabledFeatures->robustBufferAccess, &pdFeats2.features.robustBufferAccess, 55); } @@ -4599,29 +4601,36 @@ void MVKDevice::enableFeatures(const VkDeviceCreateInfo* pCreateInfo) { switch ((uint32_t)next->sType) { case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2: { auto* requestedFeatures = (VkPhysicalDeviceFeatures2*)next; - enableFeatures(&_enabledFeatures.robustBufferAccess, + enableFeatures(requestedFeatures, + &_enabledFeatures.robustBufferAccess, &requestedFeatures->features.robustBufferAccess, &pdFeats2.features.robustBufferAccess, 55); break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES: { auto* requestedFeatures = (VkPhysicalDeviceVulkan11Features*)next; - enableFeatures(&_enabled16BitStorageFeatures.storageBuffer16BitAccess, + enableFeatures(requestedFeatures, + &_enabled16BitStorageFeatures.storageBuffer16BitAccess, &requestedFeatures->storageBuffer16BitAccess, &pd16BitStorageFeatures.storageBuffer16BitAccess, 4); - enableFeatures(&_enabledMultiviewFeatures.multiview, + enableFeatures(requestedFeatures, + &_enabledMultiviewFeatures.multiview, &requestedFeatures->multiview, &pdMultiviewFeatures.multiview, 3); - enableFeatures(&_enabledVariablePointerFeatures.variablePointersStorageBuffer, + enableFeatures(requestedFeatures, + &_enabledVariablePointerFeatures.variablePointersStorageBuffer, &requestedFeatures->variablePointersStorageBuffer, &pdVariablePointerFeatures.variablePointersStorageBuffer, 2); - enableFeatures(&_enabledProtectedMemoryFeatures.protectedMemory, + enableFeatures(requestedFeatures, + &_enabledProtectedMemoryFeatures.protectedMemory, &requestedFeatures->protectedMemory, &pdProtectedMemoryFeatures.protectedMemory, 1); - enableFeatures(&_enabledSamplerYcbcrConversionFeatures.samplerYcbcrConversion, + enableFeatures(requestedFeatures, + &_enabledSamplerYcbcrConversionFeatures.samplerYcbcrConversion, &requestedFeatures->samplerYcbcrConversion, &pdSamplerYcbcrConversionFeatures.samplerYcbcrConversion, 1); - enableFeatures(&_enabledShaderDrawParametersFeatures.shaderDrawParameters, + enableFeatures(requestedFeatures, + &_enabledShaderDrawParametersFeatures.shaderDrawParameters, &requestedFeatures->shaderDrawParameters, &pdShaderDrawParametersFeatures.shaderDrawParameters, 1); break; @@ -4629,55 +4638,72 @@ void MVKDevice::enableFeatures(const VkDeviceCreateInfo* pCreateInfo) { case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES: { auto& pdvulkan12FeaturesNoExt = _physicalDevice->_vulkan12FeaturesNoExt; auto* requestedFeatures = (VkPhysicalDeviceVulkan12Features*)next; - enableFeatures(&_enabledVulkan12FeaturesNoExt.samplerMirrorClampToEdge, + enableFeatures(requestedFeatures, + &_enabledVulkan12FeaturesNoExt.samplerMirrorClampToEdge, &requestedFeatures->samplerMirrorClampToEdge, &pdvulkan12FeaturesNoExt.samplerMirrorClampToEdge, 2); - enableFeatures(&_enabled8BitStorageFeatures.storageBuffer8BitAccess, + enableFeatures(requestedFeatures, + &_enabled8BitStorageFeatures.storageBuffer8BitAccess, &requestedFeatures->storageBuffer8BitAccess, &pd8BitStorageFeatures.storageBuffer8BitAccess, 3); - enableFeatures(&_enabledShaderAtomicInt64Features.shaderBufferInt64Atomics, + enableFeatures(requestedFeatures, + &_enabledShaderAtomicInt64Features.shaderBufferInt64Atomics, &requestedFeatures->shaderBufferInt64Atomics, &pdShaderAtomicInt64Features.shaderBufferInt64Atomics, 2); - enableFeatures(&_enabledShaderFloat16Int8Features.shaderFloat16, + enableFeatures(requestedFeatures, + &_enabledShaderFloat16Int8Features.shaderFloat16, &requestedFeatures->shaderFloat16, &pdShaderFloat16Int8Features.shaderFloat16, 2); - enableFeatures(&_enabledVulkan12FeaturesNoExt.descriptorIndexing, + enableFeatures(requestedFeatures, + &_enabledVulkan12FeaturesNoExt.descriptorIndexing, &requestedFeatures->descriptorIndexing, &pdvulkan12FeaturesNoExt.descriptorIndexing, 1); - enableFeatures(&_enabledDescriptorIndexingFeatures.shaderInputAttachmentArrayDynamicIndexing, + enableFeatures(requestedFeatures, + &_enabledDescriptorIndexingFeatures.shaderInputAttachmentArrayDynamicIndexing, &requestedFeatures->shaderInputAttachmentArrayDynamicIndexing, &pdDescriptorIndexingFeatures.shaderInputAttachmentArrayDynamicIndexing, 20); - enableFeatures(&_enabledVulkan12FeaturesNoExt.samplerFilterMinmax, + enableFeatures(requestedFeatures, + &_enabledVulkan12FeaturesNoExt.samplerFilterMinmax, &requestedFeatures->samplerFilterMinmax, &pdvulkan12FeaturesNoExt.samplerFilterMinmax, 1); - enableFeatures(&_enabledScalarBlockLayoutFeatures.scalarBlockLayout, + enableFeatures(requestedFeatures, + &_enabledScalarBlockLayoutFeatures.scalarBlockLayout, &requestedFeatures->scalarBlockLayout, &pdScalarBlockLayoutFeatures.scalarBlockLayout, 1); - enableFeatures(&_enabledImagelessFramebufferFeatures.imagelessFramebuffer, + enableFeatures(requestedFeatures, + &_enabledImagelessFramebufferFeatures.imagelessFramebuffer, &requestedFeatures->imagelessFramebuffer, &pdImagelessFramebufferFeatures.imagelessFramebuffer, 1); - enableFeatures(&_enabledUniformBufferStandardLayoutFeatures.uniformBufferStandardLayout, + enableFeatures(requestedFeatures, + &_enabledUniformBufferStandardLayoutFeatures.uniformBufferStandardLayout, &requestedFeatures->uniformBufferStandardLayout, &pdUniformBufferStandardLayoutFeatures.uniformBufferStandardLayout, 1); - enableFeatures(&_enabledShaderSubgroupExtendedTypesFeatures.shaderSubgroupExtendedTypes, + enableFeatures(requestedFeatures, + &_enabledShaderSubgroupExtendedTypesFeatures.shaderSubgroupExtendedTypes, &requestedFeatures->shaderSubgroupExtendedTypes, &pdShaderSubgroupExtendedTypesFeatures.shaderSubgroupExtendedTypes, 1); - enableFeatures(&_enabledSeparateDepthStencilLayoutsFeatures.separateDepthStencilLayouts, + enableFeatures(requestedFeatures, + &_enabledSeparateDepthStencilLayoutsFeatures.separateDepthStencilLayouts, &requestedFeatures->separateDepthStencilLayouts, &pdSeparateDepthStencilLayoutsFeatures.separateDepthStencilLayouts, 1); - enableFeatures(&_enabledHostQueryResetFeatures.hostQueryReset, + enableFeatures(requestedFeatures, + &_enabledHostQueryResetFeatures.hostQueryReset, &requestedFeatures->hostQueryReset, &pdHostQueryResetFeatures.hostQueryReset, 1); - enableFeatures(&_enabledTimelineSemaphoreFeatures.timelineSemaphore, + enableFeatures(requestedFeatures, + &_enabledTimelineSemaphoreFeatures.timelineSemaphore, &requestedFeatures->timelineSemaphore, &pdTimelineSemaphoreFeatures.timelineSemaphore, 1); - enableFeatures(&_enabledBufferDeviceAddressFeatures.bufferDeviceAddress, + enableFeatures(requestedFeatures, + &_enabledBufferDeviceAddressFeatures.bufferDeviceAddress, &requestedFeatures->bufferDeviceAddress, &pdBufferDeviceAddressFeatures.bufferDeviceAddress, 3); - enableFeatures(&_enabledVulkanMemoryModelFeatures.vulkanMemoryModel, + enableFeatures(requestedFeatures, + &_enabledVulkanMemoryModelFeatures.vulkanMemoryModel, &requestedFeatures->vulkanMemoryModel, &pdVulkanMemoryModelFeatures.vulkanMemoryModel, 3); - enableFeatures(&_enabledVulkan12FeaturesNoExt.shaderOutputViewportIndex, + enableFeatures(requestedFeatures, + &_enabledVulkan12FeaturesNoExt.shaderOutputViewportIndex, &requestedFeatures->shaderOutputViewportIndex, &pdvulkan12FeaturesNoExt.shaderOutputViewportIndex, 3); break; @@ -4685,17 +4711,17 @@ void MVKDevice::enableFeatures(const VkDeviceCreateInfo* pCreateInfo) { #define MVK_DEVICE_FEATURE(structName, enumName, flagCount) \ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_##enumName##_FEATURES: { \ - enableFeatures((VkBaseInStructure*)&_enabled##structName##Features, \ - next, \ - (VkBaseInStructure*)&pd##structName##Features, \ + enableFeatures(&_enabled##structName##Features, \ + (VkPhysicalDevice##structName##Features*)next, \ + &pd##structName##Features, \ flagCount); \ break; \ } #define MVK_DEVICE_FEATURE_EXTN(structName, enumName, extnSfx, flagCount) \ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_##enumName##_FEATURES_##extnSfx: { \ - enableFeatures((VkBaseInStructure*)&_enabled##structName##Features, \ - next, \ - (VkBaseInStructure*)&pd##structName##Features, \ + enableFeatures(&_enabled##structName##Features, \ + (VkPhysicalDevice##structName##Features##extnSfx*)next, \ + &pd##structName##Features, \ flagCount); \ break; \ } @@ -4707,18 +4733,23 @@ void MVKDevice::enableFeatures(const VkDeviceCreateInfo* pCreateInfo) { } } -void MVKDevice::enableFeatures(VkBaseInStructure* pEnabled, const VkBaseInStructure* pRequested, const VkBaseInStructure* pAvailable, uint32_t count) { - enableFeatures((VkBool32*)(&(pEnabled->pNext) + 1), - (VkBool32*)(&(pRequested->pNext) + 1), - (VkBool32*)(&(pAvailable->pNext) + 1), +template +void MVKDevice::enableFeatures(S* pEnabled, const S* pRequested, const S* pAvailable, uint32_t count) { + enableFeatures(pRequested, + (VkBool32*)mvkGetAddressOfFirstMember(pEnabled), + (VkBool32*)mvkGetAddressOfFirstMember(pRequested), + (VkBool32*)mvkGetAddressOfFirstMember(pAvailable), count); } -void MVKDevice::enableFeatures(VkBool32* pEnabledBools, const VkBool32* pRequestedBools, const VkBool32* pAvailableBools, uint32_t count) { +template +void MVKDevice::enableFeatures(S* pRequested, VkBool32* pEnabledBools, const VkBool32* pRequestedBools, const VkBool32* pAvailableBools, uint32_t count) { for (uint32_t i = 0; i < count; i++) { pEnabledBools[i] = pRequestedBools[i] && pAvailableBools[i]; if (pRequestedBools[i] && !pAvailableBools[i]) { - setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateDevice(): Requested feature is not available on this device.")); + uintptr_t mbrOffset = (uintptr_t)&pRequestedBools[i] - (uintptr_t)mvkGetAddressOfFirstMember(pRequested); + size_t mbrIdxOrd = (mbrOffset / sizeof(VkBool32)) + 1; + setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateDevice(): Requested physical device feature specified by the %zu%s flag in %s is not available on this device.", mbrIdxOrd, mvk::getOrdinalSuffix(mbrIdxOrd), mvk::getTypeName(pRequested).c_str())); } } } diff --git a/MoltenVK/MoltenVK/Utility/MVKBaseObject.mm b/MoltenVK/MoltenVK/Utility/MVKBaseObject.mm index d2dc3c91..427c3227 100644 --- a/MoltenVK/MoltenVK/Utility/MVKBaseObject.mm +++ b/MoltenVK/MoltenVK/Utility/MVKBaseObject.mm @@ -21,7 +21,7 @@ #include "MVKInstance.h" #include "MVKFoundation.h" #include "MVKOSExtensions.h" -#include +#include "MVKStrings.h" using namespace std; @@ -44,13 +44,7 @@ static const char* getReportingLevelString(MVKConfigLogLevel logLevel) { #pragma mark - #pragma mark MVKBaseObject -string MVKBaseObject::getClassName() { - int status; - char* demangled = abi::__cxa_demangle(typeid(*this).name(), 0, 0, &status); - string clzName = demangled; - free(demangled); - return clzName; -} +string MVKBaseObject::getClassName() { return mvk::getTypeName(this); } void MVKBaseObject::reportMessage(MVKConfigLogLevel logLevel, const char* format, ...) { va_list args; diff --git a/MoltenVK/MoltenVK/Utility/MVKFoundation.h b/MoltenVK/MoltenVK/Utility/MVKFoundation.h index 7c4af3da..269cc9f4 100644 --- a/MoltenVK/MoltenVK/Utility/MVKFoundation.h +++ b/MoltenVK/MoltenVK/Utility/MVKFoundation.h @@ -217,6 +217,44 @@ static constexpr uint64_t mvkAlignByteCount(uint64_t byteCount, uint64_t byteAli return mvkAlignByteRef(byteCount, byteAlignment, alignDown); } +/** + * Compile time indication if the struct contains a specific member. + * + * If S::mbr is well-formed because the struct contains that member, the decltype() and + * comma operator together trigger a true_type, otherwise it falls back to a false_type. + * + * Credit to: https://fekir.info/post/detect-member-variables/ + */ +#define mvk_define_has_member(mbr) \ + template struct mvk_has_##mbr : std::false_type {}; \ + template struct mvk_has_##mbr : std::true_type {}; + +mvk_define_has_member(pNext); // Defines the mvk_has_pNext() function. + +/** Returns the address of the first member of a structure, which is just the address of the structure. */ +template +void* mvkGetAddressOfFirstMember(const S* pStruct, std::false_type){ + return (void*)pStruct; +} + +/** + * Returns the address of the first member of a Vulkan structure containing a pNext member. + * The first member is the one after the pNext member. + */ +template +void* mvkGetAddressOfFirstMember(const S* pStruct, std::true_type){ + return (void*)(&(pStruct->pNext) + 1); +} + +/** + * Returns the address of the first member of a structure. If the structure is a Vulkan + * structure containing a pNext member, the first member is the one after the pNext member. + */ +template +void* mvkGetAddressOfFirstMember(const S* pStruct){ + return mvkGetAddressOfFirstMember(pStruct, mvk_has_pNext{}); +} + /** * Reverses the order of the rows in the specified data block. * The transformation is performed in-place.