diff --git a/Common/MVKOSExtensions.h b/Common/MVKOSExtensions.h index 8e7e8213..83adaae3 100644 --- a/Common/MVKOSExtensions.h +++ b/Common/MVKOSExtensions.h @@ -18,12 +18,17 @@ #pragma once +#include "MVKCommonEnvironment.h" #include #include +#include typedef float MVKOSVersion; +/*** Constant indicating unsupported functionality in an OS. */ +static const MVKOSVersion kMVKOSVersionUnsupported = std::numeric_limits::max(); + /** * Returns the operating system version as an MVKOSVersion, which is a float in which the * whole number portion indicates the major version, and the fractional portion indicates @@ -33,9 +38,29 @@ typedef float MVKOSVersion; */ MVKOSVersion mvkOSVersion(); +/** Returns a MVKOSVersion built from the version components. */ +inline MVKOSVersion mvkMakeOSVersion(uint32_t major, uint32_t minor, uint32_t patch) { + return (float)major + ((float)minor / 100.0f) + ((float)patch / 10000.0f); +} + /** Returns whether the operating system version is at least minVer. */ inline bool mvkOSVersionIsAtLeast(MVKOSVersion minVer) { return mvkOSVersion() >= minVer; } +/** + * Returns whether the operating system version is at least the appropriate min version. + * The constant kMVKOSVersionUnsupported can be used for either value to cause the test + * to always fail on that OS, which is useful for indidicating functionalty guarded by + * this test is not supported on that OS. + */ +inline bool mvkOSVersionIsAtLeast(MVKOSVersion macOSMinVer, MVKOSVersion iOSMinVer) { +#if MVK_MACOS + return mvkOSVersionIsAtLeast(macOSMinVer); +#endif +#if MVK_IOS + return mvkOSVersionIsAtLeast(iOSMinVer); +#endif +} + /** * Returns a monotonic timestamp value for use in Vulkan and performance timestamping. * diff --git a/Common/MVKOSExtensions.mm b/Common/MVKOSExtensions.mm index d675c7e7..1ba8097a 100644 --- a/Common/MVKOSExtensions.mm +++ b/Common/MVKOSExtensions.mm @@ -29,17 +29,13 @@ using namespace std; -static const MVKOSVersion kMVKOSVersionUnknown = 0.0f; -static MVKOSVersion _mvkOSVersion = kMVKOSVersionUnknown; MVKOSVersion mvkOSVersion() { - if (_mvkOSVersion == kMVKOSVersionUnknown) { - NSOperatingSystemVersion osVer = [[NSProcessInfo processInfo] operatingSystemVersion]; - float maj = osVer.majorVersion; - float min = osVer.minorVersion; - float pat = osVer.patchVersion; - _mvkOSVersion = maj + (min / 100.0f) + + (pat / 10000.0f); - } - return _mvkOSVersion; + static MVKOSVersion _mvkOSVersion = 0; + if ( !_mvkOSVersion ) { + NSOperatingSystemVersion osVer = [[NSProcessInfo processInfo] operatingSystemVersion]; + _mvkOSVersion = mvkMakeOSVersion((uint32_t)osVer.majorVersion, (uint32_t)osVer.minorVersion, (uint32_t)osVer.patchVersion); + } + return _mvkOSVersion; } static uint64_t _mvkTimestampBase; diff --git a/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/project.pbxproj b/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/project.pbxproj index adb88b3a..145fcef1 100644 --- a/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/project.pbxproj +++ b/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/project.pbxproj @@ -659,7 +659,6 @@ PRODUCT_NAME = "API-Samples"; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; - VALID_ARCHS = arm64; }; name = Debug; }; @@ -681,7 +680,6 @@ PRODUCT_NAME = "API-Samples"; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; - VALID_ARCHS = arm64; }; name = Release; }; @@ -738,6 +736,7 @@ C01FCF4F08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -768,6 +767,7 @@ C01FCF5008A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; diff --git a/Demos/LunarG-VulkanSamples/API-Samples/generateSPIRVShaders b/Demos/LunarG-VulkanSamples/API-Samples/generateSPIRVShaders index b234111b..3233861d 100755 --- a/Demos/LunarG-VulkanSamples/API-Samples/generateSPIRVShaders +++ b/Demos/LunarG-VulkanSamples/API-Samples/generateSPIRVShaders @@ -10,27 +10,14 @@ set -e echo -echo ========== Building MoltenVKShaderConverter tool ========== +echo ========== Converting API-Samples shader files ========== echo cd "../../.." -XC_PROJ="MoltenVKPackaging.xcodeproj" -XC_SCHEME="MVKShaderConverterTool Package" - -xcodebuild \ - -project "MoltenVKPackaging.xcodeproj" \ - -scheme "MVKShaderConverterTool Package" \ - -quiet \ - build - -echo -echo ========== Converting API-Samples shader files ========== -echo - "Package/Latest/MoltenVKShaderConverter/Tools/MoltenVKShaderConverter" \ -r -gi -so -oh -xs . -q \ - -d "Demos/LunarG-VulkanSamples/VulkanSamples/API-Samples" + -d "Demos/LunarG-VulkanSamples/VulkanSamples/API-Samples" > /dev/null cd - > /dev/null diff --git a/Demos/LunarG-VulkanSamples/Cube/Cube.xcodeproj/project.pbxproj b/Demos/LunarG-VulkanSamples/Cube/Cube.xcodeproj/project.pbxproj index 75030f0d..2766dab5 100644 --- a/Demos/LunarG-VulkanSamples/Cube/Cube.xcodeproj/project.pbxproj +++ b/Demos/LunarG-VulkanSamples/Cube/Cube.xcodeproj/project.pbxproj @@ -359,7 +359,6 @@ PRODUCT_NAME = Cube; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; - VALID_ARCHS = arm64; }; name = Debug; }; @@ -380,13 +379,13 @@ PRODUCT_NAME = Cube; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; - VALID_ARCHS = arm64; }; name = Release; }; C01FCF4F08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -417,6 +416,7 @@ C01FCF5008A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; diff --git a/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/project.pbxproj b/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/project.pbxproj index 9129ae35..3f77a1bf 100644 --- a/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/project.pbxproj +++ b/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/project.pbxproj @@ -360,7 +360,6 @@ LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/../../../MoltenVK/iOS/static\""; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; - VALID_ARCHS = arm64; }; name = Debug; }; @@ -375,7 +374,6 @@ LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/../../../MoltenVK/iOS/static\""; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; - VALID_ARCHS = arm64; }; name = Release; }; @@ -416,6 +414,7 @@ C01FCF4F08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -448,6 +447,7 @@ C01FCF5008A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; diff --git a/Docs/MoltenVK_Runtime_UserGuide.md b/Docs/MoltenVK_Runtime_UserGuide.md index dbc529f8..b3270cda 100644 --- a/Docs/MoltenVK_Runtime_UserGuide.md +++ b/Docs/MoltenVK_Runtime_UserGuide.md @@ -256,6 +256,7 @@ In addition to core *Vulkan* functionality, **MoltenVK** also supports the foll - `VK_KHR_push_descriptor` - `VK_KHR_relaxed_block_layout` - `VK_KHR_sampler_mirror_clamp_to_edge` *(macOS)* +- `VK_KHR_sampler_ycbcr_conversion` - `VK_KHR_shader_draw_parameters` - `VK_KHR_shader_float16_int8` - `VK_KHR_storage_buffer_storage_class` @@ -273,6 +274,7 @@ In addition to core *Vulkan* functionality, **MoltenVK** also supports the foll - `VK_EXT_memory_budget` *(requires Metal 2.0)* - `VK_EXT_metal_surface` - `VK_EXT_post_depth_coverage` *(iOS, requires GPU family 4)* +- `VK_EXT_robustness2` - `VK_EXT_scalar_block_layout` - `VK_EXT_shader_stencil_export` *(requires Mac GPU family 2 or iOS GPU family 5)* - `VK_EXT_shader_viewport_index_layer` diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md index a114c71b..14e79ac6 100644 --- a/Docs/Whats_New.md +++ b/Docs/Whats_New.md @@ -13,6 +13,20 @@ For best results, use a Markdown reader.* +MoltenVK 1.0.44 +--------------- + +Released TBD + +- Add support for extensions: + - VK_KHR_sampler_ycbcr_conversion + - VK_EXT_robustness2 +- Fix issue where mapped host-coherent device memory not updated from image contents on *macOS*. +- Remove use of `@available()` directive as it was causing issues in some build environments. +- Refactor **MoltenVK** *Xcode* build architectures. +- Demo `API-Samples generateSPIRVShaders` no longer builds `MoltenVKShaderController` tool. + + MoltenVK 1.0.43 --------------- diff --git a/ExternalDependencies.xcodeproj/project.pbxproj b/ExternalDependencies.xcodeproj/project.pbxproj index cc7593e0..022cd7b2 100644 --- a/ExternalDependencies.xcodeproj/project.pbxproj +++ b/ExternalDependencies.xcodeproj/project.pbxproj @@ -5077,34 +5077,24 @@ A90FD89D21CC4EAB00B92BB2 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = arm64; - BITCODE_GENERATION_MODE = bitcode; - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", "SPIRV_CROSS_NAMESPACE_OVERRIDE=MVK_spirv_cross", ); PRODUCT_NAME = SPIRVCross; SDKROOT = iphoneos; - VALID_ARCHS = arm64; }; name = Debug; }; A90FD89E21CC4EAB00B92BB2 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = arm64; - BITCODE_GENERATION_MODE = bitcode; - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", "SPIRV_CROSS_NAMESPACE_OVERRIDE=MVK_spirv_cross", ); PRODUCT_NAME = SPIRVCross; SDKROOT = iphoneos; - VALID_ARCHS = arm64; }; name = Release; }; @@ -5185,10 +5175,6 @@ A972A80D21CECBBF0013AB25 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = arm64; - BITCODE_GENERATION_MODE = bitcode; - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEPLOYMENT_POSTPROCESSING = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO; HEADER_SEARCH_PATHS = ( @@ -5200,17 +5186,12 @@ ); PRODUCT_NAME = SPIRVTools; SDKROOT = iphoneos; - VALID_ARCHS = arm64; }; name = Debug; }; A972A80E21CECBBF0013AB25 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = arm64; - BITCODE_GENERATION_MODE = bitcode; - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEPLOYMENT_POSTPROCESSING = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO; HEADER_SEARCH_PATHS = ( @@ -5222,7 +5203,6 @@ ); PRODUCT_NAME = SPIRVTools; SDKROOT = iphoneos; - VALID_ARCHS = arm64; }; name = Release; }; @@ -5263,11 +5243,7 @@ A972ABDA21CED7BC0013AB25 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = arm64; - BITCODE_GENERATION_MODE = bitcode; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = NO; - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", "AMD_EXTENSIONS=1", @@ -5283,18 +5259,13 @@ ); PRODUCT_NAME = glslang; SDKROOT = iphoneos; - VALID_ARCHS = arm64; }; name = Debug; }; A972ABDB21CED7BC0013AB25 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = arm64; - BITCODE_GENERATION_MODE = bitcode; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = NO; - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", "AMD_EXTENSIONS=1", @@ -5310,7 +5281,6 @@ ); PRODUCT_NAME = glslang; SDKROOT = iphoneos; - VALID_ARCHS = arm64; }; name = Release; }; @@ -5362,6 +5332,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = NO; @@ -5410,6 +5381,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = NO; diff --git a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj index b379ee9a..9575241a 100644 --- a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj +++ b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj @@ -1525,24 +1525,18 @@ A9B8EE1E1A98D796009C5A02 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = arm64; - BITCODE_GENERATION_MODE = bitcode; IPHONEOS_DEPLOYMENT_TARGET = 9.0; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; - VALID_ARCHS = arm64; }; name = Debug; }; A9B8EE1F1A98D796009C5A02 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = arm64; - BITCODE_GENERATION_MODE = bitcode; IPHONEOS_DEPLOYMENT_TARGET = 9.0; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; - VALID_ARCHS = arm64; }; name = Release; }; @@ -1551,7 +1545,6 @@ buildSettings = { MACOSX_DEPLOYMENT_TARGET = 10.11; SDKROOT = macosx; - VALID_ARCHS = x86_64; }; name = Debug; }; @@ -1560,7 +1553,6 @@ buildSettings = { MACOSX_DEPLOYMENT_TARGET = 10.11; SDKROOT = macosx; - VALID_ARCHS = x86_64; }; name = Release; }; @@ -1568,6 +1560,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = NO; @@ -1628,6 +1621,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = NO; diff --git a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h index 80be2fc2..71e165e4 100644 --- a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h +++ b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h @@ -50,7 +50,7 @@ typedef unsigned long MTLLanguageVersion; */ #define MVK_VERSION_MAJOR 1 #define MVK_VERSION_MINOR 0 -#define MVK_VERSION_PATCH 43 +#define MVK_VERSION_PATCH 44 #define MVK_MAKE_VERSION(major, minor, patch) (((major) * 10000) + ((minor) * 100) + (patch)) #define MVK_VERSION MVK_MAKE_VERSION(MVK_VERSION_MAJOR, MVK_VERSION_MINOR, MVK_VERSION_PATCH) diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm index 13bfe6fa..4e12de57 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm +++ b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm @@ -90,7 +90,9 @@ void MVKCmdPipelineBarrier::encode(MVKCommandEncoder* cmdEncoder) { break; case MVKPipelineBarrier::Image: - resources[rezCnt++] = b.mvkImage->getMTLTexture(); + for (uint8_t planeIndex = 0; planeIndex < b.mvkImage->getPlaneCount(); planeIndex++) { + resources[rezCnt++] = b.mvkImage->getMTLTexture(planeIndex); + } break; default: diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h index 54723c8b..cedf30b1 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h +++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h @@ -103,7 +103,7 @@ public: protected: MVKCommandTypePool* getTypePool(MVKCommandPool* cmdPool) override; - bool canCopyFormats(); + bool canCopyFormats(const VkImageBlit& region); bool canCopy(const VkImageBlit& region); void populateVertices(MVKVertexPosTex* vertices, const VkImageBlit& region); @@ -125,8 +125,9 @@ typedef MVKCmdBlitImage<4> MVKCmdBlitImageMulti; /** Describes Metal texture resolve parameters. */ typedef struct { - uint32_t level; - uint32_t slice; + VkImageCopy* copyRegion; + uint32_t level; + uint32_t slice; } MVKMetalResolveSlice; /** diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm index 341c5ce5..848aa94c 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm +++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm @@ -62,129 +62,138 @@ VkResult MVKCmdCopyImage::setContent(MVKCommandBuffer* cmdBuff, _dstLayout = dstImageLayout; _vkImageCopies.clear(); // Clear for reuse - for (uint32_t i = 0; i < regionCount; i++) { - _vkImageCopies.push_back(pRegions[i]); - } - - // Validate - MVKPixelFormats* pixFmts = cmdBuff->getPixelFormats(); - if ((_dstImage->getSampleCount() != _srcImage->getSampleCount()) || - (pixFmts->getBytesPerBlock(_dstImage->getMTLPixelFormat()) != pixFmts->getBytesPerBlock(_srcImage->getMTLPixelFormat()))) { - return reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdCopyImage(): Cannot copy between incompatible formats, such as formats of different pixel sizes, or between images with different sample counts."); - } - if ((_srcImage->getMTLTextureType() == MTLTextureType3D) != (_dstImage->getMTLTextureType() == MTLTextureType3D)) { - return reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdCopyImage(): Metal does not support copying to or from slices of a 3D texture."); + for (uint32_t regionIdx = 0; regionIdx < regionCount; regionIdx++) { + auto& vkIR = pRegions[regionIdx]; + uint8_t srcPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(vkIR.srcSubresource.aspectMask); + uint8_t dstPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(vkIR.dstSubresource.aspectMask); + + // Validate + MVKPixelFormats* pixFmts = cmdBuff->getPixelFormats(); + if ((_dstImage->getSampleCount() != _srcImage->getSampleCount()) || + (pixFmts->getBytesPerBlock(_dstImage->getMTLPixelFormat(dstPlaneIndex)) != pixFmts->getBytesPerBlock(_srcImage->getMTLPixelFormat(srcPlaneIndex)))) { + return reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdCopyImage(): Cannot copy between incompatible formats, such as formats of different pixel sizes, or between images with different sample counts."); + } + + _vkImageCopies.push_back(vkIR); } + + // Validate + if ((_srcImage->getMTLTextureType() == MTLTextureType3D) != (_dstImage->getMTLTextureType() == MTLTextureType3D)) { + return reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdCopyImage(): Metal does not support copying to or from slices of a 3D texture."); + } return VK_SUCCESS; } template void MVKCmdCopyImage::encode(MVKCommandEncoder* cmdEncoder, MVKCommandUse commandUse) { + MVKPixelFormats* pixFmts = cmdEncoder->getPixelFormats(); + uint32_t copyCnt = (uint32_t)_vkImageCopies.size(); + VkBufferImageCopy vkSrcCopies[copyCnt]; + VkBufferImageCopy vkDstCopies[copyCnt]; + size_t tmpBuffSize = 0; - MTLPixelFormat srcMTLPixFmt = _srcImage->getMTLPixelFormat(); - bool isSrcCompressed = _srcImage->getIsCompressed(); + for (uint32_t copyIdx = 0; copyIdx < copyCnt; copyIdx++) { + auto& vkIC = _vkImageCopies[copyIdx]; + + uint8_t srcPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(vkIC.srcSubresource.aspectMask); + uint8_t dstPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(vkIC.dstSubresource.aspectMask); + + MTLPixelFormat srcMTLPixFmt = _srcImage->getMTLPixelFormat(srcPlaneIndex); + bool isSrcCompressed = _srcImage->getIsCompressed(); - MTLPixelFormat dstMTLPixFmt = _dstImage->getMTLPixelFormat(); - bool isDstCompressed = _dstImage->getIsCompressed(); + MTLPixelFormat dstMTLPixFmt = _dstImage->getMTLPixelFormat(dstPlaneIndex); + bool isDstCompressed = _dstImage->getIsCompressed(); - // If source and destination have different formats and at least one is compressed, use a temporary intermediary buffer - bool useTempBuffer = (srcMTLPixFmt != dstMTLPixFmt) && (isSrcCompressed || isDstCompressed); - if (useTempBuffer) { - MVKPixelFormats* pixFmts = cmdEncoder->getPixelFormats(); - uint32_t copyCnt = (uint32_t)_vkImageCopies.size(); - VkBufferImageCopy vkSrcCopies[copyCnt]; - VkBufferImageCopy vkDstCopies[copyCnt]; - size_t tmpBuffSize = 0; - for (uint32_t copyIdx = 0; copyIdx < copyCnt; copyIdx++) { - auto& vkIC = _vkImageCopies[copyIdx]; + // If source and destination have different formats and at least one is compressed, use a temporary intermediary buffer + bool useTempBuffer = (srcMTLPixFmt != dstMTLPixFmt) && (isSrcCompressed || isDstCompressed); - // Add copy from source image to temp buffer. - auto& srcCpy = vkSrcCopies[copyIdx]; - srcCpy.bufferOffset = tmpBuffSize; - srcCpy.bufferRowLength = 0; - srcCpy.bufferImageHeight = 0; - srcCpy.imageSubresource = vkIC.srcSubresource; - srcCpy.imageOffset = vkIC.srcOffset; - srcCpy.imageExtent = vkIC.extent; + if (useTempBuffer) { + // Add copy from source image to temp buffer. + auto& srcCpy = vkSrcCopies[copyIdx]; + srcCpy.bufferOffset = tmpBuffSize; + srcCpy.bufferRowLength = 0; + srcCpy.bufferImageHeight = 0; + srcCpy.imageSubresource = vkIC.srcSubresource; + srcCpy.imageOffset = vkIC.srcOffset; + srcCpy.imageExtent = vkIC.extent; - // Add copy from temp buffer to destination image. - // Extent is provided in source texels. If the source is compressed but the - // destination is not, each destination pixel will consume an entire source block, - // so we must downscale the destination extent by the size of the source block. - VkExtent3D dstExtent = vkIC.extent; - if (isSrcCompressed && !isDstCompressed) { - VkExtent2D srcBlockExtent = pixFmts->getBlockTexelSize(srcMTLPixFmt); - dstExtent.width /= srcBlockExtent.width; - dstExtent.height /= srcBlockExtent.height; - } - auto& dstCpy = vkDstCopies[copyIdx]; - dstCpy.bufferOffset = tmpBuffSize; - dstCpy.bufferRowLength = 0; - dstCpy.bufferImageHeight = 0; - dstCpy.imageSubresource = vkIC.dstSubresource; - dstCpy.imageOffset = vkIC.dstOffset; - dstCpy.imageExtent = dstExtent; + // Add copy from temp buffer to destination image. + // Extent is provided in source texels. If the source is compressed but the + // destination is not, each destination pixel will consume an entire source block, + // so we must downscale the destination extent by the size of the source block. + VkExtent3D dstExtent = vkIC.extent; + if (isSrcCompressed && !isDstCompressed) { + VkExtent2D srcBlockExtent = pixFmts->getBlockTexelSize(srcMTLPixFmt); + dstExtent.width /= srcBlockExtent.width; + dstExtent.height /= srcBlockExtent.height; + } + auto& dstCpy = vkDstCopies[copyIdx]; + dstCpy.bufferOffset = tmpBuffSize; + dstCpy.bufferRowLength = 0; + dstCpy.bufferImageHeight = 0; + dstCpy.imageSubresource = vkIC.dstSubresource; + dstCpy.imageOffset = vkIC.dstOffset; + dstCpy.imageExtent = dstExtent; - size_t bytesPerRow = pixFmts->getBytesPerRow(srcMTLPixFmt, vkIC.extent.width); - size_t bytesPerRegion = pixFmts->getBytesPerLayer(srcMTLPixFmt, bytesPerRow, vkIC.extent.height); - tmpBuffSize += bytesPerRegion; - } + size_t bytesPerRow = pixFmts->getBytesPerRow(srcMTLPixFmt, vkIC.extent.width); + size_t bytesPerRegion = pixFmts->getBytesPerLayer(srcMTLPixFmt, bytesPerRow, vkIC.extent.height); + tmpBuffSize += bytesPerRegion; + } else { + // Map the source pixel format to the dest pixel format through a texture view on the source texture. + // If the source and dest pixel formats are the same, this will simply degenerate to the source texture itself. + id srcMTLTex = _srcImage->getMTLTexture(srcPlaneIndex, _dstImage->getMTLPixelFormat(dstPlaneIndex)); + id dstMTLTex = _dstImage->getMTLTexture(dstPlaneIndex); + if ( !srcMTLTex || !dstMTLTex ) { return; } - MVKBufferDescriptorData tempBuffData; - tempBuffData.size = tmpBuffSize; - tempBuffData.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - VkBuffer tempBuff = (VkBuffer)cmdEncoder->getCommandEncodingPool()->getTransferMVKBuffer(tempBuffData); + id mtlBlitEnc = cmdEncoder->getMTLBlitEncoder(commandUse); - MVKCmdBufferImageCopy cpyCmd; + // If copies can be performed using direct texture-texture copying, do so + uint32_t srcLevel = vkIC.srcSubresource.mipLevel; + MTLOrigin srcOrigin = mvkMTLOriginFromVkOffset3D(vkIC.srcOffset); + MTLSize srcSize = mvkClampMTLSize(mvkMTLSizeFromVkExtent3D(vkIC.extent), + srcOrigin, + mvkMTLSizeFromVkExtent3D(_srcImage->getExtent3D(srcPlaneIndex, srcLevel))); + uint32_t dstLevel = vkIC.dstSubresource.mipLevel; + MTLOrigin dstOrigin = mvkMTLOriginFromVkOffset3D(vkIC.dstOffset); + uint32_t srcBaseLayer = vkIC.srcSubresource.baseArrayLayer; + uint32_t dstBaseLayer = vkIC.dstSubresource.baseArrayLayer; + uint32_t layCnt = vkIC.srcSubresource.layerCount; - // Copy from source image to buffer - // Create and execute a temporary buffer image command. - // To be threadsafe...do NOT acquire and return the command from the pool. - cpyCmd.setContent(cmdEncoder->_cmdBuffer, tempBuff, (VkImage)_srcImage, _srcLayout, copyCnt, vkSrcCopies, false); - cpyCmd.encode(cmdEncoder); + for (uint32_t layIdx = 0; layIdx < layCnt; layIdx++) { + [mtlBlitEnc copyFromTexture: srcMTLTex + sourceSlice: srcBaseLayer + layIdx + sourceLevel: srcLevel + sourceOrigin: srcOrigin + sourceSize: srcSize + toTexture: dstMTLTex + destinationSlice: dstBaseLayer + layIdx + destinationLevel: dstLevel + destinationOrigin: dstOrigin]; + } + } + } - // Copy from buffer to destination image - // Create and execute a temporary buffer image command. - // To be threadsafe...do NOT acquire and return the command from the pool. - cpyCmd.setContent(cmdEncoder->_cmdBuffer, tempBuff, (VkImage)_dstImage, _dstLayout, copyCnt, vkDstCopies, true); - cpyCmd.encode(cmdEncoder); + if (tmpBuffSize > 0) { + MVKBufferDescriptorData tempBuffData; + tempBuffData.size = tmpBuffSize; + tempBuffData.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + VkBuffer tempBuff = (VkBuffer)cmdEncoder->getCommandEncodingPool()->getTransferMVKBuffer(tempBuffData); - } else { - // Map the source pixel format to the dest pixel format through a texture view on the source texture. - // If the source and dest pixel formats are the same, this will simply degenerate to the source texture itself. - id srcMTLTex = _srcImage->getMTLTexture(_dstImage->getMTLPixelFormat()); - id dstMTLTex = _dstImage->getMTLTexture(); - if ( !srcMTLTex || !dstMTLTex ) { return; } + MVKCmdBufferImageCopy cpyCmd; - id mtlBlitEnc = cmdEncoder->getMTLBlitEncoder(commandUse); + // Copy from source image to buffer + // Create and execute a temporary buffer image command. + // To be threadsafe...do NOT acquire and return the command from the pool. + cpyCmd.setContent(cmdEncoder->_cmdBuffer, tempBuff, (VkImage)_srcImage, _srcLayout, copyCnt, vkSrcCopies, false); + cpyCmd.encode(cmdEncoder); - // If copies can be performed using direct texture-texture copying, do so - for (auto& cpyRgn : _vkImageCopies) { - uint32_t srcLevel = cpyRgn.srcSubresource.mipLevel; - MTLOrigin srcOrigin = mvkMTLOriginFromVkOffset3D(cpyRgn.srcOffset); - MTLSize srcSize = mvkClampMTLSize(mvkMTLSizeFromVkExtent3D(cpyRgn.extent), - srcOrigin, - mvkMTLSizeFromVkExtent3D(_srcImage->getExtent3D(srcLevel))); - uint32_t dstLevel = cpyRgn.dstSubresource.mipLevel; - MTLOrigin dstOrigin = mvkMTLOriginFromVkOffset3D(cpyRgn.dstOffset); - uint32_t srcBaseLayer = cpyRgn.srcSubresource.baseArrayLayer; - uint32_t dstBaseLayer = cpyRgn.dstSubresource.baseArrayLayer; - uint32_t layCnt = cpyRgn.srcSubresource.layerCount; - - for (uint32_t layIdx = 0; layIdx < layCnt; layIdx++) { - [mtlBlitEnc copyFromTexture: srcMTLTex - sourceSlice: srcBaseLayer + layIdx - sourceLevel: srcLevel - sourceOrigin: srcOrigin - sourceSize: srcSize - toTexture: dstMTLTex - destinationSlice: dstBaseLayer + layIdx - destinationLevel: dstLevel - destinationOrigin: dstOrigin]; - } - } - } + // Copy from buffer to destination image + // Create and execute a temporary buffer image command. + // To be threadsafe...do NOT acquire and return the command from the pool. + cpyCmd.setContent(cmdEncoder->_cmdBuffer, tempBuff, (VkImage)_dstImage, _dstLayout, copyCnt, vkDstCopies, true); + cpyCmd.encode(cmdEncoder); + } } template class MVKCmdCopyImage<1>; @@ -208,33 +217,37 @@ VkResult MVKCmdBlitImage::setContent(MVKCommandBuffer* cmdBuff, _srcImage = (MVKImage*)srcImage; _srcLayout = srcImageLayout; - _dstImage = (MVKImage*)dstImage; _dstLayout = dstImageLayout; _filter = filter; _vkImageBlits.clear(); // Clear for reuse - for (uint32_t i = 0; i < regionCount; i++) { - _vkImageBlits.push_back(pRegions[i]); + for (uint32_t regionIdx = 0; regionIdx < regionCount; regionIdx++) { + auto& vkIR = pRegions[regionIdx]; + uint8_t srcPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(vkIR.srcSubresource.aspectMask); + + // Validate - depth stencil formats cannot be scaled or inverted + MTLPixelFormat srcMTLPixFmt = _srcImage->getMTLPixelFormat(srcPlaneIndex); + if (pixFmts->isDepthFormat(srcMTLPixFmt) || pixFmts->isStencilFormat(srcMTLPixFmt)) { + for (auto& vkIB : _vkImageBlits) { + if ( !(canCopyFormats(vkIB) && canCopy(vkIB)) ) { + return reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdBlitImage(): Scaling or inverting depth/stencil images is not supported."); + } + } + } + + _vkImageBlits.push_back(vkIR); } - // Validate - depth stencil formats cannot be scaled or inverted - MTLPixelFormat srcMTLPixFmt = _srcImage->getMTLPixelFormat(); - if (pixFmts->isDepthFormat(srcMTLPixFmt) || pixFmts->isStencilFormat(srcMTLPixFmt)) { - bool canCopyFmts = canCopyFormats(); - for (auto& vkIB : _vkImageBlits) { - if ( !(canCopyFmts && canCopy(vkIB)) ) { - return reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdBlitImage(): Scaling or inverting depth/stencil images is not supported."); - } - } - } return VK_SUCCESS; } template -bool MVKCmdBlitImage::canCopyFormats() { - return ((_srcImage->getMTLPixelFormat() == _dstImage->getMTLPixelFormat()) && +bool MVKCmdBlitImage::canCopyFormats(const VkImageBlit& region) { + uint8_t srcPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(region.srcSubresource.aspectMask); + uint8_t dstPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(region.dstSubresource.aspectMask); + return ((_srcImage->getMTLPixelFormat(srcPlaneIndex) == _dstImage->getMTLPixelFormat(dstPlaneIndex)) && (_dstImage->getSampleCount() == _srcImage->getSampleCount())); } @@ -255,8 +268,10 @@ void MVKCmdBlitImage::populateVertices(MVKVertexPosTex* vertices, const VkIma const VkOffset3D& do1 = region.dstOffsets[1]; // Get the extents of the source and destination textures. - VkExtent3D srcExtent = _srcImage->getExtent3D(region.srcSubresource.mipLevel); - VkExtent3D dstExtent = _dstImage->getExtent3D(region.dstSubresource.mipLevel); + uint8_t srcPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(region.srcSubresource.aspectMask); + uint8_t dstPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(region.dstSubresource.aspectMask); + VkExtent3D srcExtent = _srcImage->getExtent3D(srcPlaneIndex, region.srcSubresource.mipLevel); + VkExtent3D dstExtent = _dstImage->getExtent3D(dstPlaneIndex, region.dstSubresource.mipLevel); // Determine the bottom-left and top-right corners of the source and destination // texture regions, each as a fraction of the corresponding texture size. @@ -317,9 +332,8 @@ void MVKCmdBlitImage::encode(MVKCommandEncoder* cmdEncoder, MVKCommandUse com // Separate BLITs into those that are really just simple texure region copies, // and those that require rendering - bool canCopyFmts = canCopyFormats(); for (auto& vkIB : _vkImageBlits) { - if (canCopyFmts && canCopy(vkIB)) { + if (canCopyFormats(vkIB) && canCopy(vkIB)) { const VkOffset3D& so0 = vkIB.srcOffsets[0]; const VkOffset3D& so1 = vkIB.srcOffsets[1]; @@ -351,59 +365,61 @@ void MVKCmdBlitImage::encode(MVKCommandEncoder* cmdEncoder, MVKCommandUse com } // Perform those BLITs that require rendering to destination texture. - id srcMTLTex = _srcImage->getMTLTexture(); - id dstMTLTex = _dstImage->getMTLTexture(); - if (blitCnt && srcMTLTex && dstMTLTex) { + for (uint32_t blitIdx = 0; blitIdx < blitCnt; blitIdx++) { + auto& mvkIBR = mvkBlitRenders[blitIdx]; - cmdEncoder->endCurrentMetalEncoding(); + uint8_t srcPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(mvkIBR.region.srcSubresource.aspectMask); + uint8_t dstPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(mvkIBR.region.dstSubresource.aspectMask); - MTLRenderPassDescriptor* mtlRPD = [MTLRenderPassDescriptor renderPassDescriptor]; - MTLRenderPassColorAttachmentDescriptor* mtlColorAttDesc = mtlRPD.colorAttachments[0]; - mtlColorAttDesc.loadAction = MTLLoadActionLoad; - mtlColorAttDesc.storeAction = MTLStoreActionStore; - mtlColorAttDesc.texture = dstMTLTex; + id srcMTLTex = _srcImage->getMTLTexture(srcPlaneIndex); + id dstMTLTex = _dstImage->getMTLTexture(dstPlaneIndex); + if (blitCnt && srcMTLTex && dstMTLTex) { + cmdEncoder->endCurrentMetalEncoding(); - MVKRPSKeyBlitImg blitKey; - blitKey.srcMTLPixelFormat = _srcImage->getMTLPixelFormat(); - blitKey.srcMTLTextureType = _srcImage->getMTLTextureType(); - blitKey.dstMTLPixelFormat = _dstImage->getMTLPixelFormat(); - blitKey.srcFilter = mvkMTLSamplerMinMagFilterFromVkFilter(_filter); - blitKey.dstSampleCount = mvkSampleCountFromVkSampleCountFlagBits(_dstImage->getSampleCount()); - id mtlRPS = cmdEncoder->getCommandEncodingPool()->getCmdBlitImageMTLRenderPipelineState(blitKey); + MTLRenderPassDescriptor* mtlRPD = [MTLRenderPassDescriptor renderPassDescriptor]; + MTLRenderPassColorAttachmentDescriptor* mtlColorAttDesc = mtlRPD.colorAttachments[0]; + mtlColorAttDesc.loadAction = MTLLoadActionLoad; + mtlColorAttDesc.storeAction = MTLStoreActionStore; + mtlColorAttDesc.texture = dstMTLTex; - uint32_t vtxBuffIdx = cmdEncoder->getDevice()->getMetalBufferIndexForVertexAttributeBinding(kMVKVertexContentBufferIndex); + MVKRPSKeyBlitImg blitKey; + blitKey.srcMTLPixelFormat = _srcImage->getMTLPixelFormat(srcPlaneIndex); + blitKey.srcMTLTextureType = _srcImage->getMTLTextureType(); + blitKey.dstMTLPixelFormat = _dstImage->getMTLPixelFormat(dstPlaneIndex); + blitKey.srcFilter = mvkMTLSamplerMinMagFilterFromVkFilter(_filter); + blitKey.dstSampleCount = mvkSampleCountFromVkSampleCountFlagBits(_dstImage->getSampleCount()); + id mtlRPS = cmdEncoder->getCommandEncodingPool()->getCmdBlitImageMTLRenderPipelineState(blitKey); - for (uint32_t blitIdx = 0; blitIdx < blitCnt; blitIdx++) { - auto& mvkIBR = mvkBlitRenders[blitIdx]; + uint32_t vtxBuffIdx = cmdEncoder->getDevice()->getMetalBufferIndexForVertexAttributeBinding(kMVKVertexContentBufferIndex); + + mtlColorAttDesc.level = mvkIBR.region.dstSubresource.mipLevel; - mtlColorAttDesc.level = mvkIBR.region.dstSubresource.mipLevel; + uint32_t layCnt = mvkIBR.region.srcSubresource.layerCount; + for (uint32_t layIdx = 0; layIdx < layCnt; layIdx++) { + // Update the render pass descriptor for the texture level and slice, and create a render encoder. + mtlColorAttDesc.slice = mvkIBR.region.dstSubresource.baseArrayLayer + layIdx; + id mtlRendEnc = [cmdEncoder->_mtlCmdBuffer renderCommandEncoderWithDescriptor: mtlRPD]; + setLabelIfNotNil(mtlRendEnc, mvkMTLRenderCommandEncoderLabel(commandUse)); - uint32_t layCnt = mvkIBR.region.srcSubresource.layerCount; - for (uint32_t layIdx = 0; layIdx < layCnt; layIdx++) { - // Update the render pass descriptor for the texture level and slice, and create a render encoder. - mtlColorAttDesc.slice = mvkIBR.region.dstSubresource.baseArrayLayer + layIdx; - id mtlRendEnc = [cmdEncoder->_mtlCmdBuffer renderCommandEncoderWithDescriptor: mtlRPD]; - setLabelIfNotNil(mtlRendEnc, mvkMTLRenderCommandEncoderLabel(commandUse)); + [mtlRendEnc pushDebugGroup: @"vkCmdBlitImage"]; + [mtlRendEnc setRenderPipelineState: mtlRPS]; + cmdEncoder->setVertexBytes(mtlRendEnc, mvkIBR.vertices, sizeof(mvkIBR.vertices), vtxBuffIdx); + [mtlRendEnc setFragmentTexture: srcMTLTex atIndex: 0]; - [mtlRendEnc pushDebugGroup: @"vkCmdBlitImage"]; - [mtlRendEnc setRenderPipelineState: mtlRPS]; - cmdEncoder->setVertexBytes(mtlRendEnc, mvkIBR.vertices, sizeof(mvkIBR.vertices), vtxBuffIdx); - [mtlRendEnc setFragmentTexture: srcMTLTex atIndex: 0]; + struct { + uint slice; + float lod; + } texSubRez; + texSubRez.slice = mvkIBR.region.srcSubresource.baseArrayLayer + layIdx; + texSubRez.lod = mvkIBR.region.srcSubresource.mipLevel; + cmdEncoder->setFragmentBytes(mtlRendEnc, &texSubRez, sizeof(texSubRez), 0); - struct { - uint slice; - float lod; - } texSubRez; - texSubRez.slice = mvkIBR.region.srcSubresource.baseArrayLayer + layIdx; - texSubRez.lod = mvkIBR.region.srcSubresource.mipLevel; - cmdEncoder->setFragmentBytes(mtlRendEnc, &texSubRez, sizeof(texSubRez), 0); - - [mtlRendEnc drawPrimitives: MTLPrimitiveTypeTriangleStrip vertexStart: 0 vertexCount: kMVKBlitVertexCount]; - [mtlRendEnc popDebugGroup]; - [mtlRendEnc endEncoding]; - } - } - } + [mtlRendEnc drawPrimitives: MTLPrimitiveTypeTriangleStrip vertexStart: 0 vertexCount: kMVKBlitVertexCount]; + [mtlRendEnc popDebugGroup]; + [mtlRendEnc endEncoding]; + } + } + } } template class MVKCmdBlitImage<1>; @@ -428,15 +444,18 @@ VkResult MVKCmdResolveImage::setContent(MVKCommandBuffer* cmdBuff, _vkImageResolves.clear(); // Clear for reuse _vkImageResolves.reserve(regionCount); - for (uint32_t i = 0; i < regionCount; i++) { - _vkImageResolves.push_back(pRegions[i]); - } + for (uint32_t regionIdx = 0; regionIdx < regionCount; regionIdx++) { + auto& vkIR = pRegions[regionIdx]; + uint8_t dstPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(vkIR.dstSubresource.aspectMask); - // Validate - MVKPixelFormats* pixFmts = cmdBuff->getPixelFormats(); - if ( !mvkAreAllFlagsEnabled(pixFmts->getCapabilities(_dstImage->getMTLPixelFormat()), kMVKMTLFmtCapsResolve) ) { - return reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdResolveImage(): %s cannot be used as a resolve destination on this device.", pixFmts->getName(_dstImage->getVkFormat())); - } + // Validate + MVKPixelFormats* pixFmts = cmdBuff->getPixelFormats(); + if ( !mvkAreAllFlagsEnabled(pixFmts->getCapabilities(_dstImage->getMTLPixelFormat(dstPlaneIndex)), kMVKMTLFmtCapsResolve) ) { + return reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdResolveImage(): %s cannot be used as a resolve destination on this device.", pixFmts->getName(_dstImage->getVkFormat())); + } + + _vkImageResolves.push_back(vkIR); + } return VK_SUCCESS; } @@ -457,10 +476,12 @@ void MVKCmdResolveImage::encode(MVKCommandEncoder* cmdEncoder) { uint32_t sliceCnt = 0; for (VkImageResolve& vkIR : _vkImageResolves) { + uint8_t srcPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(vkIR.srcSubresource.aspectMask); + uint8_t dstPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(vkIR.dstSubresource.aspectMask); uint32_t mipLvl = vkIR.dstSubresource.mipLevel; - VkExtent3D srcImgExt = _srcImage->getExtent3D(mipLvl); - VkExtent3D dstImgExt = _dstImage->getExtent3D(mipLvl); + VkExtent3D srcImgExt = _srcImage->getExtent3D(srcPlaneIndex, mipLvl); + VkExtent3D dstImgExt = _dstImage->getExtent3D(dstPlaneIndex, mipLvl); // If the region does not cover the entire content of the source level, expand the // destination content in the region to the temporary image. The purpose of this @@ -496,25 +517,23 @@ void MVKCmdResolveImage::encode(MVKCommandEncoder* cmdEncoder) { uint32_t layCnt = vkIR.dstSubresource.layerCount; for (uint32_t layIdx = 0; layIdx < layCnt; layIdx++) { MVKMetalResolveSlice& rslvSlice = mtlResolveSlices[sliceCnt++]; + rslvSlice.copyRegion = &cpyRgn; rslvSlice.level = vkIR.dstSubresource.mipLevel; rslvSlice.slice = baseLayer + layIdx; } } - id srcMTLTex; - if (expCnt == 0) { - // Expansion and copying is not required. Each mip level of the source image - // is being resolved entirely. Resolve directly from the source image. - srcMTLTex = _srcImage->getMTLTexture(); - - } else { + // Expansion and copying is not required. Each mip level of the source image + // is being resolved entirely. Resolve directly from the source image. + MVKImage* xfrImage = _srcImage; + if (expCnt) { // Expansion and copying is required. Acquire a temporary transfer image, expand // the destination image into it, copy from the source image to the temporary image, // and then resolve from the temporary image to the destination image. MVKImageDescriptorData xferImageData; _dstImage->getTransferDescriptorData(xferImageData); xferImageData.samples = _srcImage->getSampleCount(); - MVKImage* xfrImage = cmdEncoder->getCommandEncodingPool()->getTransferMVKImage(xferImageData); + xfrImage = cmdEncoder->getCommandEncodingPool()->getTransferMVKImage(xferImageData); // Expand the current content of the destination image to the temporary transfer image. MVKCmdBlitImage expCmd; @@ -530,23 +549,24 @@ void MVKCmdResolveImage::encode(MVKCommandEncoder* cmdEncoder) { (VkImage)xfrImage, _dstLayout, copyCnt, copyRegions); copyCmd.encode(cmdEncoder, kMVKCommandUseResolveCopyImage); - - srcMTLTex = xfrImage->getMTLTexture(); } cmdEncoder->endCurrentMetalEncoding(); MTLRenderPassDescriptor* mtlRPD = [MTLRenderPassDescriptor renderPassDescriptor]; - MTLRenderPassColorAttachmentDescriptor* mtlColorAttDesc = mtlRPD.colorAttachments[0]; - mtlColorAttDesc.loadAction = MTLLoadActionLoad; - mtlColorAttDesc.storeAction = MTLStoreActionMultisampleResolve; - mtlColorAttDesc.texture = srcMTLTex; - mtlColorAttDesc.resolveTexture = _dstImage->getMTLTexture(); + MTLRenderPassColorAttachmentDescriptor* mtlColorAttDesc = mtlRPD.colorAttachments[0]; + mtlColorAttDesc.loadAction = MTLLoadActionLoad; + mtlColorAttDesc.storeAction = MTLStoreActionMultisampleResolve; // For each resolve slice, update the render pass descriptor for // the texture level and slice and create a render encoder. for (uint32_t sIdx = 0; sIdx < sliceCnt; sIdx++) { MVKMetalResolveSlice& rslvSlice = mtlResolveSlices[sIdx]; + uint8_t srcPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(rslvSlice.copyRegion->srcSubresource.aspectMask); + uint8_t dstPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(rslvSlice.copyRegion->dstSubresource.aspectMask); + + mtlColorAttDesc.texture = xfrImage->getMTLTexture(srcPlaneIndex); + mtlColorAttDesc.resolveTexture = _dstImage->getMTLTexture(dstPlaneIndex); mtlColorAttDesc.level = rslvSlice.level; mtlColorAttDesc.slice = rslvSlice.slice; mtlColorAttDesc.resolveLevel = rslvSlice.level; @@ -675,12 +695,13 @@ VkResult MVKCmdBufferImageCopy::setContent(MVKCommandBuffer* cmdBuff, _bufferImageCopyRegions.reserve(regionCount); for (uint32_t i = 0; i < regionCount; i++) { _bufferImageCopyRegions.push_back(pRegions[i]); - } - - // Validate - if ( !_image->hasExpectedTexelSize() ) { - const char* cmdName = _toImage ? "vkCmdCopyBufferToImage" : "vkCmdCopyImageToBuffer"; - return reportError(VK_ERROR_FORMAT_NOT_SUPPORTED, "%s(): The image is using Metal format %s as a substitute for Vulkan format %s. Since the pixel size is different, content for the image cannot be copied to or from a buffer.", cmdName, cmdBuff->getPixelFormats()->getName(_image->getMTLPixelFormat()), cmdBuff->getPixelFormats()->getName(_image->getVkFormat())); + + // Validate + if ( !_image->hasExpectedTexelSize() ) { + MTLPixelFormat mtlPixFmt = _image->getMTLPixelFormat(MVKImage::getPlaneFromVkImageAspectFlags(pRegions[i].imageSubresource.aspectMask)); + const char* cmdName = _toImage ? "vkCmdCopyBufferToImage" : "vkCmdCopyImageToBuffer"; + return reportError(VK_ERROR_FORMAT_NOT_SUPPORTED, "%s(): The image is using Metal format %s as a substitute for Vulkan format %s. Since the pixel size is different, content for the image cannot be copied to or from a buffer.", cmdName, cmdBuff->getPixelFormats()->getName(mtlPixFmt), cmdBuff->getPixelFormats()->getName(_image->getVkFormat())); + } } return VK_SUCCESS; @@ -689,21 +710,23 @@ VkResult MVKCmdBufferImageCopy::setContent(MVKCommandBuffer* cmdBuff, template void MVKCmdBufferImageCopy::encode(MVKCommandEncoder* cmdEncoder) { id mtlBuffer = _buffer->getMTLBuffer(); - id mtlTexture = _image->getMTLTexture(); - if ( !mtlBuffer || !mtlTexture ) { return; } + if ( !mtlBuffer ) { return; } NSUInteger mtlBuffOffsetBase = _buffer->getMTLBufferOffset(); - MTLPixelFormat mtlPixFmt = _image->getMTLPixelFormat(); MVKCommandUse cmdUse = _toImage ? kMVKCommandUseCopyBufferToImage : kMVKCommandUseCopyImageToBuffer; MVKPixelFormats* pixFmts = cmdEncoder->getPixelFormats(); for (auto& cpyRgn : _bufferImageCopyRegions) { + uint8_t planeIndex = MVKImage::getPlaneFromVkImageAspectFlags(cpyRgn.imageSubresource.aspectMask); + MTLPixelFormat mtlPixFmt = _image->getMTLPixelFormat(planeIndex); + id mtlTexture = _image->getMTLTexture(planeIndex); + if ( !mtlTexture ) { continue; } uint32_t mipLevel = cpyRgn.imageSubresource.mipLevel; MTLOrigin mtlTxtOrigin = mvkMTLOriginFromVkOffset3D(cpyRgn.imageOffset); MTLSize mtlTxtSize = mvkClampMTLSize(mvkMTLSizeFromVkExtent3D(cpyRgn.imageExtent), mtlTxtOrigin, - mvkMTLSizeFromVkExtent3D(_image->getExtent3D(mipLevel))); + mvkMTLSizeFromVkExtent3D(_image->getExtent3D(planeIndex, mipLevel))); NSUInteger mtlBuffOffset = mtlBuffOffsetBase + cpyRgn.bufferOffset; uint32_t buffImgWd = cpyRgn.bufferRowLength; @@ -1075,29 +1098,31 @@ VkResult MVKCmdClearImage::setContent(MVKCommandBuffer* cmdBuff, // Add subresource ranges _subresourceRanges.clear(); // Clear for reuse _subresourceRanges.reserve(rangeCount); - for (uint32_t i = 0; i < rangeCount; i++) { - _subresourceRanges.push_back(pRanges[i]); - } + bool isDS = isDepthStencilClear(); + for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; rangeIdx++) { + auto& vkIR = pRanges[rangeIdx]; + uint8_t planeIndex = MVKImage::getPlaneFromVkImageAspectFlags(vkIR.aspectMask); - // Validate - bool isDS = isDepthStencilClear(); - if (_image->getImageType() == VK_IMAGE_TYPE_1D) { - return reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdClear%sImage(): Native 1D images cannot be cleared on this device. Consider enabling MVK_CONFIG_TEXTURE_1D_AS_2D.", (isDS ? "DepthStencil" : "Color")); - } - MVKMTLFmtCaps mtlFmtCaps = cmdBuff->getPixelFormats()->getCapabilities(_image->getMTLPixelFormat()); - if ((isDS && !mvkAreAllFlagsEnabled(mtlFmtCaps, kMVKMTLFmtCapsDSAtt)) || - ( !isDS && !mvkAreAllFlagsEnabled(mtlFmtCaps, kMVKMTLFmtCapsColorAtt))) { - return reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdClear%sImage(): Format %s cannot be cleared on this device.", (isDS ? "DepthStencil" : "Color"), cmdBuff->getPixelFormats()->getName(_image->getVkFormat())); - } + // Validate + MVKMTLFmtCaps mtlFmtCaps = cmdBuff->getPixelFormats()->getCapabilities(_image->getMTLPixelFormat(planeIndex)); + if ((isDS && !mvkAreAllFlagsEnabled(mtlFmtCaps, kMVKMTLFmtCapsDSAtt)) || + ( !isDS && !mvkAreAllFlagsEnabled(mtlFmtCaps, kMVKMTLFmtCapsColorAtt))) { + return reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdClear%sImage(): Format %s cannot be cleared on this device.", (isDS ? "DepthStencil" : "Color"), cmdBuff->getPixelFormats()->getName(_image->getVkFormat())); + } + + _subresourceRanges.push_back(vkIR); + } + + // Validate + if (_image->getImageType() == VK_IMAGE_TYPE_1D) { + return reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdClear%sImage(): Native 1D images cannot be cleared on this device. Consider enabling MVK_CONFIG_TEXTURE_1D_AS_2D.", (isDS ? "DepthStencil" : "Color")); + } return VK_SUCCESS; } template void MVKCmdClearImage::encode(MVKCommandEncoder* cmdEncoder) { - id imgMTLTex = _image->getMTLTexture(); - if ( !imgMTLTex ) { return; } - bool isDS = isDepthStencilClear(); NSString* mtlRendEncName = (isDS ? mvkMTLRenderCommandEncoderLabel(kMVKCommandUseClearDepthStencilImage) @@ -1107,6 +1132,8 @@ void MVKCmdClearImage::encode(MVKCommandEncoder* cmdEncoder) { MVKPixelFormats* pixFmts = cmdEncoder->getPixelFormats(); for (auto& srRange : _subresourceRanges) { + id imgMTLTex = _image->getMTLTexture(MVKImage::getPlaneFromVkImageAspectFlags(srRange.aspectMask)); + if ( !imgMTLTex ) { continue; } MTLRenderPassDescriptor* mtlRPDesc = [MTLRenderPassDescriptor renderPassDescriptor]; MTLRenderPassColorAttachmentDescriptor* mtlRPCADesc = nil; diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm index 07a2dcf6..a92b86e1 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm +++ b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm @@ -369,7 +369,7 @@ MVKImage* MVKCommandResourceFactory::newMVKImage(MVKImageDescriptorData& imgData .initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED }; MVKImage* mvkImg = _device->createImage(&createInfo, nullptr); - mvkImg->bindDeviceMemory(_transferImageMemory, 0); + mvkImg->bindDeviceMemory(_transferImageMemory, 0, 0); return mvkImg; } diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h index f920826e..04f1427d 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h @@ -40,10 +40,10 @@ public: #pragma mark Resource memory /** Returns the memory requirements of this resource by populating the specified structure. */ - VkResult getMemoryRequirements(VkMemoryRequirements* pMemoryRequirements) override; + VkResult getMemoryRequirements(VkMemoryRequirements* pMemoryRequirements); /** Returns the memory requirements of this resource by populating the specified structure. */ - VkResult getMemoryRequirements(const void* pInfo, VkMemoryRequirements2* pMemoryRequirements) override; + VkResult getMemoryRequirements(const void* pInfo, VkMemoryRequirements2* pMemoryRequirements); /** Binds this resource to the specified offset within the specified memory allocation. */ VkResult bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset) override; @@ -86,7 +86,6 @@ public: protected: friend class MVKDeviceMemory; - using MVKResource::needsHostReadSync; void propagateDebugName() override; bool needsHostReadSync(VkPipelineStageFlags srcStageMask, diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.h b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.h index 65131c77..e283a737 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.h @@ -71,7 +71,7 @@ public: inline uint32_t getBinding() { return _info.binding; } /** Returns the number of descriptors in this layout. */ - inline uint32_t getDescriptorCount() { return _info.descriptorCount; } + inline uint32_t getDescriptorCount() { return (_info.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT) ? 1 : _info.descriptorCount; } /** Returns the descriptor type of this layout. */ inline VkDescriptorType getDescriptorType() { return _info.descriptorType; } @@ -115,6 +115,7 @@ public: ~MVKDescriptorSetLayoutBinding() override; protected: + friend class MVKInlineUniformBlockDescriptor; void initMetalResourceIndexOffsets(MVKShaderStageResourceBinding* pBindingIndexes, MVKShaderStageResourceBinding* pDescSetCounts, const VkDescriptorSetLayoutBinding* pBinding); @@ -152,11 +153,12 @@ public: /** * Updates the internal binding from the specified content. The format of the content depends - * on the descriptor type, and is extracted from pData at the location given by srcIndex * stride. + * on the descriptor type, and is extracted from pData at the location given by index * stride. + * MVKInlineUniformBlockDescriptor uses the index as byte offset to write to. */ virtual void write(MVKDescriptorSet* mvkDescSet, VkDescriptorType descriptorType, - uint32_t srcIndex, + uint32_t index, size_t stride, const void* pData) = 0; @@ -167,12 +169,13 @@ public: * specified pImageInfo, pBufferInfo, or pTexelBufferView arrays, and the other * arrays are ignored (and may be a null pointer). * - * The dstIndex parameter indicates the index of the initial descriptor element + * The index parameter indicates the index of the initial descriptor element * at which to start writing. + * MVKInlineUniformBlockDescriptor uses the index as byte offset to read from. */ virtual void read(MVKDescriptorSet* mvkDescSet, VkDescriptorType descriptorType, - uint32_t dstIndex, + uint32_t index, VkDescriptorImageInfo* pImageInfo, VkDescriptorBufferInfo* pBufferInfo, VkBufferView* pTexelBufferView, @@ -284,25 +287,27 @@ public: void write(MVKDescriptorSet* mvkDescSet, VkDescriptorType descriptorType, - uint32_t srcIndex, + uint32_t dstOffset, // For inline buffers we are using this parameter as dst offset not as src descIdx size_t stride, const void* pData) override; void read(MVKDescriptorSet* mvkDescSet, VkDescriptorType descriptorType, - uint32_t dstIndex, + uint32_t srcOffset, // For inline buffers we are using this parameter as src offset not as dst descIdx VkDescriptorImageInfo* pImageInfo, VkDescriptorBufferInfo* pBufferInfo, VkBufferView* pTexelBufferView, VkWriteDescriptorSetInlineUniformBlockEXT* inlineUniformBlock) override; + + void setLayout(MVKDescriptorSetLayoutBinding* dslBinding, uint32_t index) override; void reset() override; ~MVKInlineUniformBlockDescriptor() { reset(); } protected: - id _mtlBuffer = nil; - uint32_t _dataSize = 0; + uint8_t* _buffer = nullptr; + uint32_t _length; }; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.mm index bd556de6..96dc03b7 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.mm @@ -89,13 +89,12 @@ uint32_t MVKDescriptorSetLayoutBinding::bind(MVKCommandEncoder* cmdEncoder, // Establish the resource indices to use, by combining the offsets of the DSL and this DSL binding. MVKShaderResourceBinding mtlIdxs = _mtlResourceIndexOffsets + dslMTLRezIdxOffsets; - uint32_t descCnt = _info.descriptorCount; + uint32_t descCnt = getDescriptorCount(); for (uint32_t descIdx = 0; descIdx < descCnt; descIdx++) { - MVKDescriptor* mvkDesc = descSet->getDescriptor(descStartIndex + descIdx); - mvkDesc->bind(cmdEncoder, _info.descriptorType, descIdx, _applyToStage, - mtlIdxs, dynamicOffsets, pDynamicOffsetIndex); + MVKDescriptor* mvkDesc = descSet->getDescriptor(descStartIndex + descIdx); + mvkDesc->bind(cmdEncoder, _info.descriptorType, descIdx, _applyToStage, mtlIdxs, dynamicOffsets, pDynamicOffsetIndex); } - return descCnt; + return descCnt; } template @@ -167,13 +166,13 @@ void MVKDescriptorSetLayoutBinding::push(MVKCommandEncoder* cmdEncoder, } case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: { - const auto& inlineUniformBlock = get(pData, stride, rezIdx - dstArrayElement); + const auto& inlineUniformBlock = *(VkWriteDescriptorSetInlineUniformBlockEXT*)pData; bb.mtlBytes = inlineUniformBlock.pData; bb.size = inlineUniformBlock.dataSize; bb.isInline = true; for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { if (_applyToStage[i]) { - bb.index = mtlIdxs.stages[i].bufferIndex + rezIdx; + bb.index = mtlIdxs.stages[i].bufferIndex; if (i == kMVKShaderStageCompute) { if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindBuffer(bb); } } else { @@ -189,33 +188,32 @@ void MVKDescriptorSetLayoutBinding::push(MVKCommandEncoder* cmdEncoder, case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { const auto& imageInfo = get(pData, stride, rezIdx - dstArrayElement); MVKImageView* imageView = (MVKImageView*)imageInfo.imageView; - tb.mtlTexture = imageView->getMTLTexture(); - if (_info.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE) { - tb.swizzle = imageView->getPackedSwizzle(); - } else { - tb.swizzle = 0; - } - if (_info.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) { - id mtlTex = tb.mtlTexture; - if (mtlTex.parentTexture) { mtlTex = mtlTex.parentTexture; } - bb.mtlBuffer = mtlTex.buffer; - bb.offset = mtlTex.bufferOffset; - bb.size = (uint32_t)(mtlTex.height * mtlTex.bufferBytesPerRow); - } - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { - if (_applyToStage[i]) { - tb.index = mtlIdxs.stages[i].textureIndex + rezIdx; - if (i == kMVKShaderStageCompute) { - if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } - } else { - if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } - } - if (_info.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) { - bb.index = mtlIdxs.stages[i].bufferIndex + rezIdx; + uint8_t planeCount = (imageView) ? imageView->getPlaneCount() : 1; + for (uint8_t planeIndex = 0; planeIndex < planeCount; planeIndex++) { + tb.mtlTexture = imageView->getMTLTexture(planeIndex); + tb.swizzle = (_info.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE) ? imageView->getPackedSwizzle() : 0; + if (_info.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) { + id mtlTex = tb.mtlTexture; + if (mtlTex.parentTexture) { mtlTex = mtlTex.parentTexture; } + bb.mtlBuffer = mtlTex.buffer; + bb.offset = mtlTex.bufferOffset; + bb.size = (uint32_t)(mtlTex.height * mtlTex.bufferBytesPerRow); + } + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { + if (_applyToStage[i]) { + tb.index = mtlIdxs.stages[i].textureIndex + rezIdx + planeIndex; if (i == kMVKShaderStageCompute) { - if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindBuffer(bb); } + if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } } else { - if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindBuffer(MVKShaderStage(i), bb); } + if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } + } + if (_info.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) { + bb.index = mtlIdxs.stages[i].bufferIndex + rezIdx; + if (i == kMVKShaderStageCompute) { + if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindBuffer(bb); } + } else { + if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindBuffer(MVKShaderStage(i), bb); } + } } } } @@ -280,30 +278,29 @@ void MVKDescriptorSetLayoutBinding::push(MVKCommandEncoder* cmdEncoder, case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { const auto& imageInfo = get(pData, stride, rezIdx - dstArrayElement); MVKImageView* imageView = (MVKImageView*)imageInfo.imageView; - tb.mtlTexture = imageView->getMTLTexture(); - if (imageView) { - tb.swizzle = imageView->getPackedSwizzle(); - } else { - tb.swizzle = 0; - } - MVKSampler* sampler; - if (_immutableSamplers.empty()) { - sampler = (MVKSampler*)imageInfo.sampler; - validate(sampler); - } else { - sampler = _immutableSamplers[rezIdx]; - } - sb.mtlSamplerState = sampler->getMTLSamplerState(); - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { - if (_applyToStage[i]) { - tb.index = mtlIdxs.stages[i].textureIndex + rezIdx; - sb.index = mtlIdxs.stages[i].samplerIndex + rezIdx; - if (i == kMVKShaderStageCompute) { - if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } - if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindSamplerState(sb); } - } else { - if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } - if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindSamplerState(MVKShaderStage(i), sb); } + uint8_t planeCount = (imageView) ? imageView->getPlaneCount() : 1; + for (uint8_t planeIndex = 0; planeIndex < planeCount; planeIndex++) { + tb.mtlTexture = imageView->getMTLTexture(planeIndex); + tb.swizzle = (imageView) ? imageView->getPackedSwizzle() : 0; + MVKSampler* sampler; + if (_immutableSamplers.empty()) { + sampler = (MVKSampler*)imageInfo.sampler; + validate(sampler); + } else { + sampler = _immutableSamplers[rezIdx]; + } + sb.mtlSamplerState = sampler->getMTLSamplerState(); + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { + if (_applyToStage[i]) { + tb.index = mtlIdxs.stages[i].textureIndex + rezIdx + planeIndex; + sb.index = mtlIdxs.stages[i].samplerIndex + rezIdx; + if (i == kMVKShaderStageCompute) { + if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } + if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindSamplerState(sb); } + } else { + if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } + if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindSamplerState(MVKShaderStage(i), sb); } + } } } } @@ -327,7 +324,7 @@ void MVKDescriptorSetLayoutBinding::push(MVKCommandEncoder* cmdEncoder, // If depth compare is required, but unavailable on the device, the sampler can only be used as an immutable sampler bool MVKDescriptorSetLayoutBinding::validate(MVKSampler* mvkSampler) { if (mvkSampler->getRequiresConstExprSampler()) { - mvkSampler->reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdPushDescriptorSet/vkCmdPushDescriptorSetWithTemplate(): Depth texture samplers using a compare operation can only be used as immutable samplers on this device."); + mvkSampler->reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdPushDescriptorSet/vkCmdPushDescriptorSetWithTemplate(): Tried to push an immutable sampler."); return false; } return true; @@ -437,7 +434,19 @@ void MVKDescriptorSetLayoutBinding::initMetalResourceIndexOffsets(MVKShaderStage if ( !_device->_pMetalFeatures->arrayOfSamplers ) { _layout->setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "Device %s does not support arrays of samplers.", _device->getName())); } + if ( pBinding->pImmutableSamplers ) { + _layout->setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "Sampler arrays contaning multi planar samplers are not supported.")); + } } + + if ( pBinding->pImmutableSamplers ) { + for (uint32_t i = 0; i < pBinding->descriptorCount; i++) { + uint8_t planeCount = ((MVKSampler*)pBinding->pImmutableSamplers[i])->getPlaneCount(); + if (planeCount > 1) { + pDescSetCounts->textureIndex += planeCount - 1; + } + } + } break; case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: @@ -460,11 +469,15 @@ void MVKDescriptorSetLayoutBinding::initMetalResourceIndexOffsets(MVKShaderStage case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: - case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: pBindingIndexes->bufferIndex = pDescSetCounts->bufferIndex; pDescSetCounts->bufferIndex += pBinding->descriptorCount; break; + case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: + pBindingIndexes->bufferIndex = pDescSetCounts->bufferIndex; + pDescSetCounts->bufferIndex += 1; + break; + default: break; } @@ -592,21 +605,21 @@ void MVKInlineUniformBlockDescriptor::bind(MVKCommandEncoder* cmdEncoder, MVKArrayRef dynamicOffsets, uint32_t* pDynamicOffsetIndex) { MVKMTLBufferBinding bb; - switch (descriptorType) { case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: { - bb.mtlBuffer = _mtlBuffer; - bb.size = _dataSize; - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { - if (stages[i]) { - bb.index = mtlIndexes.stages[i].bufferIndex + descriptorIndex; - if (i == kMVKShaderStageCompute) { - if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindBuffer(bb); } - } else { - if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindBuffer(MVKShaderStage(i), bb); } - } - } - } + bb.mtlBytes = _buffer; + bb.size = _length; + bb.isInline = true; + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { + if (stages[i]) { + bb.index = mtlIndexes.stages[i].bufferIndex; + if (i == kMVKShaderStageCompute) { + if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindBuffer(bb); } + } else { + if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindBuffer(MVKShaderStage(i), bb); } + } + } + } break; } @@ -616,25 +629,16 @@ void MVKInlineUniformBlockDescriptor::bind(MVKCommandEncoder* cmdEncoder, } void MVKInlineUniformBlockDescriptor::write(MVKDescriptorSet* mvkDescSet, - VkDescriptorType descriptorType, - uint32_t srcIndex, - size_t stride, - const void* pData) { + VkDescriptorType descriptorType, + uint32_t dstOffset, + size_t stride, + const void* pData) { switch (descriptorType) { case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: { - const auto& srcInlineUniformBlock = get(pData, stride, srcIndex); - _dataSize = srcInlineUniformBlock.dataSize; - - [_mtlBuffer release]; - if (srcInlineUniformBlock.dataSize > 0) { - MTLResourceOptions mtlBuffOpts = MTLResourceStorageModeShared | MTLResourceCPUCacheModeDefaultCache; - _mtlBuffer = [mvkDescSet->getMTLDevice() newBufferWithBytes: srcInlineUniformBlock.pData - length: srcInlineUniformBlock.dataSize - options:mtlBuffOpts]; // retained - } else { - _mtlBuffer = nil; - } - + const auto& pInlineUniformBlock = *(VkWriteDescriptorSetInlineUniformBlockEXT*)pData; + if (pInlineUniformBlock.pData && _buffer) { + memcpy(_buffer + dstOffset, pInlineUniformBlock.pData, pInlineUniformBlock.dataSize); + } break; } @@ -644,23 +648,17 @@ void MVKInlineUniformBlockDescriptor::write(MVKDescriptorSet* mvkDescSet, } void MVKInlineUniformBlockDescriptor::read(MVKDescriptorSet* mvkDescSet, - VkDescriptorType descriptorType, - uint32_t dstIndex, - VkDescriptorImageInfo* pImageInfo, - VkDescriptorBufferInfo* pBufferInfo, - VkBufferView* pTexelBufferView, - VkWriteDescriptorSetInlineUniformBlockEXT* pInlineUniformBlock) { + VkDescriptorType descriptorType, + uint32_t srcOffset, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView, + VkWriteDescriptorSetInlineUniformBlockEXT* pInlineUniformBlock) { switch (descriptorType) { case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: { - auto& dstInlineUniformBlock = pInlineUniformBlock[dstIndex]; - void* pDstData = const_cast(dstInlineUniformBlock.pData); - void* pSrcData = _mtlBuffer.contents; - if (pSrcData && pDstData) { - memcpy(pDstData, pSrcData, _dataSize); - dstInlineUniformBlock.dataSize = _dataSize; - } else { - dstInlineUniformBlock.dataSize = 0; - } + if (_buffer && pInlineUniformBlock->pData) { + memcpy((void*)pInlineUniformBlock->pData, _buffer + srcOffset, pInlineUniformBlock->dataSize); + } break; } @@ -669,10 +667,15 @@ void MVKInlineUniformBlockDescriptor::read(MVKDescriptorSet* mvkDescSet, } } +void MVKInlineUniformBlockDescriptor::setLayout(MVKDescriptorSetLayoutBinding* dslBinding, uint32_t index) { + _length = dslBinding->_info.descriptorCount; + _buffer = (uint8_t*)malloc(_length); +} + void MVKInlineUniformBlockDescriptor::reset() { - [_mtlBuffer release]; - _mtlBuffer = nil; - _dataSize = 0; + free(_buffer); + _buffer = nullptr; + _length = 0; MVKDescriptor::reset(); } @@ -688,53 +691,54 @@ void MVKImageDescriptor::bind(MVKCommandEncoder* cmdEncoder, MVKShaderResourceBinding& mtlIndexes, MVKArrayRef dynamicOffsets, uint32_t* pDynamicOffsetIndex) { - MVKMTLTextureBinding tb; - MVKMTLBufferBinding bb; switch (descriptorType) { case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: - case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { - if (_mvkImageView) { - tb.mtlTexture = _mvkImageView->getMTLTexture(); - } - if ((descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) && - tb.mtlTexture) { - tb.swizzle = _mvkImageView->getPackedSwizzle(); - } else { - tb.swizzle = 0; - } - if (descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE && tb.mtlTexture) { - id mtlTex = tb.mtlTexture; - if (mtlTex.parentTexture) { mtlTex = mtlTex.parentTexture; } - bb.mtlBuffer = mtlTex.buffer; - bb.offset = mtlTex.bufferOffset; - bb.size = (uint32_t)(mtlTex.height * mtlTex.bufferBytesPerRow); - } - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { - if (stages[i]) { - tb.index = mtlIndexes.stages[i].textureIndex + descriptorIndex; - if (i == kMVKShaderStageCompute) { - if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } - } else { - if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } - } - if (descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) { - bb.index = mtlIndexes.stages[i].bufferIndex + descriptorIndex; - if (i == kMVKShaderStageCompute) { - if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindBuffer(bb); } - } else { - if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindBuffer(MVKShaderStage(i), bb); } - } - } - } - } + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: break; - } default: - break; + return; } + + uint8_t planeCount = (_mvkImageView) ? _mvkImageView->getPlaneCount() : 1; + for (uint8_t planeIndex = 0; planeIndex < planeCount; planeIndex++) { + MVKMTLTextureBinding tb; + MVKMTLBufferBinding bb; + + if (_mvkImageView) { + tb.mtlTexture = _mvkImageView->getMTLTexture(planeIndex); + } + tb.swizzle = ((descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || + descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) && + tb.mtlTexture) ? _mvkImageView->getPackedSwizzle() : 0; + if (descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE && tb.mtlTexture) { + id mtlTex = tb.mtlTexture; + if (mtlTex.parentTexture) { mtlTex = mtlTex.parentTexture; } + bb.mtlBuffer = mtlTex.buffer; + bb.offset = mtlTex.bufferOffset; + bb.size = (uint32_t)(mtlTex.height * mtlTex.bufferBytesPerRow); + } + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { + if (stages[i]) { + tb.index = mtlIndexes.stages[i].textureIndex + descriptorIndex + planeIndex; + if (i == kMVKShaderStageCompute) { + if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } + } else { + if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } + } + if (descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) { + bb.index = mtlIndexes.stages[i].bufferIndex + descriptorIndex + planeIndex; + if (i == kMVKShaderStageCompute) { + if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindBuffer(bb); } + } else { + if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindBuffer(MVKShaderStage(i), bb); } + } + } + } + } + } } void MVKImageDescriptor::write(MVKDescriptorSet* mvkDescSet, @@ -845,7 +849,7 @@ void MVKSamplerDescriptorMixin::write(MVKDescriptorSet* mvkDescSet, const auto* pImgInfo = &get(pData, stride, srcIndex); _mvkSampler = (MVKSampler*)pImgInfo->sampler; if (_mvkSampler && _mvkSampler->getRequiresConstExprSampler()) { - _mvkSampler->reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkUpdateDescriptorSets(): Depth texture samplers using a compare operation can only be used as immutable samplers on this device."); + _mvkSampler->reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkUpdateDescriptorSets(): Tried to push an immutable sampler."); } if (_mvkSampler) { _mvkSampler->retain(); } diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm index 32decbd3..de5a9f36 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm @@ -209,12 +209,18 @@ void MVKDescriptorSet::write(const DescriptorAction* pDescriptorAction, const void* pData) { VkDescriptorType descType = getDescriptorType(pDescriptorAction->dstBinding); - uint32_t dstStartIdx = _layout->getDescriptorIndex(pDescriptorAction->dstBinding, - pDescriptorAction->dstArrayElement); uint32_t descCnt = pDescriptorAction->descriptorCount; - for (uint32_t descIdx = 0; descIdx < descCnt; descIdx++) { - _descriptors[dstStartIdx + descIdx]->write(this, descType, descIdx, stride, pData); - } + if (descType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT) { + uint32_t dstStartIdx = _layout->getDescriptorIndex(pDescriptorAction->dstBinding, 0); + // For inline buffers we are using the index argument as dst offset not as src descIdx + _descriptors[dstStartIdx]->write(this, descType, pDescriptorAction->dstArrayElement, stride, pData); + } else { + uint32_t dstStartIdx = _layout->getDescriptorIndex(pDescriptorAction->dstBinding, + pDescriptorAction->dstArrayElement); + for (uint32_t descIdx = 0; descIdx < descCnt; descIdx++) { + _descriptors[dstStartIdx + descIdx]->write(this, descType, descIdx, stride, pData); + } + } } // Create concrete implementations of the three variations of the write() function. @@ -232,13 +238,19 @@ void MVKDescriptorSet::read(const VkCopyDescriptorSet* pDescriptorCopy, VkWriteDescriptorSetInlineUniformBlockEXT* pInlineUniformBlock) { VkDescriptorType descType = getDescriptorType(pDescriptorCopy->srcBinding); - uint32_t srcStartIdx = _layout->getDescriptorIndex(pDescriptorCopy->srcBinding, - pDescriptorCopy->srcArrayElement); uint32_t descCnt = pDescriptorCopy->descriptorCount; - for (uint32_t descIdx = 0; descIdx < descCnt; descIdx++) { - _descriptors[srcStartIdx + descIdx]->read(this, descType, descIdx, pImageInfo, pBufferInfo, - pTexelBufferView, pInlineUniformBlock); - } + if (descType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT) { + pInlineUniformBlock->dataSize = pDescriptorCopy->descriptorCount; + uint32_t srcStartIdx = _layout->getDescriptorIndex(pDescriptorCopy->srcBinding, 0); + // For inline buffers we are using the index argument as src offset not as dst descIdx + _descriptors[srcStartIdx]->read(this, descType, pDescriptorCopy->srcArrayElement, pImageInfo, pBufferInfo, pTexelBufferView, pInlineUniformBlock); + } else { + uint32_t srcStartIdx = _layout->getDescriptorIndex(pDescriptorCopy->srcBinding, + pDescriptorCopy->srcArrayElement); + for (uint32_t descIdx = 0; descIdx < descCnt; descIdx++) { + _descriptors[srcStartIdx + descIdx]->read(this, descType, descIdx, pImageInfo, pBufferInfo, pTexelBufferView, pInlineUniformBlock); + } + } } // If the descriptor pool fails to allocate a descriptor, record a configuration error @@ -249,15 +261,23 @@ MVKDescriptorSet::MVKDescriptorSet(MVKDescriptorSetLayout* layout, MVKDescriptor uint32_t bindCnt = (uint32_t)layout->_bindings.size(); for (uint32_t bindIdx = 0; bindIdx < bindCnt; bindIdx++) { MVKDescriptorSetLayoutBinding* mvkDSLBind = &layout->_bindings[bindIdx]; - uint32_t descCnt = mvkDSLBind->getDescriptorCount(); - for (uint32_t descIdx = 0; descIdx < descCnt; descIdx++) { - MVKDescriptor* mvkDesc = nullptr; - setConfigurationResult(_pool->allocateDescriptor(mvkDSLBind->getDescriptorType(), &mvkDesc)); - if ( !wasConfigurationSuccessful() ) { break; } + MVKDescriptor* mvkDesc = nullptr; + if (mvkDSLBind->getDescriptorType() == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT) { + setConfigurationResult(_pool->allocateDescriptor(mvkDSLBind->getDescriptorType(), &mvkDesc)); + if ( !wasConfigurationSuccessful() ) { break; } - mvkDesc->setLayout(mvkDSLBind, descIdx); - _descriptors.push_back(mvkDesc); - } + mvkDesc->setLayout(mvkDSLBind, 0); + _descriptors.push_back(mvkDesc); + } else { + uint32_t descCnt = mvkDSLBind->getDescriptorCount(); + for (uint32_t descIdx = 0; descIdx < descCnt; descIdx++) { + setConfigurationResult(_pool->allocateDescriptor(mvkDSLBind->getDescriptorType(), &mvkDesc)); + if ( !wasConfigurationSuccessful() ) { break; } + + mvkDesc->setLayout(mvkDSLBind, descIdx); + _descriptors.push_back(mvkDesc); + } + } if ( !wasConfigurationSuccessful() ) { break; } } } diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h index abfb94d6..760bc93d 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h @@ -57,6 +57,7 @@ class MVKPipelineCache; class MVKPipelineLayout; class MVKPipeline; class MVKSampler; +class MVKSamplerYcbcrConversion; class MVKDescriptorSetLayout; class MVKDescriptorPool; class MVKDescriptorUpdateTemplate; @@ -521,6 +522,11 @@ public: void destroySampler(MVKSampler* mvkSamp, const VkAllocationCallbacks* pAllocator); + MVKSamplerYcbcrConversion* createSamplerYcbcrConversion(const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + void destroySamplerYcbcrConversion(MVKSamplerYcbcrConversion* mvkSampConv, + const VkAllocationCallbacks* pAllocator); + MVKDescriptorSetLayout* createDescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator); void destroyDescriptorSetLayout(MVKDescriptorSetLayout* mvkDSL, @@ -652,6 +658,7 @@ public: const VkPhysicalDeviceVariablePointerFeatures _enabledVarPtrFeatures; const VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT _enabledInterlockFeatures; const VkPhysicalDeviceHostQueryResetFeaturesEXT _enabledHostQryResetFeatures; + const VkPhysicalDeviceSamplerYcbcrConversionFeatures _enabledSamplerYcbcrConversionFeatures; const VkPhysicalDeviceScalarBlockLayoutFeaturesEXT _enabledScalarLayoutFeatures; const VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT _enabledTexelBuffAlignFeatures; const VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT _enabledVtxAttrDivFeatures; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm index 16e8fe7d..94bed76a 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm @@ -114,6 +114,13 @@ void MVKPhysicalDevice::getFeatures(VkPhysicalDeviceFeatures2* features) { hostQueryResetFeatures->hostQueryReset = true; break; } + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT: { + auto* robustness2Features = (VkPhysicalDeviceRobustness2FeaturesEXT*)next; + robustness2Features->robustBufferAccess2 = false; + robustness2Features->robustImageAccess2 = true; + robustness2Features->nullDescriptor = false; + break; + } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT: { auto* scalarLayoutFeatures = (VkPhysicalDeviceScalarBlockLayoutFeaturesEXT*)next; scalarLayoutFeatures->scalarBlockLayout = true; @@ -139,6 +146,11 @@ void MVKPhysicalDevice::getFeatures(VkPhysicalDeviceFeatures2* features) { portabilityFeatures->samplerMipLodBias = false; break; } + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES: { + auto* samplerYcbcrConvFeatures = (VkPhysicalDeviceSamplerYcbcrConversionFeatures*)next; + samplerYcbcrConvFeatures->samplerYcbcrConversion = true; + break; + } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_FUNCTIONS_2_FEATURES_INTEL: { auto* shaderIntFuncsFeatures = (VkPhysicalDeviceShaderIntegerFunctions2FeaturesINTEL*)next; shaderIntFuncsFeatures->shaderIntegerFunctions2 = true; @@ -181,6 +193,14 @@ void MVKPhysicalDevice::getProperties(VkPhysicalDeviceProperties2* properties) { pushDescProps->maxPushDescriptors = _properties.limits.maxPerStageResources; break; } + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT: { + auto* robustness2Props = (VkPhysicalDeviceRobustness2PropertiesEXT*)next; + // This isn't implemented yet, but when it is, I expect that we'll wind up + // doing it manually. + robustness2Props->robustStorageBufferAccessSizeAlignment = 1; + robustness2Props->robustUniformBufferAccessSizeAlignment = 1; + 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 @@ -204,6 +224,15 @@ void MVKPhysicalDevice::getProperties(VkPhysicalDeviceProperties2* properties) { portabilityProps->minVertexInputBindingStrideAlignment = 4; break; } + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES_EXT: { + auto* inlineUniformBlockProps = (VkPhysicalDeviceInlineUniformBlockPropertiesEXT*)next; + inlineUniformBlockProps->maxInlineUniformBlockSize = _metalFeatures.dynamicMTLBufferSize; + inlineUniformBlockProps->maxPerStageDescriptorInlineUniformBlocks = _properties.limits.maxPerStageDescriptorUniformBuffers; + inlineUniformBlockProps->maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks = _properties.limits.maxPerStageDescriptorUniformBuffers; + inlineUniformBlockProps->maxDescriptorSetInlineUniformBlocks = _properties.limits.maxDescriptorSetUniformBuffers; + inlineUniformBlockProps->maxDescriptorSetUpdateAfterBindInlineUniformBlocks = _properties.limits.maxDescriptorSetUniformBuffers; + break; + } default: break; } @@ -429,6 +458,18 @@ VkResult MVKPhysicalDevice::getImageFormatProperties(const VkPhysicalDeviceImage } } + for (const auto* nextProps = (VkBaseInStructure*)pImageFormatProperties->pNext; nextProps; nextProps = nextProps->pNext) { + switch (nextProps->sType) { + case VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES: { + auto* samplerYcbcrConvProps = (VkSamplerYcbcrConversionImageFormatProperties*)nextProps; + samplerYcbcrConvProps->combinedImageSamplerDescriptorCount = _pixelFormats.getChromaSubsamplingPlaneCount(pImageFormatInfo->format); + break; + } + default: + break; + } + } + if ( !_pixelFormats.isSupported(pImageFormatInfo->format) ) { return VK_ERROR_FORMAT_NOT_SUPPORTED; } if ( !getImageViewIsSupported(pImageFormatInfo) ) { return VK_ERROR_FORMAT_NOT_SUPPORTED; } @@ -462,9 +503,9 @@ bool MVKPhysicalDevice::getImageViewIsSupported(const VkPhysicalDeviceImageForma .baseArrayLayer = 0, .layerCount = 1}, }; - MTLPixelFormat mtlPixFmt; + MTLPixelFormat mtlPixFmt = _pixelFormats.getMTLPixelFormat(viewInfo.format); bool useSwizzle; - return (MVKImageView::validateSwizzledMTLPixelFormat(&viewInfo, &_pixelFormats, this, + return (MVKImageView::validateSwizzledMTLPixelFormat(&viewInfo, this, _metalFeatures.nativeTextureSwizzle, _mvkInstance->getMoltenVKConfiguration()->fullImageViewSwizzle, mtlPixFmt, useSwizzle) == VK_SUCCESS); @@ -1356,6 +1397,7 @@ void MVKPhysicalDevice::initProperties() { uint32_t maxStorage = 0, maxUniform = 0; bool singleTexelStorage = true, singleTexelUniform = true; _pixelFormats.enumerateSupportedFormats({0, 0, VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT}, true, [&](VkFormat vk) { + if ( _pixelFormats.getChromaSubsamplingComponentBits(vk) > 0 ) { return false; } // Skip chroma subsampling formats MTLPixelFormat mtlFmt = _pixelFormats.getMTLPixelFormat(vk); if ( !mtlFmt ) { return false; } // If format is invalid, avoid validation errors on MTLDevice format alignment calls @@ -1535,7 +1577,7 @@ void MVKPhysicalDevice::initProperties() { _properties.limits.maxComputeWorkGroupCount[1] = kMVKUndefinedLargeUInt32; _properties.limits.maxComputeWorkGroupCount[2] = kMVKUndefinedLargeUInt32; - _properties.limits.maxDrawIndexedIndexValue = numeric_limits::max(); + _properties.limits.maxDrawIndexedIndexValue = numeric_limits::max(); // Must be (2^32 - 1) to support fullDrawIndexUint32 _properties.limits.maxDrawIndirectCount = kMVKUndefinedLargeUInt32; _properties.limits.maxClipDistances = kMVKUndefinedLargeUInt32; @@ -2314,16 +2356,21 @@ MVKImage* MVKDevice::createImage(const VkImageCreateInfo* pCreateInfo, break; } } - if (swapchainInfo) { - return (MVKImage*)addResource(new MVKPeerSwapchainImage(this, pCreateInfo, (MVKSwapchain*)swapchainInfo->swapchain, uint32_t(-1))); - } - return (MVKImage*)addResource(new MVKImage(this, pCreateInfo)); + MVKImage* mvkImg = (swapchainInfo) + ? new MVKPeerSwapchainImage(this, pCreateInfo, (MVKSwapchain*)swapchainInfo->swapchain, uint32_t(-1)) + : new MVKImage(this, pCreateInfo); + for (auto& memoryBinding : mvkImg->_memoryBindings) { + addResource(memoryBinding); + } + return mvkImg; } void MVKDevice::destroyImage(MVKImage* mvkImg, const VkAllocationCallbacks* pAllocator) { if (mvkImg) { - removeResource(mvkImg); + for (auto& memoryBinding : mvkImg->_memoryBindings) { + removeResource(memoryBinding); + } mvkImg->destroy(); } } @@ -2352,13 +2399,19 @@ MVKPresentableSwapchainImage* MVKDevice::createPresentableSwapchainImage(const V MVKSwapchain* swapchain, uint32_t swapchainIndex, const VkAllocationCallbacks* pAllocator) { - return (MVKPresentableSwapchainImage*)addResource(new MVKPresentableSwapchainImage(this, pCreateInfo, swapchain, swapchainIndex)); + MVKPresentableSwapchainImage* mvkImg = new MVKPresentableSwapchainImage(this, pCreateInfo, swapchain, swapchainIndex); + for (auto& memoryBinding : mvkImg->_memoryBindings) { + addResource(memoryBinding); + } + return mvkImg; } void MVKDevice::destroyPresentableSwapchainImage(MVKPresentableSwapchainImage* mvkImg, const VkAllocationCallbacks* pAllocator) { if (mvkImg) { - removeResource(mvkImg); + for (auto& memoryBinding : mvkImg->_memoryBindings) { + removeResource(memoryBinding); + } mvkImg->destroy(); } } @@ -2522,6 +2575,16 @@ void MVKDevice::destroySampler(MVKSampler* mvkSamp, if (mvkSamp) { mvkSamp->destroy(); } } +MVKSamplerYcbcrConversion* MVKDevice::createSamplerYcbcrConversion(const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + return new MVKSamplerYcbcrConversion(this, pCreateInfo); +} + +void MVKDevice::destroySamplerYcbcrConversion(MVKSamplerYcbcrConversion* mvkSampConv, + const VkAllocationCallbacks* pAllocator) { + mvkSampConv->destroy(); +} + MVKDescriptorSetLayout* MVKDevice::createDescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator) { return new MVKDescriptorSetLayout(this, pCreateInfo); @@ -2773,6 +2836,7 @@ MVKDevice::MVKDevice(MVKPhysicalDevice* physicalDevice, const VkDeviceCreateInfo _enabledVarPtrFeatures(), _enabledInterlockFeatures(), _enabledHostQryResetFeatures(), + _enabledSamplerYcbcrConversionFeatures(), _enabledScalarLayoutFeatures(), _enabledTexelBuffAlignFeatures(), _enabledVtxAttrDivFeatures(), @@ -2934,6 +2998,7 @@ void MVKDevice::enableFeatures(const VkDeviceCreateInfo* pCreateInfo) { mvkClear(&_enabledVarPtrFeatures); mvkClear(&_enabledInterlockFeatures); mvkClear(&_enabledHostQryResetFeatures); + mvkClear(&_enabledSamplerYcbcrConversionFeatures); mvkClear(&_enabledScalarLayoutFeatures); mvkClear(&_enabledTexelBuffAlignFeatures); mvkClear(&_enabledVtxAttrDivFeatures); @@ -2956,9 +3021,13 @@ void MVKDevice::enableFeatures(const VkDeviceCreateInfo* pCreateInfo) { pdScalarLayoutFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT; pdScalarLayoutFeatures.pNext = &pdTexelBuffAlignFeatures; + VkPhysicalDeviceSamplerYcbcrConversionFeatures pdSamplerYcbcrConversionFeatures; + pdSamplerYcbcrConversionFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES; + pdSamplerYcbcrConversionFeatures.pNext = &pdScalarLayoutFeatures; + VkPhysicalDeviceHostQueryResetFeaturesEXT pdHostQryResetFeatures; pdHostQryResetFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT; - pdHostQryResetFeatures.pNext = &pdScalarLayoutFeatures; + pdHostQryResetFeatures.pNext = &pdSamplerYcbcrConversionFeatures; VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT pdInterlockFeatures; pdInterlockFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT; @@ -3055,6 +3124,13 @@ void MVKDevice::enableFeatures(const VkDeviceCreateInfo* pCreateInfo) { &pdHostQryResetFeatures.hostQueryReset, 1); break; } + case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES: { + auto* requestedFeatures = (VkPhysicalDeviceSamplerYcbcrConversionFeatures*)next; + enableFeatures(&_enabledSamplerYcbcrConversionFeatures.samplerYcbcrConversion, + &requestedFeatures->samplerYcbcrConversion, + &pdSamplerYcbcrConversionFeatures.samplerYcbcrConversion, 1); + break; + } case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT: { auto* requestedFeatures = (VkPhysicalDeviceScalarBlockLayoutFeaturesEXT*)next; enableFeatures(&_enabledScalarLayoutFeatures.scalarBlockLayout, diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h index b00340e6..98faa38e 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h @@ -24,8 +24,7 @@ #import -class MVKBuffer; -class MVKImage; +class MVKImageMemoryBinding; // 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; @@ -34,6 +33,12 @@ static const VkExternalMemoryHandleTypeFlagBits VK_EXTERNAL_MEMORY_HANDLE_TYPE_M #pragma mark MVKDeviceMemory +typedef struct { + VkDeviceSize offset = 0; + VkDeviceSize size = 0; +} MVKMappedMemoryRange; + + /** Represents a Vulkan device-space memory allocation. */ class MVKDeviceMemory : public MVKVulkanAPIDeviceObject { @@ -78,7 +83,17 @@ public: /** Unmaps a previously mapped memory range. */ void unmap(); - /** + /** + * If this device memory is currently mapped to host memory, returns the range within + * this device memory that is currently mapped to host memory, or returns {0,0} if + * this device memory is not currently mapped to host memory. + */ + inline const MVKMappedMemoryRange& getMappedRange() { return _mappedRange; } + + /** Returns whether this device memory is currently mapped to host memory. */ + bool isMapped() { return _mappedRange.size > 0; } + + /** * If this memory is host-visible, the specified memory range is flushed to the device. * Normally, flushing will only occur if the device memory is non-coherent, but flushing * to coherent memory can be forced by setting evenIfCoherent to true. @@ -131,15 +146,15 @@ public: ~MVKDeviceMemory() override; protected: - friend MVKBuffer; - friend MVKImage; + friend class MVKBuffer; + friend class MVKImageMemoryBinding; void propagateDebugName() override; VkDeviceSize adjustMemorySize(VkDeviceSize size, VkDeviceSize offset); VkResult addBuffer(MVKBuffer* mvkBuff); void removeBuffer(MVKBuffer* mvkBuff); - VkResult addImage(MVKImage* mvkImg); - void removeImage(MVKImage* mvkImg); + VkResult addImageMemoryBinding(MVKImageMemoryBinding* mvkImg); + void removeImageMemoryBinding(MVKImageMemoryBinding* mvkImg); bool ensureMTLHeap(); bool ensureMTLBuffer(); bool ensureHostMemory(); @@ -148,16 +163,14 @@ protected: void initExternalMemory(VkExternalMemoryHandleTypeFlags handleTypes); MVKSmallVector _buffers; - MVKSmallVector _images; + MVKSmallVector _imageMemoryBindings; std::mutex _rezLock; VkDeviceSize _allocationSize = 0; - VkDeviceSize _mapOffset = 0; - VkDeviceSize _mapSize = 0; + MVKMappedMemoryRange _mappedRange; id _mtlBuffer = nil; id _mtlHeap = nil; void* _pMemory = nullptr; void* _pHostMemory = nullptr; - bool _isMapped = false; bool _isDedicated = false; MTLStorageMode _mtlStorageMode; MTLCPUCacheMode _mtlCPUCacheMode; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm index 36f335bc..cfa410ed 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm @@ -43,7 +43,7 @@ VkResult MVKDeviceMemory::map(VkDeviceSize offset, VkDeviceSize size, VkMemoryMa return reportError(VK_ERROR_MEMORY_MAP_FAILED, "Private GPU-only memory cannot be mapped to host memory."); } - if (_isMapped) { + if (isMapped()) { return reportError(VK_ERROR_MEMORY_MAP_FAILED, "Memory is already mapped. Call vkUnmapMemory() first."); } @@ -51,9 +51,8 @@ VkResult MVKDeviceMemory::map(VkDeviceSize offset, VkDeviceSize size, VkMemoryMa return reportError(VK_ERROR_OUT_OF_HOST_MEMORY, "Could not allocate %llu bytes of host-accessible device memory.", _allocationSize); } - _mapOffset = offset; - _mapSize = adjustMemorySize(size, offset); - _isMapped = true; + _mappedRange.offset = offset; + _mappedRange.size = adjustMemorySize(size, offset); *ppData = (void*)((uintptr_t)_pMemory + offset); @@ -65,17 +64,16 @@ VkResult MVKDeviceMemory::map(VkDeviceSize offset, VkDeviceSize size, VkMemoryMa void MVKDeviceMemory::unmap() { - if ( !_isMapped ) { + if ( !isMapped() ) { reportError(VK_ERROR_MEMORY_MAP_FAILED, "Memory is not mapped. Call vkMapMemory() first."); return; } // Coherent memory does not require flushing by app, so we must flush now. - flushToDevice(_mapOffset, _mapSize, isMemoryHostCoherent()); + flushToDevice(_mappedRange.offset, _mappedRange.size, isMemoryHostCoherent()); - _mapOffset = 0; - _mapSize = 0; - _isMapped = false; + _mappedRange.offset = 0; + _mappedRange.size = 0; } VkResult MVKDeviceMemory::flushToDevice(VkDeviceSize offset, VkDeviceSize size, bool evenIfCoherent) { @@ -92,7 +90,7 @@ VkResult MVKDeviceMemory::flushToDevice(VkDeviceSize offset, VkDeviceSize size, // If we have an MTLHeap object, there's no need to sync memory manually between images and the buffer. if (!_mtlHeap) { lock_guard lock(_rezLock); - for (auto& img : _images) { img->flushToDevice(offset, memSize); } + for (auto& img : _imageMemoryBindings) { img->flushToDevice(offset, memSize); } for (auto& buf : _buffers) { buf->flushToDevice(offset, memSize); } } } @@ -107,7 +105,7 @@ VkResult MVKDeviceMemory::pullFromDevice(VkDeviceSize offset, VkDeviceSize memSize = adjustMemorySize(size, offset); if (memSize > 0 && isMemoryHostAccessible() && (evenIfCoherent || !isMemoryHostCoherent()) && !_mtlHeap) { lock_guard lock(_rezLock); - for (auto& img : _images) { img->pullFromDevice(offset, memSize); } + for (auto& img : _imageMemoryBindings) { img->pullFromDevice(offset, memSize); } for (auto& buf : _buffers) { buf->pullFromDevice(offset, memSize); } #if MVK_MACOS @@ -152,23 +150,23 @@ void MVKDeviceMemory::removeBuffer(MVKBuffer* mvkBuff) { mvkRemoveAllOccurances(_buffers, mvkBuff); } -VkResult MVKDeviceMemory::addImage(MVKImage* mvkImg) { +VkResult MVKDeviceMemory::addImageMemoryBinding(MVKImageMemoryBinding* mvkImg) { lock_guard lock(_rezLock); // If a dedicated alloc, ensure this image is the one and only image // I am dedicated to. - if (_isDedicated && (_images.empty() || _images[0] != mvkImg) ) { + if (_isDedicated && (_imageMemoryBindings.empty() || _imageMemoryBindings[0] != mvkImg) ) { return reportError(VK_ERROR_OUT_OF_DEVICE_MEMORY, "Could not bind VkImage %p to a VkDeviceMemory dedicated to resource %p. A dedicated allocation may only be used with the resource it was dedicated to.", mvkImg, getDedicatedResource() ); } - if (!_isDedicated) { _images.push_back(mvkImg); } + if (!_isDedicated) { _imageMemoryBindings.push_back(mvkImg); } return VK_SUCCESS; } -void MVKDeviceMemory::removeImage(MVKImage* mvkImg) { +void MVKDeviceMemory::removeImageMemoryBinding(MVKImageMemoryBinding* mvkImg) { lock_guard lock(_rezLock); - mvkRemoveAllOccurances(_images, mvkImg); + mvkRemoveAllOccurances(_imageMemoryBindings, mvkImg); } // Ensures that this instance is backed by a MTLHeap object, @@ -266,7 +264,7 @@ void MVKDeviceMemory::freeHostMemory() { MVKResource* MVKDeviceMemory::getDedicatedResource() { MVKAssert(_isDedicated, "This method should only be called on dedicated allocations!"); - return _buffers.empty() ? (MVKResource*)_images[0] : (MVKResource*)_buffers[0]; + return _buffers.empty() ? (MVKResource*)_imageMemoryBindings[0] : (MVKResource*)_buffers[0]; } MVKDeviceMemory::MVKDeviceMemory(MVKDevice* device, @@ -319,7 +317,9 @@ MVKDeviceMemory::MVKDeviceMemory(MVKDevice* device, } } #endif - _images.push_back((MVKImage*)dedicatedImage); + for (auto& memoryBinding : ((MVKImage*)dedicatedImage)->_memoryBindings) { + _imageMemoryBindings.push_back(memoryBinding); + } return; } @@ -367,7 +367,7 @@ MVKDeviceMemory::~MVKDeviceMemory() { // to allow the resource to callback to remove itself from the collection. auto buffCopies = _buffers; for (auto& buf : buffCopies) { buf->bindDeviceMemory(nullptr, 0); } - auto imgCopies = _images; + auto imgCopies = _imageMemoryBindings; for (auto& img : imgCopies) { img->bindDeviceMemory(nullptr, 0); } [_mtlBuffer release]; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h index b699ca57..0d453737 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h @@ -28,24 +28,132 @@ #import +class MVKImage; class MVKImageView; class MVKSwapchain; class MVKCommandEncoder; +#pragma mark - +#pragma mark MVKImagePlane + /** Tracks the state of an image subresource. */ typedef struct { - VkImageSubresource subresource; - VkSubresourceLayout layout; - VkImageLayout layoutState; + VkImageSubresource subresource; + VkSubresourceLayout layout; + VkImageLayout layoutState; } MVKImageSubresource; +class MVKImagePlane : public MVKBaseObject { + +public: + + /** Returns the Vulkan API opaque object controlling this object. */ + MVKVulkanAPIObject* getVulkanAPIObject() override; + + /** Returns the Metal texture underlying this image plane. */ + id getMTLTexture(); + + /** Returns a Metal texture that interprets the pixels in the specified format. */ + id getMTLTexture(MTLPixelFormat mtlPixFmt); + + void releaseMTLTexture(); + + ~MVKImagePlane(); + +protected: + friend class MVKImageMemoryBinding; + friend MVKImage; + + MTLTextureDescriptor* newMTLTextureDescriptor(); + void initSubresources(const VkImageCreateInfo* pCreateInfo); + MVKImageSubresource* getSubresource(uint32_t mipLevel, uint32_t arrayLayer); + void updateMTLTextureContent(MVKImageSubresource& subresource, VkDeviceSize offset, VkDeviceSize size); + void getMTLTextureContent(MVKImageSubresource& subresource, VkDeviceSize offset, VkDeviceSize size); + bool overlaps(VkSubresourceLayout& imgLayout, VkDeviceSize offset, VkDeviceSize size); + void propagateDebugName(); + MVKImageMemoryBinding* getMemoryBinding() const; + void applyImageMemoryBarrier(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + MVKPipelineBarrier& barrier, + MVKCommandEncoder* cmdEncoder, + MVKCommandUse cmdUse); + void pullFromDeviceOnCompletion(MVKCommandEncoder* cmdEncoder, + MVKImageSubresource& subresource, + const MVKMappedMemoryRange& mappedRange); + + MVKImagePlane(MVKImage* image, uint8_t planeIndex); + + MVKImage* _image; + uint8_t _planeIndex; + VkExtent2D _blockTexelSize; + uint32_t _bytesPerBlock; + MTLPixelFormat _mtlPixFmt; + id _mtlTexture; + std::unordered_map> _mtlTextureViews; + MVKSmallVector _subresources; +}; + + +#pragma mark - +#pragma mark MVKImageMemoryBinding + +class MVKImageMemoryBinding : public MVKResource { + +public: + + /** Returns the Vulkan type of this object. */ + VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_UNKNOWN; } + + /** Returns the debug report object type of this object. */ + VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT; } + + /** Returns the memory requirements of this resource by populating the specified structure. */ + VkResult getMemoryRequirements(VkMemoryRequirements* pMemoryRequirements); + + /** Returns the memory requirements of this resource by populating the specified structure. */ + VkResult getMemoryRequirements(const void* pInfo, VkMemoryRequirements2* pMemoryRequirements); + + /** Binds this resource to the specified offset within the specified memory allocation. */ + VkResult bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset) override; + + /** Applies the specified global memory barrier. */ + void applyMemoryBarrier(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + MVKPipelineBarrier& barrier, + MVKCommandEncoder* cmdEncoder, + MVKCommandUse cmdUse) override; + + ~MVKImageMemoryBinding(); + +protected: + friend MVKDeviceMemory; + friend MVKImagePlane; + friend MVKImage; + + void propagateDebugName() override; + bool needsHostReadSync(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + MVKPipelineBarrier& barrier); + bool shouldFlushHostMemory(); + VkResult flushToDevice(VkDeviceSize offset, VkDeviceSize size); + VkResult pullFromDevice(VkDeviceSize offset, VkDeviceSize size); + uint8_t beginPlaneIndex() const; + uint8_t endPlaneIndex() const; + + MVKImageMemoryBinding(MVKDevice* device, MVKImage* image, uint8_t planeIndex); + + MVKImage* _image; + uint8_t _planeIndex; + bool _usesTexelBuffer; +}; + #pragma mark - #pragma mark MVKImage /** Represents a Vulkan image. */ -class MVKImage : public MVKResource { +class MVKImage : public MVKVulkanAPIDeviceObject { public: @@ -55,6 +163,9 @@ public: /** Returns the debug report object type of this object. */ VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT; } + /** Returns the plane index of VkImageAspectFlags. */ + static uint8_t getPlaneFromVkImageAspectFlags(VkImageAspectFlags aspectMask); + /** * Returns the Vulkan image type of this image. * This may be different than the value originally specified for the image @@ -63,7 +174,7 @@ public: VkImageType getImageType(); /** Returns the Vulkan image format of this image. */ - VkFormat getVkFormat(); + VkFormat getVkFormat() { return _vkFormat; }; /** Returns whether this image has a depth or stencil format. */ bool getIsDepthStencil(); @@ -81,7 +192,7 @@ public: * Returns the 3D extent of this image at the specified mipmap level. * For 2D or cube images, the Z component will be 1. */ - VkExtent3D getExtent3D(uint32_t mipLevel); + VkExtent3D getExtent3D(uint8_t planeIndex, uint32_t mipLevel); /** Returns the number of mipmap levels in this image. */ inline uint32_t getMipLevelCount() { return _mipLevels; } @@ -101,7 +212,7 @@ public: * For compressed formats, this is the number of bytes in a row of blocks, which * will typically span more than one row of texels. */ - VkDeviceSize getBytesPerRow(uint32_t mipLevel); + VkDeviceSize getBytesPerRow(uint8_t planeIndex, uint32_t mipLevel); /** * Returns the number of bytes per image layer (for cube, array, or 3D images) @@ -109,7 +220,10 @@ public: * of bytes per row (as returned by the getBytesPerRow() function, multiplied by * the height of each 2D image. */ - VkDeviceSize getBytesPerLayer(uint32_t mipLevel); + VkDeviceSize getBytesPerLayer(uint8_t planeIndex, uint32_t mipLevel); + + /** Returns the number of planes of this image view. */ + inline uint8_t getPlaneCount() { return _planes.size(); } /** Populates the specified layout for the specified sub-resource. */ VkResult getSubresourceLayout(const VkImageSubresource* pSubresource, @@ -122,24 +236,17 @@ public: #pragma mark Resource memory /** Returns the memory requirements of this resource by populating the specified structure. */ - VkResult getMemoryRequirements(VkMemoryRequirements* pMemoryRequirements) override; + VkResult getMemoryRequirements(VkMemoryRequirements* pMemoryRequirements, uint8_t planeIndex); /** Returns the memory requirements of this resource by populating the specified structure. */ - VkResult getMemoryRequirements(const void* pInfo, VkMemoryRequirements2* pMemoryRequirements) override; + VkResult getMemoryRequirements(const void* pInfo, VkMemoryRequirements2* pMemoryRequirements); /** Binds this resource to the specified offset within the specified memory allocation. */ - VkResult bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset) override; + virtual VkResult bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset, uint8_t planeIndex); /** Binds this resource to the specified offset within the specified memory allocation. */ virtual VkResult bindDeviceMemory2(const VkBindImageMemoryInfo* pBindInfo); - /** Applies the specified global memory barrier. */ - void applyMemoryBarrier(VkPipelineStageFlags srcStageMask, - VkPipelineStageFlags dstStageMask, - MVKPipelineBarrier& barrier, - MVKCommandEncoder* cmdEncoder, - MVKCommandUse cmdUse) override; - /** Applies the specified image memory barrier. */ void applyImageMemoryBarrier(VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, @@ -150,10 +257,10 @@ public: #pragma mark Metal /** Returns the Metal texture underlying this image. */ - virtual id getMTLTexture(); + virtual id getMTLTexture(uint8_t planeIndex); /** Returns a Metal texture that interprets the pixels in the specified format. */ - id getMTLTexture(MTLPixelFormat mtlPixFmt); + id getMTLTexture(uint8_t planeIndex, MTLPixelFormat mtlPixFmt); /** * Sets this image to use the specified MTLTexture. @@ -163,7 +270,7 @@ public: * * If a MTLTexture has already been created for this image, it will be destroyed. */ - VkResult setMTLTexture(id mtlTexture); + VkResult setMTLTexture(uint8_t planeIndex, id mtlTexture); /** * Indicates that this VkImage should use an IOSurface to underlay the Metal texture. @@ -189,7 +296,7 @@ public: IOSurfaceRef getIOSurface(); /** Returns the Metal pixel format of this image. */ - inline MTLPixelFormat getMTLPixelFormat() { return _mtlPixelFormat; } + inline MTLPixelFormat getMTLPixelFormat(uint8_t planeIndex) { return _planes[planeIndex]->_mtlPixFmt; } /** Returns the Metal texture type of this image. */ inline MTLTextureType getMTLTextureType() { return _mtlTextureType; } @@ -212,12 +319,6 @@ public: /** Returns the Metal CPU cache mode used by this image. */ MTLCPUCacheMode getMTLCPUCacheMode(); - /** - * Returns whether the memory is automatically coherent between device and host. - * On macOS, this always returns false because textures cannot use Shared storage mode. - */ - bool isMemoryHostCoherent(); - #pragma mark Construction MVKImage(MVKDevice* device, const VkImageCreateInfo* pCreateInfo); @@ -225,50 +326,37 @@ public: ~MVKImage() override; protected: - friend class MVKDeviceMemory; - friend class MVKImageView; - using MVKResource::needsHostReadSync; + friend MVKDeviceMemory; + friend MVKDevice; + friend MVKImageMemoryBinding; + friend MVKImagePlane; + friend class MVKImageViewPlane; + friend MVKImageView; void propagateDebugName() override; - MVKImageSubresource* getSubresource(uint32_t mipLevel, uint32_t arrayLayer); void validateConfig(const VkImageCreateInfo* pCreateInfo, bool isAttachment); VkSampleCountFlagBits validateSamples(const VkImageCreateInfo* pCreateInfo, bool isAttachment); uint32_t validateMipLevels(const VkImageCreateInfo* pCreateInfo, bool isAttachment); bool validateLinear(const VkImageCreateInfo* pCreateInfo, bool isAttachment); - bool validateUseTexelBuffer(); - void initSubresources(const VkImageCreateInfo* pCreateInfo); - void initSubresourceLayout(MVKImageSubresource& imgSubRez); void initExternalMemory(VkExternalMemoryHandleTypeFlags handleTypes); - id newMTLTexture(); - void releaseMTLTexture(); void releaseIOSurface(); - MTLTextureDescriptor* newMTLTextureDescriptor(); - void updateMTLTextureContent(MVKImageSubresource& subresource, VkDeviceSize offset, VkDeviceSize size); - void getMTLTextureContent(MVKImageSubresource& subresource, VkDeviceSize offset, VkDeviceSize size); - bool shouldFlushHostMemory(); - VkResult flushToDevice(VkDeviceSize offset, VkDeviceSize size); - VkResult pullFromDevice(VkDeviceSize offset, VkDeviceSize size); - bool needsHostReadSync(VkPipelineStageFlags srcStageMask, - VkPipelineStageFlags dstStageMask, - MVKPipelineBarrier& barrier); - MVKSmallVector _subresources; - std::unordered_map> _mtlTextureViews; + MVKSmallVector _memoryBindings; + MVKSmallVector _planes; VkExtent3D _extent; uint32_t _mipLevels; uint32_t _arrayLayers; VkSampleCountFlagBits _samples; VkImageUsageFlags _usage; - MTLPixelFormat _mtlPixelFormat; + VkFormat _vkFormat; MTLTextureType _mtlTextureType; - id _mtlTexture; std::mutex _lock; IOSurfaceRef _ioSurface; VkDeviceSize _rowByteAlignment; bool _isDepthStencilAttachment; bool _canSupportMTLTextureView; bool _hasExpectedTexelSize; - bool _usesTexelBuffer; + bool _hasChromaSubsampling; bool _isLinear; bool _is3DCompressed; bool _isAliasable; @@ -284,12 +372,12 @@ class MVKSwapchainImage : public MVKImage { public: /** Binds this resource to the specified offset within the specified memory allocation. */ - VkResult bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset) override; + VkResult bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset, uint8_t planeIndex) override; #pragma mark Metal /** Returns the Metal texture used by the CAMetalDrawable underlying this image. */ - id getMTLTexture() override; + id getMTLTexture(uint8_t planeIndex) override; #pragma mark Construction @@ -401,6 +489,37 @@ protected: }; +#pragma mark - +#pragma mark MVKImageViewPlane + +class MVKImageViewPlane : public MVKBaseDeviceObject { + + /** Returns the Vulkan API opaque object controlling this object. */ + MVKVulkanAPIObject* getVulkanAPIObject() override; + +public: + /** Returns the Metal texture underlying this image view. */ + id getMTLTexture(); + + void releaseMTLTexture(); + + ~MVKImageViewPlane(); + +protected: + void propagateDebugName(); + id newMTLTexture(); + MVKImageViewPlane(MVKImageView* imageView, uint8_t planeIndex, MTLPixelFormat mtlPixFmt, const VkImageViewCreateInfo* pCreateInfo); + + friend MVKImageView; + MVKImageView* _imageView; + uint8_t _planeIndex; + MTLPixelFormat _mtlPixFmt; + uint32_t _packedSwizzle; + id _mtlTexture; + bool _useMTLTextureView; +}; + + #pragma mark - #pragma mark MVKImageView @@ -418,17 +537,20 @@ public: #pragma mark Metal /** Returns the Metal texture underlying this image view. */ - id getMTLTexture(); + id getMTLTexture(uint8_t planeIndex) { return _planes[planeIndex]->getMTLTexture(); } /** Returns the Metal pixel format of this image view. */ - inline MTLPixelFormat getMTLPixelFormat() { return _mtlPixelFormat; } + inline MTLPixelFormat getMTLPixelFormat(uint8_t planeIndex) { return _planes[planeIndex]->_mtlPixFmt; } + + /** Returns the packed component swizzle of this image view. */ + inline uint32_t getPackedSwizzle() { return _planes[0]->_packedSwizzle; } + + /** Returns the number of planes of this image view. */ + inline uint8_t getPlaneCount() { return _planes.size(); } /** Returns the Metal texture type of this image view. */ inline MTLTextureType getMTLTextureType() { return _mtlTextureType; } - /** Returns the packed component swizzle of this image view. */ - inline uint32_t getPackedSwizzle() { return _packedSwizzle; } - /** * Populates the texture of the specified render pass descriptor * with the Metal texture underlying this image. @@ -458,7 +580,6 @@ public: * This is a static function that can be used to validate image view formats prior to creating one. */ static VkResult validateSwizzledMTLPixelFormat(const VkImageViewCreateInfo* pCreateInfo, - MVKPixelFormats* mvkPixFmts, MVKVulkanAPIObject* apiObject, bool hasNativeSwizzleSupport, bool hasShaderSwizzleSupport, @@ -472,23 +593,56 @@ public: const VkImageViewCreateInfo* pCreateInfo, const MVKConfiguration* pAltMVKConfig = nullptr); - ~MVKImageView() override; + ~MVKImageView(); protected: + friend MVKImageViewPlane; + void propagateDebugName() override; - id newMTLTexture(); - void initMTLTextureViewSupport(); - void validateImageViewConfig(const VkImageViewCreateInfo* pCreateInfo); MVKImage* _image; + MVKSmallVector _planes; VkImageSubresourceRange _subresourceRange; VkImageUsageFlags _usage; - id _mtlTexture; std::mutex _lock; - MTLPixelFormat _mtlPixelFormat; MTLTextureType _mtlTextureType; - uint32_t _packedSwizzle; - bool _useMTLTextureView; +}; + + +#pragma mark - +#pragma mark MVKSamplerYcbcrConversion + +/** Represents a Vulkan sampler ycbcr conversion. */ +class MVKSamplerYcbcrConversion : public MVKVulkanAPIDeviceObject { + +public: + /** Returns the Vulkan type of this object. */ + VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION; } + + /** Returns the debug report object type of this object. */ + VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_EXT; } + + /** Returns the number of planes of this ycbcr conversion. */ + inline uint8_t getPlaneCount() { return _planes; } + + /** Writes this conversion settings to a MSL constant sampler */ + void updateConstExprSampler(SPIRV_CROSS_NAMESPACE::MSLConstexprSampler& constExprSampler) const; + + MVKSamplerYcbcrConversion(MVKDevice* device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo); + + ~MVKSamplerYcbcrConversion() override {} + +protected: + void propagateDebugName() override {} + + uint8_t _planes, _bpc; + SPIRV_CROSS_NAMESPACE::MSLFormatResolution _resolution; + SPIRV_CROSS_NAMESPACE::MSLSamplerFilter _chroma_filter; + SPIRV_CROSS_NAMESPACE::MSLChromaLocation _x_chroma_offset, _y_chroma_offset; + SPIRV_CROSS_NAMESPACE::MSLComponentSwizzle _swizzle[4]; + SPIRV_CROSS_NAMESPACE::MSLSamplerYCbCrModelConversion _ycbcr_model; + SPIRV_CROSS_NAMESPACE::MSLSamplerYCbCrRange _ycbcr_range; + bool _forceExplicitReconstruction; }; @@ -508,6 +662,10 @@ public: /** Returns the Metal sampler state. */ inline id getMTLSamplerState() { return _mtlSamplerState; } + + /** Returns the number of planes if this is a ycbcr conversion or 0 otherwise. */ + inline uint8_t getPlaneCount() { return (_ycbcrConversion) ? _ycbcrConversion->getPlaneCount() : 0; } + /** * If this sampler requires hardcoding in MSL, populates the hardcoded sampler in the resource binding. @@ -529,5 +687,6 @@ protected: id _mtlSamplerState; SPIRV_CROSS_NAMESPACE::MSLConstexprSampler _constExprSampler; + MVKSamplerYcbcrConversion* _ycbcrConversion; bool _requiresConstExprSampler; }; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm index 845b9c20..b48d6993 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm @@ -32,40 +32,519 @@ using namespace std; using namespace SPIRV_CROSS_NAMESPACE; +#pragma mark - +#pragma mark MVKImagePlane + +MVKVulkanAPIObject* MVKImagePlane::getVulkanAPIObject() { return _image; } + +id MVKImagePlane::getMTLTexture() { + if ( !_mtlTexture && _image->_vkFormat ) { + // Lock and check again in case another thread has created the texture. + lock_guard lock(_image->_lock); + if (_mtlTexture) { return _mtlTexture; } + + MTLTextureDescriptor* mtlTexDesc = newMTLTextureDescriptor(); // temp retain + MVKImageMemoryBinding* memoryBinding = getMemoryBinding(); + + if (_image->_ioSurface) { + _mtlTexture = [_image->getMTLDevice() + newTextureWithDescriptor: mtlTexDesc + iosurface: _image->_ioSurface + plane: _planeIndex]; + } else if (memoryBinding->_usesTexelBuffer) { + _mtlTexture = [memoryBinding->_deviceMemory->getMTLBuffer() + newTextureWithDescriptor: mtlTexDesc + offset: memoryBinding->getDeviceMemoryOffset() + bytesPerRow: _subresources[0].layout.rowPitch]; + } else if (memoryBinding->_deviceMemory->getMTLHeap() && !_image->getIsDepthStencil()) { + // Metal support for depth/stencil from heaps is flaky + _mtlTexture = [memoryBinding->_deviceMemory->getMTLHeap() + newTextureWithDescriptor: mtlTexDesc + offset: memoryBinding->getDeviceMemoryOffset()]; + if (_image->_isAliasable) { [_mtlTexture makeAliasable]; } + } else { + _mtlTexture = [_image->getMTLDevice() newTextureWithDescriptor: mtlTexDesc]; + } + + [mtlTexDesc release]; // temp release + + propagateDebugName(); + } + return _mtlTexture; +} + +id MVKImagePlane::getMTLTexture(MTLPixelFormat mtlPixFmt) { + if (mtlPixFmt == _mtlPixFmt) { return _mtlTexture; } + id mtlTex = _mtlTextureViews[mtlPixFmt]; + if ( !mtlTex ) { + // Lock and check again in case another thread has created the view texture. + // baseTex retreived outside of lock to avoid deadlock if it too needs to be lazily created. + lock_guard lock(_image->_lock); + mtlTex = _mtlTextureViews[mtlPixFmt]; + if ( !mtlTex ) { + mtlTex = [_mtlTexture newTextureViewWithPixelFormat: mtlPixFmt]; // retained + _mtlTextureViews[mtlPixFmt] = mtlTex; + } + } + return mtlTex; +} + +void MVKImagePlane::releaseMTLTexture() { + [_mtlTexture release]; + for (auto elem : _mtlTextureViews) { + [elem.second release]; + } +} + +// Returns a Metal texture descriptor constructed from the properties of this image. +// It is the caller's responsibility to release the returned descriptor object. +MTLTextureDescriptor* MVKImagePlane::newMTLTextureDescriptor() { + MTLPixelFormat mtlPixFmt = _mtlPixFmt; + MTLTextureUsage minUsage = MTLTextureUsageUnknown; +#if MVK_MACOS + if (_image->_is3DCompressed) { + // Metal before 3.0 doesn't support 3D compressed textures, so we'll decompress + // the texture ourselves. This, then, is the *uncompressed* format. + mtlPixFmt = MTLPixelFormatBGRA8Unorm; + minUsage = MTLTextureUsageShaderWrite; + } +#endif + + VkExtent3D extent = _image->getExtent3D(_planeIndex, 0); + MTLTextureDescriptor* mtlTexDesc = [MTLTextureDescriptor new]; // retained + mtlTexDesc.pixelFormat = mtlPixFmt; + mtlTexDesc.textureType = _image->_mtlTextureType; + mtlTexDesc.width = extent.width; + mtlTexDesc.height = extent.height; + mtlTexDesc.depth = extent.depth; + mtlTexDesc.mipmapLevelCount = _image->_mipLevels; + mtlTexDesc.sampleCount = mvkSampleCountFromVkSampleCountFlagBits(_image->_samples); + mtlTexDesc.arrayLength = _image->_arrayLayers; + mtlTexDesc.usageMVK = _image->getPixelFormats()->getMTLTextureUsage(_image->_usage, mtlPixFmt, minUsage); + mtlTexDesc.storageModeMVK = _image->getMTLStorageMode(); + mtlTexDesc.cpuCacheMode = _image->getMTLCPUCacheMode(); + + return mtlTexDesc; +} + +// Initializes the subresource definitions. +void MVKImagePlane::initSubresources(const VkImageCreateInfo* pCreateInfo) { + _subresources.reserve(_image->_mipLevels * _image->_arrayLayers); + + MVKImageSubresource subRez; + subRez.subresource.aspectMask = VK_IMAGE_ASPECT_PLANE_0_BIT << _planeIndex; + subRez.layoutState = pCreateInfo->initialLayout; + + VkDeviceSize offset = 0; + if (_planeIndex > 0 && _image->_memoryBindings.size() == 1) { + auto subresources = &_image->_planes[_planeIndex-1]->_subresources; + VkSubresourceLayout& lastLayout = (*subresources)[subresources->size()-1].layout; + offset = lastLayout.offset+lastLayout.size; + } + + for (uint32_t mipLvl = 0; mipLvl < _image->_mipLevels; mipLvl++) { + subRez.subresource.mipLevel = mipLvl; + VkDeviceSize rowPitch = _image->getBytesPerRow(_planeIndex, mipLvl); + VkDeviceSize depthPitch = _image->getBytesPerLayer(_planeIndex, mipLvl); + + for (uint32_t layer = 0; layer < _image->_arrayLayers; layer++) { + subRez.subresource.arrayLayer = layer; + + VkSubresourceLayout& layout = subRez.layout; + layout.offset = offset; + layout.size = depthPitch * _image->_extent.depth; + layout.rowPitch = rowPitch; + layout.depthPitch = depthPitch; + + _subresources.push_back(subRez); + offset += layout.size; + } + } +} + +// Returns a pointer to the internal subresource for the specified MIP level layer. +MVKImageSubresource* MVKImagePlane::getSubresource(uint32_t mipLevel, uint32_t arrayLayer) { + uint32_t srIdx = (mipLevel * _image->_arrayLayers) + arrayLayer; + return (srIdx < _subresources.size()) ? &_subresources[srIdx] : NULL; +} + +// Updates the contents of the underlying MTLTexture, corresponding to the +// specified subresource definition, from the underlying memory buffer. +void MVKImagePlane::updateMTLTextureContent(MVKImageSubresource& subresource, + VkDeviceSize offset, VkDeviceSize size) { + + VkImageSubresource& imgSubRez = subresource.subresource; + VkSubresourceLayout& imgLayout = subresource.layout; + + // Check if subresource overlaps the memory range. + if ( !overlaps(imgLayout, offset, size) ) { return; } + + // Don't update if host memory has not been mapped yet. + void* pHostMem = getMemoryBinding()->getHostMemoryAddress(); + if ( !pHostMem ) { return; } + + VkExtent3D mipExtent = _image->getExtent3D(_planeIndex, imgSubRez.mipLevel); + void* pImgBytes = (void*)((uintptr_t)pHostMem + imgLayout.offset); + + MTLRegion mtlRegion; + mtlRegion.origin = MTLOriginMake(0, 0, 0); + mtlRegion.size = mvkMTLSizeFromVkExtent3D(mipExtent); + +#if MVK_MACOS + std::unique_ptr decompBuffer; + if (_image->_is3DCompressed) { + // We cannot upload the texture data directly in this case. But we + // can upload the decompressed image data. + std::unique_ptr codec = mvkCreateCodec(_image->getVkFormat()); + if (!codec) { + _image->reportError(VK_ERROR_FORMAT_NOT_SUPPORTED, "A 3D texture used a compressed format that MoltenVK does not yet support."); + return; + } + VkSubresourceLayout destLayout; + destLayout.rowPitch = 4 * mipExtent.width; + destLayout.depthPitch = destLayout.rowPitch * mipExtent.height; + destLayout.size = destLayout.depthPitch * mipExtent.depth; + decompBuffer = std::unique_ptr(new char[destLayout.size]); + codec->decompress(decompBuffer.get(), pImgBytes, destLayout, imgLayout, mipExtent); + pImgBytes = decompBuffer.get(); + imgLayout = destLayout; + } +#endif + + VkImageType imgType = _image->getImageType(); + VkDeviceSize bytesPerRow = (imgType != VK_IMAGE_TYPE_1D) ? imgLayout.rowPitch : 0; + VkDeviceSize bytesPerImage = (imgType == VK_IMAGE_TYPE_3D) ? imgLayout.depthPitch : 0; + + id mtlTex = getMTLTexture(); + if (_image->getPixelFormats()->isPVRTCFormat(mtlTex.pixelFormat)) { + bytesPerRow = 0; + bytesPerImage = 0; + } + + [mtlTex replaceRegion: mtlRegion + mipmapLevel: imgSubRez.mipLevel + slice: imgSubRez.arrayLayer + withBytes: pImgBytes + bytesPerRow: bytesPerRow + bytesPerImage: bytesPerImage]; +} + +// Updates the contents of the underlying memory buffer from the contents of +// the underlying MTLTexture, corresponding to the specified subresource definition. +void MVKImagePlane::getMTLTextureContent(MVKImageSubresource& subresource, + VkDeviceSize offset, VkDeviceSize size) { + + VkImageSubresource& imgSubRez = subresource.subresource; + VkSubresourceLayout& imgLayout = subresource.layout; + + // Check if subresource overlaps the memory range. + if ( !overlaps(imgLayout, offset, size) ) { return; } + + // Don't update if host memory has not been mapped yet. + void* pHostMem = getMemoryBinding()->getHostMemoryAddress(); + if ( !pHostMem ) { return; } + + VkExtent3D mipExtent = _image->getExtent3D(_planeIndex, imgSubRez.mipLevel); + void* pImgBytes = (void*)((uintptr_t)pHostMem + imgLayout.offset); + + MTLRegion mtlRegion; + mtlRegion.origin = MTLOriginMake(0, 0, 0); + mtlRegion.size = mvkMTLSizeFromVkExtent3D(mipExtent); + + VkImageType imgType = _image->getImageType(); + VkDeviceSize bytesPerRow = (imgType != VK_IMAGE_TYPE_1D) ? imgLayout.rowPitch : 0; + VkDeviceSize bytesPerImage = (imgType == VK_IMAGE_TYPE_3D) ? imgLayout.depthPitch : 0; + + [_mtlTexture getBytes: pImgBytes + bytesPerRow: bytesPerRow + bytesPerImage: bytesPerImage + fromRegion: mtlRegion + mipmapLevel: imgSubRez.mipLevel + slice: imgSubRez.arrayLayer]; +} + +// Returns whether subresource layout overlaps the memory range. +bool MVKImagePlane::overlaps(VkSubresourceLayout& imgLayout, VkDeviceSize offset, VkDeviceSize size) { + VkDeviceSize memStart = offset; + VkDeviceSize memEnd = offset + size; + VkDeviceSize imgStart = imgLayout.offset; + VkDeviceSize imgEnd = imgLayout.offset + imgLayout.size; + return imgStart < memEnd && imgEnd > memStart; +} + +void MVKImagePlane::propagateDebugName() { + setLabelIfNotNil(_image->_planes[_planeIndex]->_mtlTexture, _image->_debugName); +} + +MVKImageMemoryBinding* MVKImagePlane::getMemoryBinding() const { + return (_image->_memoryBindings.size() > 1) ? _image->_memoryBindings[_planeIndex] : _image->_memoryBindings[0]; +} + +void MVKImagePlane::applyImageMemoryBarrier(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + MVKPipelineBarrier& barrier, + MVKCommandEncoder* cmdEncoder, + MVKCommandUse cmdUse) { + + // Extract the mipmap levels that are to be updated + uint32_t mipLvlStart = barrier.baseMipLevel; + uint32_t mipLvlEnd = (barrier.levelCount == (uint8_t)VK_REMAINING_MIP_LEVELS + ? _image->getMipLevelCount() + : (mipLvlStart + barrier.levelCount)); + + // Extract the cube or array layers (slices) that are to be updated + uint32_t layerStart = barrier.baseArrayLayer; + uint32_t layerEnd = (barrier.layerCount == (uint16_t)VK_REMAINING_ARRAY_LAYERS + ? _image->getLayerCount() + : (layerStart + barrier.layerCount)); + + MVKImageMemoryBinding* memBind = getMemoryBinding(); + bool needsSync = memBind->needsHostReadSync(srcStageMask, dstStageMask, barrier); + bool needsPull = (!memBind->_usesTexelBuffer && + memBind->isMemoryHostCoherent() && + barrier.newLayout == VK_IMAGE_LAYOUT_GENERAL && + mvkIsAnyFlagEnabled(barrier.dstAccessMask, (VK_ACCESS_HOST_READ_BIT | VK_ACCESS_MEMORY_READ_BIT))); + MVKDeviceMemory* dvcMem = memBind->getDeviceMemory(); + const MVKMappedMemoryRange& mappedRange = dvcMem ? dvcMem->getMappedRange() : MVKMappedMemoryRange(); + + // Iterate across mipmap levels and layers, and update the image layout state for each + for (uint32_t mipLvl = mipLvlStart; mipLvl < mipLvlEnd; mipLvl++) { + for (uint32_t layer = layerStart; layer < layerEnd; layer++) { + MVKImageSubresource* pImgRez = getSubresource(mipLvl, layer); + if (pImgRez) { pImgRez->layoutState = barrier.newLayout; } + if (needsSync) { +#if MVK_MACOS + [cmdEncoder->getMTLBlitEncoder(cmdUse) synchronizeTexture: _mtlTexture slice: layer level: mipLvl]; +#endif + } + // Check if image content should be pulled into host-mapped device memory after + // the GPU is done with it. This only applies if the image is intended to be + // host-coherent, is not using a texel buffer, is transitioning to host-readable, + // AND the device memory has an already-open memory mapping. If a memory mapping is + // created later, it will pull the image contents in at that time, so it is not needed now. + // The mapped range will be {0,0} if device memory is not currently mapped. + if (needsPull && pImgRez && overlaps(pImgRez->layout, mappedRange.offset, mappedRange.size)) { + pullFromDeviceOnCompletion(cmdEncoder, *pImgRez, mappedRange); + } + } + } +} + +// Once the command buffer completes, pull the content of the subresource into host memory. +// This is only necessary when the image memory is intended to be host-coherent, and the +// device memory is currently mapped to host memory +void MVKImagePlane::pullFromDeviceOnCompletion(MVKCommandEncoder* cmdEncoder, + MVKImageSubresource& subresource, + const MVKMappedMemoryRange& mappedRange) { + + [cmdEncoder->_mtlCmdBuffer addCompletedHandler: ^(id mcb) { + getMTLTextureContent(subresource, mappedRange.offset, mappedRange.size); + }]; +} + +MVKImagePlane::MVKImagePlane(MVKImage* image, uint8_t planeIndex) { + _image = image; + _planeIndex = planeIndex; + _mtlTexture = nil; +} + +MVKImagePlane::~MVKImagePlane() { + releaseMTLTexture(); +} + + +#pragma mark - +#pragma mark MVKImageMemoryBinding + +VkResult MVKImageMemoryBinding::getMemoryRequirements(VkMemoryRequirements* pMemoryRequirements) { + pMemoryRequirements->size = _byteCount; + pMemoryRequirements->alignment = _byteAlignment; + return VK_SUCCESS; +} + +VkResult MVKImageMemoryBinding::getMemoryRequirements(const void*, VkMemoryRequirements2* pMemoryRequirements) { + pMemoryRequirements->sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2; + for (auto* next = (VkBaseOutStructure*)pMemoryRequirements->pNext; next; next = next->pNext) { + switch (next->sType) { + case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS: { + auto* dedicatedReqs = (VkMemoryDedicatedRequirements*)next; + bool writable = mvkIsAnyFlagEnabled(_image->_usage, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT); + dedicatedReqs->requiresDedicatedAllocation = _requiresDedicatedMemoryAllocation; + dedicatedReqs->prefersDedicatedAllocation = (dedicatedReqs->requiresDedicatedAllocation || + (!_usesTexelBuffer && (writable || !_device->_pMetalFeatures->placementHeaps))); + break; + } + default: + break; + } + } + return VK_SUCCESS; +} + +// Memory may have been mapped before image was bound, and needs to be loaded into the MTLTexture. +VkResult MVKImageMemoryBinding::bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset) { + if (_deviceMemory) { _deviceMemory->removeImageMemoryBinding(this); } + MVKResource::bindDeviceMemory(mvkMem, memOffset); + + _usesTexelBuffer = _device->_pMetalFeatures->texelBuffers && _deviceMemory && _deviceMemory->_mtlBuffer; // Texel buffers available + _usesTexelBuffer = _usesTexelBuffer && (isMemoryHostAccessible() || _device->_pMetalFeatures->placementHeaps) && _image->_isLinear && !_image->getIsCompressed(); // Applicable memory layout + +#if MVK_MACOS + // macOS cannot use shared memory for texel buffers. + _usesTexelBuffer = _usesTexelBuffer && !isMemoryHostCoherent(); +#endif + + flushToDevice(getDeviceMemoryOffset(), getByteCount()); + return _deviceMemory ? _deviceMemory->addImageMemoryBinding(this) : VK_SUCCESS; +} + +void MVKImageMemoryBinding::applyMemoryBarrier(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + MVKPipelineBarrier& barrier, + MVKCommandEncoder* cmdEncoder, + MVKCommandUse cmdUse) { +#if MVK_MACOS + if ( needsHostReadSync(srcStageMask, dstStageMask, barrier) ) { + for(uint8_t planeIndex = beginPlaneIndex(); planeIndex < endPlaneIndex(); planeIndex++) { + [cmdEncoder->getMTLBlitEncoder(cmdUse) synchronizeResource: _image->_planes[planeIndex]->_mtlTexture]; + } + } +#endif +} + +void MVKImageMemoryBinding::propagateDebugName() { + for(uint8_t planeIndex = beginPlaneIndex(); planeIndex < endPlaneIndex(); planeIndex++) { + _image->_planes[planeIndex]->propagateDebugName(); + } +} + +// Returns whether the specified image memory barrier requires a sync between this +// texture and host memory for the purpose of the host reading texture memory. +bool MVKImageMemoryBinding::needsHostReadSync(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + MVKPipelineBarrier& barrier) { +#if MVK_MACOS + // On macOS, texture memory is never host-coherent, so don't test for it. + return ((barrier.newLayout == VK_IMAGE_LAYOUT_GENERAL) && + mvkIsAnyFlagEnabled(barrier.dstAccessMask, (VK_ACCESS_HOST_READ_BIT | VK_ACCESS_MEMORY_READ_BIT)) && + isMemoryHostAccessible()); +#endif +#if MVK_IOS + return false; +#endif +} + +bool MVKImageMemoryBinding::shouldFlushHostMemory() { return isMemoryHostAccessible() && !_usesTexelBuffer; } + +// Flushes the device memory at the specified memory range into the MTLTexture. Updates +// all subresources that overlap the specified range and are in an updatable layout state. +VkResult MVKImageMemoryBinding::flushToDevice(VkDeviceSize offset, VkDeviceSize size) { + if (shouldFlushHostMemory()) { + for(uint8_t planeIndex = beginPlaneIndex(); planeIndex < endPlaneIndex(); planeIndex++) { + for (auto& subRez : _image->_planes[planeIndex]->_subresources) { + switch (subRez.layoutState) { + case VK_IMAGE_LAYOUT_UNDEFINED: + case VK_IMAGE_LAYOUT_PREINITIALIZED: + case VK_IMAGE_LAYOUT_GENERAL: { + _image->_planes[planeIndex]->updateMTLTextureContent(subRez, offset, size); + break; + } + default: + break; + } + } + } + } + return VK_SUCCESS; +} + +// Pulls content from the MTLTexture into the device memory at the specified memory range. +// Pulls from all subresources that overlap the specified range and are in an updatable layout state. +VkResult MVKImageMemoryBinding::pullFromDevice(VkDeviceSize offset, VkDeviceSize size) { + if (shouldFlushHostMemory()) { + for(uint8_t planeIndex = beginPlaneIndex(); planeIndex < endPlaneIndex(); planeIndex++) { + for (auto& subRez : _image->_planes[planeIndex]->_subresources) { + switch (subRez.layoutState) { + case VK_IMAGE_LAYOUT_GENERAL: { + _image->_planes[planeIndex]->getMTLTextureContent(subRez, offset, size); + break; + } + default: + break; + } + } + } + } + return VK_SUCCESS; +} + +uint8_t MVKImageMemoryBinding::beginPlaneIndex() const { + return (_image->_memoryBindings.size() > 1) ? _planeIndex : 0; +} + +uint8_t MVKImageMemoryBinding::endPlaneIndex() const { + return (_image->_memoryBindings.size() > 1) ? _planeIndex : (uint8_t)_image->_memoryBindings.size(); +} + +MVKImageMemoryBinding::MVKImageMemoryBinding(MVKDevice* device, MVKImage* image, uint8_t planeIndex) : MVKResource(device) { + _image = image; + _planeIndex = planeIndex; + _usesTexelBuffer = false; +} + +MVKImageMemoryBinding::~MVKImageMemoryBinding() { + if (_deviceMemory) { _deviceMemory->removeImageMemoryBinding(this); } +} + #pragma mark MVKImage -void MVKImage::propagateDebugName() { setLabelIfNotNil(_mtlTexture, _debugName); } +uint8_t MVKImage::getPlaneFromVkImageAspectFlags(VkImageAspectFlags aspectMask) { + return (aspectMask & VK_IMAGE_ASPECT_PLANE_2_BIT) ? 2 : + (aspectMask & VK_IMAGE_ASPECT_PLANE_1_BIT) ? 1 : + 0; +} + +void MVKImage::propagateDebugName() { + for (uint8_t planeIndex = 0; planeIndex < _planes.size(); planeIndex++) { + _planes[planeIndex]->propagateDebugName(); + } +} VkImageType MVKImage::getImageType() { return mvkVkImageTypeFromMTLTextureType(_mtlTextureType); } -VkFormat MVKImage::getVkFormat() { return getPixelFormats()->getVkFormat(_mtlPixelFormat); } +bool MVKImage::getIsDepthStencil() { return getPixelFormats()->getFormatType(_vkFormat) == kMVKFormatDepthStencil; } -bool MVKImage::getIsDepthStencil() { return getPixelFormats()->getFormatType(_mtlPixelFormat) == kMVKFormatDepthStencil; } +bool MVKImage::getIsCompressed() { return getPixelFormats()->getFormatType(_vkFormat) == kMVKFormatCompressed; } -bool MVKImage::getIsCompressed() { return getPixelFormats()->getFormatType(_mtlPixelFormat) == kMVKFormatCompressed; } - -VkExtent3D MVKImage::getExtent3D(uint32_t mipLevel) { - return mvkMipmapLevelSizeFromBaseSize3D(_extent, mipLevel); +VkExtent3D MVKImage::getExtent3D(uint8_t planeIndex, uint32_t mipLevel) { + VkExtent3D extent = _extent; + if (_hasChromaSubsampling) { + extent.width /= _planes[planeIndex]->_blockTexelSize.width; + extent.height /= _planes[planeIndex]->_blockTexelSize.height; + } + return mvkMipmapLevelSizeFromBaseSize3D(extent, mipLevel); } -VkDeviceSize MVKImage::getBytesPerRow(uint32_t mipLevel) { - size_t bytesPerRow = getPixelFormats()->getBytesPerRow(_mtlPixelFormat, getExtent3D(mipLevel).width); +VkDeviceSize MVKImage::getBytesPerRow(uint8_t planeIndex, uint32_t mipLevel) { + size_t bytesPerRow = getPixelFormats()->getBytesPerRow(_vkFormat, getExtent3D(planeIndex, mipLevel).width); return mvkAlignByteCount(bytesPerRow, _rowByteAlignment); } -VkDeviceSize MVKImage::getBytesPerLayer(uint32_t mipLevel) { - return getPixelFormats()->getBytesPerLayer(_mtlPixelFormat, getBytesPerRow(mipLevel), getExtent3D(mipLevel).height); +VkDeviceSize MVKImage::getBytesPerLayer(uint8_t planeIndex, uint32_t mipLevel) { + VkExtent3D extent = getExtent3D(planeIndex, mipLevel); + size_t bytesPerRow = getPixelFormats()->getBytesPerRow(_vkFormat, extent.width); + return getPixelFormats()->getBytesPerLayer(_vkFormat, bytesPerRow, extent.height); } VkResult MVKImage::getSubresourceLayout(const VkImageSubresource* pSubresource, VkSubresourceLayout* pLayout) { - MVKImageSubresource* pImgRez = getSubresource(pSubresource->mipLevel, - pSubresource->arrayLayer); - if ( !pImgRez ) { return VK_INCOMPLETE; } + uint8_t planeIndex = MVKImage::getPlaneFromVkImageAspectFlags(pSubresource->aspectMask); + MVKImageSubresource* pImgRez = _planes[planeIndex]->getSubresource(pSubresource->mipLevel, pSubresource->arrayLayer); + if ( !pImgRez ) { return VK_INCOMPLETE; } - *pLayout = pImgRez->layout; - return VK_SUCCESS; + *pLayout = pImgRez->layout; + return VK_SUCCESS; } void MVKImage::getTransferDescriptorData(MVKImageDescriptorData& imgData) { @@ -81,256 +560,93 @@ void MVKImage::getTransferDescriptorData(MVKImageDescriptorData& imgData) { #pragma mark Resource memory -void MVKImage::applyMemoryBarrier(VkPipelineStageFlags srcStageMask, - VkPipelineStageFlags dstStageMask, - MVKPipelineBarrier& barrier, - MVKCommandEncoder* cmdEncoder, - MVKCommandUse cmdUse) { -#if MVK_MACOS - if ( needsHostReadSync(srcStageMask, dstStageMask, barrier) ) { - [cmdEncoder->getMTLBlitEncoder(cmdUse) synchronizeResource: getMTLTexture()]; - } -#endif -} - void MVKImage::applyImageMemoryBarrier(VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, MVKPipelineBarrier& barrier, MVKCommandEncoder* cmdEncoder, MVKCommandUse cmdUse) { - // Extract the mipmap levels that are to be updated - uint32_t mipLvlStart = barrier.baseMipLevel; - uint32_t mipLvlEnd = (barrier.levelCount == (uint8_t)VK_REMAINING_MIP_LEVELS - ? getMipLevelCount() - : (mipLvlStart + barrier.levelCount)); - - // Extract the cube or array layers (slices) that are to be updated - uint32_t layerStart = barrier.baseArrayLayer; - uint32_t layerEnd = (barrier.layerCount == (uint16_t)VK_REMAINING_ARRAY_LAYERS - ? getLayerCount() - : (layerStart + barrier.layerCount)); - -#if MVK_MACOS - bool needsSync = needsHostReadSync(srcStageMask, dstStageMask, barrier); - id mtlTex = needsSync ? getMTLTexture() : nil; - id mtlBlitEncoder = needsSync ? cmdEncoder->getMTLBlitEncoder(cmdUse) : nil; -#endif - - // Iterate across mipmap levels and layers, and update the image layout state for each - for (uint32_t mipLvl = mipLvlStart; mipLvl < mipLvlEnd; mipLvl++) { - for (uint32_t layer = layerStart; layer < layerEnd; layer++) { - MVKImageSubresource* pImgRez = getSubresource(mipLvl, layer); - if (pImgRez) { pImgRez->layoutState = barrier.newLayout; } -#if MVK_MACOS - if (needsSync) { [mtlBlitEncoder synchronizeTexture: mtlTex slice: layer level: mipLvl]; } -#endif + for (uint8_t planeIndex = 0; planeIndex < _planes.size(); planeIndex++) { + if ( !_hasChromaSubsampling || mvkIsAnyFlagEnabled(barrier.aspectMask, (VK_IMAGE_ASPECT_PLANE_0_BIT << planeIndex)) ) { + _planes[planeIndex]->applyImageMemoryBarrier(srcStageMask, dstStageMask, barrier, cmdEncoder, cmdUse); } - } + } } -// Returns whether the specified image memory barrier requires a sync between this -// texture and host memory for the purpose of the host reading texture memory. -bool MVKImage::needsHostReadSync(VkPipelineStageFlags srcStageMask, - VkPipelineStageFlags dstStageMask, - MVKPipelineBarrier& barrier) { +VkResult MVKImage::getMemoryRequirements(VkMemoryRequirements* pMemoryRequirements, uint8_t planeIndex) { + pMemoryRequirements->memoryTypeBits = (_isDepthStencilAttachment) + ? _device->getPhysicalDevice()->getPrivateMemoryTypes() + : _device->getPhysicalDevice()->getAllMemoryTypes(); #if MVK_MACOS - return ((barrier.newLayout == VK_IMAGE_LAYOUT_GENERAL) && - mvkIsAnyFlagEnabled(barrier.dstAccessMask, (VK_ACCESS_HOST_READ_BIT | VK_ACCESS_MEMORY_READ_BIT)) && - isMemoryHostAccessible() && !isMemoryHostCoherent()); -#endif -#if MVK_IOS_OR_TVOS - return false; -#endif -} - -// Returns a pointer to the internal subresource for the specified MIP level layer. -MVKImageSubresource* MVKImage::getSubresource(uint32_t mipLevel, uint32_t arrayLayer) { - uint32_t srIdx = (mipLevel * _arrayLayers) + arrayLayer; - return (srIdx < _subresources.size()) ? &_subresources[srIdx] : NULL; -} - -VkResult MVKImage::getMemoryRequirements(VkMemoryRequirements* pMemoryRequirements) { - pMemoryRequirements->size = _byteCount; - pMemoryRequirements->alignment = _byteAlignment; - pMemoryRequirements->memoryTypeBits = (_isDepthStencilAttachment - ? _device->getPhysicalDevice()->getPrivateMemoryTypes() - : _device->getPhysicalDevice()->getAllMemoryTypes()); -#if MVK_MACOS - // Metal on macOS does not provide native support for host-coherent memory, but Vulkan requires it for Linear images - if ( !_isLinear ) { - mvkDisableFlags(pMemoryRequirements->memoryTypeBits, _device->getPhysicalDevice()->getHostCoherentMemoryTypes()); - } + // Metal on macOS does not provide native support for host-coherent memory, but Vulkan requires it for Linear images + if ( !_isLinear ) { + mvkDisableFlags(pMemoryRequirements->memoryTypeBits, _device->getPhysicalDevice()->getHostCoherentMemoryTypes()); + } #endif #if MVK_IOS - // Only transient attachments may use memoryless storage - if (!mvkAreAllFlagsEnabled(_usage, VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT) ) { - mvkDisableFlags(pMemoryRequirements->memoryTypeBits, _device->getPhysicalDevice()->getLazilyAllocatedMemoryTypes()); - } + // Only transient attachments may use memoryless storage + if (!mvkAreAllFlagsEnabled(_usage, VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT) ) { + mvkDisableFlags(pMemoryRequirements->memoryTypeBits, _device->getPhysicalDevice()->getLazilyAllocatedMemoryTypes()); + } #endif - return VK_SUCCESS; + return _memoryBindings[planeIndex]->getMemoryRequirements(pMemoryRequirements); } -VkResult MVKImage::getMemoryRequirements(const void*, VkMemoryRequirements2* pMemoryRequirements) { - pMemoryRequirements->sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2; - getMemoryRequirements(&pMemoryRequirements->memoryRequirements); +VkResult MVKImage::getMemoryRequirements(const void* pInfo, VkMemoryRequirements2* pMemoryRequirements) { + uint8_t planeIndex = 0; for (auto* next = (VkBaseOutStructure*)pMemoryRequirements->pNext; next; next = next->pNext) { switch (next->sType) { - 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->requiresDedicatedAllocation = _requiresDedicatedMemoryAllocation; - dedicatedReqs->prefersDedicatedAllocation = (dedicatedReqs->requiresDedicatedAllocation || - (!_usesTexelBuffer && (writable || !_device->_pMetalFeatures->placementHeaps))); + case VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO: { + auto* planeReqs = (VkImagePlaneMemoryRequirementsInfo*)next; + planeIndex = MVKImage::getPlaneFromVkImageAspectFlags(planeReqs->planeAspect); break; } default: break; } } - return VK_SUCCESS; + return getMemoryRequirements(&pMemoryRequirements->memoryRequirements, planeIndex); } -// Memory may have been mapped before image was bound, and needs to be loaded into the MTLTexture. -VkResult MVKImage::bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset) { - if (_deviceMemory) { _deviceMemory->removeImage(this); } - - MVKResource::bindDeviceMemory(mvkMem, memOffset); - - _usesTexelBuffer = validateUseTexelBuffer(); - - flushToDevice(getDeviceMemoryOffset(), getByteCount()); - - return _deviceMemory ? _deviceMemory->addImage(this) : VK_SUCCESS; +VkResult MVKImage::bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset, uint8_t planeIndex) { + return _memoryBindings[planeIndex]->bindDeviceMemory(mvkMem, memOffset); } VkResult MVKImage::bindDeviceMemory2(const VkBindImageMemoryInfo* pBindInfo) { - return bindDeviceMemory((MVKDeviceMemory*)pBindInfo->memory, pBindInfo->memoryOffset); -} - -bool MVKImage::validateUseTexelBuffer() { - VkExtent2D blockExt = getPixelFormats()->getBlockTexelSize(_mtlPixelFormat); - bool isUncompressed = blockExt.width == 1 && blockExt.height == 1; - - bool useTexelBuffer = _device->_pMetalFeatures->texelBuffers; // Texel buffers available - useTexelBuffer = useTexelBuffer && (isMemoryHostAccessible() || _device->_pMetalFeatures->placementHeaps) && _isLinear && isUncompressed; // Applicable memory layout - useTexelBuffer = useTexelBuffer && _deviceMemory && _deviceMemory->_mtlBuffer; // Buffer is available to overlay - -#if MVK_MACOS - // macOS cannot use shared memory for texel buffers. - // Test _deviceMemory->isMemoryHostCoherent() directly because local version overrides. - useTexelBuffer = useTexelBuffer && _deviceMemory && !_deviceMemory->isMemoryHostCoherent(); -#endif - - return useTexelBuffer; -} - -bool MVKImage::shouldFlushHostMemory() { return isMemoryHostAccessible() && !_usesTexelBuffer; } - -// Flushes the device memory at the specified memory range into the MTLTexture. Updates -// all subresources that overlap the specified range and are in an updatable layout state. -VkResult MVKImage::flushToDevice(VkDeviceSize offset, VkDeviceSize size) { - if (shouldFlushHostMemory()) { - for (auto& subRez : _subresources) { - switch (subRez.layoutState) { - case VK_IMAGE_LAYOUT_UNDEFINED: - case VK_IMAGE_LAYOUT_PREINITIALIZED: - case VK_IMAGE_LAYOUT_GENERAL: { - updateMTLTextureContent(subRez, offset, size); - break; - } - default: - break; - } - } - } - return VK_SUCCESS; -} - -// Pulls content from the MTLTexture into the device memory at the specified memory range. -// Pulls from all subresources that overlap the specified range and are in an updatable layout state. -VkResult MVKImage::pullFromDevice(VkDeviceSize offset, VkDeviceSize size) { - if (shouldFlushHostMemory()) { - for (auto& subRez : _subresources) { - switch (subRez.layoutState) { - case VK_IMAGE_LAYOUT_GENERAL: { - getMTLTextureContent(subRez, offset, size); - break; - } - default: - break; - } - } - } - return VK_SUCCESS; + uint8_t planeIndex = 0; + for (const auto* next = (const VkBaseInStructure*)pBindInfo->pNext; next; next = next->pNext) { + switch (next->sType) { + case VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO: { + const VkBindImagePlaneMemoryInfo* imagePlaneMemoryInfo = (const VkBindImagePlaneMemoryInfo*)next; + planeIndex = MVKImage::getPlaneFromVkImageAspectFlags(imagePlaneMemoryInfo->planeAspect); + break; + } + default: + break; + } + } + return bindDeviceMemory((MVKDeviceMemory*)pBindInfo->memory, pBindInfo->memoryOffset, planeIndex); } #pragma mark Metal -id MVKImage::getMTLTexture() { - if ( !_mtlTexture && _mtlPixelFormat ) { - - // Lock and check again in case another thread has created the texture. - lock_guard lock(_lock); - if (_mtlTexture) { return _mtlTexture; } - - _mtlTexture = newMTLTexture(); // retained - - propagateDebugName(); - } - return _mtlTexture; +id MVKImage::getMTLTexture(uint8_t planeIndex) { + return _planes[planeIndex]->getMTLTexture(); } -id MVKImage::getMTLTexture(MTLPixelFormat mtlPixFmt) { - if (mtlPixFmt == _mtlPixelFormat) { return getMTLTexture(); } - - id mtlTex = _mtlTextureViews[mtlPixFmt]; - if ( !mtlTex ) { - // Lock and check again in case another thread has created the view texture. - // baseTex retrieved outside of lock to avoid deadlock if it too needs to be lazily created. - id baseTex = getMTLTexture(); - lock_guard lock(_lock); - mtlTex = _mtlTextureViews[mtlPixFmt]; - if ( !mtlTex ) { - mtlTex = [baseTex newTextureViewWithPixelFormat: mtlPixFmt]; // retained - _mtlTextureViews[mtlPixFmt] = mtlTex; - } - } - return mtlTex; +id MVKImage::getMTLTexture(uint8_t planeIndex, MTLPixelFormat mtlPixFmt) { + return _planes[planeIndex]->getMTLTexture(mtlPixFmt); } -id MVKImage::newMTLTexture() { - id mtlTex = nil; - MTLTextureDescriptor* mtlTexDesc = newMTLTextureDescriptor(); // temp retain - - if (_ioSurface) { - mtlTex = [getMTLDevice() newTextureWithDescriptor: mtlTexDesc iosurface: _ioSurface plane: 0]; - } else if (_usesTexelBuffer) { - mtlTex = [_deviceMemory->_mtlBuffer newTextureWithDescriptor: mtlTexDesc - offset: getDeviceMemoryOffset() - bytesPerRow: _subresources[0].layout.rowPitch]; - } else if (_deviceMemory->_mtlHeap && !getIsDepthStencil()) { // Metal support for depth/stencil from heaps is flaky - mtlTex = [_deviceMemory->_mtlHeap newTextureWithDescriptor: mtlTexDesc - offset: getDeviceMemoryOffset()]; - if (_isAliasable) [mtlTex makeAliasable]; - } else { - mtlTex = [getMTLDevice() newTextureWithDescriptor: mtlTexDesc]; - } - - [mtlTexDesc release]; // temp release - return mtlTex; -} - -VkResult MVKImage::setMTLTexture(id mtlTexture) { +VkResult MVKImage::setMTLTexture(uint8_t planeIndex, id mtlTexture) { lock_guard lock(_lock); - releaseMTLTexture(); releaseIOSurface(); + _planes[planeIndex]->releaseMTLTexture(); + _planes[planeIndex]->_mtlTexture = [mtlTexture retain]; // retained - _mtlTexture = [mtlTexture retain]; // retained - - _mtlPixelFormat = mtlTexture.pixelFormat; + _vkFormat = getPixelFormats()->getVkFormat(mtlTexture.pixelFormat); _mtlTextureType = mtlTexture.textureType; _extent.width = uint32_t(mtlTexture.width); _extent.height = uint32_t(mtlTexture.height); @@ -338,7 +654,7 @@ VkResult MVKImage::setMTLTexture(id mtlTexture) { _mipLevels = uint32_t(mtlTexture.mipmapLevelCount); _samples = mvkVkSampleCountFlagBitsFromSampleCount(mtlTexture.sampleCount); _arrayLayers = uint32_t(mtlTexture.arrayLength); - _usage = getPixelFormats()->getVkImageUsageFlags(mtlTexture.usage, _mtlPixelFormat); + _usage = getPixelFormats()->getVkImageUsageFlags(mtlTexture.usage, mtlTexture.pixelFormat); if (_device->_pMetalFeatures->ioSurfaces) { _ioSurface = mtlTexture.iosurface; @@ -348,14 +664,6 @@ VkResult MVKImage::setMTLTexture(id mtlTexture) { return VK_SUCCESS; } -// Removes and releases the MTLTexture object, and all associated texture views -void MVKImage::releaseMTLTexture() { - [_mtlTexture release]; - _mtlTexture = nil; - for (auto elem : _mtlTextureViews) { [elem.second release]; } - _mtlTextureViews.clear(); -} - void MVKImage::releaseIOSurface() { if (_ioSurface) { CFRelease(_ioSurface); @@ -372,35 +680,60 @@ VkResult MVKImage::useIOSurface(IOSurfaceRef ioSurface) { #if MVK_SUPPORT_IOSURFACE_BOOL - releaseMTLTexture(); + for (uint8_t planeIndex = 0; planeIndex < _planes.size(); planeIndex++) { + _planes[planeIndex]->releaseMTLTexture(); + } releaseIOSurface(); - MVKPixelFormats* pixFmts = getPixelFormats(); + MVKPixelFormats* pixFmts = getPixelFormats(); if (ioSurface) { if (IOSurfaceGetWidth(ioSurface) != _extent.width) { return reportError(VK_ERROR_INITIALIZATION_FAILED, "vkUseIOSurfaceMVK() : IOSurface width %zu does not match VkImage width %d.", IOSurfaceGetWidth(ioSurface), _extent.width); } if (IOSurfaceGetHeight(ioSurface) != _extent.height) { return reportError(VK_ERROR_INITIALIZATION_FAILED, "vkUseIOSurfaceMVK() : IOSurface height %zu does not match VkImage height %d.", IOSurfaceGetHeight(ioSurface), _extent.height); } - if (IOSurfaceGetBytesPerElement(ioSurface) != pixFmts->getBytesPerBlock(_mtlPixelFormat)) { return reportError(VK_ERROR_INITIALIZATION_FAILED, "vkUseIOSurfaceMVK() : IOSurface bytes per element %zu does not match VkImage bytes per element %d.", IOSurfaceGetBytesPerElement(ioSurface), pixFmts->getBytesPerBlock(_mtlPixelFormat)); } - if (IOSurfaceGetElementWidth(ioSurface) != pixFmts->getBlockTexelSize(_mtlPixelFormat).width) { return reportError(VK_ERROR_INITIALIZATION_FAILED, "vkUseIOSurfaceMVK() : IOSurface element width %zu does not match VkImage element width %d.", IOSurfaceGetElementWidth(ioSurface), pixFmts->getBlockTexelSize(_mtlPixelFormat).width); } - if (IOSurfaceGetElementHeight(ioSurface) != pixFmts->getBlockTexelSize(_mtlPixelFormat).height) { return reportError(VK_ERROR_INITIALIZATION_FAILED, "vkUseIOSurfaceMVK() : IOSurface element height %zu does not match VkImage element height %d.", IOSurfaceGetElementHeight(ioSurface), pixFmts->getBlockTexelSize(_mtlPixelFormat).height); } + if (IOSurfaceGetBytesPerElement(ioSurface) != pixFmts->getBytesPerBlock(_vkFormat)) { return reportError(VK_ERROR_INITIALIZATION_FAILED, "vkUseIOSurfaceMVK() : IOSurface bytes per element %zu does not match VkImage bytes per element %d.", IOSurfaceGetBytesPerElement(ioSurface), pixFmts->getBytesPerBlock(_vkFormat)); } + if (IOSurfaceGetElementWidth(ioSurface) != pixFmts->getBlockTexelSize(_vkFormat).width) { return reportError(VK_ERROR_INITIALIZATION_FAILED, "vkUseIOSurfaceMVK() : IOSurface element width %zu does not match VkImage element width %d.", IOSurfaceGetElementWidth(ioSurface), pixFmts->getBlockTexelSize(_vkFormat).width); } + if (IOSurfaceGetElementHeight(ioSurface) != pixFmts->getBlockTexelSize(_vkFormat).height) { return reportError(VK_ERROR_INITIALIZATION_FAILED, "vkUseIOSurfaceMVK() : IOSurface element height %zu does not match VkImage element height %d.", IOSurfaceGetElementHeight(ioSurface), pixFmts->getBlockTexelSize(_vkFormat).height); } + if (IOSurfaceGetPlaneCount(ioSurface) != _planes.size()) { return reportError(VK_ERROR_INITIALIZATION_FAILED, "vkUseIOSurfaceMVK() : IOSurface plane count %zu does not match VkImage plane count %lu.", IOSurfaceGetPlaneCount(ioSurface), _planes.size()); } + if (_hasChromaSubsampling) { + for (uint8_t planeIndex = 0; planeIndex < _planes.size(); ++planeIndex) { + if (IOSurfaceGetWidthOfPlane(ioSurface, planeIndex) != getExtent3D(planeIndex, 0).width) { return reportError(VK_ERROR_INITIALIZATION_FAILED, "vkUseIOSurfaceMVK() : IOSurface width %zu of plane %d does not match VkImage width %d.", IOSurfaceGetWidthOfPlane(ioSurface, planeIndex), planeIndex, getExtent3D(planeIndex, 0).width); } + if (IOSurfaceGetHeightOfPlane(ioSurface, planeIndex) != getExtent3D(planeIndex, 0).height) { return reportError(VK_ERROR_INITIALIZATION_FAILED, "vkUseIOSurfaceMVK() : IOSurface height %zu of plane %d does not match VkImage height %d.", IOSurfaceGetHeightOfPlane(ioSurface, planeIndex), planeIndex, getExtent3D(planeIndex, 0).height); } + if (IOSurfaceGetBytesPerElementOfPlane(ioSurface, planeIndex) != _planes[planeIndex]->_bytesPerBlock) { return reportError(VK_ERROR_INITIALIZATION_FAILED, "vkUseIOSurfaceMVK() : IOSurface bytes per element %zu of plane %d does not match VkImage bytes per element %d.", IOSurfaceGetBytesPerElementOfPlane(ioSurface, planeIndex), planeIndex, _planes[planeIndex]->_bytesPerBlock); } + if (IOSurfaceGetElementWidthOfPlane(ioSurface, planeIndex) != _planes[planeIndex]->_blockTexelSize.width) { return reportError(VK_ERROR_INITIALIZATION_FAILED, "vkUseIOSurfaceMVK() : IOSurface element width %zu of plane %d does not match VkImage element width %d.", IOSurfaceGetElementWidthOfPlane(ioSurface, planeIndex), planeIndex, _planes[planeIndex]->_blockTexelSize.width); } + if (IOSurfaceGetElementHeightOfPlane(ioSurface, planeIndex) != _planes[planeIndex]->_blockTexelSize.height) { return reportError(VK_ERROR_INITIALIZATION_FAILED, "vkUseIOSurfaceMVK() : IOSurface element height %zu of plane %d does not match VkImage element height %d.", IOSurfaceGetElementHeightOfPlane(ioSurface, planeIndex), planeIndex, _planes[planeIndex]->_blockTexelSize.height); } + } + } _ioSurface = ioSurface; CFRetain(_ioSurface); } else { + @autoreleasepool { + CFMutableDictionaryRef properties = CFDictionaryCreateMutableCopy(NULL, 0, (CFDictionaryRef)@{ + (id)kIOSurfaceWidth: @(_extent.width), + (id)kIOSurfaceHeight: @(_extent.height), + (id)kIOSurfaceBytesPerElement: @(pixFmts->getBytesPerBlock(_vkFormat)), + (id)kIOSurfaceElementWidth: @(pixFmts->getBlockTexelSize(_vkFormat).width), + (id)kIOSurfaceElementHeight: @(pixFmts->getBlockTexelSize(_vkFormat).height), #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - @autoreleasepool { - _ioSurface = IOSurfaceCreate((CFDictionaryRef)@{ - (id)kIOSurfaceWidth: @(_extent.width), - (id)kIOSurfaceHeight: @(_extent.height), - (id)kIOSurfaceBytesPerElement: @(pixFmts->getBytesPerBlock(_mtlPixelFormat)), - (id)kIOSurfaceElementWidth: @(pixFmts->getBlockTexelSize(_mtlPixelFormat).width), - (id)kIOSurfaceElementHeight: @(pixFmts->getBlockTexelSize(_mtlPixelFormat).height), - (id)kIOSurfaceIsGlobal: @(true), // Deprecated but needed for interprocess transfers - }); - } + (id)kIOSurfaceIsGlobal: @(true), // Deprecated but needed for interprocess transfers #pragma clang diagnostic pop - + }); + if(_hasChromaSubsampling) { + CFMutableArrayRef planeProperties = CFArrayCreateMutable(NULL, _planes.size(), NULL); + for (uint8_t planeIndex = 0; planeIndex < _planes.size(); ++planeIndex) { + CFArrayAppendValue(planeProperties, (CFDictionaryRef)@{ + (id)kIOSurfacePlaneWidth: @(getExtent3D(planeIndex, 0).width), + (id)kIOSurfacePlaneHeight: @(getExtent3D(planeIndex, 0).height), + (id)kIOSurfacePlaneBytesPerElement: @(_planes[planeIndex]->_bytesPerBlock), + (id)kIOSurfacePlaneElementWidth: @(_planes[planeIndex]->_blockTexelSize.width), + (id)kIOSurfacePlaneElementHeight: @(_planes[planeIndex]->_blockTexelSize.height), + }); + } + CFDictionaryAddValue(properties, (id)kIOSurfacePlaneInfo, planeProperties); + } + _ioSurface = IOSurfaceCreate(properties); + } } #endif @@ -408,40 +741,10 @@ VkResult MVKImage::useIOSurface(IOSurfaceRef ioSurface) { return VK_SUCCESS; } -// Returns a Metal texture descriptor constructed from the properties of this image. -// It is the caller's responsibility to release the returned descriptor object. -MTLTextureDescriptor* MVKImage::newMTLTextureDescriptor() { - MTLPixelFormat mtlPixFmt = _mtlPixelFormat; - MTLTextureUsage minUsage = MTLTextureUsageUnknown; -#if MVK_MACOS - if (_is3DCompressed) { - // Metal before 3.0 doesn't support 3D compressed textures, so we'll decompress - // the texture ourselves. This, then, is the *uncompressed* format. - mtlPixFmt = MTLPixelFormatBGRA8Unorm; - minUsage = MTLTextureUsageShaderWrite; - } -#endif - - MTLTextureDescriptor* mtlTexDesc = [MTLTextureDescriptor new]; // retained - mtlTexDesc.pixelFormat = mtlPixFmt; - mtlTexDesc.textureType = _mtlTextureType; - mtlTexDesc.width = _extent.width; - mtlTexDesc.height = _extent.height; - mtlTexDesc.depth = _extent.depth; - mtlTexDesc.mipmapLevelCount = _mipLevels; - mtlTexDesc.sampleCount = mvkSampleCountFromVkSampleCountFlagBits(_samples); - mtlTexDesc.arrayLength = _arrayLayers; - mtlTexDesc.usageMVK = getPixelFormats()->getMTLTextureUsage(_usage, mtlPixFmt, minUsage); - mtlTexDesc.storageModeMVK = getMTLStorageMode(); - mtlTexDesc.cpuCacheMode = getMTLCPUCacheMode(); - - return mtlTexDesc; -} - MTLStorageMode MVKImage::getMTLStorageMode() { - if ( !_deviceMemory ) return MTLStorageModePrivate; + if ( !_memoryBindings[0]->_deviceMemory ) return MTLStorageModePrivate; - MTLStorageMode stgMode = _deviceMemory->getMTLStorageMode(); + MTLStorageMode stgMode = _memoryBindings[0]->_deviceMemory->getMTLStorageMode(); if (_ioSurface && stgMode == MTLStorageModePrivate) { stgMode = MTLStorageModeShared; } @@ -453,121 +756,14 @@ MTLStorageMode MVKImage::getMTLStorageMode() { } MTLCPUCacheMode MVKImage::getMTLCPUCacheMode() { - return _deviceMemory ? _deviceMemory->getMTLCPUCacheMode() : MTLCPUCacheModeDefaultCache; -} - -bool MVKImage::isMemoryHostCoherent() { - return (getMTLStorageMode() == MTLStorageModeShared); -} - -// Updates the contents of the underlying MTLTexture, corresponding to the -// specified subresource definition, from the underlying memory buffer. -void MVKImage::updateMTLTextureContent(MVKImageSubresource& subresource, - VkDeviceSize offset, VkDeviceSize size) { - - VkImageSubresource& imgSubRez = subresource.subresource; - VkSubresourceLayout& imgLayout = subresource.layout; - - // Check if subresource overlaps the memory range. - VkDeviceSize memStart = offset; - VkDeviceSize memEnd = offset + size; - VkDeviceSize imgStart = imgLayout.offset; - VkDeviceSize imgEnd = imgLayout.offset + imgLayout.size; - if (imgStart >= memEnd || imgEnd <= memStart) { return; } - - // Don't update if host memory has not been mapped yet. - void* pHostMem = getHostMemoryAddress(); - if ( !pHostMem ) { return; } - - VkExtent3D mipExtent = getExtent3D(imgSubRez.mipLevel); - VkImageType imgType = getImageType(); - void* pImgBytes = (void*)((uintptr_t)pHostMem + imgLayout.offset); - - MTLRegion mtlRegion; - mtlRegion.origin = MTLOriginMake(0, 0, 0); - mtlRegion.size = mvkMTLSizeFromVkExtent3D(mipExtent); - -#if MVK_MACOS - std::unique_ptr decompBuffer; - if (_is3DCompressed) { - // We cannot upload the texture data directly in this case. But we - // can upload the decompressed image data. - std::unique_ptr codec = mvkCreateCodec(getVkFormat()); - if (!codec) { - reportError(VK_ERROR_FORMAT_NOT_SUPPORTED, "A 3D texture used a compressed format that MoltenVK does not yet support."); - return; - } - VkSubresourceLayout destLayout; - destLayout.rowPitch = 4 * mipExtent.width; - destLayout.depthPitch = destLayout.rowPitch * mipExtent.height; - destLayout.size = destLayout.depthPitch * mipExtent.depth; - decompBuffer = std::unique_ptr(new char[destLayout.size]); - codec->decompress(decompBuffer.get(), pImgBytes, destLayout, imgLayout, mipExtent); - pImgBytes = decompBuffer.get(); - imgLayout = destLayout; - } -#endif - - VkDeviceSize bytesPerRow = (imgType != VK_IMAGE_TYPE_1D) ? imgLayout.rowPitch : 0; - VkDeviceSize bytesPerImage = (imgType == VK_IMAGE_TYPE_3D) ? imgLayout.depthPitch : 0; - - id mtlTex = getMTLTexture(); - if (getPixelFormats()->isPVRTCFormat(mtlTex.pixelFormat)) { - bytesPerRow = 0; - bytesPerImage = 0; - } - - [mtlTex replaceRegion: mtlRegion - mipmapLevel: imgSubRez.mipLevel - slice: imgSubRez.arrayLayer - withBytes: pImgBytes - bytesPerRow: bytesPerRow - bytesPerImage: bytesPerImage]; -} - -// Updates the contents of the underlying memory buffer from the contents of -// the underlying MTLTexture, corresponding to the specified subresource definition. -void MVKImage::getMTLTextureContent(MVKImageSubresource& subresource, - VkDeviceSize offset, VkDeviceSize size) { - - VkImageSubresource& imgSubRez = subresource.subresource; - VkSubresourceLayout& imgLayout = subresource.layout; - - // Check if subresource overlaps the memory range. - VkDeviceSize memStart = offset; - VkDeviceSize memEnd = offset + size; - VkDeviceSize imgStart = imgLayout.offset; - VkDeviceSize imgEnd = imgLayout.offset + imgLayout.size; - if (imgStart >= memEnd || imgEnd <= memStart) { return; } - - // Don't update if host memory has not been mapped yet. - void* pHostMem = getHostMemoryAddress(); - if ( !pHostMem ) { return; } - - VkExtent3D mipExtent = getExtent3D(imgSubRez.mipLevel); - VkImageType imgType = getImageType(); - void* pImgBytes = (void*)((uintptr_t)pHostMem + imgLayout.offset); - - MTLRegion mtlRegion; - mtlRegion.origin = MTLOriginMake(0, 0, 0); - mtlRegion.size = mvkMTLSizeFromVkExtent3D(mipExtent); - - [getMTLTexture() getBytes: pImgBytes - bytesPerRow: (imgType != VK_IMAGE_TYPE_1D ? imgLayout.rowPitch : 0) - bytesPerImage: (imgType == VK_IMAGE_TYPE_3D ? imgLayout.depthPitch : 0) - fromRegion: mtlRegion - mipmapLevel: imgSubRez.mipLevel - slice: imgSubRez.arrayLayer]; + return _memoryBindings[0]->_deviceMemory ? _memoryBindings[0]->_deviceMemory->getMTLCPUCacheMode() : MTLCPUCacheModeDefaultCache; } #pragma mark Construction -MVKImage::MVKImage(MVKDevice* device, const VkImageCreateInfo* pCreateInfo) : MVKResource(device) { - - _mtlTexture = nil; +MVKImage::MVKImage(MVKDevice* device, const VkImageCreateInfo* pCreateInfo) : MVKVulkanAPIDeviceObject(device) { _ioSurface = nil; - _usesTexelBuffer = false; // Adjust the info components to be compatible with Metal, then use the modified versions to set other // config info. Vulkan allows unused extent dimensions to be zero, but Metal requires minimum of one. @@ -591,32 +787,53 @@ MVKImage::MVKImage(MVKDevice* device, const VkImageCreateInfo* pCreateInfo) : MV _isLinear = validateLinear(pCreateInfo, isAttachment); MVKPixelFormats* pixFmts = getPixelFormats(); - _mtlPixelFormat = pixFmts->getMTLPixelFormat(pCreateInfo->format); + _vkFormat = pCreateInfo->format; _usage = pCreateInfo->usage; _is3DCompressed = (getImageType() == VK_IMAGE_TYPE_3D) && (pixFmts->getFormatType(pCreateInfo->format) == kMVKFormatCompressed) && !_device->_pMetalFeatures->native3DCompressedTextures; _isDepthStencilAttachment = (mvkAreAllFlagsEnabled(pCreateInfo->usage, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) || mvkAreAllFlagsEnabled(pixFmts->getVkFormatProperties(pCreateInfo->format).optimalTilingFeatures, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)); _canSupportMTLTextureView = !_isDepthStencilAttachment || _device->_pMetalFeatures->stencilViews; - _hasExpectedTexelSize = (pixFmts->getBytesPerBlock(_mtlPixelFormat) == pixFmts->getBytesPerBlock(pCreateInfo->format)); - _rowByteAlignment = _isLinear ? _device->getVkFormatTexelBufferAlignment(pCreateInfo->format, this) : mvkEnsurePowerOfTwo(pixFmts->getBytesPerBlock(pCreateInfo->format)); - if (!_isLinear && _device->_pMetalFeatures->placementHeaps) { - MTLTextureDescriptor *mtlTexDesc = newMTLTextureDescriptor(); // temp retain - MTLSizeAndAlign sizeAndAlign = [_device->getMTLDevice() heapTextureSizeAndAlignWithDescriptor: mtlTexDesc]; - [mtlTexDesc release]; - _byteCount = sizeAndAlign.size; - _byteAlignment = sizeAndAlign.align; - _isAliasable = mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_IMAGE_CREATE_ALIAS_BIT); - } else { - // Calc _byteCount after _rowByteAlignment - _byteAlignment = _rowByteAlignment; - for (uint32_t mipLvl = 0; mipLvl < _mipLevels; mipLvl++) { - _byteCount += getBytesPerLayer(mipLvl) * _extent.depth * _arrayLayers; - } - } - initSubresources(pCreateInfo); + VkExtent2D blockTexelSizeOfPlane[3]; + uint32_t bytesPerBlockOfPlane[3]; + MTLPixelFormat mtlPixFmtOfPlane[3]; + uint8_t subsamplingPlaneCount = pixFmts->getChromaSubsamplingPlanes(_vkFormat, blockTexelSizeOfPlane, bytesPerBlockOfPlane, mtlPixFmtOfPlane); + uint8_t planeCount = std::max(subsamplingPlaneCount, (uint8_t)1); + uint8_t memoryBindingCount = (pCreateInfo->flags & VK_IMAGE_CREATE_DISJOINT_BIT) ? planeCount : 1; + _hasChromaSubsampling = (subsamplingPlaneCount > 0); + + for (uint8_t planeIndex = 0; planeIndex < memoryBindingCount; ++planeIndex) { + _memoryBindings.push_back(new MVKImageMemoryBinding(device, this, planeIndex)); + } + + for (uint8_t planeIndex = 0; planeIndex < planeCount; ++planeIndex) { + _planes.push_back(new MVKImagePlane(this, planeIndex)); + if (_hasChromaSubsampling) { + _planes[planeIndex]->_blockTexelSize = blockTexelSizeOfPlane[planeIndex]; + _planes[planeIndex]->_bytesPerBlock = bytesPerBlockOfPlane[planeIndex]; + _planes[planeIndex]->_mtlPixFmt = mtlPixFmtOfPlane[planeIndex]; + } else { + _planes[planeIndex]->_mtlPixFmt = getPixelFormats()->getMTLPixelFormat(_vkFormat); + } + _planes[planeIndex]->initSubresources(pCreateInfo); + MVKImageMemoryBinding* memoryBinding = _planes[planeIndex]->getMemoryBinding(); + if (!_isLinear && _device->_pMetalFeatures->placementHeaps) { + MTLTextureDescriptor* mtlTexDesc = _planes[planeIndex]->newMTLTextureDescriptor(); // temp retain + MTLSizeAndAlign sizeAndAlign = [_device->getMTLDevice() heapTextureSizeAndAlignWithDescriptor: mtlTexDesc]; + [mtlTexDesc release]; + memoryBinding->_byteCount += sizeAndAlign.size; + memoryBinding->_byteAlignment = std::max(memoryBinding->_byteAlignment, (VkDeviceSize)sizeAndAlign.align); + _isAliasable = mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_IMAGE_CREATE_ALIAS_BIT); + } else { + for (uint32_t mipLvl = 0; mipLvl < _mipLevels; mipLvl++) { + memoryBinding->_byteCount += getBytesPerLayer(planeIndex, mipLvl) * _extent.depth * _arrayLayers; + } + memoryBinding->_byteAlignment = std::max(memoryBinding->_byteAlignment, _rowByteAlignment); + } + } + _hasExpectedTexelSize = _hasChromaSubsampling || (pixFmts->getBytesPerBlock(_planes[0]->_mtlPixFmt) == pixFmts->getBytesPerBlock(_vkFormat)); for (const auto* next = (const VkBaseInStructure*)pCreateInfo->pNext; next; next = next->pNext) { switch (next->sType) { @@ -755,62 +972,21 @@ bool MVKImage::validateLinear(const VkImageCreateInfo* pCreateInfo, bool isAttac return isLin; } - -// Initializes the subresource definitions. -void MVKImage::initSubresources(const VkImageCreateInfo* pCreateInfo) { - _subresources.reserve(_mipLevels * _arrayLayers); - - MVKImageSubresource subRez; - subRez.layoutState = pCreateInfo->initialLayout; - - for (uint32_t mipLvl = 0; mipLvl < _mipLevels; mipLvl++) { - subRez.subresource.mipLevel = mipLvl; - - for (uint32_t layer = 0; layer < _arrayLayers; layer++) { - subRez.subresource.arrayLayer = layer; - initSubresourceLayout(subRez); - _subresources.push_back(subRez); - } - } -} - -// Initializes the layout element of the specified image subresource. -void MVKImage::initSubresourceLayout(MVKImageSubresource& imgSubRez) { - VkImageSubresource subresource = imgSubRez.subresource; - uint32_t currMipLevel = subresource.mipLevel; - uint32_t currArrayLayer = subresource.arrayLayer; - - VkDeviceSize bytesPerLayerCurrLevel = getBytesPerLayer(currMipLevel); - - // Accumulate the byte offset for the specified sub-resource. - // This is the sum of the bytes consumed by all layers in all mipmap levels before the - // desired level, plus the layers before the desired layer at the desired level. - VkDeviceSize offset = 0; - for (uint32_t mipLvl = 0; mipLvl < currMipLevel; mipLvl++) { - offset += (getBytesPerLayer(mipLvl) * _extent.depth * _arrayLayers); - } - offset += (bytesPerLayerCurrLevel * currArrayLayer); - - VkSubresourceLayout& layout = imgSubRez.layout; - layout.offset = offset; - layout.size = bytesPerLayerCurrLevel; - layout.rowPitch = getBytesPerRow(currMipLevel); - 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); + auto& xmProps = _device->getPhysicalDevice()->getExternalImageProperties(VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR); + for(auto& memoryBinding : _memoryBindings) { + memoryBinding->_externalMemoryHandleTypes = handleTypes; + memoryBinding->_requiresDedicatedMemoryAllocation = memoryBinding->_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(); + mvkDestroyContainerContents(_memoryBindings); + mvkDestroyContainerContents(_planes); releaseIOSurface(); } @@ -818,7 +994,7 @@ MVKImage::~MVKImage() { #pragma mark - #pragma mark MVKSwapchainImage -VkResult MVKSwapchainImage::bindDeviceMemory(MVKDeviceMemory*, VkDeviceSize) { +VkResult MVKSwapchainImage::bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset, uint8_t planeIndex) { return VK_ERROR_OUT_OF_DEVICE_MEMORY; } @@ -826,7 +1002,7 @@ VkResult MVKSwapchainImage::bindDeviceMemory(MVKDeviceMemory*, VkDeviceSize) { #pragma mark Metal // Overridden to always retrieve the MTLTexture directly from the CAMetalDrawable. -id MVKSwapchainImage::getMTLTexture() { return [getCAMetalDrawable() texture]; } +id MVKSwapchainImage::getMTLTexture(uint8_t planeIndex) { return [getCAMetalDrawable() texture]; } #pragma mark Construction @@ -968,7 +1144,7 @@ id MVKPresentableSwapchainImage::getCAMetalDrawable() { // Present the drawable and make myself available only once the command buffer has completed. void MVKPresentableSwapchainImage::presentCAMetalDrawable(id mtlCmdBuff, bool hasPresentTime, uint32_t presentID, uint64_t desiredPresentTime) { - _swapchain->willPresentSurface(getMTLTexture(), mtlCmdBuff); + _swapchain->willPresentSurface(getMTLTexture(0), mtlCmdBuff); NSString* scName = _swapchain->getDebugName(); if (scName) { mvkPushDebugGroup(mtlCmdBuff, scName); } @@ -991,7 +1167,7 @@ void MVKPresentableSwapchainImage::presentCAMetalDrawable(id m }]; if (hasPresentTime) { - if (@available(iOS 10.3, macOS 10.15.4, *)) { + if ([_mtlDrawable respondsToSelector: @selector(addPresentedHandler:)]) { [_mtlDrawable addPresentedHandler: ^(id drawable) { // Record the presentation time CFTimeInterval presentedTimeSeconds = drawable.presentedTime; @@ -1008,8 +1184,10 @@ void MVKPresentableSwapchainImage::presentCAMetalDrawable(id m // Resets the MTLTexture and CAMetalDrawable underlying this image. void MVKPresentableSwapchainImage::releaseMetalDrawable() { - releaseMTLTexture(); // Release texture first so drawable will be last to release it - [_mtlDrawable release]; + for (uint8_t planeIndex = 0; planeIndex < _planes.size(); ++planeIndex) { + _planes[planeIndex]->releaseMTLTexture(); + } + [_mtlDrawable release]; _mtlDrawable = nil; } @@ -1072,83 +1250,142 @@ MVKPeerSwapchainImage::MVKPeerSwapchainImage(MVKDevice* device, +#pragma mark - +#pragma mark MVKImageViewPlane + +MVKVulkanAPIObject* MVKImageViewPlane::getVulkanAPIObject() { return _imageView; } + +void MVKImageViewPlane::propagateDebugName() { setLabelIfNotNil(_mtlTexture, _imageView->_debugName); } + + +#pragma mark Metal + +id MVKImageViewPlane::getMTLTexture() { + // If we can use a Metal texture view, lazily create it, otherwise use the image texture directly. + if (_useMTLTextureView) { + if ( !_mtlTexture && _mtlPixFmt ) { + + // Lock and check again in case another thread created the texture view + lock_guard lock(_imageView->_lock); + if (_mtlTexture) { return _mtlTexture; } + + _mtlTexture = newMTLTexture(); // retained + + propagateDebugName(); + } + return _mtlTexture; + } else { + return _imageView->_image->getMTLTexture(_planeIndex); + } +} + +// Creates and returns a retained Metal texture as an +// overlay on the Metal texture of the underlying image. +id MVKImageViewPlane::newMTLTexture() { + MTLTextureType mtlTextureType = _imageView->_mtlTextureType; + NSRange sliceRange = NSMakeRange(_imageView->_subresourceRange.baseArrayLayer, _imageView->_subresourceRange.layerCount); + // Fake support for 2D views of 3D textures. + if (_imageView->_image->getImageType() == VK_IMAGE_TYPE_3D && + (mtlTextureType == MTLTextureType2D || mtlTextureType == MTLTextureType2DArray)) { + mtlTextureType = MTLTextureType3D; + sliceRange = NSMakeRange(0, 1); + } + id mtlTex = _imageView->_image->getMTLTexture(MVKImage::getPlaneFromVkImageAspectFlags(_imageView->_subresourceRange.aspectMask)); + if (_device->_pMetalFeatures->nativeTextureSwizzle && _packedSwizzle) { + return [mtlTex newTextureViewWithPixelFormat: _mtlPixFmt + textureType: mtlTextureType + levels: NSMakeRange(_imageView->_subresourceRange.baseMipLevel, _imageView->_subresourceRange.levelCount) + slices: sliceRange + swizzle: mvkMTLTextureSwizzleChannelsFromVkComponentMapping(mvkUnpackSwizzle(_packedSwizzle))]; // retained + } else { + return [mtlTex newTextureViewWithPixelFormat: _mtlPixFmt + textureType: mtlTextureType + levels: NSMakeRange(_imageView->_subresourceRange.baseMipLevel, _imageView->_subresourceRange.levelCount) + slices: sliceRange]; // retained + } +} + + +#pragma mark Construction + +MVKImageViewPlane::MVKImageViewPlane(MVKImageView* imageView, + uint8_t planeIndex, + MTLPixelFormat mtlPixFmt, + const VkImageViewCreateInfo* pCreateInfo) : MVKBaseDeviceObject(imageView->_device) { + _imageView = imageView; + _planeIndex = planeIndex; + _mtlPixFmt = mtlPixFmt; + _mtlTexture = nil; + + bool useSwizzle; + _imageView->setConfigurationResult(_imageView->validateSwizzledMTLPixelFormat(pCreateInfo, + _imageView, + _device->_pMetalFeatures->nativeTextureSwizzle, + _device->_pMVKConfig->fullImageViewSwizzle, + _mtlPixFmt, + useSwizzle)); + _packedSwizzle = (useSwizzle) ? mvkPackSwizzle(pCreateInfo->components) : 0; + + // Determine whether this image view should use a Metal texture view, + // and set the _useMTLTextureView variable appropriately. + if ( _imageView->_image ) { + _useMTLTextureView = _imageView->_image->_canSupportMTLTextureView; + bool is3D = _imageView->_image->_mtlTextureType == MTLTextureType3D; + // If the view is identical to underlying image, don't bother using a Metal view + if (_mtlPixFmt == _imageView->_image->getMTLPixelFormat(planeIndex) && + (_imageView->_mtlTextureType == _imageView->_image->_mtlTextureType || + ((_imageView->_mtlTextureType == MTLTextureType2D || _imageView->_mtlTextureType == MTLTextureType2DArray) && is3D)) && + _imageView->_subresourceRange.levelCount == _imageView->_image->_mipLevels && + (is3D || _imageView->_subresourceRange.layerCount == _imageView->_image->_arrayLayers) && + (!_device->_pMetalFeatures->nativeTextureSwizzle || !_packedSwizzle)) { + _useMTLTextureView = false; + } + } else { + _useMTLTextureView = false; + } +} + +MVKImageViewPlane::~MVKImageViewPlane() { + [_mtlTexture release]; +} + + #pragma mark - #pragma mark MVKImageView -void MVKImageView::propagateDebugName() { setLabelIfNotNil(_mtlTexture, _debugName); } +void MVKImageView::propagateDebugName() { + for (uint8_t planeIndex = 0; planeIndex < _planes.size(); planeIndex++) { + _planes[planeIndex]->propagateDebugName(); + } +} void MVKImageView::populateMTLRenderPassAttachmentDescriptor(MTLRenderPassAttachmentDescriptor* mtlAttDesc) { - mtlAttDesc.texture = getMTLTexture(); // Use image view, necessary if image view format differs from image format - mtlAttDesc.level = _useMTLTextureView ? 0 : _subresourceRange.baseMipLevel; + MVKImageViewPlane* plane = _planes[0]; + mtlAttDesc.texture = plane->getMTLTexture(); // Use image view, necessary if image view format differs from image format + mtlAttDesc.level = plane->_useMTLTextureView ? 0 : _subresourceRange.baseMipLevel; if (mtlAttDesc.texture.textureType == MTLTextureType3D) { mtlAttDesc.slice = 0; - mtlAttDesc.depthPlane = _useMTLTextureView ? 0 : _subresourceRange.baseArrayLayer; + mtlAttDesc.depthPlane = plane->_useMTLTextureView ? 0 : _subresourceRange.baseArrayLayer; } else { - mtlAttDesc.slice = _useMTLTextureView ? 0 : _subresourceRange.baseArrayLayer; + mtlAttDesc.slice = plane->_useMTLTextureView ? 0 : _subresourceRange.baseArrayLayer; mtlAttDesc.depthPlane = 0; } } void MVKImageView::populateMTLRenderPassAttachmentDescriptorResolve(MTLRenderPassAttachmentDescriptor* mtlAttDesc) { - mtlAttDesc.resolveTexture = getMTLTexture(); // Use image view, necessary if image view format differs from image format - mtlAttDesc.resolveLevel = _useMTLTextureView ? 0 : _subresourceRange.baseMipLevel; + MVKImageViewPlane* plane = _planes[0]; + mtlAttDesc.resolveTexture = plane->getMTLTexture(); // Use image view, necessary if image view format differs from image format + mtlAttDesc.resolveLevel = plane->_useMTLTextureView ? 0 : _subresourceRange.baseMipLevel; if (mtlAttDesc.resolveTexture.textureType == MTLTextureType3D) { mtlAttDesc.resolveSlice = 0; - mtlAttDesc.resolveDepthPlane = _useMTLTextureView ? 0 : _subresourceRange.baseArrayLayer; + mtlAttDesc.resolveDepthPlane = plane->_useMTLTextureView ? 0 : _subresourceRange.baseArrayLayer; } else { - mtlAttDesc.resolveSlice = _useMTLTextureView ? 0 : _subresourceRange.baseArrayLayer; + mtlAttDesc.resolveSlice = plane->_useMTLTextureView ? 0 : _subresourceRange.baseArrayLayer; mtlAttDesc.resolveDepthPlane = 0; } } -#pragma mark Metal - -id MVKImageView::getMTLTexture() { - // If we can use a Metal texture view, lazily create it, otherwise use the image texture directly. - if (_useMTLTextureView) { - if ( !_mtlTexture && _mtlPixelFormat ) { - - // Lock and check again in case another thread created the texture view - lock_guard lock(_lock); - if (_mtlTexture) { return _mtlTexture; } - - _mtlTexture = newMTLTexture(); // retained - - propagateDebugName(); - } - return _mtlTexture; - } else { - return _image->getMTLTexture(); - } -} - -// Creates and returns a retained Metal texture as an -// overlay on the Metal texture of the underlying image. -id MVKImageView::newMTLTexture() { - MTLTextureType mtlTextureType = _mtlTextureType; - NSRange sliceRange = NSMakeRange(_subresourceRange.baseArrayLayer, _subresourceRange.layerCount); - // Fake support for 2D views of 3D textures. - if (_image->getImageType() == VK_IMAGE_TYPE_3D && - (mtlTextureType == MTLTextureType2D || mtlTextureType == MTLTextureType2DArray)) { - mtlTextureType = MTLTextureType3D; - sliceRange = NSMakeRange(0, 1); - } - if (_device->_pMetalFeatures->nativeTextureSwizzle && _packedSwizzle) { - return [_image->getMTLTexture() newTextureViewWithPixelFormat: _mtlPixelFormat - textureType: mtlTextureType - levels: NSMakeRange(_subresourceRange.baseMipLevel, _subresourceRange.levelCount) - slices: sliceRange - swizzle: mvkMTLTextureSwizzleChannelsFromVkComponentMapping(mvkUnpackSwizzle(_packedSwizzle))]; // retained - } else { - return [_image->getMTLTexture() newTextureViewWithPixelFormat: _mtlPixelFormat - textureType: mtlTextureType - levels: NSMakeRange(_subresourceRange.baseMipLevel, _subresourceRange.levelCount) - slices: sliceRange]; // retained - } -} - - #pragma mark Construction MVKImageView::MVKImageView(MVKDevice* device, @@ -1156,6 +1393,8 @@ MVKImageView::MVKImageView(MVKDevice* device, const MVKConfiguration* pAltMVKConfig) : MVKVulkanAPIDeviceObject(device) { _image = (MVKImage*)pCreateInfo->image; _usage = _image->_usage; + _mtlTextureType = mvkMTLTextureTypeFromVkImageViewType(pCreateInfo->viewType, + _image->getSampleCount() != VK_SAMPLE_COUNT_1_BIT); for (const auto* next = (VkBaseInStructure*)pCreateInfo->pNext; next; next = next->pNext) { switch (next->sType) { @@ -1164,12 +1403,32 @@ MVKImageView::MVKImageView(MVKDevice* device, if (!(pViewUsageInfo->usage & ~_usage)) { _usage = pViewUsageInfo->usage; } break; } + /* case VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO_KHR: { + const VkSamplerYcbcrConversionInfoKHR* sampConvInfo = (const VkSamplerYcbcrConversionInfoKHR*)next; + break; + } */ default: break; } } - validateImageViewConfig(pCreateInfo); + // Validate whether the image view configuration can be supported + if ( _image ) { + VkImageType imgType = _image->getImageType(); + VkImageViewType viewType = pCreateInfo->viewType; + + // VK_KHR_maintenance1 supports taking 2D image views of 3D slices. No dice in Metal. + if ((viewType == VK_IMAGE_VIEW_TYPE_2D || viewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY) && (imgType == VK_IMAGE_TYPE_3D)) { + if (pCreateInfo->subresourceRange.layerCount != _image->_extent.depth) { + reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImageView(): Metal does not fully support views on a subset of a 3D texture."); + } + if ( !mvkIsAnyFlagEnabled(_usage, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) ) { + setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImageView(): 2D views on 3D images can only be used as color attachments.")); + } else if (mvkIsOnlyAnyFlagEnabled(_usage, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) { + reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImageView(): 2D views on 3D images can only be used as color attachments."); + } + } + } // Remember the subresource range, and determine the actual number of mip levels and texture slices _subresourceRange = pCreateInfo->subresourceRange; @@ -1180,50 +1439,35 @@ MVKImageView::MVKImageView(MVKDevice* device, _subresourceRange.layerCount = _image->getLayerCount() - _subresourceRange.baseArrayLayer; } - _mtlTexture = nil; - bool useSwizzle; - setConfigurationResult(validateSwizzledMTLPixelFormat(pCreateInfo, getPixelFormats(), this, - _device->_pMetalFeatures->nativeTextureSwizzle, - _device->_pMVKConfig->fullImageViewSwizzle, - _mtlPixelFormat, useSwizzle)); - _packedSwizzle = useSwizzle ? mvkPackSwizzle(pCreateInfo->components) : 0; - _mtlTextureType = mvkMTLTextureTypeFromVkImageViewType(pCreateInfo->viewType, - _image->getSampleCount() != VK_SAMPLE_COUNT_1_BIT); - initMTLTextureViewSupport(); -} - -// Validate whether the image view configuration can be supported -void MVKImageView::validateImageViewConfig(const VkImageViewCreateInfo* pCreateInfo) { - - // No image if we are just validating view config - MVKImage* image = (MVKImage*)pCreateInfo->image; - if ( !image ) { return; } - - VkImageType imgType = image->getImageType(); - VkImageViewType viewType = pCreateInfo->viewType; - - // VK_KHR_maintenance1 supports taking 2D image views of 3D slices. No dice in Metal. - if ((viewType == VK_IMAGE_VIEW_TYPE_2D || viewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY) && (imgType == VK_IMAGE_TYPE_3D)) { - if (pCreateInfo->subresourceRange.layerCount != image->_extent.depth) { - reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImageView(): Metal does not fully support views on a subset of a 3D texture."); - } - if ( !mvkIsAnyFlagEnabled(_usage, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) ) { - setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImageView(): 2D views on 3D images can only be used as color attachments.")); - } else if (mvkIsOnlyAnyFlagEnabled(_usage, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) { - reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImageView(): 2D views on 3D images can only be used as color attachments."); - } - } + VkExtent2D blockTexelSizeOfPlane[3]; + uint32_t bytesPerBlockOfPlane[3]; + MTLPixelFormat mtlPixFmtOfPlane[3]; + uint8_t subsamplingPlaneCount = getPixelFormats()->getChromaSubsamplingPlanes(pCreateInfo->format, blockTexelSizeOfPlane, bytesPerBlockOfPlane, mtlPixFmtOfPlane), + beginPlaneIndex = 0, + endPlaneIndex = subsamplingPlaneCount; + if (subsamplingPlaneCount == 0) { + endPlaneIndex = 1; + mtlPixFmtOfPlane[0] = getPixelFormats()->getMTLPixelFormat(pCreateInfo->format); + } else { + if (!mvkVkComponentMappingsMatch(pCreateInfo->components, {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A})) { + setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "Image view swizzling for multi planar formats is not supported.")); + } + if (_subresourceRange.aspectMask & (VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT | VK_IMAGE_ASPECT_PLANE_2_BIT)) { + beginPlaneIndex = endPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(_subresourceRange.aspectMask); + } + } + for (uint8_t planeIndex = beginPlaneIndex; planeIndex < endPlaneIndex; planeIndex++) { + _planes.push_back(new MVKImageViewPlane(this, planeIndex, mtlPixFmtOfPlane[planeIndex], pCreateInfo)); + } } VkResult MVKImageView::validateSwizzledMTLPixelFormat(const VkImageViewCreateInfo* pCreateInfo, - MVKPixelFormats* mvkPixFmts, MVKVulkanAPIObject* apiObject, bool hasNativeSwizzleSupport, bool hasShaderSwizzleSupport, MTLPixelFormat& mtlPixFmt, bool& useSwizzle) { useSwizzle = false; - mtlPixFmt = mvkPixFmts->getMTLPixelFormat(pCreateInfo->format); VkComponentMapping components = pCreateInfo->components; #define SWIZZLE_MATCHES(R, G, B, A) mvkVkComponentMappingsMatch(components, {VK_COMPONENT_SWIZZLE_ ##R, VK_COMPONENT_SWIZZLE_ ##G, VK_COMPONENT_SWIZZLE_ ##B, VK_COMPONENT_SWIZZLE_ ##A} ) @@ -1329,32 +1573,94 @@ VkResult MVKImageView::validateSwizzledMTLPixelFormat(const VkImageViewCreateInf mvkVkComponentSwizzleName(components.b), mvkVkComponentSwizzleName(components.a)); } -// Determine whether this image view should use a Metal texture view, -// and set the _useMTLTextureView variable appropriately. -void MVKImageView::initMTLTextureViewSupport() { +MVKImageView::~MVKImageView() { + mvkDestroyContainerContents(_planes); +} - // If no image we're just validating image iview config - if ( !_image ) { - _useMTLTextureView = false; - return; - } - _useMTLTextureView = _image->_canSupportMTLTextureView; +#pragma mark - +#pragma mark MVKSamplerYcbcrConversion - bool is3D = _image->_mtlTextureType == MTLTextureType3D; - // If the view is identical to underlying image, don't bother using a Metal view - if (_mtlPixelFormat == _image->_mtlPixelFormat && - (_mtlTextureType == _image->_mtlTextureType || - ((_mtlTextureType == MTLTextureType2D || _mtlTextureType == MTLTextureType2DArray) && is3D)) && - _subresourceRange.levelCount == _image->_mipLevels && - (is3D || _subresourceRange.layerCount == _image->_arrayLayers) && - (!_device->_pMetalFeatures->nativeTextureSwizzle || !_packedSwizzle)) { - _useMTLTextureView = false; +void MVKSamplerYcbcrConversion::updateConstExprSampler(MSLConstexprSampler& constExprSampler) const { + constExprSampler.planes = _planes; + constExprSampler.resolution = _resolution; + constExprSampler.chroma_filter = _chroma_filter; + constExprSampler.x_chroma_offset = _x_chroma_offset; + constExprSampler.y_chroma_offset = _y_chroma_offset; + for (uint32_t i = 0; i < 4; ++i) { + constExprSampler.swizzle[i] = _swizzle[i]; + } + constExprSampler.ycbcr_model = _ycbcr_model; + constExprSampler.ycbcr_range = _ycbcr_range; + constExprSampler.bpc = _bpc; + constExprSampler.ycbcr_conversion_enable = true; +} + +static MSLSamplerFilter getSpvMinMagFilterFromVkFilter(VkFilter vkFilter) { + switch (vkFilter) { + case VK_FILTER_LINEAR: return MSL_SAMPLER_FILTER_LINEAR; + + case VK_FILTER_NEAREST: + default: + return MSL_SAMPLER_FILTER_NEAREST; + } +} + +static MSLChromaLocation getSpvChromaLocationFromVkChromaLocation(VkChromaLocation vkChromaLocation) { + switch (vkChromaLocation) { + default: + case VK_CHROMA_LOCATION_COSITED_EVEN: return MSL_CHROMA_LOCATION_COSITED_EVEN; + case VK_CHROMA_LOCATION_MIDPOINT: return MSL_CHROMA_LOCATION_MIDPOINT; } } -MVKImageView::~MVKImageView() { - [_mtlTexture release]; +static MSLComponentSwizzle getSpvComponentSwizzleFromVkComponentMapping(VkComponentSwizzle vkComponentSwizzle) { + switch (vkComponentSwizzle) { + default: + case VK_COMPONENT_SWIZZLE_IDENTITY: return MSL_COMPONENT_SWIZZLE_IDENTITY; + case VK_COMPONENT_SWIZZLE_ZERO: return MSL_COMPONENT_SWIZZLE_ZERO; + case VK_COMPONENT_SWIZZLE_ONE: return MSL_COMPONENT_SWIZZLE_ONE; + case VK_COMPONENT_SWIZZLE_R: return MSL_COMPONENT_SWIZZLE_R; + case VK_COMPONENT_SWIZZLE_G: return MSL_COMPONENT_SWIZZLE_G; + case VK_COMPONENT_SWIZZLE_B: return MSL_COMPONENT_SWIZZLE_B; + case VK_COMPONENT_SWIZZLE_A: return MSL_COMPONENT_SWIZZLE_A; + } +} + +static MSLSamplerYCbCrModelConversion getSpvSamplerYCbCrModelConversionFromVkSamplerYcbcrModelConversion(VkSamplerYcbcrModelConversion vkSamplerYcbcrModelConversion) { + switch (vkSamplerYcbcrModelConversion) { + default: + case VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY: return MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY; + case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY: return MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY; + case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709: return MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_709; + case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601: return MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_601; + case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020: return MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_2020; + } +} + +static MSLSamplerYCbCrRange getSpvSamplerYcbcrRangeFromVkSamplerYcbcrRange(VkSamplerYcbcrRange vkSamplerYcbcrRange) { + switch (vkSamplerYcbcrRange) { + default: + case VK_SAMPLER_YCBCR_RANGE_ITU_FULL: return MSL_SAMPLER_YCBCR_RANGE_ITU_FULL; + case VK_SAMPLER_YCBCR_RANGE_ITU_NARROW: return MSL_SAMPLER_YCBCR_RANGE_ITU_NARROW; + } +} + +MVKSamplerYcbcrConversion::MVKSamplerYcbcrConversion(MVKDevice* device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo) : MVKVulkanAPIDeviceObject(device) { + MVKPixelFormats* pixFmts = getPixelFormats(); + _planes = pixFmts->getChromaSubsamplingPlaneCount(pCreateInfo->format); + _bpc = pixFmts->getChromaSubsamplingComponentBits(pCreateInfo->format); + _resolution = pixFmts->getChromaSubsamplingResolution(pCreateInfo->format); + _chroma_filter = getSpvMinMagFilterFromVkFilter(pCreateInfo->chromaFilter); + _x_chroma_offset = getSpvChromaLocationFromVkChromaLocation(pCreateInfo->xChromaOffset); + _y_chroma_offset = getSpvChromaLocationFromVkChromaLocation(pCreateInfo->yChromaOffset); + _swizzle[0] = getSpvComponentSwizzleFromVkComponentMapping(pCreateInfo->components.r); + _swizzle[1] = getSpvComponentSwizzleFromVkComponentMapping(pCreateInfo->components.g); + _swizzle[2] = getSpvComponentSwizzleFromVkComponentMapping(pCreateInfo->components.b); + _swizzle[3] = getSpvComponentSwizzleFromVkComponentMapping(pCreateInfo->components.a); + _ycbcr_model = getSpvSamplerYCbCrModelConversionFromVkSamplerYcbcrModelConversion(pCreateInfo->ycbcrModel); + _ycbcr_range = getSpvSamplerYcbcrRangeFromVkSamplerYcbcrRange(pCreateInfo->ycbcrRange); + _forceExplicitReconstruction = pCreateInfo->forceExplicitReconstruction; } @@ -1415,7 +1721,20 @@ MTLSamplerDescriptor* MVKSampler::newMTLSamplerDescriptor(const VkSamplerCreateI } MVKSampler::MVKSampler(MVKDevice* device, const VkSamplerCreateInfo* pCreateInfo) : MVKVulkanAPIDeviceObject(device) { - _requiresConstExprSampler = pCreateInfo->compareEnable && !_device->_pMetalFeatures->depthSampleCompare; + _ycbcrConversion = NULL; + for (const auto* next = (const VkBaseInStructure*)pCreateInfo->pNext; next; next = next->pNext) { + switch (next->sType) { + case VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO_KHR: { + const VkSamplerYcbcrConversionInfoKHR* sampConvInfo = (const VkSamplerYcbcrConversionInfoKHR*)next; + _ycbcrConversion = (MVKSamplerYcbcrConversion*)(sampConvInfo->conversion); + break; + } + default: + break; + } + } + + _requiresConstExprSampler = (pCreateInfo->compareEnable && !_device->_pMetalFeatures->depthSampleCompare) || _ycbcrConversion; MTLSamplerDescriptor* mtlSampDesc = newMTLSamplerDescriptor(pCreateInfo); // temp retain _mtlSamplerState = [getMTLDevice() newSamplerStateWithDescriptor: mtlSampDesc]; @@ -1424,16 +1743,6 @@ MVKSampler::MVKSampler(MVKDevice* device, const VkSamplerCreateInfo* pCreateInfo initConstExprSampler(pCreateInfo); } -static MSLSamplerFilter getSpvMinMagFilterFromVkFilter(VkFilter vkFilter) { - switch (vkFilter) { - case VK_FILTER_LINEAR: return MSL_SAMPLER_FILTER_LINEAR; - - case VK_FILTER_NEAREST: - default: - return MSL_SAMPLER_FILTER_NEAREST; - } -} - static MSLSamplerMipFilter getSpvMipFilterFromVkMipMode(VkSamplerMipmapMode vkMipMode) { switch (vkMipMode) { case VK_SAMPLER_MIPMAP_MODE_LINEAR: return MSL_SAMPLER_MIP_FILTER_LINEAR; @@ -1489,7 +1798,7 @@ static MSLSamplerBorderColor getSpvBorderColorFromVkBorderColor(VkBorderColor vk return MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK; } } -\ + void MVKSampler::initConstExprSampler(const VkSamplerCreateInfo* pCreateInfo) { if ( !_requiresConstExprSampler ) { return; } @@ -1508,6 +1817,9 @@ void MVKSampler::initConstExprSampler(const VkSamplerCreateInfo* pCreateInfo) { _constExprSampler.compare_enable = pCreateInfo->compareEnable; _constExprSampler.lod_clamp_enable = false; _constExprSampler.anisotropy_enable = pCreateInfo->anisotropyEnable; + if (_ycbcrConversion) { + _ycbcrConversion->updateConstExprSampler(_constExprSampler); + } } MVKSampler::~MVKSampler() { diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm index da6661a2..48c6c488 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm @@ -622,6 +622,8 @@ void MVKInstance::initProcAddrs() { ADD_DVC_EXT_ENTRY_POINT(vkGetDescriptorSetLayoutSupportKHR, KHR_MAINTENANCE3); ADD_DVC_EXT_ENTRY_POINT(vkCmdPushDescriptorSetKHR, KHR_PUSH_DESCRIPTOR); ADD_DVC_EXT2_ENTRY_POINT(vkCmdPushDescriptorSetWithTemplateKHR, KHR_PUSH_DESCRIPTOR, KHR_DESCRIPTOR_UPDATE_TEMPLATE); + ADD_DVC_EXT_ENTRY_POINT(vkCreateSamplerYcbcrConversionKHR, KHR_SAMPLER_YCBCR_CONVERSION); + ADD_DVC_EXT_ENTRY_POINT(vkDestroySamplerYcbcrConversionKHR, KHR_SAMPLER_YCBCR_CONVERSION); ADD_DVC_EXT_ENTRY_POINT(vkCreateSwapchainKHR, KHR_SWAPCHAIN); ADD_DVC_EXT_ENTRY_POINT(vkDestroySwapchainKHR, KHR_SWAPCHAIN); ADD_DVC_EXT_ENTRY_POINT(vkGetSwapchainImagesKHR, KHR_SWAPCHAIN); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h index 42277781..e7b657da 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h @@ -21,6 +21,7 @@ #include "mvk_datatypes.h" #include "MVKEnvironment.h" #include "MVKBaseObject.h" +#include #include #import @@ -70,6 +71,8 @@ typedef enum : uint16_t { kMVKMTLFmtCapsDRMR = (kMVKMTLFmtCapsDRM | kMVKMTLFmtCapsResolve), kMVKMTLFmtCapsDRFMR = (kMVKMTLFmtCapsDRMR | kMVKMTLFmtCapsFilter), + kMVKMTLFmtCapsChromaSubsampling = kMVKMTLFmtCapsRF, + kMVKMTLFmtCapsMultiPlanar = kMVKMTLFmtCapsChromaSubsampling, } MVKMTLFmtCaps; @@ -83,16 +86,18 @@ typedef struct { MTLPixelFormat mtlPixelFormatSubstitute; MTLVertexFormat mtlVertexFormat; MTLVertexFormat mtlVertexFormatSubstitute; + uint8_t chromaSubsamplingPlaneCount; + uint8_t chromaSubsamplingComponentBits; VkExtent2D blockTexelSize; uint32_t bytesPerBlock; MVKFormatType formatType; VkFormatProperties properties; const char* name; bool hasReportedSubstitution; + + inline double bytesPerTexel() const { return (double)bytesPerBlock / (double)(blockTexelSize.width * blockTexelSize.height); }; - inline double bytesPerTexel() const { return (double)bytesPerBlock / (double)(blockTexelSize.width * blockTexelSize.height); }; - - inline bool isSupported() const { return (mtlPixelFormat != MTLPixelFormatInvalid); }; + inline bool isSupported() const { return (mtlPixelFormat != MTLPixelFormatInvalid || chromaSubsamplingPlaneCount > 0); }; inline bool isSupportedOrSubstitutable() const { return isSupported() || (mtlPixelFormatSubstitute != MTLPixelFormatInvalid); }; inline bool vertexIsSupported() const { return (mtlVertexFormat != MTLVertexFormatInvalid); }; @@ -174,16 +179,28 @@ public: /** * Returns the size of the compression block, measured in texels for a Vulkan format. - * The returned value will be {1, 1} for non-compressed formats. + * The returned value will be {1, 1} for non-compressed formats without chroma-subsampling. */ VkExtent2D getBlockTexelSize(VkFormat vkFormat); /** * Returns the size of the compression block, measured in texels for a Metal format. - * The returned value will be {1, 1} for non-compressed formats. + * The returned value will be {1, 1} for non-compressed formats without chroma-subsampling. */ VkExtent2D getBlockTexelSize(MTLPixelFormat mtlFormat); + /** Returns the number of planes of the specified chroma-subsampling (YCbCr) VkFormat */ + uint8_t getChromaSubsamplingPlaneCount(VkFormat vkFormat); + + /** Returns the number of bits per channel of the specified chroma-subsampling (YCbCr) VkFormat */ + uint8_t getChromaSubsamplingComponentBits(VkFormat vkFormat); + + /** Returns the MSLFormatResolution of the specified chroma-subsampling (YCbCr) VkFormat */ + SPIRV_CROSS_NAMESPACE::MSLFormatResolution getChromaSubsamplingResolution(VkFormat vkFormat); + + /** Returns the number of planes, blockTexelSize, bytesPerBlock and mtlPixFmt of each plane of the specified chroma-subsampling (YCbCr) VkFormat into the given arrays */ + uint8_t getChromaSubsamplingPlanes(VkFormat vkFormat, VkExtent2D blockTexelSize[3], uint32_t bytesPerBlock[3], MTLPixelFormat mtlPixFmt[3]); + /** * Returns the size, in bytes, of a texel of the specified Vulkan format. * The returned value may be fractional for certain compressed formats. diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm index 2e4a4200..9e728db4 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm @@ -236,6 +236,61 @@ VkExtent2D MVKPixelFormats::getBlockTexelSize(MTLPixelFormat mtlFormat) { return getVkFormatDesc(mtlFormat).blockTexelSize; } +uint8_t MVKPixelFormats::getChromaSubsamplingPlaneCount(VkFormat vkFormat) { + return getVkFormatDesc(vkFormat).chromaSubsamplingPlaneCount; +} + +uint8_t MVKPixelFormats::getChromaSubsamplingComponentBits(VkFormat vkFormat) { + return getVkFormatDesc(vkFormat).chromaSubsamplingComponentBits; +} + +SPIRV_CROSS_NAMESPACE::MSLFormatResolution MVKPixelFormats::getChromaSubsamplingResolution(VkFormat vkFormat) { + VkExtent2D blockTexelSize = getVkFormatDesc(vkFormat).blockTexelSize; + return (blockTexelSize.width != 2) ? SPIRV_CROSS_NAMESPACE::MSL_FORMAT_RESOLUTION_444 + : (blockTexelSize.height != 2) ? SPIRV_CROSS_NAMESPACE::MSL_FORMAT_RESOLUTION_422 + : SPIRV_CROSS_NAMESPACE::MSL_FORMAT_RESOLUTION_420; +} + +uint8_t MVKPixelFormats::getChromaSubsamplingPlanes(VkFormat vkFormat, VkExtent2D blockTexelSize[3], uint32_t bytesPerBlock[3], MTLPixelFormat mtlPixFmt[3]) { + uint8_t planes = getChromaSubsamplingPlaneCount(vkFormat); + uint8_t bits = getChromaSubsamplingComponentBits(vkFormat); + SPIRV_CROSS_NAMESPACE::MSLFormatResolution resolution = getChromaSubsamplingResolution(vkFormat); + bytesPerBlock[0] = mvkCeilingDivide((uint32_t)bits/8U, 1U); + switch(resolution) { + default: + return 0; + case SPIRV_CROSS_NAMESPACE::MSL_FORMAT_RESOLUTION_444: + blockTexelSize[0] = blockTexelSize[1] = blockTexelSize[2] = VkExtent2D{1, 1}; + break; + case SPIRV_CROSS_NAMESPACE::MSL_FORMAT_RESOLUTION_422: + blockTexelSize[0] = blockTexelSize[1] = blockTexelSize[2] = VkExtent2D{2, 1}; + break; + case SPIRV_CROSS_NAMESPACE::MSL_FORMAT_RESOLUTION_420: + blockTexelSize[0] = blockTexelSize[1] = blockTexelSize[2] = VkExtent2D{2, 2}; + break; + } + switch(planes) { + default: + return 0; + case 1: + bytesPerBlock[0] *= 4; + mtlPixFmt[0] = (bits == 8) ? MTLPixelFormatRGBA8Unorm : MTLPixelFormatRGBA16Unorm; + break; + case 2: + blockTexelSize[0] = VkExtent2D{1, 1}; + bytesPerBlock[1] = bytesPerBlock[0]*2; + mtlPixFmt[0] = (bits == 8) ? MTLPixelFormatR8Unorm : MTLPixelFormatR16Unorm; + mtlPixFmt[1] = (bits == 8) ? MTLPixelFormatRG8Unorm : MTLPixelFormatRG16Unorm; + break; + case 3: + blockTexelSize[0] = VkExtent2D{1, 1}; + bytesPerBlock[1] = bytesPerBlock[2] = bytesPerBlock[0]; + mtlPixFmt[0] = mtlPixFmt[1] = mtlPixFmt[2] = (bits == 8) ? MTLPixelFormatR8Unorm : MTLPixelFormatR16Unorm; + break; + } + return planes; +} + float MVKPixelFormats::getBytesPerTexel(VkFormat vkFormat) { return getVkFormatDesc(vkFormat).bytesPerTexel(); } @@ -492,10 +547,16 @@ MVKPixelFormats::MVKPixelFormats(MVKPhysicalDevice* physicalDevice) : _physicalD // test(); } -#define addVkFormatDesc(VK_FMT, MTL_FMT, MTL_FMT_ALT, MTL_VTX_FMT, MTL_VTX_FMT_ALT, BLK_W, BLK_H, BLK_BYTE_CNT, MVK_FMT_TYPE) \ +#define addVkFormatDescFull(VK_FMT, MTL_FMT, MTL_FMT_ALT, MTL_VTX_FMT, MTL_VTX_FMT_ALT, CSPC, CSCB, BLK_W, BLK_H, BLK_BYTE_CNT, MVK_FMT_TYPE) \ MVKAssert(fmtIdx < _vkFormatCount, "Attempting to describe %d VkFormats, but only have space for %d. Increase the value of _vkFormatCount", fmtIdx + 1, _vkFormatCount); \ _vkFormatDescriptions[fmtIdx++] = { VK_FORMAT_ ##VK_FMT, MTLPixelFormat ##MTL_FMT, MTLPixelFormat ##MTL_FMT_ALT, MTLVertexFormat ##MTL_VTX_FMT, MTLVertexFormat ##MTL_VTX_FMT_ALT, \ - { BLK_W, BLK_H }, BLK_BYTE_CNT, kMVKFormat ##MVK_FMT_TYPE, { 0, 0, 0 }, "VK_FORMAT_" #VK_FMT, false } + CSPC, CSCB, { BLK_W, BLK_H }, BLK_BYTE_CNT, kMVKFormat ##MVK_FMT_TYPE, { 0, 0, 0 }, "VK_FORMAT_" #VK_FMT, false } + +#define addVkFormatDesc(VK_FMT, MTL_FMT, MTL_FMT_ALT, MTL_VTX_FMT, MTL_VTX_FMT_ALT, BLK_W, BLK_H, BLK_BYTE_CNT, MVK_FMT_TYPE) \ + addVkFormatDescFull(VK_FMT, MTL_FMT, MTL_FMT_ALT, MTL_VTX_FMT, MTL_VTX_FMT_ALT, 0, 0, BLK_W, BLK_H, BLK_BYTE_CNT, MVK_FMT_TYPE) + +#define addVkFormatDescChromaSubsampling(VK_FMT, MTL_FMT, CSPC, CSCB, BLK_W, BLK_H, BLK_BYTE_CNT) \ +addVkFormatDescFull(VK_FMT, MTL_FMT, Invalid, Invalid, Invalid, CSPC, CSCB, BLK_W, BLK_H, BLK_BYTE_CNT, ColorFloat) void MVKPixelFormats::initVkFormatCapabilities() { @@ -744,9 +805,41 @@ void MVKPixelFormats::initVkFormatCapabilities() { addVkFormatDesc( PVRTC2_2BPP_SRGB_BLOCK_IMG, Invalid, Invalid, Invalid, Invalid, 8, 4, 8, Compressed ); addVkFormatDesc( PVRTC2_4BPP_SRGB_BLOCK_IMG, Invalid, Invalid, Invalid, Invalid, 4, 4, 8, Compressed ); - // Future extension VK_KHX_color_conversion and Vulkan 1.1. - addVkFormatDesc( UNDEFINED, GBGR422, Invalid, Invalid, Invalid, 2, 1, 4, ColorFloat ); - addVkFormatDesc( UNDEFINED, BGRG422, Invalid, Invalid, Invalid, 2, 1, 4, ColorFloat ); + // Extension VK_KHR_sampler_ycbcr_conversion + addVkFormatDescChromaSubsampling( G8B8G8R8_422_UNORM, GBGR422, 1, 8, 2, 1, 4 ); + addVkFormatDescChromaSubsampling( B8G8R8G8_422_UNORM, BGRG422, 1, 8, 2, 1, 4 ); + addVkFormatDescChromaSubsampling( G8_B8_R8_3PLANE_420_UNORM, Invalid, 3, 8, 2, 2, 6 ); + addVkFormatDescChromaSubsampling( G8_B8R8_2PLANE_420_UNORM, Invalid, 2, 8, 2, 2, 6 ); + addVkFormatDescChromaSubsampling( G8_B8_R8_3PLANE_422_UNORM, Invalid, 3, 8, 2, 1, 4 ); + addVkFormatDescChromaSubsampling( G8_B8R8_2PLANE_422_UNORM, Invalid, 2, 8, 2, 1, 4 ); + addVkFormatDescChromaSubsampling( G8_B8_R8_3PLANE_444_UNORM, Invalid, 3, 8, 1, 1, 3 ); + addVkFormatDescChromaSubsampling( R10X6_UNORM_PACK16, Invalid, 0, 10, 1, 1, 2 ); + addVkFormatDescChromaSubsampling( R10X6G10X6_UNORM_2PACK16, Invalid, 0, 10, 1, 1, 4 ); + addVkFormatDescChromaSubsampling( R10X6G10X6B10X6A10X6_UNORM_4PACK16, Invalid, 0, 10, 1, 1, 8 ); + addVkFormatDescChromaSubsampling( G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, Invalid, 1, 10, 2, 1, 8 ); + addVkFormatDescChromaSubsampling( B10X6G10X6R10X6G10X6_422_UNORM_4PACK16, Invalid, 1, 10, 2, 1, 8 ); + addVkFormatDescChromaSubsampling( G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16, Invalid, 3, 10, 2, 2, 12 ); + addVkFormatDescChromaSubsampling( G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, Invalid, 2, 10, 2, 2, 12 ); + addVkFormatDescChromaSubsampling( G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16, Invalid, 3, 10, 2, 1, 8 ); + addVkFormatDescChromaSubsampling( G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, Invalid, 2, 10, 2, 1, 8 ); + addVkFormatDescChromaSubsampling( G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, Invalid, 3, 10, 1, 1, 6 ); + addVkFormatDescChromaSubsampling( R12X4_UNORM_PACK16, Invalid, 0, 12, 1, 1, 2 ); + addVkFormatDescChromaSubsampling( R12X4G12X4_UNORM_2PACK16, Invalid, 0, 12, 1, 1, 4 ); + addVkFormatDescChromaSubsampling( R12X4G12X4B12X4A12X4_UNORM_4PACK16, Invalid, 0, 12, 1, 1, 8 ); + addVkFormatDescChromaSubsampling( G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, Invalid, 1, 12, 2, 1, 8 ); + addVkFormatDescChromaSubsampling( B12X4G12X4R12X4G12X4_422_UNORM_4PACK16, Invalid, 1, 12, 2, 1, 8 ); + addVkFormatDescChromaSubsampling( G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16, Invalid, 3, 12, 2, 2, 12 ); + addVkFormatDescChromaSubsampling( G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, Invalid, 2, 12, 2, 2, 12 ); + addVkFormatDescChromaSubsampling( G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16, Invalid, 3, 12, 2, 1, 8 ); + addVkFormatDescChromaSubsampling( G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16, Invalid, 2, 12, 2, 1, 8 ); + addVkFormatDescChromaSubsampling( G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16, Invalid, 3, 12, 1, 1, 6 ); + addVkFormatDescChromaSubsampling( G16B16G16R16_422_UNORM, Invalid, 1, 16, 2, 1, 8 ); + addVkFormatDescChromaSubsampling( B16G16R16G16_422_UNORM, Invalid, 1, 16, 2, 1, 8 ); + addVkFormatDescChromaSubsampling( G16_B16_R16_3PLANE_420_UNORM, Invalid, 3, 16, 2, 2, 12 ); + addVkFormatDescChromaSubsampling( G16_B16R16_2PLANE_420_UNORM, Invalid, 2, 16, 2, 2, 12 ); + addVkFormatDescChromaSubsampling( G16_B16_R16_3PLANE_422_UNORM, Invalid, 3, 16, 2, 1, 8 ); + addVkFormatDescChromaSubsampling( G16_B16R16_2PLANE_422_UNORM, Invalid, 2, 16, 2, 1, 8 ); + addVkFormatDescChromaSubsampling( G16_B16_R16_3PLANE_444_UNORM, Invalid, 3, 16, 1, 1, 6 ); // When adding to this list, be sure to ensure _vkFormatCount is large enough for the format count } @@ -1211,44 +1304,44 @@ void MVKPixelFormats::modifyMTLFormatCapabilities(id mtlDevice) { addMTLPixelFormatCapabilities( iOS_GPUFamily1_v2, RGBA32Sint, RWC ); addMTLPixelFormatCapabilities( iOS_GPUFamily1_v2, RGBA32Float, RWC ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_4x4_LDR, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_4x4_sRGB, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_5x4_LDR, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_5x4_sRGB, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_5x5_LDR, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_5x5_sRGB, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_6x5_LDR, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_6x5_sRGB, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_6x6_LDR, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_6x6_sRGB, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_8x5_LDR, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_8x5_sRGB, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_8x6_LDR, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_8x6_sRGB, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_8x8_LDR, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_8x8_sRGB, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_10x5_LDR, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_10x5_sRGB, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_10x6_LDR, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_10x6_sRGB, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_10x8_LDR, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_10x8_sRGB, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_10x10_LDR, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_10x10_sRGB, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_12x10_LDR, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_12x10_sRGB, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_12x12_LDR, RF ); - addMTLPixelFormatCapabilities(iOS_GPUFamily2_v1, ASTC_12x12_sRGB, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_4x4_LDR, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_4x4_sRGB, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_5x4_LDR, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_5x4_sRGB, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_5x5_LDR, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_5x5_sRGB, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_6x5_LDR, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_6x5_sRGB, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_6x6_LDR, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_6x6_sRGB, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_8x5_LDR, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_8x5_sRGB, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_8x6_LDR, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_8x6_sRGB, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_8x8_LDR, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_8x8_sRGB, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_10x5_LDR, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_10x5_sRGB, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_10x6_LDR, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_10x6_sRGB, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_10x8_LDR, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_10x8_sRGB, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_10x10_LDR, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_10x10_sRGB, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_12x10_LDR, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_12x10_sRGB, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_12x12_LDR, RF ); + addMTLPixelFormatCapabilities( iOS_GPUFamily2_v1, ASTC_12x12_sRGB, RF ); addMTLPixelFormatCapabilities( iOS_GPUFamily3_v1, Depth32Float, DRMR ); addMTLPixelFormatCapabilities( iOS_GPUFamily3_v1, Depth32Float_Stencil8, DRMR ); - addMTLPixelFormatCapabilities(iOS_GPUFamily3_v2, BGRA10_XR, All ); - addMTLPixelFormatCapabilities(iOS_GPUFamily3_v2, BGRA10_XR_sRGB, All ); - addMTLPixelFormatCapabilities(iOS_GPUFamily3_v2, BGR10_XR, All ); - addMTLPixelFormatCapabilities(iOS_GPUFamily3_v2, BGR10_XR_sRGB, All ); + addMTLPixelFormatCapabilities( iOS_GPUFamily3_v2, BGRA10_XR, All ); + addMTLPixelFormatCapabilities( iOS_GPUFamily3_v2, BGRA10_XR_sRGB, All ); + addMTLPixelFormatCapabilities( iOS_GPUFamily3_v2, BGR10_XR, All ); + addMTLPixelFormatCapabilities( iOS_GPUFamily3_v2, BGR10_XR_sRGB, All ); - addMTLPixelFormatCapabilities(iOS_GPUFamily1_v4, BGR10A2Unorm, All ); + addMTLPixelFormatCapabilities( iOS_GPUFamily1_v4, BGR10A2Unorm, All ); addMTLVertexFormatCapabilities( iOS_GPUFamily1_v4, UCharNormalized, Vertex ); addMTLVertexFormatCapabilities( iOS_GPUFamily1_v4, CharNormalized, Vertex ); @@ -1328,6 +1421,17 @@ typedef enum : VkFormatFeatureFlags { VK_FORMAT_FEATURE_BLIT_DST_BIT), kMVKVkFormatFeatureFlagsTexDSAtt = (VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT), kMVKVkFormatFeatureFlagsTexBlend = (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT), + kMVKVkFormatFeatureFlagsTexTransfer = (VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | + VK_FORMAT_FEATURE_TRANSFER_DST_BIT | + VK_FORMAT_FEATURE_BLIT_SRC_BIT | + VK_FORMAT_FEATURE_BLIT_DST_BIT), + kMVKVkFormatFeatureFlagsTexChromaSubsampling = (VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT_KHR | + VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT_KHR | + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT_KHR | + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT_KHR | + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT_KHR | + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT_KHR), + kMVKVkFormatFeatureFlagsTexMultiPlanar = (VK_FORMAT_FEATURE_DISJOINT_BIT_KHR), kMVKVkFormatFeatureFlagsBufRead = (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT), kMVKVkFormatFeatureFlagsBufWrite = (VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT), kMVKVkFormatFeatureFlagsBufAtomic = (VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT), @@ -1344,9 +1448,23 @@ void MVKPixelFormats::setFormatProperties(MVKVkFormatDesc& vkDesc) { VkFormatProperties& vkProps = vkDesc.properties; MVKMTLFmtCaps mtlPixFmtCaps = getMTLPixelFormatDesc(vkDesc.mtlPixelFormat).mtlFmtCaps; + vkProps.optimalTilingFeatures = kMVKVkFormatFeatureFlagsTexNone; + vkProps.linearTilingFeatures = kMVKVkFormatFeatureFlagsTexNone; - // Set optimal tiling features first - vkProps.optimalTilingFeatures = kMVKVkFormatFeatureFlagsTexNone; + // Chroma subsampling and multi planar features + if (getChromaSubsamplingComponentBits(vkDesc.vkFormat) > 0) { + vkProps.optimalTilingFeatures = kMVKVkFormatFeatureFlagsTexTransfer; + } + uint8_t chromaSubsamplingPlaneCount = getChromaSubsamplingPlaneCount(vkDesc.vkFormat); + if (chromaSubsamplingPlaneCount > 0) { + mtlPixFmtCaps = kMVKMTLFmtCapsRF; + enableFormatFeatures(ChromaSubsampling, Tex, mtlPixFmtCaps, vkProps.optimalTilingFeatures); + } + if (chromaSubsamplingPlaneCount > 1) { + enableFormatFeatures(MultiPlanar, Tex, mtlPixFmtCaps, vkProps.optimalTilingFeatures); + } + + // Optimal tiling features enableFormatFeatures(Read, Tex, mtlPixFmtCaps, vkProps.optimalTilingFeatures); enableFormatFeatures(Filter, Tex, mtlPixFmtCaps, vkProps.optimalTilingFeatures); enableFormatFeatures(Write, Tex, mtlPixFmtCaps, vkProps.optimalTilingFeatures); @@ -1355,9 +1473,7 @@ void MVKPixelFormats::setFormatProperties(MVKVkFormatDesc& vkDesc) { enableFormatFeatures(Blend, Tex, mtlPixFmtCaps, vkProps.optimalTilingFeatures); // Linear tiling is not available to depth/stencil or compressed formats. - vkProps.linearTilingFeatures = kMVKVkFormatFeatureFlagsTexNone; if ( !(vkDesc.formatType == kMVKFormatDepthStencil || vkDesc.formatType == kMVKFormatCompressed) ) { - // Start with optimal tiling features, and modify. vkProps.linearTilingFeatures = vkProps.optimalTilingFeatures; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm index e8213619..8ce7738c 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm @@ -110,7 +110,7 @@ void MVKRenderSubpass::populateMTLRenderPassDescriptor(MTLRenderPassDescriptor* if (dsRPAttIdx != VK_ATTACHMENT_UNUSED) { MVKRenderPassAttachment* dsMVKRPAtt = &_renderPass->_attachments[dsRPAttIdx]; MVKImageView* dsImage = framebuffer->getAttachment(dsRPAttIdx); - MTLPixelFormat mtlDSFormat = dsImage->getMTLPixelFormat(); + MTLPixelFormat mtlDSFormat = dsImage->getMTLPixelFormat(0); if (pixFmts->isDepthFormat(mtlDSFormat)) { MTLRenderPassDepthAttachmentDescriptor* mtlDepthAttDesc = mtlRPDesc.depthAttachment; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKResource.h b/MoltenVK/MoltenVK/GPUObjects/MVKResource.h index ca3ccfa6..ac76c62a 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKResource.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKResource.h @@ -39,12 +39,6 @@ public: /** Returns the byte offset in the bound device memory. */ inline VkDeviceSize getDeviceMemoryOffset() { return _deviceMemoryOffset; } - /** Returns the memory requirements of this resource by populating the specified structure. */ - virtual VkResult getMemoryRequirements(VkMemoryRequirements* pMemoryRequirements) = 0; - - /** Returns the memory requirements of this resource by populating the specified structure. */ - virtual VkResult getMemoryRequirements(const void* pInfo, VkMemoryRequirements2* pMemoryRequirements) = 0; - /** Binds this resource to the specified offset within the specified memory allocation. */ virtual VkResult bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset); @@ -78,10 +72,6 @@ public: MVKResource(MVKDevice* device) : MVKVulkanAPIDeviceObject(device) {} protected: - virtual bool needsHostReadSync(VkPipelineStageFlags srcStageMask, - VkPipelineStageFlags dstStageMask, - VkMemoryBarrier* pMemoryBarrier); - MVKDeviceMemory* _deviceMemory = nullptr; VkDeviceSize _deviceMemoryOffset = 0; VkDeviceSize _byteCount = 0; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKResource.mm b/MoltenVK/MoltenVK/GPUObjects/MVKResource.mm index 2f9e5976..013fbed6 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKResource.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKResource.mm @@ -34,18 +34,3 @@ VkResult MVKResource::bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize mem return VK_SUCCESS; } -// Returns whether the specified global memory barrier requires a sync between this -// texture and host memory for the purpose of the host reading texture memory. -bool MVKResource::needsHostReadSync(VkPipelineStageFlags srcStageMask, - VkPipelineStageFlags dstStageMask, - VkMemoryBarrier* pMemoryBarrier) { -#if MVK_IOS_OR_TVOS - return false; -#endif -#if MVK_MACOS - return (mvkIsAnyFlagEnabled(dstStageMask, (VK_PIPELINE_STAGE_HOST_BIT)) && - mvkIsAnyFlagEnabled(pMemoryBarrier->dstAccessMask, (VK_ACCESS_HOST_READ_BIT)) && - isMemoryHostAccessible() && !isMemoryHostCoherent()); -#endif -} - diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm index e7fee655..52c59ea4 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm @@ -384,18 +384,16 @@ void MVKSwapchain::initSurfaceImages(const VkSwapchainCreateInfoKHR* pCreateInfo VkResult MVKSwapchain::getRefreshCycleDuration(VkRefreshCycleDurationGOOGLE *pRefreshCycleDuration) { #if MVK_IOS_OR_TVOS - NSInteger framesPerSecond = 60; - if (@available(iOS 10.3, tvOS 10.3, *)) { - framesPerSecond = [UIScreen mainScreen].maximumFramesPerSecond; - } else { - // TODO: fallback + UIScreen* screen = [UIScreen mainScreen]; + if ([screen respondsToSelector: @selector(maximumFramesPerSecond)]) { + framesPerSecond = screen.maximumFramesPerSecond; } #endif #if MVK_MACOS // TODO: hook this up for macOS, probably need to use CGDisplayModeGetRefeshRate - NSInteger framesPerSecond = 60; #endif - pRefreshCycleDuration->refreshDuration = 1e9 / ( uint64_t ) framesPerSecond; + + pRefreshCycleDuration->refreshDuration = (uint64_t)1e9 / framesPerSecond; return VK_SUCCESS; } diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.h b/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.h index 35f83974..eab909cd 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.h @@ -63,7 +63,7 @@ public: * Called when this instance has been retained as a reference by another object, * indicating that this instance will not be deleted until that reference is released. */ - void retain(); + inline void retain() { _refCount++; } /** * Called when this instance has been released as a reference from another object. @@ -71,14 +71,14 @@ public: * If the destroy() function has already been called on this instance by the time * this function is called, this instance will be deleted. */ - void release(); + inline void release() { if (--_refCount == 0) { MVKConfigurableObject::destroy(); } } /** * Marks this instance as destroyed. If all previous references to this instance * have been released, this instance will be deleted, otherwise deletion of this * instance will automatically be deferred until all references have been released. */ - void destroy() override; + void destroy() override { release(); } /** Gets the debug object name of this instance. */ inline NSString* getDebugName() { return _debugName; } diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.mm b/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.mm index ec5fef03..36998bc6 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.mm @@ -24,18 +24,6 @@ using namespace std; #pragma mark - #pragma mark MVKVulkanAPIObject -void MVKVulkanAPIObject::retain() { - _refCount++; -} - -void MVKVulkanAPIObject::release() { - if (--_refCount == 0) { MVKConfigurableObject::destroy(); } -} - -void MVKVulkanAPIObject::destroy() { - release(); -} - VkResult MVKVulkanAPIObject::setDebugName(const char* pObjectName) { if (pObjectName) { [_debugName release]; diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.def b/MoltenVK/MoltenVK/Layers/MVKExtensions.def index 33ce097b..a3638b1c 100644 --- a/MoltenVK/MoltenVK/Layers/MVKExtensions.def +++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.def @@ -58,6 +58,7 @@ MVK_EXTENSION(KHR_maintenance3, KHR_MAINTENANCE3, DEVICE) MVK_EXTENSION(KHR_push_descriptor, KHR_PUSH_DESCRIPTOR, DEVICE) MVK_EXTENSION(KHR_relaxed_block_layout, KHR_RELAXED_BLOCK_LAYOUT, DEVICE) MVK_EXTENSION(KHR_sampler_mirror_clamp_to_edge, KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE, DEVICE) +MVK_EXTENSION(KHR_sampler_ycbcr_conversion, KHR_SAMPLER_YCBCR_CONVERSION, DEVICE) MVK_EXTENSION(KHR_shader_draw_parameters, KHR_SHADER_DRAW_PARAMETERS, DEVICE) MVK_EXTENSION(KHR_shader_float16_int8, KHR_SHADER_FLOAT16_INT8, DEVICE) MVK_EXTENSION(KHR_storage_buffer_storage_class, KHR_STORAGE_BUFFER_STORAGE_CLASS, DEVICE) @@ -76,6 +77,7 @@ MVK_EXTENSION(EXT_inline_uniform_block, EXT_INLINE_UNIFORM_BLOCK, DEVICE) MVK_EXTENSION(EXT_memory_budget, EXT_MEMORY_BUDGET, DEVICE) MVK_EXTENSION(EXT_metal_surface, EXT_METAL_SURFACE, INSTANCE) MVK_EXTENSION(EXT_post_depth_coverage, EXT_POST_DEPTH_COVERAGE, DEVICE) +MVK_EXTENSION(EXT_robustness2, EXT_ROBUSTNESS_2, DEVICE) MVK_EXTENSION(EXT_scalar_block_layout, EXT_SCALAR_BLOCK_LAYOUT, DEVICE) MVK_EXTENSION(EXT_shader_stencil_export, EXT_SHADER_STENCIL_EXPORT, DEVICE) MVK_EXTENSION(EXT_shader_viewport_index_layer, EXT_SHADER_VIEWPORT_INDEX_LAYER, DEVICE) diff --git a/MoltenVK/MoltenVK/Utility/MVKFoundation.h b/MoltenVK/MoltenVK/Utility/MVKFoundation.h index 37c31216..0b19b902 100644 --- a/MoltenVK/MoltenVK/Utility/MVKFoundation.h +++ b/MoltenVK/MoltenVK/Utility/MVKFoundation.h @@ -523,7 +523,7 @@ void mvkDisableFlags(Tv& value, const Tm bitMask) { value = (Tv)(value & ~(Tv)bi /** Returns whether the specified value has ANY of the flags specified in bitMask enabled (set to 1). */ template -bool mvkIsAnyFlagEnabled(Tv value, const Tm bitMask) { return !!(value & bitMask); } +bool mvkIsAnyFlagEnabled(Tv value, const Tm bitMask) { return ((value & bitMask) != 0); } /** Returns whether the specified value has ALL of the flags specified in bitMask enabled (set to 1). */ template diff --git a/MoltenVK/MoltenVK/Vulkan/vk_mvk_moltenvk.mm b/MoltenVK/MoltenVK/Vulkan/vk_mvk_moltenvk.mm index bccb8c17..3976621e 100644 --- a/MoltenVK/MoltenVK/Vulkan/vk_mvk_moltenvk.mm +++ b/MoltenVK/MoltenVK/Vulkan/vk_mvk_moltenvk.mm @@ -115,7 +115,7 @@ MVK_PUBLIC_SYMBOL VkResult vkSetMTLTextureMVK( id mtlTexture) { MVKImage* mvkImg = (MVKImage*)image; - return mvkImg->setMTLTexture(mtlTexture); + return mvkImg->setMTLTexture(0, mtlTexture); } MVK_PUBLIC_SYMBOL void vkGetMTLTextureMVK( @@ -123,7 +123,7 @@ MVK_PUBLIC_SYMBOL void vkGetMTLTextureMVK( id* pMTLTexture) { MVKImage* mvkImg = (MVKImage*)image; - *pMTLTexture = mvkImg->getMTLTexture(); + *pMTLTexture = mvkImg->getMTLTexture(0); } MVK_PUBLIC_SYMBOL void vkGetMTLBufferMVK( diff --git a/MoltenVK/MoltenVK/Vulkan/vulkan.mm b/MoltenVK/MoltenVK/Vulkan/vulkan.mm index 90e6f03e..b84a88c1 100644 --- a/MoltenVK/MoltenVK/Vulkan/vulkan.mm +++ b/MoltenVK/MoltenVK/Vulkan/vulkan.mm @@ -526,7 +526,7 @@ MVK_PUBLIC_SYMBOL VkResult vkBindImageMemory( MVKTraceVulkanCallStart(); MVKImage* mvkImg = (MVKImage*)image; MVKDeviceMemory* mvkMem = (MVKDeviceMemory*)mem; - VkResult rslt = mvkImg->bindDeviceMemory(mvkMem, memOffset); + VkResult rslt = mvkImg->bindDeviceMemory(mvkMem, memOffset, 0); MVKTraceVulkanCallEnd(); return rslt; } @@ -549,7 +549,7 @@ MVK_PUBLIC_SYMBOL void vkGetImageMemoryRequirements( MVKTraceVulkanCallStart(); MVKImage* mvkImg = (MVKImage*)image; - mvkImg->getMemoryRequirements(pMemoryRequirements); + mvkImg->getMemoryRequirements(pMemoryRequirements, 0); MVKTraceVulkanCallEnd(); } @@ -2218,6 +2218,37 @@ MVK_PUBLIC_SYMBOL void vkCmdPushDescriptorSetWithTemplateKHR( } +#pragma mark - +#pragma mark VK_KHR_sampler_ycbcr_conversion extension + +MVK_PUBLIC_SYMBOL VkResult vkCreateSamplerYcbcrConversionKHR( + VkDevice device, + const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSamplerYcbcrConversion* pYcbcrConversion) { + + MVKTraceVulkanCallStart(); + MVKDevice* mvkDev = MVKDevice::getMVKDevice(device); + MVKSamplerYcbcrConversion* mvkSampConv = mvkDev->createSamplerYcbcrConversion(pCreateInfo, pAllocator); + *pYcbcrConversion = (VkSamplerYcbcrConversion)mvkSampConv; + VkResult rslt = mvkSampConv->getConfigurationResult(); + MVKTraceVulkanCallEnd(); + return rslt; +} + +MVK_PUBLIC_SYMBOL void vkDestroySamplerYcbcrConversionKHR( + VkDevice device, + VkSamplerYcbcrConversion ycbcrConversion, + const VkAllocationCallbacks* pAllocator) { + + MVKTraceVulkanCallStart(); + if ( !ycbcrConversion ) { return; } + MVKDevice* mvkDev = MVKDevice::getMVKDevice(device); + mvkDev->destroySamplerYcbcrConversion((MVKSamplerYcbcrConversion*)ycbcrConversion, pAllocator); + MVKTraceVulkanCallEnd(); +} + + #pragma mark - #pragma mark VK_KHR_swapchain extension diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp index fedb09e1..cfbe20aa 100644 --- a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp +++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp @@ -136,9 +136,22 @@ MVK_PUBLIC_SYMBOL bool mvk::MSLResourceBinding::matches(const MSLResourceBinding if (constExprSampler.lod_clamp_min != other.constExprSampler.lod_clamp_min) { return false; } if (constExprSampler.lod_clamp_max != other.constExprSampler.lod_clamp_max) { return false; } if (constExprSampler.max_anisotropy != other.constExprSampler.max_anisotropy) { return false; } + + if (constExprSampler.planes != other.constExprSampler.planes) { return false; } + if (constExprSampler.resolution != other.constExprSampler.resolution) { return false; } + if (constExprSampler.chroma_filter != other.constExprSampler.chroma_filter) { return false; } + if (constExprSampler.x_chroma_offset != other.constExprSampler.x_chroma_offset) { return false; } + if (constExprSampler.y_chroma_offset != other.constExprSampler.y_chroma_offset) { return false; } + for(uint32_t i = 0; i < 4; ++i) + if (constExprSampler.swizzle[i] != other.constExprSampler.swizzle[i]) { return false; } + if (constExprSampler.ycbcr_model != other.constExprSampler.ycbcr_model) { return false; } + if (constExprSampler.ycbcr_range != other.constExprSampler.ycbcr_range) { return false; } + if (constExprSampler.bpc != other.constExprSampler.bpc) { return false; } + if (constExprSampler.compare_enable != other.constExprSampler.compare_enable) { return false; } if (constExprSampler.lod_clamp_enable != other.constExprSampler.lod_clamp_enable) { return false; } if (constExprSampler.anisotropy_enable != other.constExprSampler.anisotropy_enable) { return false; } + if (constExprSampler.ycbcr_conversion_enable != other.constExprSampler.ycbcr_conversion_enable) { return false; } } return true; diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj index 43f69ae4..63605d4b 100644 --- a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj +++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj @@ -989,8 +989,6 @@ A93747401A9A8B2900F29B34 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = arm64; - BITCODE_GENERATION_MODE = bitcode; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = NO; CLANG_WARN_UNREACHABLE_CODE = NO; GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = NO; @@ -1000,15 +998,12 @@ PRODUCT_NAME = MoltenVKGLSLToSPIRVConverter; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; - VALID_ARCHS = arm64; }; name = Debug; }; A93747411A9A8B2900F29B34 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = arm64; - BITCODE_GENERATION_MODE = bitcode; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = NO; CLANG_WARN_UNREACHABLE_CODE = NO; GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = NO; @@ -1018,7 +1013,6 @@ PRODUCT_NAME = MoltenVKGLSLToSPIRVConverter; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; - VALID_ARCHS = arm64; }; name = Release; }; @@ -1053,8 +1047,6 @@ A93903BD1C57E9D700FE90DC /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = arm64; - BITCODE_GENERATION_MODE = bitcode; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", "SPIRV_CROSS_NAMESPACE_OVERRIDE=MVK_spirv_cross", @@ -1064,15 +1056,12 @@ PRODUCT_NAME = MoltenVKSPIRVToMSLConverter; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; - VALID_ARCHS = arm64; }; name = Debug; }; A93903BE1C57E9D700FE90DC /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = arm64; - BITCODE_GENERATION_MODE = bitcode; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", "SPIRV_CROSS_NAMESPACE_OVERRIDE=MVK_spirv_cross", @@ -1082,7 +1071,6 @@ PRODUCT_NAME = MoltenVKSPIRVToMSLConverter; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; - VALID_ARCHS = arm64; }; name = Release; }; @@ -1118,6 +1106,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = NO; @@ -1131,9 +1120,10 @@ CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = NO; CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = NO; + CLANG_WARN_UNGUARDED_AVAILABILITY = NO; + CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -1150,8 +1140,8 @@ GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_PARAMETER = YES; GCC_WARN_UNUSED_VARIABLE = NO; HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1164,7 +1154,6 @@ MACOSX_DEPLOYMENT_TARGET = 10.11; ONLY_ACTIVE_ARCH = YES; SKIP_INSTALL = YES; - VALID_ARCHS = x86_64; }; name = Debug; }; @@ -1172,6 +1161,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = NO; @@ -1185,9 +1175,10 @@ CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = NO; CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = NO; + CLANG_WARN_UNGUARDED_AVAILABILITY = NO; + CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; @@ -1204,8 +1195,8 @@ GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_PARAMETER = YES; GCC_WARN_UNUSED_VARIABLE = NO; HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1218,7 +1209,6 @@ MACOSX_DEPLOYMENT_TARGET = 10.11; SKIP_INSTALL = YES; VALIDATE_PRODUCT = YES; - VALID_ARCHS = x86_64; }; name = Release; }; diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverterTool/OSSupport.mm b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/OSSupport.mm index 91d2dc98..5b72b696 100644 --- a/MoltenVKShaderConverter/MoltenVKShaderConverterTool/OSSupport.mm +++ b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/OSSupport.mm @@ -19,6 +19,7 @@ #include "OSSupport.h" #include "FileSupport.h" #include "MoltenVKShaderConverterTool.h" +#include "MVKOSExtensions.h" #import #import @@ -71,13 +72,9 @@ bool mvk::compile(const string& mslSourceCode, MTLLanguageVersion mslVerEnum = (MTLLanguageVersion)0; if (mslVer(2, 1, 0)) { - if (@available(macOS 10.14, *)) { - mslVerEnum = MTLLanguageVersion2_1; - } + mslVerEnum = MTLLanguageVersion2_1; } else if (mslVer(2, 0, 0)) { - if (@available(macOS 10.13, *)) { - mslVerEnum = MTLLanguageVersion2_0; - } + mslVerEnum = MTLLanguageVersion2_0; } else if (mslVer(1, 2, 0)) { mslVerEnum = MTLLanguageVersion1_2; } else if (mslVer(1, 1, 0)) {