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.
This commit is contained in:
Bill Hollings 2023-05-15 14:32:22 -04:00
parent 7d4f5745f7
commit 4893f78b29
6 changed files with 133 additions and 47 deletions

View File

@ -21,6 +21,8 @@
#include <string>
#include <streambuf>
#include <vector>
#include <cxxabi.h>
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<typename T>
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

View File

@ -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.

View File

@ -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<typename S> void enableFeatures(S* pEnabled, const S* pRequested, const S* pAvailable, uint32_t count);
template<typename S> 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);

View File

@ -32,6 +32,7 @@
#include "MVKCommandPool.h"
#include "MVKFoundation.h"
#include "MVKCodec.h"
#include "MVKStrings.h"
#include <MoltenVKShaderConverter/SPIRVToMSLConverter.h>
#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<typename S>
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<typename S>
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()));
}
}
}

View File

@ -21,7 +21,7 @@
#include "MVKInstance.h"
#include "MVKFoundation.h"
#include "MVKOSExtensions.h"
#include <cxxabi.h>
#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;

View File

@ -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 <typename T, typename = void> struct mvk_has_##mbr : std::false_type {}; \
template <typename T> struct mvk_has_##mbr<T, decltype((void)T::mbr, void())> : 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 <typename S>
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 <class S>
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 <class S>
void* mvkGetAddressOfFirstMember(const S* pStruct){
return mvkGetAddressOfFirstMember(pStruct, mvk_has_pNext<S>{});
}
/**
* Reverses the order of the rows in the specified data block.
* The transformation is performed in-place.