diff --git a/Docs/MoltenVK_Runtime_UserGuide.md b/Docs/MoltenVK_Runtime_UserGuide.md index a89e6726..ab4c4800 100644 --- a/Docs/MoltenVK_Runtime_UserGuide.md +++ b/Docs/MoltenVK_Runtime_UserGuide.md @@ -253,11 +253,13 @@ In addition to the core *Vulkan* API, **MoltenVK** also supports the following - `VK_EXT_debug_report` - `VK_EXT_debug_utils` - `VK_EXT_host_query_reset` -- `VK_EXT_memory_budget` +- `VK_EXT_memory_budget` *(requires Metal 2.0)* - `VK_EXT_metal_surface` - `VK_EXT_shader_stencil_export` *(requires Mac GPU family 2 or iOS GPU family 5)* - `VK_EXT_shader_viewport_index_layer` +- `VK_EXT_swapchain_colorspace` *(macOS)* - `VK_EXT_vertex_attribute_divisor` +- `VK_EXT_texel_buffer_alignment` *(requires Metal 2.0)* - `VK_EXTX_portability_subset` - `VK_MVK_ios_surface` *(iOS) (Obsolete. Use `VK_EXT_metal_surface` instead.)* - `VK_MVK_macos_surface` *(macOS) (Obsolete. Use `VK_EXT_metal_surface` instead.)* diff --git a/ExternalRevisions/Vulkan-Headers_repo_revision b/ExternalRevisions/Vulkan-Headers_repo_revision index 6dd277b5..eb32d0fc 100644 --- a/ExternalRevisions/Vulkan-Headers_repo_revision +++ b/ExternalRevisions/Vulkan-Headers_repo_revision @@ -1 +1 @@ -097a1045098213919fd56442f52c716fc78eeb27 +737f4c1cd96283747967c2024a0108b742214455 diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm index e60dab44..ee23a312 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm @@ -223,6 +223,7 @@ MVKBufferView::MVKBufferView(MVKDevice* device, const VkBufferViewCreateInfo* pC // We can just use a simple 1D texel array. _textureSize.width = uint32_t(blockCount * fmtBlockSize.width); _textureSize.height = 1; + _mtlBytesPerRow = byteCount; } if ( !_device->_pMetalFeatures->texelBuffers ) { diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h index 233d360b..e22b7243 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h @@ -336,6 +336,7 @@ protected: VkPhysicalDeviceFeatures _features; MVKPhysicalDeviceMetalFeatures _metalFeatures; VkPhysicalDeviceProperties _properties; + VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT _texelBuffAlignProperties; VkPhysicalDeviceMemoryProperties _memoryProperties; std::vector _queueFamilies; uint32_t _allMemoryTypes; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm index 9bc202d2..3f3d8234 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm @@ -69,8 +69,7 @@ void MVKPhysicalDevice::getFeatures(VkPhysicalDeviceFeatures2* features) { if (features) { features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; features->features = _features; - auto* next = (VkBaseOutStructure*)features->pNext; - while (next) { + for (auto* next = (VkBaseOutStructure*)features->pNext; next; next = next->pNext) { switch ((uint32_t)next->sType) { case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES: { auto* storageFeatures = (VkPhysicalDevice16BitStorageFeatures*)next; @@ -109,6 +108,11 @@ void MVKPhysicalDevice::getFeatures(VkPhysicalDeviceFeatures2* features) { hostQueryResetFeatures->hostQueryReset = true; break; } + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT: { + auto* texelBuffAlignFeatures = (VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT*)next; + texelBuffAlignFeatures->texelBufferAlignment = _metalFeatures.texelBuffers && [_mtlDevice respondsToSelector: @selector(minimumLinearTextureAlignmentForPixelFormat:)]; + break; + } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT: { auto* divisorFeatures = (VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT*)next; divisorFeatures->vertexAttributeInstanceRateDivisor = true; @@ -127,7 +131,6 @@ void MVKPhysicalDevice::getFeatures(VkPhysicalDeviceFeatures2* features) { default: break; } - next = next->pNext; } } } @@ -140,42 +143,44 @@ void MVKPhysicalDevice::getProperties(VkPhysicalDeviceProperties2* properties) { if (properties) { properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; properties->properties = _properties; - auto* next = (MVKVkAPIStructHeader*)properties->pNext; - while (next) { + for (auto* next = (VkBaseOutStructure*)properties->pNext; next; next = next->pNext) { switch ((uint32_t)next->sType) { case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES: { auto* pointClipProps = (VkPhysicalDevicePointClippingProperties*)next; pointClipProps->pointClippingBehavior = VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES; - next = (MVKVkAPIStructHeader*)pointClipProps->pNext; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES: { auto* maint3Props = (VkPhysicalDeviceMaintenance3Properties*)next; - maint3Props->maxPerSetDescriptors = (_metalFeatures.maxPerStageBufferCount + _metalFeatures.maxPerStageTextureCount + _metalFeatures.maxPerStageSamplerCount) * 2; + maint3Props->maxPerSetDescriptors = (_metalFeatures.maxPerStageBufferCount + _metalFeatures.maxPerStageTextureCount + _metalFeatures.maxPerStageSamplerCount) * 4; maint3Props->maxMemoryAllocationSize = _metalFeatures.maxMTLBufferSize; - next = (MVKVkAPIStructHeader*)maint3Props->pNext; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR: { auto* pushDescProps = (VkPhysicalDevicePushDescriptorPropertiesKHR*)next; pushDescProps->maxPushDescriptors = _properties.limits.maxPerStageResources; - next = (MVKVkAPIStructHeader*)pushDescProps->pNext; + break; + } + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES_EXT: { + auto* texelBuffAlignProps = (VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT*)next; + // Save the 'next' pointer; we'll unintentionally overwrite it + // on the next line. Put it back when we're done. + void* savedNext = texelBuffAlignProps->pNext; + *texelBuffAlignProps = _texelBuffAlignProperties; + texelBuffAlignProps->pNext = savedNext; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT: { auto* divisorProps = (VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT*)next; divisorProps->maxVertexAttribDivisor = kMVKUndefinedLargeUInt32; - next = (MVKVkAPIStructHeader*)divisorProps->pNext; break; } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_PROPERTIES_EXTX: { auto* portabilityProps = (VkPhysicalDevicePortabilitySubsetPropertiesEXTX*)next; portabilityProps->minVertexInputBindingStrideAlignment = 4; - next = (MVKVkAPIStructHeader*)portabilityProps->pNext; break; } default: - next = (MVKVkAPIStructHeader*)next->pNext; break; } } @@ -466,22 +471,42 @@ VkResult MVKPhysicalDevice::getSurfaceFormats(MVKSurface* surface, MTLPixelFormatRGBA16Float, }; + MVKVectorInline colorSpaces; + colorSpaces.push_back(VK_COLOR_SPACE_SRGB_NONLINEAR_KHR); +#if MVK_MACOS + if (getInstance()->_enabledExtensions.vk_EXT_swapchain_colorspace.enabled) { + // 10.11 supports some but not all of the color spaces specified by VK_EXT_swapchain_colorspace. + colorSpaces.push_back(VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT); + colorSpaces.push_back(VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT); + colorSpaces.push_back(VK_COLOR_SPACE_BT709_NONLINEAR_EXT); + colorSpaces.push_back(VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT); + colorSpaces.push_back(VK_COLOR_SPACE_PASS_THROUGH_EXT); + if (mvkOSVersion() >= 10.12) { + colorSpaces.push_back(VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT); + colorSpaces.push_back(VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT); + } + } +#endif + const uint mtlFmtsCnt = sizeof(mtlFormats) / sizeof(MTLPixelFormat); + const uint vkFmtsCnt = mtlFmtsCnt * (uint)colorSpaces.size(); // If properties aren't actually being requested yet, simply update the returned count if ( !pSurfaceFormats ) { - *pCount = mtlFmtsCnt; + *pCount = vkFmtsCnt; return VK_SUCCESS; } // Determine how many results we'll return, and return that number - VkResult result = (*pCount >= mtlFmtsCnt) ? VK_SUCCESS : VK_INCOMPLETE; - *pCount = min(*pCount, mtlFmtsCnt); + VkResult result = (*pCount >= vkFmtsCnt) ? VK_SUCCESS : VK_INCOMPLETE; + *pCount = min(*pCount, vkFmtsCnt); // Now populate the supplied array - for (uint fmtIdx = 0; fmtIdx < *pCount; fmtIdx++) { - pSurfaceFormats[fmtIdx].format = mvkVkFormatFromMTLPixelFormat(mtlFormats[fmtIdx]); - pSurfaceFormats[fmtIdx].colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + for (uint csIdx = 0, idx = 0; idx < *pCount && csIdx < colorSpaces.size(); csIdx++) { + for (uint fmtIdx = 0; idx < *pCount && fmtIdx < mtlFmtsCnt; fmtIdx++, idx++) { + pSurfaceFormats[idx].format = mvkVkFormatFromMTLPixelFormat(mtlFormats[fmtIdx]); + pSurfaceFormats[idx].colorSpace = colorSpaces[csIdx]; + } } return result; @@ -1093,14 +1118,86 @@ void MVKPhysicalDevice::initProperties() { _properties.limits.bufferImageGranularity = _metalFeatures.mtlBufferAlignment; _properties.limits.nonCoherentAtomSize = _metalFeatures.mtlBufferAlignment; + if ([_mtlDevice respondsToSelector: @selector(minimumLinearTextureAlignmentForPixelFormat:)]) { + // Figure out the greatest alignment required by all supported formats, and + // whether or not they only require alignment to a single texel. We'll use this + // information to fill out the VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT + // struct. + uint32_t maxStorage = 0, maxUniform = 0; + bool singleTexelStorage = true, singleTexelUniform = true; + mvkEnumerateSupportedFormats({0, 0, VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT}, true, [&](VkFormat vk) { + NSUInteger alignment; + if ([_mtlDevice respondsToSelector: @selector(minimumTextureBufferAlignmentForPixelFormat:)]) { + alignment = [_mtlDevice minimumTextureBufferAlignmentForPixelFormat: mvkMTLPixelFormatFromVkFormat(vk)]; + } else { + alignment = [_mtlDevice minimumLinearTextureAlignmentForPixelFormat: mvkMTLPixelFormatFromVkFormat(vk)]; + } + VkFormatProperties props = mvkVkFormatProperties(vk, getFormatIsSupported(vk)); + // For uncompressed formats, this is the size of a single texel. + // Note that no implementations of Metal support compressed formats + // in a linear texture (including texture buffers). It's likely that even + // if they did, this would be the absolute minimum alignment. + uint32_t texelSize = mvkVkFormatBytesPerBlock(vk); + // From the spec: + // "If the size of a single texel is a multiple of three bytes, then + // the size of a single component of the format is used instead." + if (texelSize % 3 == 0) { + switch (mvkFormatTypeFromVkFormat(vk)) { + case kMVKFormatColorInt8: + case kMVKFormatColorUInt8: + texelSize = 1; + break; + case kMVKFormatColorHalf: + case kMVKFormatColorInt16: + case kMVKFormatColorUInt16: + texelSize = 2; + break; + case kMVKFormatColorFloat: + case kMVKFormatColorInt32: + case kMVKFormatColorUInt32: + default: + texelSize = 4; + break; + } + } + if (mvkAreAllFlagsEnabled(props.bufferFeatures, VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT)) { + maxStorage = max(maxStorage, uint32_t(alignment)); + if (alignment % texelSize != 0) { singleTexelStorage = false; } + } + if (mvkAreAllFlagsEnabled(props.bufferFeatures, VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT)) { + maxUniform = max(maxUniform, uint32_t(alignment)); + if (alignment % texelSize != 0) { singleTexelUniform = false; } + } + return true; + }); + _texelBuffAlignProperties.storageTexelBufferOffsetAlignmentBytes = maxStorage; + _texelBuffAlignProperties.storageTexelBufferOffsetSingleTexelAlignment = singleTexelStorage; + _texelBuffAlignProperties.uniformTexelBufferOffsetAlignmentBytes = maxUniform; + _texelBuffAlignProperties.uniformTexelBufferOffsetSingleTexelAlignment = singleTexelUniform; + _properties.limits.minTexelBufferOffsetAlignment = max(maxStorage, maxUniform); + } else { +#if MVK_IOS + if ([_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v1]) { + _properties.limits.minTexelBufferOffsetAlignment = 16; + } else { + _properties.limits.minTexelBufferOffsetAlignment = 64; + } +#endif +#if MVK_MACOS + _properties.limits.minTexelBufferOffsetAlignment = 256; +#endif + _texelBuffAlignProperties.storageTexelBufferOffsetAlignmentBytes = _properties.limits.minTexelBufferOffsetAlignment; + _texelBuffAlignProperties.storageTexelBufferOffsetSingleTexelAlignment = VK_FALSE; + _texelBuffAlignProperties.uniformTexelBufferOffsetAlignmentBytes = _properties.limits.minTexelBufferOffsetAlignment; + _texelBuffAlignProperties.uniformTexelBufferOffsetSingleTexelAlignment = VK_FALSE; + } + #if MVK_IOS _properties.limits.maxFragmentInputComponents = 60; if ([_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v1]) { - _properties.limits.minTexelBufferOffsetAlignment = 16; _properties.limits.optimalBufferCopyOffsetAlignment = 16; } else { - _properties.limits.minTexelBufferOffsetAlignment = 64; _properties.limits.optimalBufferCopyOffsetAlignment = 64; } @@ -1117,7 +1214,6 @@ void MVKPhysicalDevice::initProperties() { #endif #if MVK_MACOS _properties.limits.maxFragmentInputComponents = 128; - _properties.limits.minTexelBufferOffsetAlignment = 256; _properties.limits.optimalBufferCopyOffsetAlignment = 256; if ([_mtlDevice supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily1_v2]) { diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm index c45c8a68..ac3eabc9 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm @@ -226,12 +226,40 @@ void MVKSwapchain::initCAMetalLayer(const VkSwapchainCreateInfoKHR* pCreateInfo, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT)); +#if MVK_MACOS + switch (pCreateInfo->imageColorSpace) { + case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: + _mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + break; + case VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT: + _mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3); + break; + case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT: + _mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB); + break; + case VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT: + _mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceDCIP3); + break; + case VK_COLOR_SPACE_BT709_NONLINEAR_EXT: + _mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_709); + break; + case VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT: + _mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceAdobeRGB1998); + break; + case VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT: + _mtlLayer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedSRGB); + break; + case VK_COLOR_SPACE_PASS_THROUGH_EXT: + default: + // Nothing - the default is not to do color matching. + break; + } +#endif _mtlLayerOrigDrawSize = _mtlLayer.updatedDrawableSizeMVK; // TODO: set additional CAMetalLayer properties before extracting drawables: // - presentsWithTransaction // - drawsAsynchronously - // - colorspace (macOS only) Vulkan only supports sRGB colorspace for now. // - wantsExtendedDynamicRangeContent (macOS only) if ( [_mtlLayer.delegate isKindOfClass: [PLATFORM_VIEW_CLASS class]] ) { diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.def b/MoltenVK/MoltenVK/Layers/MVKExtensions.def index 5235f435..b7bf1980 100644 --- a/MoltenVK/MoltenVK/Layers/MVKExtensions.def +++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.def @@ -61,6 +61,8 @@ MVK_EXTENSION(EXT_memory_budget, EXT_MEMORY_BUDGET) MVK_EXTENSION(EXT_metal_surface, EXT_METAL_SURFACE) MVK_EXTENSION(EXT_shader_stencil_export, EXT_SHADER_STENCIL_EXPORT) MVK_EXTENSION(EXT_shader_viewport_index_layer, EXT_SHADER_VIEWPORT_INDEX_LAYER) +MVK_EXTENSION(EXT_swapchain_colorspace, EXT_SWAPCHAIN_COLOR_SPACE) +MVK_EXTENSION(EXT_texel_buffer_alignment, EXT_TEXEL_BUFFER_ALIGNMENT) MVK_EXTENSION(EXT_vertex_attribute_divisor, EXT_VERTEX_ATTRIBUTE_DIVISOR) MVK_EXTENSION(EXTX_portability_subset, EXTX_PORTABILITY_SUBSET) MVK_EXTENSION(MVK_ios_surface, MVK_IOS_SURFACE) @@ -68,6 +70,7 @@ MVK_EXTENSION(MVK_macos_surface, MVK_MACOS_SURFACE) MVK_EXTENSION(MVK_moltenvk, MVK_MOLTENVK) MVK_EXTENSION(AMD_gpu_shader_half_float, AMD_GPU_SHADER_HALF_FLOAT) MVK_EXTENSION(AMD_negative_viewport_height, AMD_NEGATIVE_VIEWPORT_HEIGHT) +MVK_EXTENSION(AMD_shader_image_load_store_lod, AMD_SHADER_IMAGE_LOAD_STORE_LOD) MVK_EXTENSION(IMG_format_pvrtc, IMG_FORMAT_PVRTC) MVK_EXTENSION_LAST(NV_glsl_shader, NV_GLSL_SHADER) diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.mm b/MoltenVK/MoltenVK/Layers/MVKExtensions.mm index e1580dc2..1ba1a703 100644 --- a/MoltenVK/MoltenVK/Layers/MVKExtensions.mm +++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.mm @@ -46,17 +46,21 @@ static VkExtensionProperties kVkExtProps_ ##EXT = mvkMakeExtProps(VK_ ##EXT ##_E // Returns whether the specified properties are valid for this platform static bool mvkIsSupportedOnPlatform(VkExtensionProperties* pProperties) { -#if !(MVK_IOS) +#if MVK_MACOS if (pProperties == &kVkExtProps_EXT_MEMORY_BUDGET) { return mvkOSVersion() >= 10.13; } if (pProperties == &kVkExtProps_EXT_SHADER_STENCIL_EXPORT) { return mvkOSVersion() >= 10.14; } + if (pProperties == &kVkExtProps_EXT_TEXEL_BUFFER_ALIGNMENT) { + return mvkOSVersion() >= 10.13; + } if (pProperties == &kVkExtProps_MVK_IOS_SURFACE) { return false; } + if (pProperties == &kVkExtProps_AMD_SHADER_IMAGE_LOAD_STORE_LOD) { return false; } if (pProperties == &kVkExtProps_IMG_FORMAT_PVRTC) { return false; } #endif -#if !(MVK_MACOS) +#if MVK_IOS if (pProperties == &kVkExtProps_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE) { return false; } if (pProperties == &kVkExtProps_EXT_MEMORY_BUDGET) { return mvkOSVersion() >= 11.0; @@ -64,6 +68,10 @@ static bool mvkIsSupportedOnPlatform(VkExtensionProperties* pProperties) { if (pProperties == &kVkExtProps_EXT_SHADER_STENCIL_EXPORT) { return mvkOSVersion() >= 12.0; } + if (pProperties == &kVkExtProps_EXT_SWAPCHAIN_COLOR_SPACE) { return false; } + if (pProperties == &kVkExtProps_EXT_TEXEL_BUFFER_ALIGNMENT) { + return mvkOSVersion() >= 11.0; + } if (pProperties == &kVkExtProps_MVK_MACOS_SURFACE) { return false; } #endif diff --git a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.hpp b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.hpp index 6cc918f8..62eeae74 100644 --- a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.hpp +++ b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.hpp @@ -22,6 +22,8 @@ #include "mvk_datatypes.h" +#include + class MVKBaseObject; /* @@ -75,4 +77,13 @@ MTLWinding mvkMTLWindingFromSpvExecutionModeInObj(uint32_t spvMode, MVKBaseObjec MTLTessellationPartitionMode mvkMTLTessellationPartitionModeFromSpvExecutionModeInObj(uint32_t spvMode, MVKBaseObject* mvkObj); #define mvkMTLTessellationPartitionModeFromSpvExecutionMode(spvMode) mvkMTLTessellationPartitionModeFromSpvExecutionModeInObj(spvMode, this) + +#pragma mark - +#pragma mark Image properties + +#pragma mark Texture formats + +/** Enumerates all formats that support the given features, calling a specified function for each one. */ +void mvkEnumerateSupportedFormats(VkFormatProperties properties, bool any, std::function func); + #endif diff --git a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm index b58a6038..b69f09a6 100644 --- a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm +++ b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm @@ -665,6 +665,25 @@ MVK_PUBLIC_SYMBOL const char* mvkMTLPixelFormatName(MTLPixelFormat mtlFormat) { return formatDescForMTLPixelFormat(mtlFormat).mtlName; } +void mvkEnumerateSupportedFormats(VkFormatProperties properties, bool any, std::function func) { + static const auto areFeaturesSupported = [any](uint32_t a, uint32_t b) { + if (any) + return mvkIsAnyFlagEnabled(a, b); + else + return mvkAreAllFlagsEnabled(a, b); + }; + for (auto& formatDesc : _formatDescriptions) { + if (formatDesc.isSupported() && + areFeaturesSupported(formatDesc.properties.linearTilingFeatures, properties.linearTilingFeatures) && + areFeaturesSupported(formatDesc.properties.optimalTilingFeatures, properties.optimalTilingFeatures) && + areFeaturesSupported(formatDesc.properties.bufferFeatures, properties.bufferFeatures)) { + if (!func(formatDesc.vk)) { + break; + } + } + } +} + #undef mvkMTLVertexFormatFromVkFormat MVK_PUBLIC_SYMBOL MTLVertexFormat mvkMTLVertexFormatFromVkFormat(VkFormat vkFormat) { return mvkMTLVertexFormatFromVkFormatInObj(vkFormat, nullptr);