From fd7b8234f93972be4f9e339f7af31e7cb73f94a7 Mon Sep 17 00:00:00 2001 From: Bill Hollings Date: Fri, 17 Nov 2017 11:14:29 -0500 Subject: [PATCH] Initial open-source commit. --- .gitignore | 32 + .gitmodules | 18 + Common/MVKCommonEnvironment.h | 70 + Common/MVKLogging.h | 226 ++ Common/MVKStrings.h | 49 + .../contents.xcworkspacedata | 27 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../project.pbxproj | 535 ++++ .../AsynchronousTimeWarp-VK-iOS.xcscheme | 123 + .../AsynchronousTimeWarp-VK-macOS.xcscheme | 124 + .../AsynchronousTimeWarp/atw | 1 + .../AsynchronousTimeWarp/iOS/Info.plist | 42 + .../iOS/Resources/Default-568h@2x.png | Bin 0 -> 48497 bytes .../iOS/Resources/Default~ipad.png | Bin 0 -> 49404 bytes .../iOS/Resources/Icon.png | Bin 0 -> 5914 bytes .../AsynchronousTimeWarp/macOS/Info.plist | 32 + .../AppIcon.appiconset/Contents.json | 63 + .../AppIcon.appiconset/Icon-128.png | Bin 0 -> 12449 bytes .../AppIcon.appiconset/Icon-16.png | Bin 0 -> 671 bytes .../AppIcon.appiconset/Icon-256.png | Bin 0 -> 34322 bytes .../AppIcon.appiconset/Icon-32.png | Bin 0 -> 1740 bytes .../AppIcon.appiconset/Icon-512.png | Bin 0 -> 71630 bytes .../Resources/macOS.xcassets/Contents.json | 6 + Demos/Khronos-Vulkan-Samples/Vulkan-Samples | 1 + .../API-Samples.xcodeproj/project.pbxproj | 902 ++++++ .../xcschemes/API-Samples-iOS.xcscheme | 98 + .../xcschemes/API-Samples-macOS.xcscheme | 98 + .../API-Samples/Samples.h | 106 + .../API-Samples/iOS/AppDelegate.h | 26 + .../API-Samples/iOS/AppDelegate.m | 55 + .../API-Samples/iOS/DemoViewController.h | 36 + .../API-Samples/iOS/DemoViewController.mm | 70 + .../API-Samples/iOS/Info.plist | 44 + .../API-Samples/iOS/Prefix.pch | 5 + .../iOS/Resources/Default-568h@2x.png | Bin 0 -> 48497 bytes .../iOS/Resources/Default~ipad.png | Bin 0 -> 49404 bytes .../API-Samples/iOS/Resources/Icon.png | Bin 0 -> 5914 bytes .../API-Samples/iOS/Resources/Main.storyboard | 26 + .../API-Samples/iOS/main.m | 26 + .../API-Samples/macOS/AppDelegate.h | 23 + .../API-Samples/macOS/AppDelegate.m | 39 + .../API-Samples/macOS/DemoViewController.h | 36 + .../API-Samples/macOS/DemoViewController.mm | 90 + .../API-Samples/macOS/Info.plist | 34 + .../API-Samples/macOS/Prefix.pch | 4 + .../macOS/Resources/Main.storyboard | 131 + .../AppIcon.appiconset/Contents.json | 63 + .../AppIcon.appiconset/Icon-128.png | Bin 0 -> 12449 bytes .../AppIcon.appiconset/Icon-16.png | Bin 0 -> 671 bytes .../AppIcon.appiconset/Icon-256.png | Bin 0 -> 34322 bytes .../AppIcon.appiconset/Icon-32.png | Bin 0 -> 1740 bytes .../AppIcon.appiconset/Icon-512.png | Bin 0 -> 71630 bytes .../Resources/macOS.xcassets/Contents.json | 6 + .../API-Samples/macOS/main.m | 23 + Demos/LunarG-VulkanSamples/Demos/Demos.h | 29 + .../Demos/Demos.xcodeproj/project.pbxproj | 516 ++++ .../xcshareddata/xcschemes/Cube-iOS.xcscheme | 97 + .../xcschemes/Cube-macOS.xcscheme | 97 + .../Demos/iOS/AppDelegate.h | 26 + .../Demos/iOS/AppDelegate.m | 55 + .../Demos/iOS/DemoViewController.h | 36 + .../Demos/iOS/DemoViewController.m | 69 + .../LunarG-VulkanSamples/Demos/iOS/Info.plist | 44 + .../LunarG-VulkanSamples/Demos/iOS/Prefix.pch | 5 + .../Demos/iOS/Resources/Default-568h@2x.png | Bin 0 -> 48497 bytes .../Demos/iOS/Resources/Default~ipad.png | Bin 0 -> 49404 bytes .../Demos/iOS/Resources/Icon.png | Bin 0 -> 5914 bytes .../Demos/iOS/Resources/Main.storyboard | 26 + Demos/LunarG-VulkanSamples/Demos/iOS/main.m | 26 + .../Demos/macOS/AppDelegate.h | 23 + .../Demos/macOS/AppDelegate.m | 39 + .../Demos/macOS/DemoViewController.h | 36 + .../Demos/macOS/DemoViewController.m | 88 + .../Demos/macOS/Info.plist | 34 + .../Demos/macOS/Prefix.pch | 3 + .../Demos/macOS/Resources/Main.storyboard | 131 + .../AppIcon.appiconset/Contents.json | 63 + .../AppIcon.appiconset/Icon-128.png | Bin 0 -> 12449 bytes .../AppIcon.appiconset/Icon-16.png | Bin 0 -> 671 bytes .../AppIcon.appiconset/Icon-256.png | Bin 0 -> 34322 bytes .../AppIcon.appiconset/Icon-32.png | Bin 0 -> 1740 bytes .../AppIcon.appiconset/Icon-512.png | Bin 0 -> 71630 bytes .../Resources/macOS.xcassets/Contents.json | 6 + Demos/LunarG-VulkanSamples/Demos/macOS/main.m | 23 + .../Hologram/HelpersDispatchTable.cpp | 525 ++++ .../Hologram/HelpersDispatchTable.h | 219 ++ .../Hologram.xcodeproj/project.pbxproj | 677 +++++ .../xcschemes/Hologram-iOS.xcscheme | 98 + .../xcschemes/Hologram-macOS.xcscheme | 98 + .../Hologram/ShellMVK.cpp | 131 + .../LunarG-VulkanSamples/Hologram/ShellMVK.h | 64 + .../Hologram/iOS/AppDelegate.h | 26 + .../Hologram/iOS/AppDelegate.m | 55 + .../Hologram/iOS/DemoViewController.h | 36 + .../Hologram/iOS/DemoViewController.mm | 77 + .../Hologram/iOS/Info.plist | 44 + .../Hologram/iOS/Prefix.pch | 5 + .../iOS/Resources/Default-568h@2x.png | Bin 0 -> 48497 bytes .../Hologram/iOS/Resources/Default~ipad.png | Bin 0 -> 49404 bytes .../Hologram/iOS/Resources/Icon.png | Bin 0 -> 5914 bytes .../Hologram/iOS/Resources/Main.storyboard | 26 + .../LunarG-VulkanSamples/Hologram/iOS/main.m | 26 + .../Hologram/macOS/AppDelegate.h | 23 + .../Hologram/macOS/AppDelegate.m | 39 + .../Hologram/macOS/DemoViewController.h | 36 + .../Hologram/macOS/DemoViewController.mm | 130 + .../Hologram/macOS/Info.plist | 34 + .../Hologram/macOS/Prefix.pch | 3 + .../Hologram/macOS/Resources/Main.storyboard | 132 + .../AppIcon.appiconset/Contents.json | 63 + .../AppIcon.appiconset/Icon-128.png | Bin 0 -> 12449 bytes .../AppIcon.appiconset/Icon-16.png | Bin 0 -> 671 bytes .../AppIcon.appiconset/Icon-256.png | Bin 0 -> 34322 bytes .../AppIcon.appiconset/Icon-32.png | Bin 0 -> 1740 bytes .../AppIcon.appiconset/Icon-512.png | Bin 0 -> 71630 bytes .../Resources/macOS.xcassets/Contents.json | 6 + .../Hologram/macOS/main.m | 23 + Demos/LunarG-VulkanSamples/VulkanSamples | 1 + Demos/README_MoltenVK_Demos.md | 322 ++ Docs/MoltenVK_Runtime_UserGuide.md | 480 +++ ...kan_1.0_Spec_with_MoltenVK_Extensions.html | 1 + Docs/Whats_New.md | 22 + Docs/images/MoltenVK-Logo-Banner.png | Bin 0 -> 163965 bytes External/SPIRV-Cross | 1 + External/SPIRV-Headers | 1 + External/SPIRV-Tools | 1 + External/Vulkan-Hpp | 1 + External/glslang | 1 + External/makeAll | 14 + External/makeSPIRVTools | 16 + External/makeVulkanSpec | 26 + External/makeglslang | 15 + LICENSE | 202 ++ MoltenVK/MoltenVK.xcodeproj/project.pbxproj | 1081 +++++++ .../xcschemes/MoltenVK-iOS.xcscheme | 82 + .../xcschemes/MoltenVK-macOS.xcscheme | 82 + MoltenVK/MoltenVK/API/mvk_datatypes.h | 391 +++ MoltenVK/MoltenVK/API/mvk_vulkan.h | 48 + MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h | 317 ++ MoltenVK/MoltenVK/Commands/MVKCmdDispatch.h | 75 + MoltenVK/MoltenVK/Commands/MVKCmdDispatch.mm | 90 + MoltenVK/MoltenVK/Commands/MVKCmdDraw.h | 211 ++ MoltenVK/MoltenVK/Commands/MVKCmdDraw.mm | 320 ++ MoltenVK/MoltenVK/Commands/MVKCmdPipeline.h | 170 ++ MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm | 242 ++ MoltenVK/MoltenVK/Commands/MVKCmdQueries.h | 178 ++ MoltenVK/MoltenVK/Commands/MVKCmdQueries.mm | 185 ++ MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h | 340 +++ .../MoltenVK/Commands/MVKCmdRenderPass.mm | 391 +++ MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h | 424 +++ MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm | 1182 ++++++++ MoltenVK/MoltenVK/Commands/MVKCommand.h | 110 + MoltenVK/MoltenVK/Commands/MVKCommand.mm | 37 + MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h | 503 ++++ .../MoltenVK/Commands/MVKCommandBuffer.mm | 598 ++++ .../Commands/MVKCommandEncoderState.h | 515 ++++ .../Commands/MVKCommandEncoderState.mm | 567 ++++ .../Commands/MVKCommandEncodingPool.h | 121 + .../Commands/MVKCommandEncodingPool.mm | 178 ++ ...KCommandPipelineStateFactoryShaderSource.h | 198 ++ MoltenVK/MoltenVK/Commands/MVKCommandPool.h | 166 ++ MoltenVK/MoltenVK/Commands/MVKCommandPool.mm | 123 + .../Commands/MVKCommandResourceFactory.h | 292 ++ .../Commands/MVKCommandResourceFactory.mm | 266 ++ .../Commands/MVKMTLBufferAllocation.h | 143 + .../Commands/MVKMTLBufferAllocation.mm | 104 + .../Commands/MVKMTLResourceBindings.h | 51 + MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h | 125 + MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm | 241 ++ .../MoltenVK/GPUObjects/MVKDescriptorSet.h | 317 ++ .../MoltenVK/GPUObjects/MVKDescriptorSet.mm | 710 +++++ MoltenVK/MoltenVK/GPUObjects/MVKDevice.h | 549 ++++ MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm | 1449 +++++++++ .../MoltenVK/GPUObjects/MVKDeviceMemory.h | 129 + .../MoltenVK/GPUObjects/MVKDeviceMemory.mm | 192 ++ MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.h | 49 + .../MoltenVK/GPUObjects/MVKFramebuffer.mm | 36 + MoltenVK/MoltenVK/GPUObjects/MVKImage.h | 386 +++ MoltenVK/MoltenVK/GPUObjects/MVKImage.mm | 901 ++++++ MoltenVK/MoltenVK/GPUObjects/MVKInstance.h | 91 + MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm | 319 ++ MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h | 179 ++ MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm | 446 +++ MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.h | 163 ++ MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.mm | 248 ++ MoltenVK/MoltenVK/GPUObjects/MVKQueue.h | 268 ++ MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm | 418 +++ MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h | 147 + MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm | 279 ++ MoltenVK/MoltenVK/GPUObjects/MVKResource.h | 104 + MoltenVK/MoltenVK/GPUObjects/MVKResource.mm | 79 + .../MoltenVK/GPUObjects/MVKShaderModule.h | 95 + .../MoltenVK/GPUObjects/MVKShaderModule.mm | 252 ++ MoltenVK/MoltenVK/GPUObjects/MVKSurface.h | 68 + MoltenVK/MoltenVK/GPUObjects/MVKSurface.mm | 44 + MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h | 108 + MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm | 269 ++ MoltenVK/MoltenVK/GPUObjects/MVKSync.h | 253 ++ MoltenVK/MoltenVK/GPUObjects/MVKSync.mm | 225 ++ MoltenVK/MoltenVK/Loader/MVKLayers.h | 116 + MoltenVK/MoltenVK/Loader/MVKLayers.mm | 172 ++ MoltenVK/MoltenVK/Utility/MVKBaseObject.cpp | 35 + MoltenVK/MoltenVK/Utility/MVKBaseObject.h | 67 + MoltenVK/MoltenVK/Utility/MVKEnvironment.h | 41 + MoltenVK/MoltenVK/Utility/MVKFoundation.h | 353 +++ MoltenVK/MoltenVK/Utility/MVKFoundation.mm | 102 + MoltenVK/MoltenVK/Utility/MVKOSExtensions.h | 101 + MoltenVK/MoltenVK/Utility/MVKOSExtensions.mm | 101 + MoltenVK/MoltenVK/Utility/MVKObjectPool.h | 140 + MoltenVK/MoltenVK/Utility/MVKWatermark.h | 171 ++ MoltenVK/MoltenVK/Utility/MVKWatermark.mm | 495 ++++ .../Utility/MVKWatermarkShaderSource.h | 64 + .../Utility/MVKWatermarkTextureContent.h | 2097 +++++++++++++ MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm | 1168 ++++++++ MoltenVK/MoltenVK/Vulkan/vk_mvk_moltenvk.mm | 140 + MoltenVK/MoltenVK/Vulkan/vulkan.mm | 1537 ++++++++++ MoltenVK/ThirdPartyConfig.md | 61 + MoltenVK/Vulkan-Hpp | 1 + MoltenVK/iOS | 1 + MoltenVK/include/MoltenVK | 1 + MoltenVK/include/vulkan | 1 + MoltenVK/macOS | 1 + MoltenVKPackaging.xcodeproj/project.pbxproj | 440 +++ .../xcschemes/MoltenVK (Debug).xcscheme | 85 + .../xcschemes/MoltenVK (Release).xcscheme | 85 + .../GLSLConversion.h | 105 + .../GLSLConversion.mm | 92 + .../GLSLToSPIRVConverter.cpp | 271 ++ .../GLSLToSPIRVConverter.h | 84 + .../MoltenVKGLSLToSPIRVConverter/glslang | 1 + .../MoltenVKGLSLToSPIRVConverter/iOS | 1 + .../MoltenVKGLSLToSPIRVConverter/macOS | 1 + .../MoltenVKSPIRVToMSLConverter/SPIRV-Cross | 1 + .../MoltenVKSPIRVToMSLConverter/SPIRV-Headers | 1 + .../MoltenVKSPIRVToMSLConverter/SPIRV-Tools | 1 + .../SPIRVConversion.h | 84 + .../SPIRVConversion.mm | 81 + .../SPIRVToMSLConverter.cpp | 324 +++ .../SPIRVToMSLConverter.h | 245 ++ .../MoltenVKSPIRVToMSLConverter/iOS | 1 + .../MoltenVKSPIRVToMSLConverter/macOS | 1 + .../MoltenVKSPIRVToMSLConverter/spirv.hpp | 1 + .../project.pbxproj | 2589 +++++++++++++++++ .../MoltenVKGLSLToSPIRVConverter-iOS.xcscheme | 82 + ...oltenVKGLSLToSPIRVConverter-macOS.xcscheme | 82 + .../MoltenVKSPIRVToMSLConverter-iOS.xcscheme | 82 + ...MoltenVKSPIRVToMSLConverter-macOS.xcscheme | 82 + .../MoltenVKShaderConverter.xcscheme | 131 + .../MoltenVKShaderConverterTool/FileSupport.h | 91 + .../FileSupport.mm | 175 ++ .../MoltenVKShaderConverterTool.cpp | 558 ++++ .../MoltenVKShaderConverterTool.h | 133 + .../MoltenVKShaderConverterTool/main.cpp | 27 + MoltenVKShaderConverter/ThirdPartyConfig.md | 313 ++ README.md | 196 ++ 255 files changed, 40802 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 Common/MVKCommonEnvironment.h create mode 100755 Common/MVKLogging.h create mode 100644 Common/MVKStrings.h create mode 100644 Demos/Demos.xcworkspace/contents.xcworkspacedata create mode 100644 Demos/Demos.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/AsynchronousTimeWarp.xcodeproj/project.pbxproj create mode 100644 Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/AsynchronousTimeWarp.xcodeproj/xcshareddata/xcschemes/AsynchronousTimeWarp-VK-iOS.xcscheme create mode 100644 Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/AsynchronousTimeWarp.xcodeproj/xcshareddata/xcschemes/AsynchronousTimeWarp-VK-macOS.xcscheme create mode 120000 Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/atw create mode 100644 Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/iOS/Info.plist create mode 100755 Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/iOS/Resources/Default-568h@2x.png create mode 100755 Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/iOS/Resources/Default~ipad.png create mode 100755 Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/iOS/Resources/Icon.png create mode 100644 Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/macOS/Info.plist create mode 100644 Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-128.png create mode 100644 Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-16.png create mode 100644 Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-256.png create mode 100644 Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-32.png create mode 100644 Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-512.png create mode 100644 Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/macOS/Resources/macOS.xcassets/Contents.json create mode 120000 Demos/Khronos-Vulkan-Samples/Vulkan-Samples create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/project.pbxproj create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/xcshareddata/xcschemes/API-Samples-iOS.xcscheme create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/xcshareddata/xcschemes/API-Samples-macOS.xcscheme create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/Samples.h create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/iOS/AppDelegate.h create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/iOS/AppDelegate.m create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/iOS/DemoViewController.h create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/iOS/DemoViewController.mm create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/iOS/Info.plist create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/iOS/Prefix.pch create mode 100755 Demos/LunarG-VulkanSamples/API-Samples/iOS/Resources/Default-568h@2x.png create mode 100755 Demos/LunarG-VulkanSamples/API-Samples/iOS/Resources/Default~ipad.png create mode 100755 Demos/LunarG-VulkanSamples/API-Samples/iOS/Resources/Icon.png create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/iOS/Resources/Main.storyboard create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/iOS/main.m create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/macOS/AppDelegate.h create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/macOS/AppDelegate.m create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/macOS/DemoViewController.h create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/macOS/DemoViewController.mm create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/macOS/Info.plist create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/macOS/Prefix.pch create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/Main.storyboard create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-128.png create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-16.png create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-256.png create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-32.png create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-512.png create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/macOS.xcassets/Contents.json create mode 100644 Demos/LunarG-VulkanSamples/API-Samples/macOS/main.m create mode 100644 Demos/LunarG-VulkanSamples/Demos/Demos.h create mode 100644 Demos/LunarG-VulkanSamples/Demos/Demos.xcodeproj/project.pbxproj create mode 100644 Demos/LunarG-VulkanSamples/Demos/Demos.xcodeproj/xcshareddata/xcschemes/Cube-iOS.xcscheme create mode 100644 Demos/LunarG-VulkanSamples/Demos/Demos.xcodeproj/xcshareddata/xcschemes/Cube-macOS.xcscheme create mode 100644 Demos/LunarG-VulkanSamples/Demos/iOS/AppDelegate.h create mode 100644 Demos/LunarG-VulkanSamples/Demos/iOS/AppDelegate.m create mode 100644 Demos/LunarG-VulkanSamples/Demos/iOS/DemoViewController.h create mode 100644 Demos/LunarG-VulkanSamples/Demos/iOS/DemoViewController.m create mode 100644 Demos/LunarG-VulkanSamples/Demos/iOS/Info.plist create mode 100644 Demos/LunarG-VulkanSamples/Demos/iOS/Prefix.pch create mode 100755 Demos/LunarG-VulkanSamples/Demos/iOS/Resources/Default-568h@2x.png create mode 100755 Demos/LunarG-VulkanSamples/Demos/iOS/Resources/Default~ipad.png create mode 100755 Demos/LunarG-VulkanSamples/Demos/iOS/Resources/Icon.png create mode 100644 Demos/LunarG-VulkanSamples/Demos/iOS/Resources/Main.storyboard create mode 100644 Demos/LunarG-VulkanSamples/Demos/iOS/main.m create mode 100644 Demos/LunarG-VulkanSamples/Demos/macOS/AppDelegate.h create mode 100644 Demos/LunarG-VulkanSamples/Demos/macOS/AppDelegate.m create mode 100644 Demos/LunarG-VulkanSamples/Demos/macOS/DemoViewController.h create mode 100644 Demos/LunarG-VulkanSamples/Demos/macOS/DemoViewController.m create mode 100644 Demos/LunarG-VulkanSamples/Demos/macOS/Info.plist create mode 100644 Demos/LunarG-VulkanSamples/Demos/macOS/Prefix.pch create mode 100644 Demos/LunarG-VulkanSamples/Demos/macOS/Resources/Main.storyboard create mode 100644 Demos/LunarG-VulkanSamples/Demos/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Demos/LunarG-VulkanSamples/Demos/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-128.png create mode 100644 Demos/LunarG-VulkanSamples/Demos/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-16.png create mode 100644 Demos/LunarG-VulkanSamples/Demos/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-256.png create mode 100644 Demos/LunarG-VulkanSamples/Demos/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-32.png create mode 100644 Demos/LunarG-VulkanSamples/Demos/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-512.png create mode 100644 Demos/LunarG-VulkanSamples/Demos/macOS/Resources/macOS.xcassets/Contents.json create mode 100644 Demos/LunarG-VulkanSamples/Demos/macOS/main.m create mode 100644 Demos/LunarG-VulkanSamples/Hologram/HelpersDispatchTable.cpp create mode 100644 Demos/LunarG-VulkanSamples/Hologram/HelpersDispatchTable.h create mode 100644 Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/project.pbxproj create mode 100644 Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/xcshareddata/xcschemes/Hologram-iOS.xcscheme create mode 100644 Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/xcshareddata/xcschemes/Hologram-macOS.xcscheme create mode 100644 Demos/LunarG-VulkanSamples/Hologram/ShellMVK.cpp create mode 100644 Demos/LunarG-VulkanSamples/Hologram/ShellMVK.h create mode 100644 Demos/LunarG-VulkanSamples/Hologram/iOS/AppDelegate.h create mode 100644 Demos/LunarG-VulkanSamples/Hologram/iOS/AppDelegate.m create mode 100644 Demos/LunarG-VulkanSamples/Hologram/iOS/DemoViewController.h create mode 100644 Demos/LunarG-VulkanSamples/Hologram/iOS/DemoViewController.mm create mode 100644 Demos/LunarG-VulkanSamples/Hologram/iOS/Info.plist create mode 100644 Demos/LunarG-VulkanSamples/Hologram/iOS/Prefix.pch create mode 100755 Demos/LunarG-VulkanSamples/Hologram/iOS/Resources/Default-568h@2x.png create mode 100755 Demos/LunarG-VulkanSamples/Hologram/iOS/Resources/Default~ipad.png create mode 100755 Demos/LunarG-VulkanSamples/Hologram/iOS/Resources/Icon.png create mode 100644 Demos/LunarG-VulkanSamples/Hologram/iOS/Resources/Main.storyboard create mode 100644 Demos/LunarG-VulkanSamples/Hologram/iOS/main.m create mode 100644 Demos/LunarG-VulkanSamples/Hologram/macOS/AppDelegate.h create mode 100644 Demos/LunarG-VulkanSamples/Hologram/macOS/AppDelegate.m create mode 100644 Demos/LunarG-VulkanSamples/Hologram/macOS/DemoViewController.h create mode 100644 Demos/LunarG-VulkanSamples/Hologram/macOS/DemoViewController.mm create mode 100644 Demos/LunarG-VulkanSamples/Hologram/macOS/Info.plist create mode 100644 Demos/LunarG-VulkanSamples/Hologram/macOS/Prefix.pch create mode 100644 Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/Main.storyboard create mode 100644 Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-128.png create mode 100644 Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-16.png create mode 100644 Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-256.png create mode 100644 Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-32.png create mode 100644 Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-512.png create mode 100644 Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/macOS.xcassets/Contents.json create mode 100644 Demos/LunarG-VulkanSamples/Hologram/macOS/main.m create mode 120000 Demos/LunarG-VulkanSamples/VulkanSamples create mode 100755 Demos/README_MoltenVK_Demos.md create mode 100755 Docs/MoltenVK_Runtime_UserGuide.md create mode 120000 Docs/Vulkan_1.0_Spec_with_MoltenVK_Extensions.html create mode 100644 Docs/Whats_New.md create mode 100755 Docs/images/MoltenVK-Logo-Banner.png create mode 160000 External/SPIRV-Cross create mode 160000 External/SPIRV-Headers create mode 160000 External/SPIRV-Tools create mode 160000 External/Vulkan-Hpp create mode 160000 External/glslang create mode 100755 External/makeAll create mode 100755 External/makeSPIRVTools create mode 100755 External/makeVulkanSpec create mode 100755 External/makeglslang create mode 100644 LICENSE create mode 100644 MoltenVK/MoltenVK.xcodeproj/project.pbxproj create mode 100644 MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-iOS.xcscheme create mode 100644 MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-macOS.xcscheme create mode 100644 MoltenVK/MoltenVK/API/mvk_datatypes.h create mode 100644 MoltenVK/MoltenVK/API/mvk_vulkan.h create mode 100644 MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h create mode 100644 MoltenVK/MoltenVK/Commands/MVKCmdDispatch.h create mode 100644 MoltenVK/MoltenVK/Commands/MVKCmdDispatch.mm create mode 100644 MoltenVK/MoltenVK/Commands/MVKCmdDraw.h create mode 100644 MoltenVK/MoltenVK/Commands/MVKCmdDraw.mm create mode 100644 MoltenVK/MoltenVK/Commands/MVKCmdPipeline.h create mode 100644 MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm create mode 100644 MoltenVK/MoltenVK/Commands/MVKCmdQueries.h create mode 100644 MoltenVK/MoltenVK/Commands/MVKCmdQueries.mm create mode 100644 MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h create mode 100644 MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm create mode 100644 MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h create mode 100644 MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm create mode 100644 MoltenVK/MoltenVK/Commands/MVKCommand.h create mode 100644 MoltenVK/MoltenVK/Commands/MVKCommand.mm create mode 100644 MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h create mode 100644 MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm create mode 100644 MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h create mode 100644 MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm create mode 100644 MoltenVK/MoltenVK/Commands/MVKCommandEncodingPool.h create mode 100644 MoltenVK/MoltenVK/Commands/MVKCommandEncodingPool.mm create mode 100644 MoltenVK/MoltenVK/Commands/MVKCommandPipelineStateFactoryShaderSource.h create mode 100644 MoltenVK/MoltenVK/Commands/MVKCommandPool.h create mode 100644 MoltenVK/MoltenVK/Commands/MVKCommandPool.mm create mode 100644 MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h create mode 100644 MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm create mode 100644 MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.h create mode 100644 MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.mm create mode 100644 MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKDevice.h create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.h create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.mm create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKImage.h create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKImage.mm create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKInstance.h create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.h create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.mm create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKQueue.h create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKResource.h create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKResource.mm create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKSurface.h create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKSurface.mm create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKSync.h create mode 100644 MoltenVK/MoltenVK/GPUObjects/MVKSync.mm create mode 100644 MoltenVK/MoltenVK/Loader/MVKLayers.h create mode 100644 MoltenVK/MoltenVK/Loader/MVKLayers.mm create mode 100644 MoltenVK/MoltenVK/Utility/MVKBaseObject.cpp create mode 100644 MoltenVK/MoltenVK/Utility/MVKBaseObject.h create mode 100644 MoltenVK/MoltenVK/Utility/MVKEnvironment.h create mode 100644 MoltenVK/MoltenVK/Utility/MVKFoundation.h create mode 100644 MoltenVK/MoltenVK/Utility/MVKFoundation.mm create mode 100644 MoltenVK/MoltenVK/Utility/MVKOSExtensions.h create mode 100644 MoltenVK/MoltenVK/Utility/MVKOSExtensions.mm create mode 100644 MoltenVK/MoltenVK/Utility/MVKObjectPool.h create mode 100644 MoltenVK/MoltenVK/Utility/MVKWatermark.h create mode 100644 MoltenVK/MoltenVK/Utility/MVKWatermark.mm create mode 100644 MoltenVK/MoltenVK/Utility/MVKWatermarkShaderSource.h create mode 100644 MoltenVK/MoltenVK/Utility/MVKWatermarkTextureContent.h create mode 100644 MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm create mode 100644 MoltenVK/MoltenVK/Vulkan/vk_mvk_moltenvk.mm create mode 100644 MoltenVK/MoltenVK/Vulkan/vulkan.mm create mode 100644 MoltenVK/ThirdPartyConfig.md create mode 120000 MoltenVK/Vulkan-Hpp create mode 120000 MoltenVK/iOS create mode 120000 MoltenVK/include/MoltenVK create mode 120000 MoltenVK/include/vulkan create mode 120000 MoltenVK/macOS create mode 100644 MoltenVKPackaging.xcodeproj/project.pbxproj create mode 100644 MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK (Debug).xcscheme create mode 100644 MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK (Release).xcscheme create mode 100644 MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLConversion.h create mode 100644 MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLConversion.mm create mode 100644 MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLToSPIRVConverter.cpp create mode 100644 MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLToSPIRVConverter.h create mode 120000 MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/glslang create mode 120000 MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/iOS create mode 120000 MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/macOS create mode 120000 MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRV-Cross create mode 120000 MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRV-Headers create mode 120000 MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRV-Tools create mode 100644 MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVConversion.h create mode 100644 MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVConversion.mm create mode 100644 MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp create mode 100644 MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h create mode 120000 MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/iOS create mode 120000 MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/macOS create mode 120000 MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/spirv.hpp create mode 100644 MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj create mode 100644 MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-iOS.xcscheme create mode 100644 MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-macOS.xcscheme create mode 100644 MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-iOS.xcscheme create mode 100644 MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-macOS.xcscheme create mode 100644 MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKShaderConverter.xcscheme create mode 100644 MoltenVKShaderConverter/MoltenVKShaderConverterTool/FileSupport.h create mode 100644 MoltenVKShaderConverter/MoltenVKShaderConverterTool/FileSupport.mm create mode 100644 MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.cpp create mode 100644 MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.h create mode 100644 MoltenVKShaderConverter/MoltenVKShaderConverterTool/main.cpp create mode 100644 MoltenVKShaderConverter/ThirdPartyConfig.md create mode 100755 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..030b7562 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# MoltenVK specific +Package/ + +# Mac OS X Finder +.DS_Store + +# Xcode +xcuserdata/ +project.xcworkspace/ +*.xccheckout +*.xcscmblueprint + +# build products +build/ +*.[oa] + +# Distribution package & Dynamic libs + +# Other source repository archive directories (protects when importing) +.hg +.svn +CVS + +# automatic backup files +*~.nib +*.swp +*~ +*(Autosaved).rtfd/ +Backup[ ]of[ ]*.pages/ +Backup[ ]of[ ]*.key/ +Backup[ ]of[ ]*.numbers/ + diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..99bb9c05 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,18 @@ +[submodule "External/Vulkan-Hpp"] + path = External/Vulkan-Hpp + url = https://github.com/KhronosGroup/Vulkan-Hpp.git + ignore = dirty +[submodule "External/SPIRV-Headers"] + path = External/SPIRV-Headers + url = https://github.com/KhronosGroup/SPIRV-Headers.git +[submodule "External/SPIRV-Tools"] + path = External/SPIRV-Tools + url = https://github.com/KhronosGroup/SPIRV-Tools.git + ignore = dirty +[submodule "External/glslang"] + path = External/glslang + url = https://github.com/KhronosGroup/glslang.git + ignore = dirty +[submodule "External/SPIRV-Cross"] + path = External/SPIRV-Cross + url = https://github.com/KhronosGroup/SPIRV-Cross.git diff --git a/Common/MVKCommonEnvironment.h b/Common/MVKCommonEnvironment.h new file mode 100644 index 00000000..682d43f3 --- /dev/null +++ b/Common/MVKCommonEnvironment.h @@ -0,0 +1,70 @@ +/* + * MVKCommonEnvironment.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + +#include + + +/** + * Compiler build setting that ensures a definite value for whether this build is + * a debug build or not. + * + * If the standard DEBUG build setting is defined, MVK_DEBUG is set to true, + * otherwise, it is set to false. + */ +#ifndef MVK_DEBUG +# ifdef DEBUG +# define MVK_DEBUG 1 +# else +# define MVK_DEBUG 0 +# endif // DEBUG +#endif // MVK_DEBUG + + /** Building for iOS. Use ifdef instead of defined() operator to allow MVK_IOS to be used in expansions */ +#ifndef MVK_IOS +# ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +# define MVK_IOS 1 +# else +# define MVK_IOS 0 +# endif +#endif + + /** Building for macOS. Use ifdef instead of defined() operator to allow MVK_MACOS to be used in expansions */ +#ifndef MVK_MACOS +# ifdef __MAC_OS_X_VERSION_MAX_ALLOWED +# define MVK_MACOS 1 +# else +# define MVK_MACOS 0 +# endif +#endif + +/** Directive to identify public symbols. */ +#define MVK_PUBLIC_SYMBOL __attribute__((visibility("default"))) + + +#ifdef __cplusplus +} +#endif // __cplusplus + diff --git a/Common/MVKLogging.h b/Common/MVKLogging.h new file mode 100755 index 00000000..63829426 --- /dev/null +++ b/Common/MVKLogging.h @@ -0,0 +1,226 @@ +/* + * MVKLogging.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#include "MVKCommonEnvironment.h" +#include +#include +#include +#include +#include + +/** + * This library adds flexible, non-intrusive logging and assertion capabilities + * that can be efficiently enabled or disabled via compiler switches. + * + * There are four levels of logging: Trace, Info, Error and Debug, and each can be enabled + * independently via the MVK_LOG_LEVEL_TRACE, MVK_LOG_LEVEL_INFO, MVK_LOG_LEVEL_ERROR and + * MVK_LOG_LEVEL_DEBUG switches, respectively. + * + * ALL logging can be enabled or disabled via the MVK_LOGGING_ENABLED switch. + * + * Each logging level also has a conditional logging variation, which outputs a log entry + * only if the specified conditional expression evaluates to YES. + * + * Logging functions are implemented here via macros. Disabling logging, either entirely, or + * at a specific level, completely removes the corresponding log invocations from the compiled + * code, thus eliminating both the memory and CPU overhead that the logging calls would add. + * You might choose, for example, to completely remove all logging from production release code, + * by setting MVK_LOGGING_ENABLED off in your production builds settings. Or, as another example, + * you might choose to include Error logging in your production builds by turning only + * MVK_LOGGING_ENABLED and MVK_LOG_LEVEL_ERROR on, and turning the others off. + * + * To perform logging, use any of the following function calls in your code: + * + * MVKLogError(fmt, ...) - recommended for use only when there is an error to be logged + * - will print if MVK_LOG_LEVEL_ERROR is set on. + * MVKLogErrorIf(cond, fmt, ...) - same as MVKLogError if boolean "cond" condition expression evaluates to YES, + * otherwise logs nothing. + * + * MVKLogInfo(fmt, ...) - recommended for general, infrequent, information messages + * - will print if MVK_LOG_LEVEL_INFO is set on. + * MVKLogInfoIf(cond, fmt, ...) - same as MVKLogInfo if boolean "cond" condition expression evaluates to YES, + * otherwise logs nothing. + * + * MVKLogDebug(fmt, ...) - recommended for temporary use during debugging + * - will print if MVK_LOG_LEVEL_DEBUG is set on. + * MVKLogDebugIf(cond, fmt, ...) - same as MVKLogDebug if boolean "cond" condition expression evaluates to YES, + * otherwise logs nothing. + * + * MVKLogTrace(fmt, ...) - recommended for detailed tracing of program flow + * - will print if MVK_LOG_LEVEL_TRACE is set on. + * MVKLogTraceIf(cond, fmt, ...) - same as MVKLogTrace if boolean "cond" condition expression evaluates to YES, + * otherwise logs nothing. + * + * In each case, the functions follow the general NSLog/printf template, where the first argument + * "fmt" is an NSString that optionally includes embedded Format Specifiers, and subsequent optional + * arguments indicate data to be formatted and inserted into the string. As with NSLog/printf, the number + * of optional arguments must match the number of embedded Format Specifiers. For more info, see the + * core documentation for NSLog and String Format Specifiers. + * + * This library also enchances the assertion functions. + * + * The MVKAssert() function can be used in place of the standard NSAssert() family of functions. + * MVKAssert() improves the NSAssert() family of functions in two ways: + * - MVKAssert ensures that the assertion message is logged to the console. + * - MVKAssert can be used with a variable number of arguments without the need for + * NSAssert1(), NSAssert2(), etc. + * + * Like the NSAssert() functions, you can turn assertions off in production code by either setting + * NS_BLOCK_ASSERTIONS to 1 in your compiler build settings, or setting the ENABLE_NS_ASSERTIONS + * compiler setting to 0. Doing so completely removes the corresponding assertion invocations + * from the compiled code, thus eliminating both the memory and CPU overhead that the assertion + * calls would add + * + * A special MVKAssertUnimplemented(name) and MVKAssertCUnimplemented(name) assertion functions + * are provided to conveniently raise an assertion exception when some expected functionality + * is unimplemented. Either functions may be used as a temporary placeholder for functionalty + * that will be added at a later time. MVKAssertUnimplemented(name) may also be used in a method + * body by a superclass that requires each subclass to implement that method + * + * Although you can directly edit this file to turn on or off the switches below, the preferred + * technique is to set these switches via the compiler build setting GCC_PREPROCESSOR_DEFINITIONS + * in your build configuration. + */ + +/** + * Set this switch to enable or disable logging capabilities. This can be set either here + * or via the compiler build setting GCC_PREPROCESSOR_DEFINITIONS in your build configuration. + * Using the compiler build setting is preferred for this to ensure that logging is not + * accidentally left enabled by accident in release builds. + * + * Logging is enabled by default. + */ +#ifndef MVK_LOGGING_ENABLED +# define MVK_LOGGING_ENABLED 1 +#endif + +/** + * Set any or all of these switches to enable or disable logging at specific levels. + * These can be set either here or as a compiler build settings. + */ +#ifndef MVK_LOG_LEVEL_ERROR +# define MVK_LOG_LEVEL_ERROR MVK_LOGGING_ENABLED +#endif +#ifndef MVK_LOG_LEVEL_INFO +# define MVK_LOG_LEVEL_INFO MVK_LOGGING_ENABLED +#endif +#ifndef MVK_LOG_LEVEL_DEBUG +# define MVK_LOG_LEVEL_DEBUG (MVK_LOGGING_ENABLED && MVK_DEBUG) +#endif +#ifndef MVK_LOG_LEVEL_TRACE +# define MVK_LOG_LEVEL_TRACE 0 +#endif + + +// *********** END OF USER SETTINGS - Do not change anything below this line *********** + +// Error logging - only when there is an error to be logged +#if MVK_LOG_LEVEL_ERROR +# define MVKLogError(fmt, ...) MVKLogErrorImpl(fmt, ##__VA_ARGS__) +# define MVKLogErrorIf(cond, fmt, ...) if(cond) { MVKLogErrorImpl(fmt, ##__VA_ARGS__); } +#else +# define MVKLogError(...) +# define MVKLogErrorIf(cond, fmt, ...) +#endif + +// Info logging - for general, non-performance affecting information messages +#if MVK_LOG_LEVEL_INFO +# define MVKLogInfo(fmt, ...) MVKLogInfoImpl(fmt, ##__VA_ARGS__) +# define MVKLogInfoIf(cond, fmt, ...) if(cond) { MVKLogInfoImpl(fmt, ##__VA_ARGS__); } +#else +# define MVKLogInfo(...) +# define MVKLogInfoIf(cond, fmt, ...) +#endif + +// Trace logging - for detailed tracing +#if MVK_LOG_LEVEL_TRACE +# define MVKLogTrace(fmt, ...) MVKLogTraceImpl(fmt, ##__VA_ARGS__) +# define MVKLogTraceIf(cond, fmt, ...) if(cond) { MVKLogTraceImpl(fmt, ##__VA_ARGS__); } +#else +# define MVKLogTrace(...) +# define MVKLogTraceIf(cond, fmt, ...) +#endif + +// Debug logging - use only temporarily for highlighting and tracking down problems +#if MVK_LOG_LEVEL_DEBUG +# define MVKLogDebug(fmt, ...) MVKLogDebugImpl(fmt, ##__VA_ARGS__) +# define MVKLogDebugIf(cond, fmt, ...) if(cond) { MVKLogDebugImpl(fmt, ##__VA_ARGS__); } +#else +# define MVKLogDebug(...) +# define MVKLogDebugIf(cond, fmt, ...) +#endif + +/** + * Combine the specified log level and format string, then log + * the specified args to one or both of ASL and printf. + */ +static inline void MVKLogImplV(bool logToPrintf, bool logToASL, int aslLvl, const char* lvlStr, const char* format, va_list args) { + + // Combine the level and format string + char lvlFmt[strlen(lvlStr) + strlen(format) + 5]; + sprintf(lvlFmt, "[%s] %s\n", lvlStr, format); + + if (logToPrintf) { vprintf(lvlFmt, args); } +// if (logToASL) { asl_vlog(NULL, NULL, aslLvl, lvlFmt, args); } // Multi-threaded ASL support requires a separate ASL client to be opened per thread! +} + +/** + * Combine the specified log level and format string, then log + * the specified args to one or both of ASL and printf. + */ +static inline void MVKLogImpl(bool logToPrintf, bool logToASL, int aslLvl, const char* lvlStr, const char* format, ...) { + va_list args; + va_start(args, format); + MVKLogImplV(logToPrintf, logToASL, aslLvl, lvlStr, format, args); + va_end(args); +} + +#define MVKLogErrorImpl(fmt, ...) MVKLogImpl(true, !(MVK_DEBUG), ASL_LEVEL_ERR, "***MoltenVK ERROR***", fmt, ##__VA_ARGS__) +#define MVKLogInfoImpl(fmt, ...) MVKLogImpl(true, !(MVK_DEBUG), ASL_LEVEL_NOTICE, "mvk-info", fmt, ##__VA_ARGS__) +#define MVKLogTraceImpl(fmt, ...) MVKLogImpl(true, !(MVK_DEBUG), ASL_LEVEL_DEBUG, "mvk-trace", fmt, ##__VA_ARGS__) +#define MVKLogDebugImpl(fmt, ...) MVKLogImpl(true, !(MVK_DEBUG), ASL_LEVEL_DEBUG, "mvk-debug", fmt, ##__VA_ARGS__) + +// Assertions +#if NS_BLOCK_ASSERTIONS +# define MVKAssert(test, fmt, ...) +#else +# define MVKAssert(test, fmt, ...) \ + if (!(test)) { \ + MVKLogError(fmt, ##__VA_ARGS__); \ + assert(false); \ + } +#endif + +#define MVKAssertUnimplemented(name) MVKAssert(false, "%s is not implemented!", name) + +// Use this macro to open a break-point programmatically. +#ifndef MVK_DEBUGGER +# define MVK_DEBUGGER() { kill( getpid(), SIGINT ) ; } +#endif + + +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/Common/MVKStrings.h b/Common/MVKStrings.h new file mode 100644 index 00000000..6d7444be --- /dev/null +++ b/Common/MVKStrings.h @@ -0,0 +1,49 @@ +/* + * MVKStrings.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MVKStrings_h_ +#define __MVKStrings_h_ 1 + +#include + +namespace mvk { + + static std::string _mvkDefaultWhitespaceChars = " \f\n\r\t\v"; + + /** Returns a string with whitespace trimmed from the right end of the specified string. */ + inline std::string trim_right(const std::string& s, const std::string& delimiters = _mvkDefaultWhitespaceChars) { + size_t endPos = s.find_last_not_of(delimiters); + return (endPos != std::string::npos) ? s.substr(0, endPos + 1) : ""; + } + + /** Returns a string with whitespace trimmed from the left end of the specified string. */ + inline std::string trim_left(const std::string& s, const std::string& delimiters = _mvkDefaultWhitespaceChars) { + size_t startPos = s.find_first_not_of(delimiters); + return (startPos != std::string::npos) ? s.substr(startPos) : ""; + } + + /** Returns a string with whitespace trimmed from both ends of the specified string. */ + inline std::string trim(const std::string& s, const std::string& delimiters = _mvkDefaultWhitespaceChars) { + size_t startPos = s.find_first_not_of(delimiters); + size_t endPos = s.find_last_not_of(delimiters); + return ( (startPos != std::string::npos) && (endPos != std::string::npos) ) ? s.substr(startPos, endPos + 1) : ""; + } + +} + +#endif diff --git a/Demos/Demos.xcworkspace/contents.xcworkspacedata b/Demos/Demos.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..3d4782f3 --- /dev/null +++ b/Demos/Demos.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/Demos/Demos.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Demos/Demos.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..08de0be8 --- /dev/null +++ b/Demos/Demos.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded + + + diff --git a/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/AsynchronousTimeWarp.xcodeproj/project.pbxproj b/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/AsynchronousTimeWarp.xcodeproj/project.pbxproj new file mode 100644 index 00000000..83b16c6f --- /dev/null +++ b/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/AsynchronousTimeWarp.xcodeproj/project.pbxproj @@ -0,0 +1,535 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + A9096E4D1F7EF0E200DFBEA6 /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9096E4C1F7EF0E200DFBEA6 /* IOSurface.framework */; }; + A944E5E61D1CA976000777C7 /* macOS.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A944E5E51D1CA976000777C7 /* macOS.xcassets */; }; + A944E5EE1D1CA9E0000777C7 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A944E5EA1D1CA9E0000777C7 /* Default-568h@2x.png */; }; + A944E5EF1D1CA9E0000777C7 /* Default~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = A944E5EB1D1CA9E0000777C7 /* Default~ipad.png */; }; + A944E5F01D1CA9E0000777C7 /* Icon.png in Resources */ = {isa = PBXBuildFile; fileRef = A944E5EC1D1CA9E0000777C7 /* Icon.png */; }; + A947FD781CF91202007A3232 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = A947FD771CF91202007A3232 /* libc++.tbd */; }; + A947FD7A1CF91217007A3232 /* MoltenVK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A947FD791CF91217007A3232 /* MoltenVK.framework */; }; + A95419011D11A1F0000901F0 /* MoltenVK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A95419001D11A1F0000901F0 /* MoltenVK.framework */; }; + A9A7447F1D1198A400ADB0E9 /* atw_vulkan.c in Sources */ = {isa = PBXBuildFile; fileRef = A9A980211CF92ADC00A9CCE1 /* atw_vulkan.c */; }; + A9A744841D1198A400ADB0E9 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = A947FD771CF91202007A3232 /* libc++.tbd */; }; + A9A980641CF92B0400A9CCE1 /* atw_vulkan.c in Sources */ = {isa = PBXBuildFile; fileRef = A9A980211CF92ADC00A9CCE1 /* atw_vulkan.c */; }; + A9A980651CF92B5200A9CCE1 /* atw_opengl.c in Sources */ = {isa = PBXBuildFile; fileRef = A9A980201CF92ADC00A9CCE1 /* atw_opengl.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + A9096E4C1F7EF0E200DFBEA6 /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.0.sdk/System/Library/Frameworks/IOSurface.framework; sourceTree = DEVELOPER_DIR; }; + A944E5E51D1CA976000777C7 /* macOS.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = macOS.xcassets; sourceTree = ""; }; + A944E5EA1D1CA9E0000777C7 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; + A944E5EB1D1CA9E0000777C7 /* Default~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default~ipad.png"; sourceTree = ""; }; + A944E5EC1D1CA9E0000777C7 /* Icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Icon.png; sourceTree = ""; }; + A947FD771CF91202007A3232 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; + A947FD791CF91217007A3232 /* MoltenVK.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVK.framework; path = ../../../../Package/Latest/MoltenVK/macOS/MoltenVK.framework; sourceTree = ""; }; + A95419001D11A1F0000901F0 /* MoltenVK.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVK.framework; path = ../../../../Package/Latest/MoltenVK/iOS/MoltenVK.framework; sourceTree = ""; }; + A95816661C03BC4D00792CBF /* AsynchronousTimeWarp-OGL.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "AsynchronousTimeWarp-OGL.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + A95816741C03BC4D00792CBF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A95816881C03D79500792CBF /* AsynchronousTimeWarp-VK.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "AsynchronousTimeWarp-VK.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + A9A744881D1198A400ADB0E9 /* AsynchronousTimeWarp-VK.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "AsynchronousTimeWarp-VK.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + A9A980201CF92ADC00A9CCE1 /* atw_opengl.c */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.objc; path = atw_opengl.c; sourceTree = ""; usesTabs = 1; }; + A9A980211CF92ADC00A9CCE1 /* atw_vulkan.c */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.objc; path = atw_vulkan.c; sourceTree = ""; usesTabs = 1; }; + A9A980231CF92ADC00A9CCE1 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; + A9A980631CF92ADC00A9CCE1 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + A95816631C03BC4D00792CBF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A95816811C03D79500792CBF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A947FD7A1CF91217007A3232 /* MoltenVK.framework in Frameworks */, + A947FD781CF91202007A3232 /* libc++.tbd in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A9A744821D1198A400ADB0E9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A95419011D11A1F0000901F0 /* MoltenVK.framework in Frameworks */, + A9096E4D1F7EF0E200DFBEA6 /* IOSurface.framework in Frameworks */, + A9A744841D1198A400ADB0E9 /* libc++.tbd in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + A93E22721C060C4A00206F85 /* Frameworks */ = { + isa = PBXGroup; + children = ( + A9096E4C1F7EF0E200DFBEA6 /* IOSurface.framework */, + A95419001D11A1F0000901F0 /* MoltenVK.framework */, + A947FD791CF91217007A3232 /* MoltenVK.framework */, + A947FD771CF91202007A3232 /* libc++.tbd */, + ); + name = Frameworks; + sourceTree = ""; + }; + A944E5E41D1CA976000777C7 /* Resources */ = { + isa = PBXGroup; + children = ( + A944E5E51D1CA976000777C7 /* macOS.xcassets */, + ); + path = Resources; + sourceTree = ""; + }; + A944E5E71D1CA9E0000777C7 /* iOS */ = { + isa = PBXGroup; + children = ( + A944E5E91D1CA9E0000777C7 /* Resources */, + ); + path = iOS; + sourceTree = ""; + }; + A944E5E91D1CA9E0000777C7 /* Resources */ = { + isa = PBXGroup; + children = ( + A944E5EA1D1CA9E0000777C7 /* Default-568h@2x.png */, + A944E5EB1D1CA9E0000777C7 /* Default~ipad.png */, + A944E5EC1D1CA9E0000777C7 /* Icon.png */, + ); + path = Resources; + sourceTree = ""; + }; + A958165D1C03BC4D00792CBF = { + isa = PBXGroup; + children = ( + A9A9801E1CF92ADC00A9CCE1 /* atw */, + A944E5E71D1CA9E0000777C7 /* iOS */, + A95816681C03BC4D00792CBF /* macOS */, + A93E22721C060C4A00206F85 /* Frameworks */, + A95816671C03BC4D00792CBF /* Products */, + ); + sourceTree = ""; + }; + A95816671C03BC4D00792CBF /* Products */ = { + isa = PBXGroup; + children = ( + A95816661C03BC4D00792CBF /* AsynchronousTimeWarp-OGL.app */, + A95816881C03D79500792CBF /* AsynchronousTimeWarp-VK.app */, + A9A744881D1198A400ADB0E9 /* AsynchronousTimeWarp-VK.app */, + ); + name = Products; + sourceTree = ""; + }; + A95816681C03BC4D00792CBF /* macOS */ = { + isa = PBXGroup; + children = ( + A944E5E41D1CA976000777C7 /* Resources */, + A95816741C03BC4D00792CBF /* Info.plist */, + ); + path = macOS; + sourceTree = ""; + }; + A9A9801E1CF92ADC00A9CCE1 /* atw */ = { + isa = PBXGroup; + children = ( + A9A980201CF92ADC00A9CCE1 /* atw_opengl.c */, + A9A980211CF92ADC00A9CCE1 /* atw_vulkan.c */, + A9A980231CF92ADC00A9CCE1 /* LICENSE */, + A9A980631CF92ADC00A9CCE1 /* README.md */, + ); + path = atw; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + A95816651C03BC4D00792CBF /* AsynchronousTimeWarp-OGL-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = A95816771C03BC4D00792CBF /* Build configuration list for PBXNativeTarget "AsynchronousTimeWarp-OGL-macOS" */; + buildPhases = ( + A95816621C03BC4D00792CBF /* Sources */, + A95816641C03BC4D00792CBF /* Resources */, + A95816631C03BC4D00792CBF /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "AsynchronousTimeWarp-OGL-macOS"; + productName = AsynchronousTimeWarp; + productReference = A95816661C03BC4D00792CBF /* AsynchronousTimeWarp-OGL.app */; + productType = "com.apple.product-type.application"; + }; + A958167E1C03D79500792CBF /* AsynchronousTimeWarp-VK-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = A95816851C03D79500792CBF /* Build configuration list for PBXNativeTarget "AsynchronousTimeWarp-VK-macOS" */; + buildPhases = ( + A958167F1C03D79500792CBF /* Sources */, + A95816821C03D79500792CBF /* Resources */, + A95816811C03D79500792CBF /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "AsynchronousTimeWarp-VK-macOS"; + productName = AsynchronousTimeWarp; + productReference = A95816881C03D79500792CBF /* AsynchronousTimeWarp-VK.app */; + productType = "com.apple.product-type.application"; + }; + A9A7447D1D1198A400ADB0E9 /* AsynchronousTimeWarp-VK-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = A9A744851D1198A400ADB0E9 /* Build configuration list for PBXNativeTarget "AsynchronousTimeWarp-VK-iOS" */; + buildPhases = ( + A9A7447E1D1198A400ADB0E9 /* Sources */, + A9A744801D1198A400ADB0E9 /* Resources */, + A9A744821D1198A400ADB0E9 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "AsynchronousTimeWarp-VK-iOS"; + productName = AsynchronousTimeWarp; + productReference = A9A744881D1198A400ADB0E9 /* AsynchronousTimeWarp-VK.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + A958165E1C03BC4D00792CBF /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0900; + ORGANIZATIONNAME = "The Brenwill Workshop Ltd."; + TargetAttributes = { + A95816651C03BC4D00792CBF = { + CreatedOnToolsVersion = 7.1.1; + DevelopmentTeam = VU3TCKU48B; + }; + A958167E1C03D79500792CBF = { + DevelopmentTeam = VU3TCKU48B; + }; + A9A7447D1D1198A400ADB0E9 = { + DevelopmentTeam = VU3TCKU48B; + }; + }; + }; + buildConfigurationList = A95816611C03BC4D00792CBF /* Build configuration list for PBXProject "AsynchronousTimeWarp" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = A958165D1C03BC4D00792CBF; + productRefGroup = A95816671C03BC4D00792CBF /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + A9A7447D1D1198A400ADB0E9 /* AsynchronousTimeWarp-VK-iOS */, + A958167E1C03D79500792CBF /* AsynchronousTimeWarp-VK-macOS */, + A95816651C03BC4D00792CBF /* AsynchronousTimeWarp-OGL-macOS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + A95816641C03BC4D00792CBF /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A95816821C03D79500792CBF /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A944E5E61D1CA976000777C7 /* macOS.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A9A744801D1198A400ADB0E9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A944E5F01D1CA9E0000777C7 /* Icon.png in Resources */, + A944E5EF1D1CA9E0000777C7 /* Default~ipad.png in Resources */, + A944E5EE1D1CA9E0000777C7 /* Default-568h@2x.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + A95816621C03BC4D00792CBF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A9A980651CF92B5200A9CCE1 /* atw_opengl.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A958167F1C03D79500792CBF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A9A980641CF92B0400A9CCE1 /* atw_vulkan.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A9A7447E1D1198A400ADB0E9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A9A7447F1D1198A400ADB0E9 /* atw_vulkan.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + A95816751C03BC4D00792CBF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = NO; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_BITCODE = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "\"$(SRCROOT)/../../../MoltenVK/include\"", + "\"$(SRCROOT)/../Vulkan-Samples/external/include\"", + ); + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = "-ObjC"; + SDKROOT = macosx; + }; + name = Debug; + }; + A95816761C03BC4D00792CBF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = NO; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_BITCODE = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "\"$(SRCROOT)/../../../MoltenVK/include\"", + "\"$(SRCROOT)/../Vulkan-Samples/external/include\"", + ); + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = "-ObjC"; + SDKROOT = macosx; + }; + name = Release; + }; + A95816781C03BC4D00792CBF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = macOS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.moltenvk.AsynchronousTimeWarp; + PRODUCT_NAME = "AsynchronousTimeWarp-OGL"; + }; + name = Debug; + }; + A95816791C03BC4D00792CBF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = macOS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.moltenvk.AsynchronousTimeWarp; + PRODUCT_NAME = "AsynchronousTimeWarp-OGL"; + }; + name = Release; + }; + A95816861C03D79500792CBF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = "\"$(SRCROOT)/../../../MoltenVK/macOS\""; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + VK_USE_PLATFORM_MACOS_MVK, + ); + INFOPLIST_FILE = "$(SRCROOT)/macOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + PRODUCT_BUNDLE_IDENTIFIER = com.moltenvk.AsynchronousTimeWarp; + PRODUCT_NAME = "AsynchronousTimeWarp-VK"; + }; + name = Debug; + }; + A95816871C03D79500792CBF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = "\"$(SRCROOT)/../../../MoltenVK/macOS\""; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + VK_USE_PLATFORM_MACOS_MVK, + ); + INFOPLIST_FILE = "$(SRCROOT)/macOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + PRODUCT_BUNDLE_IDENTIFIER = com.moltenvk.AsynchronousTimeWarp; + PRODUCT_NAME = "AsynchronousTimeWarp-VK"; + }; + name = Release; + }; + A9A744861D1198A400ADB0E9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = VU3TCKU48B; + FRAMEWORK_SEARCH_PATHS = "\"$(SRCROOT)/../../../MoltenVK/iOS\""; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + VK_USE_PLATFORM_IOS_MVK, + ); + INFOPLIST_FILE = "$(SRCROOT)/iOS/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.moltenvk.AsynchronousTimeWarp; + PRODUCT_NAME = "AsynchronousTimeWarp-VK"; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = arm64; + }; + name = Debug; + }; + A9A744871D1198A400ADB0E9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = VU3TCKU48B; + FRAMEWORK_SEARCH_PATHS = "\"$(SRCROOT)/../../../MoltenVK/iOS\""; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + VK_USE_PLATFORM_IOS_MVK, + ); + INFOPLIST_FILE = "$(SRCROOT)/iOS/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.moltenvk.AsynchronousTimeWarp; + PRODUCT_NAME = "AsynchronousTimeWarp-VK"; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = arm64; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + A95816611C03BC4D00792CBF /* Build configuration list for PBXProject "AsynchronousTimeWarp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A95816751C03BC4D00792CBF /* Debug */, + A95816761C03BC4D00792CBF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A95816771C03BC4D00792CBF /* Build configuration list for PBXNativeTarget "AsynchronousTimeWarp-OGL-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A95816781C03BC4D00792CBF /* Debug */, + A95816791C03BC4D00792CBF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A95816851C03D79500792CBF /* Build configuration list for PBXNativeTarget "AsynchronousTimeWarp-VK-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A95816861C03D79500792CBF /* Debug */, + A95816871C03D79500792CBF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A9A744851D1198A400ADB0E9 /* Build configuration list for PBXNativeTarget "AsynchronousTimeWarp-VK-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A9A744861D1198A400ADB0E9 /* Debug */, + A9A744871D1198A400ADB0E9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = A958165E1C03BC4D00792CBF /* Project object */; +} diff --git a/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/AsynchronousTimeWarp.xcodeproj/xcshareddata/xcschemes/AsynchronousTimeWarp-VK-iOS.xcscheme b/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/AsynchronousTimeWarp.xcodeproj/xcshareddata/xcschemes/AsynchronousTimeWarp-VK-iOS.xcscheme new file mode 100644 index 00000000..337bab2f --- /dev/null +++ b/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/AsynchronousTimeWarp.xcodeproj/xcshareddata/xcschemes/AsynchronousTimeWarp-VK-iOS.xcscheme @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/AsynchronousTimeWarp.xcodeproj/xcshareddata/xcschemes/AsynchronousTimeWarp-VK-macOS.xcscheme b/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/AsynchronousTimeWarp.xcodeproj/xcshareddata/xcschemes/AsynchronousTimeWarp-VK-macOS.xcscheme new file mode 100644 index 00000000..69aafad8 --- /dev/null +++ b/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/AsynchronousTimeWarp.xcodeproj/xcshareddata/xcschemes/AsynchronousTimeWarp-VK-macOS.xcscheme @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/atw b/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/atw new file mode 120000 index 00000000..3cdf1d81 --- /dev/null +++ b/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/atw @@ -0,0 +1 @@ +../Vulkan-Samples/samples/apps/atw \ No newline at end of file diff --git a/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/iOS/Info.plist b/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/iOS/Info.plist new file mode 100644 index 00000000..6d729bb3 --- /dev/null +++ b/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/iOS/Info.plist @@ -0,0 +1,42 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + iOS/Resources/Icon.png + CFBundleIcons~ipad + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleVersion + 1.0 + LSApplicationCategoryType + + UIRequiresFullScreen + + UIStatusBarHidden + + UISupportedInterfaceOrientations + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/iOS/Resources/Default-568h@2x.png b/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/iOS/Resources/Default-568h@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..1669d7b6845195d9e11a1af63165c359f7041fc6 GIT binary patch literal 48497 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 PlX?;)X>95|%p}7R{?x}+qCcdCM$GDH! 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 literal 0 HcmV?d00001 diff --git a/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/iOS/Resources/Icon.png b/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/iOS/Resources/Icon.png new file mode 100755 index 0000000000000000000000000000000000000000..dc3b10f5ea97460001efe8fb45e8264812ba68ab GIT binary patch literal 5914 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 + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © The Brenwill Workshop Ltd. All rights reserved. + NSPrincipalClass + NSApplication + + diff --git a/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Contents.json b/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..4124516b --- /dev/null +++ b/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,63 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "Icon-16.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "Icon-32.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "Icon-128.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "Icon-256.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "Icon-512.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-128.png b/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-128.png new file mode 100644 index 0000000000000000000000000000000000000000..8c55f1bea07d81a0366425784d928608f325c5c8 GIT binary patch literal 12449 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 literal 0 HcmV?d00001 diff --git a/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-16.png b/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-16.png new file mode 100644 index 0000000000000000000000000000000000000000..583d636d60253168d1632b648a8ae5d34eaf4843 GIT binary patch literal 671 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 literal 0 HcmV?d00001 diff --git a/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-32.png b/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-32.png new file mode 100644 index 0000000000000000000000000000000000000000..d73e1f60edb674197e45a25e24457f2c6d4dbfb6 GIT binary patch literal 1740 zcmV;-1~d7IP)Px*hDk(0R9Fe^R#|LRWfcDIojdz5vI(MKz(`Ac#rUQW zV=ysBjSn?|(N{x!5+g+01ceqN*a{M%?6oZdWtrA?x=p9kS?;}af6u*Lrll=tBI%1K zxw-#x{&T+ntUtGU^%{m5Bw=W1Xa>~-LL@T70HV?8%vwMc;}ZZ#l1y_3yY(CGvyN&=agnOKyUhx6yoLli}AfAmly zA%o|6s328-?q4DlWH|{yk|b1A?uKuk50#a>G-ay7?b~0)y7e2-c&QQL@E{mtno>nk zuyNCixPI*_oGuscwcVpLaRRLn4+WT@(|FafdJz?<`i&=EMV|a*GPR_cxuCERYJ8-n zrD;~_?d{R_bUGdK^Yh^%g|S!+Ma1Xzda-BEo5;!WX=k!#XCXH?2l04ZbEVmALLOPr z?sP(=OD6RtAgGg3N+cAxh^fB*7reZ6D^8y}g$)}v!0-2K0zDoNs;a6G2m~-^&Kw*# zaDZ;ap}4phD@%%T_s(qsk%Q9GGA(9WTU#(R6v4J_+tAX|j4T$#d!K!Q9$Mo^T*WI2 zBl8S@s%hhK5e*Fuuvo3|crr+M32JI;G(np;Z^5-|*RgNkJ{&xF5Ic74q#MU@{P>SJ zbLK3L9QjsrS8ZJ#jvhOP(z3Pi5ulT`C$WG3eq67qhRg)<2(A|v_ zKb^p?iV8fW+h4hI8UFTmgb8_b^B)?!y51iQA{ZPXvx?~K?9zPy(BH0^J(XNk{lgDP zvDt9v&sOb>HP#OU7o#K_2B0oZj0jp#(@h{h*VoshtgIXtFJ6QioT_^+TsV({fkTMf9U1^REe6Gs40>&m(uR$G zw7?cFTEy&jyV@VvvSrJe#bRL;x$N2JmNE)J=Jn2FTVC4AN>;Aa&XLcV)oNpNyUf zUo-!B9+SmF`zczg2K)M8rMa5#GPx-j8XV6GyHO7v6*a1#h>@#;#PcXk0+5}Zjf#rh zu%)D+y83&ne&;5f*|Dx1C5snArY82`;cu{KO%d`_ooM!V;L|hb$9UaT6*t*ak@rvnCjvR8&&+!=hXS^3XFr%qLe4Qi|_=(1u&$^c{Cn8mdxn{?!9lFpgZA$5OL( z40oB`0jEWPWCR-H!|w=_k%xoUj0O%iO5e|YK9tR}!))SF)!B|~Pk3)k4M2<_Vo+c+ z>rn1=XaMwd%W{_;HWJ*Hh{8Y8#?7SIV&dR3>2NT29=3W?scm!kgj`t{>>mR^-C&Xn zRK{*H`7Jq$g$^^yIR^}W4)ZN$EOl66)${nZBQ%z~CIb(nkwcNuhBZ1KijI*bc+7Db zNYHd@lj~DjK;3Sjpi*zLm_X!NEhtO1gNg_wlY+CMWWed2^my?oFlJIY$&wLqi{P_m zyz{&pMHxx=B~?xwO%EUxLdYh;k;&l|pA#?4Pk|vjffn;1T8ENB#k*zbvL)J+@a6pu zym9U}l=L{#=P5X}%>xTr=y5ddZX0DlvvCmIY!N#O9ax=D4AR8)ICRub4k!= ziMS~b;Dd$^oNgP$*R25vvqU;0;NA7EQPwH{(*jU+)OGiv%MyVxos`fgIwPpPpESQJ zkvG#D(kda$L{Pud=%d;m+!=@=CM59IMmvgpls41Qv;b6o^)~tx38|?H%r+UV!6>e_ zPb?^$nUGSV@Q&nbH4sYR{n~yA3k*= zM-h{f=2zMDxpcb~W*8QjMota?zH12K=&cyst5UG8G7Wp*&d~bD1SXn1u^SeUU|*I2 zoDR6q6vcziiS>N#>J)l8=@ICWaJFG$GxB8Uv8qIEY@o)LKt1gzsB(^{CjuDH{4~ao ib-~k6_^8bP0sIX#q`?IJsIZCv00007MlP2R#U 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 T1Z`O8XqSPmiOvfxm)QRR$Cw~? literal 0 HcmV?d00001 diff --git a/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/macOS/Resources/macOS.xcassets/Contents.json b/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/macOS/Resources/macOS.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp/macOS/Resources/macOS.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Demos/Khronos-Vulkan-Samples/Vulkan-Samples b/Demos/Khronos-Vulkan-Samples/Vulkan-Samples new file mode 120000 index 00000000..989e8385 --- /dev/null +++ b/Demos/Khronos-Vulkan-Samples/Vulkan-Samples @@ -0,0 +1 @@ +../../../../Khronos/Vulkan/Khronos-Vulkan-Samples/Vulkan-Samples \ No newline at end of file diff --git a/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/project.pbxproj b/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/project.pbxproj new file mode 100644 index 00000000..4a391d57 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/project.pbxproj @@ -0,0 +1,902 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + A9096E4F1F7EF10300DFBEA6 /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9096E4E1F7EF10300DFBEA6 /* IOSurface.framework */; }; + A95C050B1C98FC1100CC653D /* blue.ppm in Resources */ = {isa = PBXBuildFile; fileRef = A95C05021C98FC1100CC653D /* blue.ppm */; }; + A95C050C1C98FC1100CC653D /* blue.ppm in Resources */ = {isa = PBXBuildFile; fileRef = A95C05021C98FC1100CC653D /* blue.ppm */; }; + A95C050D1C98FC1100CC653D /* green.ppm in Resources */ = {isa = PBXBuildFile; fileRef = A95C05041C98FC1100CC653D /* green.ppm */; }; + A95C050E1C98FC1100CC653D /* green.ppm in Resources */ = {isa = PBXBuildFile; fileRef = A95C05041C98FC1100CC653D /* green.ppm */; }; + A95C050F1C98FC1100CC653D /* logo-256x256-solid.png in Resources */ = {isa = PBXBuildFile; fileRef = A95C05051C98FC1100CC653D /* logo-256x256-solid.png */; }; + A95C05101C98FC1100CC653D /* logo-256x256-solid.png in Resources */ = {isa = PBXBuildFile; fileRef = A95C05051C98FC1100CC653D /* logo-256x256-solid.png */; }; + A95C05111C98FC1100CC653D /* logo-256x256.png in Resources */ = {isa = PBXBuildFile; fileRef = A95C05061C98FC1100CC653D /* logo-256x256.png */; }; + A95C05121C98FC1100CC653D /* logo-256x256.png in Resources */ = {isa = PBXBuildFile; fileRef = A95C05061C98FC1100CC653D /* logo-256x256.png */; }; + A95C05131C98FC1100CC653D /* lunarg.ppm in Resources */ = {isa = PBXBuildFile; fileRef = A95C05071C98FC1100CC653D /* lunarg.ppm */; }; + A95C05141C98FC1100CC653D /* lunarg.ppm in Resources */ = {isa = PBXBuildFile; fileRef = A95C05071C98FC1100CC653D /* lunarg.ppm */; }; + A95C05151C98FC1100CC653D /* red.ppm in Resources */ = {isa = PBXBuildFile; fileRef = A95C05081C98FC1100CC653D /* red.ppm */; }; + A95C05161C98FC1100CC653D /* red.ppm in Resources */ = {isa = PBXBuildFile; fileRef = A95C05081C98FC1100CC653D /* red.ppm */; }; + A95C05171C98FC1100CC653D /* spotlight.ppm in Resources */ = {isa = PBXBuildFile; fileRef = A95C05091C98FC1100CC653D /* spotlight.ppm */; }; + A95C05181C98FC1100CC653D /* spotlight.ppm in Resources */ = {isa = PBXBuildFile; fileRef = A95C05091C98FC1100CC653D /* spotlight.ppm */; }; + A95C05191C98FC1100CC653D /* yellow.ppm in Resources */ = {isa = PBXBuildFile; fileRef = A95C050A1C98FC1100CC653D /* yellow.ppm */; }; + A95C051A1C98FC1100CC653D /* yellow.ppm in Resources */ = {isa = PBXBuildFile; fileRef = A95C050A1C98FC1100CC653D /* yellow.ppm */; }; + A964BD3D1E4EA6FC00CA9AF1 /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A964BC611E4EA6FC00CA9AF1 /* util.cpp */; }; + A964BD3E1E4EA6FC00CA9AF1 /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A964BC611E4EA6FC00CA9AF1 /* util.cpp */; }; + A964BD3F1E4EA6FC00CA9AF1 /* util_init.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A964BC631E4EA6FC00CA9AF1 /* util_init.cpp */; }; + A964BD401E4EA6FC00CA9AF1 /* util_init.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A964BC631E4EA6FC00CA9AF1 /* util_init.cpp */; }; + A9B5D09C1CF8830B00D7CBDD /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = A9B5D09B1CF8830B00D7CBDD /* libc++.tbd */; }; + A9B5D09E1CF8831400D7CBDD /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = A9B5D09D1CF8831400D7CBDD /* libc++.tbd */; }; + A9B5D0A01CF8834600D7CBDD /* MoltenVK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9B5D09F1CF8834600D7CBDD /* MoltenVK.framework */; }; + A9B5D0A21CF8835500D7CBDD /* MoltenVK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9B5D0A11CF8835500D7CBDD /* MoltenVK.framework */; }; + A9B5D0A41CF8837900D7CBDD /* MoltenVKGLSLToSPIRVConverter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9B5D0A31CF8837900D7CBDD /* MoltenVKGLSLToSPIRVConverter.framework */; }; + A9B5D0A61CF8838E00D7CBDD /* MoltenVKGLSLToSPIRVConverter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9B5D0A51CF8838E00D7CBDD /* MoltenVKGLSLToSPIRVConverter.framework */; }; + A9B67B781C3AAE9800373FFD /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A9B67B6C1C3AAE9800373FFD /* AppDelegate.m */; }; + A9B67B7A1C3AAE9800373FFD /* DemoViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9B67B6F1C3AAE9800373FFD /* DemoViewController.mm */; }; + A9B67B7C1C3AAE9800373FFD /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A9B67B711C3AAE9800373FFD /* main.m */; }; + A9B67B7D1C3AAE9800373FFD /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A9B67B741C3AAE9800373FFD /* Default-568h@2x.png */; }; + A9B67B7E1C3AAE9800373FFD /* Default~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = A9B67B751C3AAE9800373FFD /* Default~ipad.png */; }; + A9B67B7F1C3AAE9800373FFD /* Icon.png in Resources */ = {isa = PBXBuildFile; fileRef = A9B67B761C3AAE9800373FFD /* Icon.png */; }; + A9B67B801C3AAE9800373FFD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A9B67B771C3AAE9800373FFD /* Main.storyboard */; }; + A9B67B8C1C3AAEA200373FFD /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A9B67B831C3AAEA200373FFD /* AppDelegate.m */; }; + A9B67B8D1C3AAEA200373FFD /* DemoViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9B67B851C3AAEA200373FFD /* DemoViewController.mm */; }; + A9B67B8F1C3AAEA200373FFD /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A9B67B871C3AAEA200373FFD /* main.m */; }; + A9B67B901C3AAEA200373FFD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A9B67B8A1C3AAEA200373FFD /* Main.storyboard */; }; + A9B67B911C3AAEA200373FFD /* macOS.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A9B67B8B1C3AAEA200373FFD /* macOS.xcassets */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 1D6058910D05DD3D006BFB54 /* API-Samples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "API-Samples.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 2D500B990D5A79CF00DBA0E3 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + A900B1A21B5EAA1C00150D60 /* libc++.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libc++.dylib"; path = "usr/lib/libc++.dylib"; sourceTree = SDKROOT; }; + A90941BB1C582DF40094110D /* MoltenVKGLSLToSPIRVConverter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVKGLSLToSPIRVConverter.framework; path = "../../../../../../../../Library/Developer/Xcode/DerivedData/VulkanSamples-emusmkeclqfwfncmwqpcvdzumhpu/Build/Products/Release/MoltenVKGLSLToSPIRVConverter.framework"; sourceTree = ""; }; + A9096E4E1F7EF10300DFBEA6 /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.0.sdk/System/Library/Frameworks/IOSurface.framework; sourceTree = DEVELOPER_DIR; }; + A92F37071C7E1B2B008F8BC9 /* Samples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Samples.h; sourceTree = ""; }; + A92F3CED1C7E5E9D008F8BC9 /* MoltenVKGLSLToSPIRVConverter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVKGLSLToSPIRVConverter.framework; path = ../../../MoltenShaderConverter/build/Debug/MoltenVKGLSLToSPIRVConverter.framework; sourceTree = ""; }; + A92F3CEF1C7E5EB5008F8BC9 /* MoltenVKGLSLToSPIRVConverter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVKGLSLToSPIRVConverter.framework; path = ../../../MoltenShaderConverter/build/Debug/MoltenVKGLSLToSPIRVConverter.framework; sourceTree = ""; }; + A92F3CF11C7E5EBF008F8BC9 /* MoltenVKGLSLToSPIRVConverter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVKGLSLToSPIRVConverter.framework; path = "../../../MoltenShaderConverter/build/Debug-iphoneos/MoltenVKGLSLToSPIRVConverter.framework"; sourceTree = ""; }; + A94A67231B7BDE9B00F6D7C4 /* MetalGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalGL.framework; path = ../../MetalGL/macOS/MetalGL.framework; sourceTree = ""; }; + A94A67241B7BDE9B00F6D7C4 /* MetalGLShaderConverter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalGLShaderConverter.framework; path = ../../MetalGLShaderConverter/macOS/MetalGLShaderConverter.framework; sourceTree = ""; }; + A95C05021C98FC1100CC653D /* blue.ppm */ = {isa = PBXFileReference; lastKnownFileType = file; path = blue.ppm; sourceTree = ""; }; + A95C05031C98FC1100CC653D /* cube_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cube_data.h; sourceTree = ""; }; + A95C05041C98FC1100CC653D /* green.ppm */ = {isa = PBXFileReference; lastKnownFileType = file; path = green.ppm; sourceTree = ""; }; + A95C05051C98FC1100CC653D /* logo-256x256-solid.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "logo-256x256-solid.png"; sourceTree = ""; }; + A95C05061C98FC1100CC653D /* logo-256x256.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "logo-256x256.png"; sourceTree = ""; }; + A95C05071C98FC1100CC653D /* lunarg.ppm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = lunarg.ppm; sourceTree = ""; }; + A95C05081C98FC1100CC653D /* red.ppm */ = {isa = PBXFileReference; lastKnownFileType = file; path = red.ppm; sourceTree = ""; }; + A95C05091C98FC1100CC653D /* spotlight.ppm */ = {isa = PBXFileReference; lastKnownFileType = file; path = spotlight.ppm; sourceTree = ""; }; + A95C050A1C98FC1100CC653D /* yellow.ppm */ = {isa = PBXFileReference; lastKnownFileType = file; path = yellow.ppm; sourceTree = ""; }; + A95C07051C98FDB800CC653D /* MoltenSPIRVToMSLConverter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenSPIRVToMSLConverter.framework; path = "../../../../MoltenShaderConverter/build/Debug-iphoneos/MoltenSPIRVToMSLConverter.framework"; sourceTree = ""; }; + A95C07071C98FDD400CC653D /* MoltenVKGLSLToSPIRVConverter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVKGLSLToSPIRVConverter.framework; path = "../../../../../../../../../Library/Developer/Xcode/DerivedData/LunarGSamples-csovmpqhztbhjscdjbysjxxoqgvu/Build/Products/Debug/MoltenVKGLSLToSPIRVConverter.framework"; sourceTree = ""; }; + A964BAF11E4E95E500CA9AF1 /* copy_blit_image.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = copy_blit_image.cpp; sourceTree = ""; }; + A964BAF51E4E968F00CA9AF1 /* 15-draw_cube.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "15-draw_cube.cpp"; sourceTree = ""; }; + A964BAF91E4E96B400CA9AF1 /* draw_subpasses.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = draw_subpasses.cpp; sourceTree = ""; }; + A964BAFD1E4E976C00CA9AF1 /* draw_textured_cube.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = draw_textured_cube.cpp; sourceTree = ""; }; + A964BAFF1E4E976C00CA9AF1 /* dynamic_uniform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dynamic_uniform.cpp; sourceTree = ""; }; + A964BB011E4E976C00CA9AF1 /* enable_validation_with_callback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = enable_validation_with_callback.cpp; sourceTree = ""; }; + A964BB031E4E976C00CA9AF1 /* enumerate_devices_adv.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = enumerate_devices_adv.cpp; sourceTree = ""; }; + A964BB051E4E976C00CA9AF1 /* events.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = events.cpp; sourceTree = ""; }; + A964BB071E4E976C00CA9AF1 /* immutable_sampler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = immutable_sampler.cpp; sourceTree = ""; }; + A964BB091E4E976C00CA9AF1 /* init_texture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = init_texture.cpp; sourceTree = ""; }; + A964BB0B1E4E976C00CA9AF1 /* input_attachment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = input_attachment.cpp; sourceTree = ""; }; + A964BB0D1E4E976C00CA9AF1 /* instance_extension_properties.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = instance_extension_properties.cpp; sourceTree = ""; }; + A964BB0F1E4E976C00CA9AF1 /* instance_layer_extension_properties.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = instance_layer_extension_properties.cpp; sourceTree = ""; }; + A964BB111E4E976C00CA9AF1 /* instance_layer_properties.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = instance_layer_properties.cpp; sourceTree = ""; }; + A964BB131E4E976C00CA9AF1 /* memory_barriers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = memory_barriers.cpp; sourceTree = ""; }; + A964BB151E4E976C00CA9AF1 /* multiple_sets.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = multiple_sets.cpp; sourceTree = ""; }; + A964BB171E4E976C00CA9AF1 /* multithreaded_command_buffers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = multithreaded_command_buffers.cpp; sourceTree = ""; }; + A964BB191E4E976C00CA9AF1 /* occlusion_query.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = occlusion_query.cpp; sourceTree = ""; }; + A964BB1B1E4E976C00CA9AF1 /* pipeline_cache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pipeline_cache.cpp; sourceTree = ""; }; + A964BB1D1E4E976C00CA9AF1 /* pipeline_derivative.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pipeline_derivative.cpp; sourceTree = ""; }; + A964BB1F1E4E976C00CA9AF1 /* push_constants.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = push_constants.cpp; sourceTree = ""; }; + A964BB211E4E976C00CA9AF1 /* secondary_command_buffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = secondary_command_buffer.cpp; sourceTree = ""; }; + A964BB231E4E976C00CA9AF1 /* separate_image_sampler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = separate_image_sampler.cpp; sourceTree = ""; }; + A964BB251E4E976C00CA9AF1 /* spirv_assembly.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = spirv_assembly.cpp; sourceTree = ""; }; + A964BB271E4E976C00CA9AF1 /* spirv_specialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = spirv_specialization.cpp; sourceTree = ""; }; + A964BB291E4E976C00CA9AF1 /* template.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = template.cpp; sourceTree = ""; }; + A964BB2B1E4E976C00CA9AF1 /* texel_buffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = texel_buffer.cpp; sourceTree = ""; }; + A964BC601E4EA6FC00CA9AF1 /* samples_platform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = samples_platform.h; sourceTree = ""; }; + A964BC611E4EA6FC00CA9AF1 /* util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = util.cpp; sourceTree = ""; }; + A964BC621E4EA6FC00CA9AF1 /* util.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = util.hpp; sourceTree = ""; }; + A964BC631E4EA6FC00CA9AF1 /* util_init.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = util_init.cpp; sourceTree = ""; }; + A964BC641E4EA6FC00CA9AF1 /* util_init.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = util_init.hpp; sourceTree = ""; }; + A977BCFE1B66BB010067E5BF /* API-Samples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "API-Samples.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + A977BD211B67186B0067E5BF /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/AppKit.framework; sourceTree = DEVELOPER_DIR; }; + A977BD221B67186B0067E5BF /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; }; + A977BD231B67186B0067E5BF /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + A977BD251B67186B0067E5BF /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/Metal.framework; sourceTree = DEVELOPER_DIR; }; + A977BD261B67186B0067E5BF /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; }; + A9A222171B5D69F40050A5F9 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; }; + A9B5D09B1CF8830B00D7CBDD /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; + A9B5D09D1CF8831400D7CBDD /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/lib/libc++.tbd"; sourceTree = DEVELOPER_DIR; }; + A9B5D09F1CF8834600D7CBDD /* MoltenVK.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVK.framework; path = ../../../../Package/Latest/MoltenVK/macOS/MoltenVK.framework; sourceTree = ""; }; + A9B5D0A11CF8835500D7CBDD /* MoltenVK.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVK.framework; path = ../../../../Package/Latest/MoltenVK/iOS/MoltenVK.framework; sourceTree = ""; }; + A9B5D0A31CF8837900D7CBDD /* MoltenVKGLSLToSPIRVConverter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVKGLSLToSPIRVConverter.framework; path = ../../../../Package/Latest/MoltenShaderConverter/MoltenVKGLSLToSPIRVConverter/macOS/MoltenVKGLSLToSPIRVConverter.framework; sourceTree = ""; }; + A9B5D0A51CF8838E00D7CBDD /* MoltenVKGLSLToSPIRVConverter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVKGLSLToSPIRVConverter.framework; path = ../../../../Package/Latest/MoltenShaderConverter/MoltenVKGLSLToSPIRVConverter/iOS/MoltenVKGLSLToSPIRVConverter.framework; sourceTree = ""; }; + A9B67B6B1C3AAE9800373FFD /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + A9B67B6C1C3AAE9800373FFD /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + A9B67B6E1C3AAE9800373FFD /* DemoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoViewController.h; sourceTree = ""; }; + A9B67B6F1C3AAE9800373FFD /* DemoViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DemoViewController.mm; sourceTree = ""; }; + A9B67B701C3AAE9800373FFD /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A9B67B711C3AAE9800373FFD /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + A9B67B721C3AAE9800373FFD /* Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Prefix.pch; sourceTree = ""; }; + A9B67B741C3AAE9800373FFD /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; + A9B67B751C3AAE9800373FFD /* Default~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default~ipad.png"; sourceTree = ""; }; + A9B67B761C3AAE9800373FFD /* Icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Icon.png; sourceTree = ""; }; + A9B67B771C3AAE9800373FFD /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; + A9B67B821C3AAEA200373FFD /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + A9B67B831C3AAEA200373FFD /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + A9B67B841C3AAEA200373FFD /* DemoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoViewController.h; sourceTree = ""; }; + A9B67B851C3AAEA200373FFD /* DemoViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DemoViewController.mm; sourceTree = ""; }; + A9B67B861C3AAEA200373FFD /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A9B67B871C3AAEA200373FFD /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + A9B67B881C3AAEA200373FFD /* Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Prefix.pch; sourceTree = ""; }; + A9B67B8A1C3AAEA200373FFD /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; + A9B67B8B1C3AAEA200373FFD /* macOS.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = macOS.xcassets; sourceTree = ""; }; + A9B6B75F1C0F795000A9E33A /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.1.sdk/System/Library/Frameworks/CoreAudio.framework; sourceTree = DEVELOPER_DIR; }; + A9B6B7641C0F795D00A9E33A /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; }; + A9BF67C21C582EC900B8CF77 /* MoltenVKGLSLToSPIRVConverter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVKGLSLToSPIRVConverter.framework; path = "../../../MoltenShaderConverter/build/Debug-iphoneos/MoltenVKGLSLToSPIRVConverter.framework"; sourceTree = ""; }; + A9CDEA271B6A782C00F7B008 /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = System/Library/Frameworks/GLKit.framework; sourceTree = SDKROOT; }; + A9E264761B671B0A00FE691A /* libc++.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libc++.dylib"; path = "Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libc++.dylib"; sourceTree = DEVELOPER_DIR; }; + BA240AC10FEFE77A00DE852D /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1D60588F0D05DD3D006BFB54 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A9B5D0A21CF8835500D7CBDD /* MoltenVK.framework in Frameworks */, + A9B5D0A61CF8838E00D7CBDD /* MoltenVKGLSLToSPIRVConverter.framework in Frameworks */, + A9096E4F1F7EF10300DFBEA6 /* IOSurface.framework in Frameworks */, + A9B5D09E1CF8831400D7CBDD /* libc++.tbd in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A977BCF11B66BB010067E5BF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A9B5D0A01CF8834600D7CBDD /* MoltenVK.framework in Frameworks */, + A9B5D0A41CF8837900D7CBDD /* MoltenVKGLSLToSPIRVConverter.framework in Frameworks */, + A9B5D09C1CF8830B00D7CBDD /* libc++.tbd in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 1D6058910D05DD3D006BFB54 /* API-Samples.app */, + A977BCFE1B66BB010067E5BF /* API-Samples.app */, + ); + name = Products; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { + isa = PBXGroup; + children = ( + A92F37071C7E1B2B008F8BC9 /* Samples.h */, + A95C03971C98FBED00CC653D /* API-Samples */, + A9B67B6A1C3AAE9800373FFD /* iOS */, + A9B67B811C3AAEA200373FFD /* macOS */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = CustomTemplate; + sourceTree = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + A9096E4E1F7EF10300DFBEA6 /* IOSurface.framework */, + A9B5D0A51CF8838E00D7CBDD /* MoltenVKGLSLToSPIRVConverter.framework */, + A9B5D0A31CF8837900D7CBDD /* MoltenVKGLSLToSPIRVConverter.framework */, + A9B5D0A11CF8835500D7CBDD /* MoltenVK.framework */, + A9B5D09F1CF8834600D7CBDD /* MoltenVK.framework */, + A9B5D09D1CF8831400D7CBDD /* libc++.tbd */, + A9B5D09B1CF8830B00D7CBDD /* libc++.tbd */, + A95C07071C98FDD400CC653D /* MoltenVKGLSLToSPIRVConverter.framework */, + A95C07051C98FDB800CC653D /* MoltenSPIRVToMSLConverter.framework */, + A92F3CF11C7E5EBF008F8BC9 /* MoltenVKGLSLToSPIRVConverter.framework */, + A92F3CEF1C7E5EB5008F8BC9 /* MoltenVKGLSLToSPIRVConverter.framework */, + A92F3CED1C7E5E9D008F8BC9 /* MoltenVKGLSLToSPIRVConverter.framework */, + A9BF67C21C582EC900B8CF77 /* MoltenVKGLSLToSPIRVConverter.framework */, + A90941BB1C582DF40094110D /* MoltenVKGLSLToSPIRVConverter.framework */, + A9B6B7641C0F795D00A9E33A /* CoreAudio.framework */, + A9B6B75F1C0F795000A9E33A /* CoreAudio.framework */, + A9ADEC601B6EC2EB00DBA48C /* iOS */, + A9ADEC611B6EC2F300DBA48C /* macOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + A95C03971C98FBED00CC653D /* API-Samples */ = { + isa = PBXGroup; + children = ( + A964BAF01E4E95E500CA9AF1 /* copy_blit_image */, + A95C05011C98FC1100CC653D /* data */, + A964BAF41E4E968F00CA9AF1 /* 15-draw_cube */, + A964BAF81E4E96B400CA9AF1 /* draw_subpasses */, + A964BAFC1E4E976C00CA9AF1 /* draw_textured_cube */, + A964BAFE1E4E976C00CA9AF1 /* dynamic_uniform */, + A964BB001E4E976C00CA9AF1 /* enable_validation_with_callback */, + A964BB021E4E976C00CA9AF1 /* enumerate_devices_adv */, + A964BB041E4E976C00CA9AF1 /* events */, + A964BB061E4E976C00CA9AF1 /* immutable_sampler */, + A964BB081E4E976C00CA9AF1 /* init_texture */, + A964BB0A1E4E976C00CA9AF1 /* input_attachment */, + A964BB0C1E4E976C00CA9AF1 /* instance_extension_properties */, + A964BB0E1E4E976C00CA9AF1 /* instance_layer_extension_properties */, + A964BB101E4E976C00CA9AF1 /* instance_layer_properties */, + A964BB121E4E976C00CA9AF1 /* memory_barriers */, + A964BB141E4E976C00CA9AF1 /* multiple_sets */, + A964BB161E4E976C00CA9AF1 /* multithreaded_command_buffers */, + A964BB181E4E976C00CA9AF1 /* occlusion_query */, + A964BB1A1E4E976C00CA9AF1 /* pipeline_cache */, + A964BB1C1E4E976C00CA9AF1 /* pipeline_derivative */, + A964BB1E1E4E976C00CA9AF1 /* push_constants */, + A964BB201E4E976C00CA9AF1 /* secondary_command_buffer */, + A964BB221E4E976C00CA9AF1 /* separate_image_sampler */, + A964BB241E4E976C00CA9AF1 /* spirv_assembly */, + A964BB261E4E976C00CA9AF1 /* spirv_specialization */, + A964BB281E4E976C00CA9AF1 /* template */, + A964BB2A1E4E976C00CA9AF1 /* texel_buffer */, + A964BB5C1E4EA6FB00CA9AF1 /* utils */, + ); + name = "API-Samples"; + path = "../VulkanSamples/API-Samples"; + sourceTree = ""; + }; + A95C05011C98FC1100CC653D /* data */ = { + isa = PBXGroup; + children = ( + A95C05021C98FC1100CC653D /* blue.ppm */, + A95C05031C98FC1100CC653D /* cube_data.h */, + A95C05041C98FC1100CC653D /* green.ppm */, + A95C05051C98FC1100CC653D /* logo-256x256-solid.png */, + A95C05061C98FC1100CC653D /* logo-256x256.png */, + A95C05071C98FC1100CC653D /* lunarg.ppm */, + A95C05081C98FC1100CC653D /* red.ppm */, + A95C05091C98FC1100CC653D /* spotlight.ppm */, + A95C050A1C98FC1100CC653D /* yellow.ppm */, + ); + path = data; + sourceTree = ""; + }; + A964BAF01E4E95E500CA9AF1 /* copy_blit_image */ = { + isa = PBXGroup; + children = ( + A964BAF11E4E95E500CA9AF1 /* copy_blit_image.cpp */, + ); + path = copy_blit_image; + sourceTree = ""; + }; + A964BAF41E4E968F00CA9AF1 /* 15-draw_cube */ = { + isa = PBXGroup; + children = ( + A964BAF51E4E968F00CA9AF1 /* 15-draw_cube.cpp */, + ); + path = "15-draw_cube"; + sourceTree = ""; + }; + A964BAF81E4E96B400CA9AF1 /* draw_subpasses */ = { + isa = PBXGroup; + children = ( + A964BAF91E4E96B400CA9AF1 /* draw_subpasses.cpp */, + ); + path = draw_subpasses; + sourceTree = ""; + }; + A964BAFC1E4E976C00CA9AF1 /* draw_textured_cube */ = { + isa = PBXGroup; + children = ( + A964BAFD1E4E976C00CA9AF1 /* draw_textured_cube.cpp */, + ); + path = draw_textured_cube; + sourceTree = ""; + }; + A964BAFE1E4E976C00CA9AF1 /* dynamic_uniform */ = { + isa = PBXGroup; + children = ( + A964BAFF1E4E976C00CA9AF1 /* dynamic_uniform.cpp */, + ); + path = dynamic_uniform; + sourceTree = ""; + }; + A964BB001E4E976C00CA9AF1 /* enable_validation_with_callback */ = { + isa = PBXGroup; + children = ( + A964BB011E4E976C00CA9AF1 /* enable_validation_with_callback.cpp */, + ); + path = enable_validation_with_callback; + sourceTree = ""; + }; + A964BB021E4E976C00CA9AF1 /* enumerate_devices_adv */ = { + isa = PBXGroup; + children = ( + A964BB031E4E976C00CA9AF1 /* enumerate_devices_adv.cpp */, + ); + path = enumerate_devices_adv; + sourceTree = ""; + }; + A964BB041E4E976C00CA9AF1 /* events */ = { + isa = PBXGroup; + children = ( + A964BB051E4E976C00CA9AF1 /* events.cpp */, + ); + path = events; + sourceTree = ""; + }; + A964BB061E4E976C00CA9AF1 /* immutable_sampler */ = { + isa = PBXGroup; + children = ( + A964BB071E4E976C00CA9AF1 /* immutable_sampler.cpp */, + ); + path = immutable_sampler; + sourceTree = ""; + }; + A964BB081E4E976C00CA9AF1 /* init_texture */ = { + isa = PBXGroup; + children = ( + A964BB091E4E976C00CA9AF1 /* init_texture.cpp */, + ); + path = init_texture; + sourceTree = ""; + }; + A964BB0A1E4E976C00CA9AF1 /* input_attachment */ = { + isa = PBXGroup; + children = ( + A964BB0B1E4E976C00CA9AF1 /* input_attachment.cpp */, + ); + path = input_attachment; + sourceTree = ""; + }; + A964BB0C1E4E976C00CA9AF1 /* instance_extension_properties */ = { + isa = PBXGroup; + children = ( + A964BB0D1E4E976C00CA9AF1 /* instance_extension_properties.cpp */, + ); + path = instance_extension_properties; + sourceTree = ""; + }; + A964BB0E1E4E976C00CA9AF1 /* instance_layer_extension_properties */ = { + isa = PBXGroup; + children = ( + A964BB0F1E4E976C00CA9AF1 /* instance_layer_extension_properties.cpp */, + ); + path = instance_layer_extension_properties; + sourceTree = ""; + }; + A964BB101E4E976C00CA9AF1 /* instance_layer_properties */ = { + isa = PBXGroup; + children = ( + A964BB111E4E976C00CA9AF1 /* instance_layer_properties.cpp */, + ); + path = instance_layer_properties; + sourceTree = ""; + }; + A964BB121E4E976C00CA9AF1 /* memory_barriers */ = { + isa = PBXGroup; + children = ( + A964BB131E4E976C00CA9AF1 /* memory_barriers.cpp */, + ); + path = memory_barriers; + sourceTree = ""; + }; + A964BB141E4E976C00CA9AF1 /* multiple_sets */ = { + isa = PBXGroup; + children = ( + A964BB151E4E976C00CA9AF1 /* multiple_sets.cpp */, + ); + path = multiple_sets; + sourceTree = ""; + }; + A964BB161E4E976C00CA9AF1 /* multithreaded_command_buffers */ = { + isa = PBXGroup; + children = ( + A964BB171E4E976C00CA9AF1 /* multithreaded_command_buffers.cpp */, + ); + path = multithreaded_command_buffers; + sourceTree = ""; + }; + A964BB181E4E976C00CA9AF1 /* occlusion_query */ = { + isa = PBXGroup; + children = ( + A964BB191E4E976C00CA9AF1 /* occlusion_query.cpp */, + ); + path = occlusion_query; + sourceTree = ""; + }; + A964BB1A1E4E976C00CA9AF1 /* pipeline_cache */ = { + isa = PBXGroup; + children = ( + A964BB1B1E4E976C00CA9AF1 /* pipeline_cache.cpp */, + ); + path = pipeline_cache; + sourceTree = ""; + }; + A964BB1C1E4E976C00CA9AF1 /* pipeline_derivative */ = { + isa = PBXGroup; + children = ( + A964BB1D1E4E976C00CA9AF1 /* pipeline_derivative.cpp */, + ); + path = pipeline_derivative; + sourceTree = ""; + }; + A964BB1E1E4E976C00CA9AF1 /* push_constants */ = { + isa = PBXGroup; + children = ( + A964BB1F1E4E976C00CA9AF1 /* push_constants.cpp */, + ); + path = push_constants; + sourceTree = ""; + }; + A964BB201E4E976C00CA9AF1 /* secondary_command_buffer */ = { + isa = PBXGroup; + children = ( + A964BB211E4E976C00CA9AF1 /* secondary_command_buffer.cpp */, + ); + path = secondary_command_buffer; + sourceTree = ""; + }; + A964BB221E4E976C00CA9AF1 /* separate_image_sampler */ = { + isa = PBXGroup; + children = ( + A964BB231E4E976C00CA9AF1 /* separate_image_sampler.cpp */, + ); + path = separate_image_sampler; + sourceTree = ""; + }; + A964BB241E4E976C00CA9AF1 /* spirv_assembly */ = { + isa = PBXGroup; + children = ( + A964BB251E4E976C00CA9AF1 /* spirv_assembly.cpp */, + ); + path = spirv_assembly; + sourceTree = ""; + }; + A964BB261E4E976C00CA9AF1 /* spirv_specialization */ = { + isa = PBXGroup; + children = ( + A964BB271E4E976C00CA9AF1 /* spirv_specialization.cpp */, + ); + path = spirv_specialization; + sourceTree = ""; + }; + A964BB281E4E976C00CA9AF1 /* template */ = { + isa = PBXGroup; + children = ( + A964BB291E4E976C00CA9AF1 /* template.cpp */, + ); + path = template; + sourceTree = ""; + }; + A964BB2A1E4E976C00CA9AF1 /* texel_buffer */ = { + isa = PBXGroup; + children = ( + A964BB2B1E4E976C00CA9AF1 /* texel_buffer.cpp */, + ); + path = texel_buffer; + sourceTree = ""; + }; + A964BB5C1E4EA6FB00CA9AF1 /* utils */ = { + isa = PBXGroup; + children = ( + A964BC601E4EA6FC00CA9AF1 /* samples_platform.h */, + A964BC611E4EA6FC00CA9AF1 /* util.cpp */, + A964BC621E4EA6FC00CA9AF1 /* util.hpp */, + A964BC631E4EA6FC00CA9AF1 /* util_init.cpp */, + A964BC641E4EA6FC00CA9AF1 /* util_init.hpp */, + ); + path = utils; + sourceTree = ""; + }; + A9ADEC601B6EC2EB00DBA48C /* iOS */ = { + isa = PBXGroup; + children = ( + A9A222171B5D69F40050A5F9 /* Metal.framework */, + BA240AC10FEFE77A00DE852D /* OpenGLES.framework */, + 1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */, + 2D500B990D5A79CF00DBA0E3 /* QuartzCore.framework */, + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, + 1D30AB110D05D00D00671497 /* Foundation.framework */, + A9CDEA271B6A782C00F7B008 /* GLKit.framework */, + A900B1A21B5EAA1C00150D60 /* libc++.dylib */, + ); + name = iOS; + sourceTree = ""; + }; + A9ADEC611B6EC2F300DBA48C /* macOS */ = { + isa = PBXGroup; + children = ( + A94A67231B7BDE9B00F6D7C4 /* MetalGL.framework */, + A94A67241B7BDE9B00F6D7C4 /* MetalGLShaderConverter.framework */, + A9E264761B671B0A00FE691A /* libc++.dylib */, + A977BD251B67186B0067E5BF /* Metal.framework */, + A977BD261B67186B0067E5BF /* QuartzCore.framework */, + A977BD211B67186B0067E5BF /* AppKit.framework */, + A977BD221B67186B0067E5BF /* CoreGraphics.framework */, + A977BD231B67186B0067E5BF /* Foundation.framework */, + ); + name = macOS; + sourceTree = ""; + }; + A9B67B6A1C3AAE9800373FFD /* iOS */ = { + isa = PBXGroup; + children = ( + A9B67B6B1C3AAE9800373FFD /* AppDelegate.h */, + A9B67B6C1C3AAE9800373FFD /* AppDelegate.m */, + A9B67B6E1C3AAE9800373FFD /* DemoViewController.h */, + A9B67B6F1C3AAE9800373FFD /* DemoViewController.mm */, + A9B67B701C3AAE9800373FFD /* Info.plist */, + A9B67B711C3AAE9800373FFD /* main.m */, + A9B67B721C3AAE9800373FFD /* Prefix.pch */, + A9B67B731C3AAE9800373FFD /* Resources */, + ); + path = iOS; + sourceTree = ""; + }; + A9B67B731C3AAE9800373FFD /* Resources */ = { + isa = PBXGroup; + children = ( + A9B67B741C3AAE9800373FFD /* Default-568h@2x.png */, + A9B67B751C3AAE9800373FFD /* Default~ipad.png */, + A9B67B761C3AAE9800373FFD /* Icon.png */, + A9B67B771C3AAE9800373FFD /* Main.storyboard */, + ); + path = Resources; + sourceTree = ""; + }; + A9B67B811C3AAEA200373FFD /* macOS */ = { + isa = PBXGroup; + children = ( + A9B67B821C3AAEA200373FFD /* AppDelegate.h */, + A9B67B831C3AAEA200373FFD /* AppDelegate.m */, + A9B67B841C3AAEA200373FFD /* DemoViewController.h */, + A9B67B851C3AAEA200373FFD /* DemoViewController.mm */, + A9B67B861C3AAEA200373FFD /* Info.plist */, + A9B67B871C3AAEA200373FFD /* main.m */, + A9B67B881C3AAEA200373FFD /* Prefix.pch */, + A9B67B891C3AAEA200373FFD /* Resources */, + ); + path = macOS; + sourceTree = ""; + }; + A9B67B891C3AAEA200373FFD /* Resources */ = { + isa = PBXGroup; + children = ( + A9B67B8A1C3AAEA200373FFD /* Main.storyboard */, + A9B67B8B1C3AAEA200373FFD /* macOS.xcassets */, + ); + path = Resources; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1D6058900D05DD3D006BFB54 /* API-Samples-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "API-Samples-iOS" */; + buildPhases = ( + 1D60588D0D05DD3D006BFB54 /* Resources */, + 1D60588E0D05DD3D006BFB54 /* Sources */, + 1D60588F0D05DD3D006BFB54 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "API-Samples-iOS"; + productName = foo; + productReference = 1D6058910D05DD3D006BFB54 /* API-Samples.app */; + productType = "com.apple.product-type.application"; + }; + A977BCBD1B66BB010067E5BF /* API-Samples-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = A977BCFB1B66BB010067E5BF /* Build configuration list for PBXNativeTarget "API-Samples-macOS" */; + buildPhases = ( + A977BCBE1B66BB010067E5BF /* Resources */, + A977BCC91B66BB010067E5BF /* Sources */, + A977BCF11B66BB010067E5BF /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "API-Samples-macOS"; + productName = foo; + productReference = A977BCFE1B66BB010067E5BF /* API-Samples.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0900; + TargetAttributes = { + 1D6058900D05DD3D006BFB54 = { + DevelopmentTeam = VU3TCKU48B; + }; + A977BCBD1B66BB010067E5BF = { + DevelopmentTeam = VU3TCKU48B; + }; + }; + }; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "API-Samples" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); + mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1D6058900D05DD3D006BFB54 /* API-Samples-iOS */, + A977BCBD1B66BB010067E5BF /* API-Samples-macOS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 1D60588D0D05DD3D006BFB54 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A9B67B7F1C3AAE9800373FFD /* Icon.png in Resources */, + A95C050B1C98FC1100CC653D /* blue.ppm in Resources */, + A9B67B801C3AAE9800373FFD /* Main.storyboard in Resources */, + A95C05151C98FC1100CC653D /* red.ppm in Resources */, + A95C05131C98FC1100CC653D /* lunarg.ppm in Resources */, + A95C05111C98FC1100CC653D /* logo-256x256.png in Resources */, + A95C050D1C98FC1100CC653D /* green.ppm in Resources */, + A9B67B7E1C3AAE9800373FFD /* Default~ipad.png in Resources */, + A95C050F1C98FC1100CC653D /* logo-256x256-solid.png in Resources */, + A95C05191C98FC1100CC653D /* yellow.ppm in Resources */, + A95C05171C98FC1100CC653D /* spotlight.ppm in Resources */, + A9B67B7D1C3AAE9800373FFD /* Default-568h@2x.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A977BCBE1B66BB010067E5BF /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A9B67B911C3AAEA200373FFD /* macOS.xcassets in Resources */, + A95C05141C98FC1100CC653D /* lunarg.ppm in Resources */, + A95C050E1C98FC1100CC653D /* green.ppm in Resources */, + A95C050C1C98FC1100CC653D /* blue.ppm in Resources */, + A95C05181C98FC1100CC653D /* spotlight.ppm in Resources */, + A9B67B901C3AAEA200373FFD /* Main.storyboard in Resources */, + A95C05161C98FC1100CC653D /* red.ppm in Resources */, + A95C051A1C98FC1100CC653D /* yellow.ppm in Resources */, + A95C05101C98FC1100CC653D /* logo-256x256-solid.png in Resources */, + A95C05121C98FC1100CC653D /* logo-256x256.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D60588E0D05DD3D006BFB54 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A9B67B7A1C3AAE9800373FFD /* DemoViewController.mm in Sources */, + A964BD3F1E4EA6FC00CA9AF1 /* util_init.cpp in Sources */, + A9B67B781C3AAE9800373FFD /* AppDelegate.m in Sources */, + A964BD3D1E4EA6FC00CA9AF1 /* util.cpp in Sources */, + A9B67B7C1C3AAE9800373FFD /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A977BCC91B66BB010067E5BF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A9B67B8C1C3AAEA200373FFD /* AppDelegate.m in Sources */, + A964BD401E4EA6FC00CA9AF1 /* util_init.cpp in Sources */, + A9B67B8F1C3AAEA200373FFD /* main.m in Sources */, + A964BD3E1E4EA6FC00CA9AF1 /* util.cpp in Sources */, + A9B67B8D1C3AAEA200373FFD /* DemoViewController.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1D6058940D05DD3E006BFB54 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + DEVELOPMENT_TEAM = VU3TCKU48B; + FRAMEWORK_SEARCH_PATHS = ( + "\"$(SRCROOT)/../../../MoltenVK/iOS\"", + "\"$(SRCROOT)/../../../MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/iOS\"", + ); + GCC_PREFIX_HEADER = "$(SRCROOT)/iOS/Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DEBUG=1", + _DEBUG, + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + INFOPLIST_FILE = "$(SRCROOT)/iOS/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.moltenvk.API-Samples"; + PRODUCT_NAME = "API-Samples"; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = arm64; + }; + name = Debug; + }; + 1D6058950D05DD3E006BFB54 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + DEVELOPMENT_TEAM = VU3TCKU48B; + FRAMEWORK_SEARCH_PATHS = ( + "\"$(SRCROOT)/../../../MoltenVK/iOS\"", + "\"$(SRCROOT)/../../../MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/iOS\"", + ); + GCC_PREFIX_HEADER = "$(SRCROOT)/iOS/Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + INFOPLIST_FILE = "$(SRCROOT)/iOS/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.moltenvk.API-Samples"; + PRODUCT_NAME = "API-Samples"; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = arm64; + }; + name = Release; + }; + A977BCFC1B66BB010067E5BF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "\"$(SRCROOT)/../../../MoltenVK/macOS\"", + "\"$(SRCROOT)/../../../MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/macOS\"", + ); + GCC_PREFIX_HEADER = "$(SRCROOT)/macOS/Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DEBUG=1", + _DEBUG, + ); + INFOPLIST_FILE = "$(SRCROOT)/macOS/Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + PRODUCT_BUNDLE_IDENTIFIER = "com.moltenvk.API-Samples"; + PRODUCT_NAME = "API-Samples"; + SDKROOT = macosx; + }; + name = Debug; + }; + A977BCFD1B66BB010067E5BF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "\"$(SRCROOT)/../../../MoltenVK/macOS\"", + "\"$(SRCROOT)/../../../MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/macOS\"", + ); + GCC_PREFIX_HEADER = "$(SRCROOT)/macOS/Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "$(SRCROOT)/macOS/Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + PRODUCT_BUNDLE_IDENTIFIER = "com.moltenvk.API-Samples"; + PRODUCT_NAME = "API-Samples"; + SDKROOT = macosx; + }; + name = Release; + }; + C01FCF4F08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = NO; + COPY_PHASE_STRIP = NO; + ENABLE_BITCODE = NO; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "VULKAN_SAMPLES_BASE_DIR=\\\"Samples\\\"", + MVK_SAMP_15_draw_cube, + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + HEADER_SEARCH_PATHS = ( + "\"$(SRCROOT)/../../../MoltenVK/include\"", + "\"$(SRCROOT)/../VulkanSamples/API-Samples/utils\"", + ); + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.moltenvk.${PRODUCT_NAME:identifier}"; + PRODUCT_NAME = "${PROJECT}"; + }; + name = Debug; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = NO; + ENABLE_BITCODE = NO; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "VULKAN_SAMPLES_BASE_DIR=\\\"Samples\\\"", + MVK_SAMP_15_draw_cube, + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + HEADER_SEARCH_PATHS = ( + "\"$(SRCROOT)/../../../MoltenVK/include\"", + "\"$(SRCROOT)/../VulkanSamples/API-Samples/utils\"", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.moltenvk.${PRODUCT_NAME:identifier}"; + PRODUCT_NAME = "${PROJECT}"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "API-Samples-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D6058940D05DD3E006BFB54 /* Debug */, + 1D6058950D05DD3E006BFB54 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A977BCFB1B66BB010067E5BF /* Build configuration list for PBXNativeTarget "API-Samples-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A977BCFC1B66BB010067E5BF /* Debug */, + A977BCFD1B66BB010067E5BF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "API-Samples" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/xcshareddata/xcschemes/API-Samples-iOS.xcscheme b/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/xcshareddata/xcschemes/API-Samples-iOS.xcscheme new file mode 100644 index 00000000..2e3805a1 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/xcshareddata/xcschemes/API-Samples-iOS.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/xcshareddata/xcschemes/API-Samples-macOS.xcscheme b/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/xcshareddata/xcschemes/API-Samples-macOS.xcscheme new file mode 100644 index 00000000..0ad90bf0 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/xcshareddata/xcschemes/API-Samples-macOS.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demos/LunarG-VulkanSamples/API-Samples/Samples.h b/Demos/LunarG-VulkanSamples/API-Samples/Samples.h new file mode 100644 index 00000000..33373dd2 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/Samples.h @@ -0,0 +1,106 @@ +/* + * Samples.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * Loads the appropriate sample code, as indicated by the appropriate compiler build setting below. + * + * To select a sample to run, define one (and only one) of the macros below, either by adding + * a #define XXX statement at the top of this file, or more flexibily, by adding the macro value + * to the Preprocessor Macros (aka GCC_PREPROCESSOR_DEFINITIONS) compiler setting. + * + * To add a compiler setting, select the project in the Xcode Project Navigator panel, + * select the Build Settings panel, and add the value to the Preprocessor Macros + * (aka GCC_PREPROCESSOR_DEFINITIONS) entry. + * + * If you choose to add a #define statement to this file, be sure to clear the existing macro + * from the Preprocessor Macros (aka GCC_PREPROCESSOR_DEFINITIONS) compiler setting in Xcode. + */ + +#include + +// Rename main() in sample file so it won't conflict with the application main() +#define main(argc, argv) sample_main(argc, argv) + + +#ifdef MVK_SAMP_15_draw_cube +# include "../VulkanSamples/API-Samples/15-draw_cube/15-draw_cube.cpp" +#endif + +#ifdef MVK_SAMP_copy_blit_image +# include "../VulkanSamples/API-Samples/copy_blit_image/copy_blit_image.cpp" +#endif + +#ifdef MVK_SAMP_draw_subpasses +# include "../VulkanSamples/API-Samples/draw_subpasses/draw_subpasses.cpp" +#endif + +#ifdef MVK_SAMP_draw_textured_cube +# include "../VulkanSamples/API-Samples/draw_textured_cube/draw_textured_cube.cpp" +#endif + +#ifdef MVK_SAMP_dynamic_uniform +# include "../VulkanSamples/API-Samples/dynamic_uniform/dynamic_uniform.cpp" +#endif + +#ifdef MVK_SAMP_immutable_sampler +# include "../VulkanSamples/API-Samples/immutable_sampler/immutable_sampler.cpp" +#endif + +#ifdef MVK_SAMP_memory_barriers +# include "../VulkanSamples/API-Samples/memory_barriers/memory_barriers.cpp" +#endif + +#ifdef MVK_SAMP_multiple_sets +# include "../VulkanSamples/API-Samples/multiple_sets/multiple_sets.cpp" +#endif + +#ifdef MVK_SAMP_multithreaded_command_buffers +# include "../VulkanSamples/API-Samples/multithreaded_command_buffers/multithreaded_command_buffers.cpp" +#endif + +#ifdef MVK_SAMP_occlusion_query // iOS: Occlusion boolean supported, but not occlusion counting. +# include "../VulkanSamples/API-Samples/occlusion_query/occlusion_query.cpp" +#endif + +#ifdef MVK_SAMP_pipeline_cache +# include "../VulkanSamples/API-Samples/pipeline_cache/pipeline_cache.cpp" +#endif + +#ifdef MVK_SAMP_push_constants +# include "../VulkanSamples/API-Samples/push_constants/push_constants.cpp" +#endif + +#ifdef MVK_SAMP_secondary_command_buffer +# include "../VulkanSamples/API-Samples/secondary_command_buffer/secondary_command_buffer.cpp" +#endif + +#ifdef MVK_SAMP_separate_image_sampler +# include "../VulkanSamples/API-Samples/separate_image_sampler/separate_image_sampler.cpp" +#endif + +#ifdef MVK_SAMP_template +# include "../VulkanSamples/API-Samples/template/template.cpp" +#endif + +#ifdef MVK_SAMP_texel_buffer // iOS only. Texel buffers are not supported on macOS +# include "../VulkanSamples/API-Samples/texel_buffer/texel_buffer.cpp" +#endif + + + diff --git a/Demos/LunarG-VulkanSamples/API-Samples/iOS/AppDelegate.h b/Demos/LunarG-VulkanSamples/API-Samples/iOS/AppDelegate.h new file mode 100644 index 00000000..c80540c6 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/iOS/AppDelegate.h @@ -0,0 +1,26 @@ +/* + * AppDelegate.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + +@end + diff --git a/Demos/LunarG-VulkanSamples/API-Samples/iOS/AppDelegate.m b/Demos/LunarG-VulkanSamples/API-Samples/iOS/AppDelegate.m new file mode 100644 index 00000000..26b9a3eb --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/iOS/AppDelegate.m @@ -0,0 +1,55 @@ +/* + * AppDelegate.m + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. +} + +- (void)applicationDidEnterBackground:(UIApplication *)application { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. +} + +- (void)applicationWillEnterForeground:(UIApplication *)application { + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. +} + +- (void)applicationWillTerminate:(UIApplication *)application { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. +} + +@end diff --git a/Demos/LunarG-VulkanSamples/API-Samples/iOS/DemoViewController.h b/Demos/LunarG-VulkanSamples/API-Samples/iOS/DemoViewController.h new file mode 100644 index 00000000..a9b01a40 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/iOS/DemoViewController.h @@ -0,0 +1,36 @@ +/* + * DemoViewController.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + + +#pragma mark - +#pragma mark DemoViewController + +/** The main view controller for the demo storyboard. */ +@interface DemoViewController : UIViewController +@end + + +#pragma mark - +#pragma mark DemoView + +/** The Metal-compatibile view for the demo Storyboard. */ +@interface DemoView : UIView +@end + diff --git a/Demos/LunarG-VulkanSamples/API-Samples/iOS/DemoViewController.mm b/Demos/LunarG-VulkanSamples/API-Samples/iOS/DemoViewController.mm new file mode 100644 index 00000000..54926066 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/iOS/DemoViewController.mm @@ -0,0 +1,70 @@ +/* + * DemoViewController.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "DemoViewController.h" + + +#pragma mark - +#pragma mark VulkanSamples extension for iOS & macOS support + +#include "Samples.h" // The LunarG VulkanSamples code + + +static UIView* sampleView; // Global variable to pass UIView to LunarG sample code + +/** + * Called from sample. + * Initialize sample from view, and resize view in accordance with the sample. + */ +void init_window(struct sample_info &info) { + info.window = sampleView; + sampleView.bounds = CGRectMake(0, 0, info.width, info.height); +} + +/** Called from sample. Return path to resource folder. */ +std::string get_base_data_dir() { + return [NSBundle.mainBundle.resourcePath stringByAppendingString: @"/"].UTF8String; +} + + +#pragma mark - +#pragma mark DemoViewController + +@implementation DemoViewController {} + +/** Since this is a single-view app, init Vulkan when the view is loaded. */ +-(void) viewDidLoad { + [super viewDidLoad]; + + sampleView = self.view; // Pass the view to the sample code + sample_main(0, NULL); // Run the LunarG sample +} + +@end + + +#pragma mark - +#pragma mark DemoView + +@implementation DemoView + +/** Returns a Metal-compatible layer. */ ++(Class) layerClass { return [CAMetalLayer class]; } + +@end + diff --git a/Demos/LunarG-VulkanSamples/API-Samples/iOS/Info.plist b/Demos/LunarG-VulkanSamples/API-Samples/iOS/Info.plist new file mode 100644 index 00000000..dcbf9e84 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/iOS/Info.plist @@ -0,0 +1,44 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + iOS/Resources/Icon.png + CFBundleIcons~ipad + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleVersion + 1.0 + LSApplicationCategoryType + + UIMainStoryboardFile + Main + UIRequiresFullScreen + + UIStatusBarHidden + + UISupportedInterfaceOrientations + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/Demos/LunarG-VulkanSamples/API-Samples/iOS/Prefix.pch b/Demos/LunarG-VulkanSamples/API-Samples/iOS/Prefix.pch new file mode 100644 index 00000000..e41a7241 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/iOS/Prefix.pch @@ -0,0 +1,5 @@ +#import + +#ifdef __OBJC__ + #import +#endif diff --git a/Demos/LunarG-VulkanSamples/API-Samples/iOS/Resources/Default-568h@2x.png b/Demos/LunarG-VulkanSamples/API-Samples/iOS/Resources/Default-568h@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..1669d7b6845195d9e11a1af63165c359f7041fc6 GIT binary patch literal 48497 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 PlX?;)X>95|%p}7R{?x}+qCcdCM$GDH! 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 literal 0 HcmV?d00001 diff --git a/Demos/LunarG-VulkanSamples/API-Samples/iOS/Resources/Icon.png b/Demos/LunarG-VulkanSamples/API-Samples/iOS/Resources/Icon.png new file mode 100755 index 0000000000000000000000000000000000000000..dc3b10f5ea97460001efe8fb45e8264812ba68ab GIT binary patch literal 5914 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 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demos/LunarG-VulkanSamples/API-Samples/iOS/main.m b/Demos/LunarG-VulkanSamples/API-Samples/iOS/main.m new file mode 100644 index 00000000..b958eb21 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/iOS/main.m @@ -0,0 +1,26 @@ +/* + * main.m + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, @"AppDelegate"); + } +} + diff --git a/Demos/LunarG-VulkanSamples/API-Samples/macOS/AppDelegate.h b/Demos/LunarG-VulkanSamples/API-Samples/macOS/AppDelegate.h new file mode 100644 index 00000000..8675ee1b --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/macOS/AppDelegate.h @@ -0,0 +1,23 @@ +/* + * AppDelegate.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +@interface AppDelegate : NSObject + +@end diff --git a/Demos/LunarG-VulkanSamples/API-Samples/macOS/AppDelegate.m b/Demos/LunarG-VulkanSamples/API-Samples/macOS/AppDelegate.m new file mode 100644 index 00000000..3e405efe --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/macOS/AppDelegate.m @@ -0,0 +1,39 @@ +/* + * AppDelegate.m + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + // Insert code here to initialize your application +} + +- (void)applicationWillTerminate:(NSNotification *)aNotification { + // Insert code here to tear down your application +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender { + return YES; +} + +@end diff --git a/Demos/LunarG-VulkanSamples/API-Samples/macOS/DemoViewController.h b/Demos/LunarG-VulkanSamples/API-Samples/macOS/DemoViewController.h new file mode 100644 index 00000000..adf00728 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/macOS/DemoViewController.h @@ -0,0 +1,36 @@ +/* + * DemoViewController.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + + +#pragma mark - +#pragma mark DemoViewController + +/** The main view controller for the demo storyboard. */ +@interface DemoViewController : NSViewController +@end + + +#pragma mark - +#pragma mark DemoView + +/** The Metal-compatibile view for the demo Storyboard. */ +@interface DemoView : NSView +@end + diff --git a/Demos/LunarG-VulkanSamples/API-Samples/macOS/DemoViewController.mm b/Demos/LunarG-VulkanSamples/API-Samples/macOS/DemoViewController.mm new file mode 100644 index 00000000..7fcacf6a --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/macOS/DemoViewController.mm @@ -0,0 +1,90 @@ +/* + * DemoViewController.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "DemoViewController.h" +#import + + +#pragma mark - +#pragma mark VulkanSamples extension for iOS & OSX support + +#include "Samples.h" // The LunarG VulkanSamples code + + +static NSView* sampleView; // Global variable to pass NSView to LunarG sample code + +/** + * Called from sample. + * Initialize sample from view, and resize view in accordance with the sample. + */ +void init_window(struct sample_info &info) { + info.window = sampleView; + sampleView.bounds = CGRectMake(0, 0, info.width, info.height); +} + +/** Called from sample. Return path to resource folder. */ +std::string get_base_data_dir() { + return [NSBundle.mainBundle.resourcePath stringByAppendingString: @"/"].UTF8String; +} + + +#pragma mark - +#pragma mark DemoViewController + +@implementation DemoViewController {} + +/** Since this is a single-view app, initialize Vulkan during view loading. */ +-(void) viewDidLoad { + [super viewDidLoad]; + + self.view.wantsLayer = YES; // Back the view with a layer created by the makeBackingLayer method. + + sampleView = self.view; // Pass the view to the sample code + sample_main(0, NULL); // Run the LunarG sample +} + +/** Resize the window to fit the size of the content as set by the sample code. */ +-(void) viewWillAppear { + [super viewWillAppear]; + + CGSize vSz = self.view.bounds.size; + NSWindow *window = self.view.window; + NSRect wFrm = [window contentRectForFrameRect: window.frame]; + NSRect newWFrm = [window frameRectForContentRect: NSMakeRect(wFrm.origin.x, wFrm.origin.y, vSz.width, vSz.height)]; + [window setFrame: newWFrm display: YES animate: window.isVisible]; + [window center]; +} + +@end + + +#pragma mark - +#pragma mark DemoView + +@implementation DemoView + +/** Indicates that the view wants to draw using the backing layer instead of using drawRect:. */ +-(BOOL) wantsUpdateLayer { return YES; } + +/** Returns a Metal-compatible layer. */ ++(Class) layerClass { return [CAMetalLayer class]; } + +/** If the wantsLayer property is set to YES, this method will be invoked to return a layer instance. */ +-(CALayer*) makeBackingLayer { return [self.class.layerClass layer]; } + +@end diff --git a/Demos/LunarG-VulkanSamples/API-Samples/macOS/Info.plist b/Demos/LunarG-VulkanSamples/API-Samples/macOS/Info.plist new file mode 100644 index 00000000..d690fdab --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/macOS/Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSApplicationCategoryType + + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + NSHumanReadableCopyright + Copyright (c) 2015 The Brenwill Workshop Ltd. All rights reserved. + NSMainStoryboardFile + Main + NSPrincipalClass + NSApplication + + diff --git a/Demos/LunarG-VulkanSamples/API-Samples/macOS/Prefix.pch b/Demos/LunarG-VulkanSamples/API-Samples/macOS/Prefix.pch new file mode 100644 index 00000000..2af02ba7 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/macOS/Prefix.pch @@ -0,0 +1,4 @@ +// +// Prefix header for all source files of the project +// + diff --git a/Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/Main.storyboard b/Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/Main.storyboard new file mode 100644 index 00000000..64bab25c --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/Main.storyboard @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Contents.json b/Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..4124516b --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,63 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "Icon-16.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "Icon-32.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "Icon-128.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "Icon-256.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "Icon-512.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-128.png b/Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-128.png new file mode 100644 index 0000000000000000000000000000000000000000..8c55f1bea07d81a0366425784d928608f325c5c8 GIT binary patch literal 12449 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 literal 0 HcmV?d00001 diff --git a/Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-16.png b/Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-16.png new file mode 100644 index 0000000000000000000000000000000000000000..583d636d60253168d1632b648a8ae5d34eaf4843 GIT binary patch literal 671 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 literal 0 HcmV?d00001 diff --git a/Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-32.png b/Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-32.png new file mode 100644 index 0000000000000000000000000000000000000000..d73e1f60edb674197e45a25e24457f2c6d4dbfb6 GIT binary patch literal 1740 zcmV;-1~d7IP)Px*hDk(0R9Fe^R#|LRWfcDIojdz5vI(MKz(`Ac#rUQW zV=ysBjSn?|(N{x!5+g+01ceqN*a{M%?6oZdWtrA?x=p9kS?;}af6u*Lrll=tBI%1K zxw-#x{&T+ntUtGU^%{m5Bw=W1Xa>~-LL@T70HV?8%vwMc;}ZZ#l1y_3yY(CGvyN&=agnOKyUhx6yoLli}AfAmly zA%o|6s328-?q4DlWH|{yk|b1A?uKuk50#a>G-ay7?b~0)y7e2-c&QQL@E{mtno>nk zuyNCixPI*_oGuscwcVpLaRRLn4+WT@(|FafdJz?<`i&=EMV|a*GPR_cxuCERYJ8-n zrD;~_?d{R_bUGdK^Yh^%g|S!+Ma1Xzda-BEo5;!WX=k!#XCXH?2l04ZbEVmALLOPr z?sP(=OD6RtAgGg3N+cAxh^fB*7reZ6D^8y}g$)}v!0-2K0zDoNs;a6G2m~-^&Kw*# zaDZ;ap}4phD@%%T_s(qsk%Q9GGA(9WTU#(R6v4J_+tAX|j4T$#d!K!Q9$Mo^T*WI2 zBl8S@s%hhK5e*Fuuvo3|crr+M32JI;G(np;Z^5-|*RgNkJ{&xF5Ic74q#MU@{P>SJ zbLK3L9QjsrS8ZJ#jvhOP(z3Pi5ulT`C$WG3eq67qhRg)<2(A|v_ zKb^p?iV8fW+h4hI8UFTmgb8_b^B)?!y51iQA{ZPXvx?~K?9zPy(BH0^J(XNk{lgDP zvDt9v&sOb>HP#OU7o#K_2B0oZj0jp#(@h{h*VoshtgIXtFJ6QioT_^+TsV({fkTMf9U1^REe6Gs40>&m(uR$G zw7?cFTEy&jyV@VvvSrJe#bRL;x$N2JmNE)J=Jn2FTVC4AN>;Aa&XLcV)oNpNyUf zUo-!B9+SmF`zczg2K)M8rMa5#GPx-j8XV6GyHO7v6*a1#h>@#;#PcXk0+5}Zjf#rh zu%)D+y83&ne&;5f*|Dx1C5snArY82`;cu{KO%d`_ooM!V;L|hb$9UaT6*t*ak@rvnCjvR8&&+!=hXS^3XFr%qLe4Qi|_=(1u&$^c{Cn8mdxn{?!9lFpgZA$5OL( z40oB`0jEWPWCR-H!|w=_k%xoUj0O%iO5e|YK9tR}!))SF)!B|~Pk3)k4M2<_Vo+c+ z>rn1=XaMwd%W{_;HWJ*Hh{8Y8#?7SIV&dR3>2NT29=3W?scm!kgj`t{>>mR^-C&Xn zRK{*H`7Jq$g$^^yIR^}W4)ZN$EOl66)${nZBQ%z~CIb(nkwcNuhBZ1KijI*bc+7Db zNYHd@lj~DjK;3Sjpi*zLm_X!NEhtO1gNg_wlY+CMWWed2^my?oFlJIY$&wLqi{P_m zyz{&pMHxx=B~?xwO%EUxLdYh;k;&l|pA#?4Pk|vjffn;1T8ENB#k*zbvL)J+@a6pu zym9U}l=L{#=P5X}%>xTr=y5ddZX0DlvvCmIY!N#O9ax=D4AR8)ICRub4k!= ziMS~b;Dd$^oNgP$*R25vvqU;0;NA7EQPwH{(*jU+)OGiv%MyVxos`fgIwPpPpESQJ zkvG#D(kda$L{Pud=%d;m+!=@=CM59IMmvgpls41Qv;b6o^)~tx38|?H%r+UV!6>e_ zPb?^$nUGSV@Q&nbH4sYR{n~yA3k*= zM-h{f=2zMDxpcb~W*8QjMota?zH12K=&cyst5UG8G7Wp*&d~bD1SXn1u^SeUU|*I2 zoDR6q6vcziiS>N#>J)l8=@ICWaJFG$GxB8Uv8qIEY@o)LKt1gzsB(^{CjuDH{4~ao ib-~k6_^8bP0sIX#q`?IJsIZCv00007MlP2R#U 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 T1Z`O8XqSPmiOvfxm)QRR$Cw~? literal 0 HcmV?d00001 diff --git a/Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/macOS.xcassets/Contents.json b/Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/macOS.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/macOS/Resources/macOS.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Demos/LunarG-VulkanSamples/API-Samples/macOS/main.m b/Demos/LunarG-VulkanSamples/API-Samples/macOS/main.m new file mode 100644 index 00000000..9da3591d --- /dev/null +++ b/Demos/LunarG-VulkanSamples/API-Samples/macOS/main.m @@ -0,0 +1,23 @@ +/* + * main.m + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +int main(int argc, const char * argv[]) { + return NSApplicationMain(argc, argv); +} diff --git a/Demos/LunarG-VulkanSamples/Demos/Demos.h b/Demos/LunarG-VulkanSamples/Demos/Demos.h new file mode 100644 index 00000000..f25b5a58 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/Demos.h @@ -0,0 +1,29 @@ +/* + * Demos.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + + +/** Loads the appropriate sample code, as indicated by the appropriate compiler build setting. */ + +#include + +#ifdef MVK_SAMP_CUBE +# include "../VulkanSamples/demos/cube.c" +#endif + + diff --git a/Demos/LunarG-VulkanSamples/Demos/Demos.xcodeproj/project.pbxproj b/Demos/LunarG-VulkanSamples/Demos/Demos.xcodeproj/project.pbxproj new file mode 100644 index 00000000..7c9d9aec --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/Demos.xcodeproj/project.pbxproj @@ -0,0 +1,516 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + A904B5711C9A08C90008C013 /* cube-frag.spv in Resources */ = {isa = PBXBuildFile; fileRef = A904B52E1C9A08C90008C013 /* cube-frag.spv */; }; + A904B5721C9A08C90008C013 /* cube-frag.spv in Resources */ = {isa = PBXBuildFile; fileRef = A904B52E1C9A08C90008C013 /* cube-frag.spv */; }; + A904B5751C9A08C90008C013 /* cube-vert.spv in Resources */ = {isa = PBXBuildFile; fileRef = A904B52F1C9A08C90008C013 /* cube-vert.spv */; }; + A904B5761C9A08C90008C013 /* cube-vert.spv in Resources */ = {isa = PBXBuildFile; fileRef = A904B52F1C9A08C90008C013 /* cube-vert.spv */; }; + A904B5891C9A08C90008C013 /* lunarg.ppm in Resources */ = {isa = PBXBuildFile; fileRef = A904B5351C9A08C90008C013 /* lunarg.ppm */; }; + A904B58A1C9A08C90008C013 /* lunarg.ppm in Resources */ = {isa = PBXBuildFile; fileRef = A904B5351C9A08C90008C013 /* lunarg.ppm */; }; + A9096E511F7EF11A00DFBEA6 /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9096E501F7EF11A00DFBEA6 /* IOSurface.framework */; }; + A91F43A01EDDD46B00733D01 /* libMoltenVK.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A91F439F1EDDD46B00733D01 /* libMoltenVK.dylib */; }; + A91F43A51EDDD5CF00733D01 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = A91F43A41EDDD5CF00733D01 /* libc++.tbd */; }; + A91F43A61EDDD61100733D01 /* libMoltenVK.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = A91F439F1EDDD46B00733D01 /* libMoltenVK.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + A91F43A71EDDD61D00733D01 /* libMoltenVK.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = A9B2A5551D7E4FB300F66656 /* libMoltenVK.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + A9A262E91CF0C60F00A87A75 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = A9A262E71CF0C60500A87A75 /* libc++.tbd */; }; + A9B2A5561D7E4FB300F66656 /* libMoltenVK.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A9B2A5551D7E4FB300F66656 /* libMoltenVK.dylib */; }; + A9B53B151C3AC0BE00ABC6F6 /* macOS.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A9B67B8B1C3AAEA200373FFD /* macOS.xcassets */; }; + A9B53B161C3AC0BE00ABC6F6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A9B67B8A1C3AAEA200373FFD /* Main.storyboard */; }; + A9B53B181C3AC0BE00ABC6F6 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A9B67B831C3AAEA200373FFD /* AppDelegate.m */; }; + A9B53B191C3AC0BE00ABC6F6 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A9B67B871C3AAEA200373FFD /* main.m */; }; + A9B53B1A1C3AC0BE00ABC6F6 /* DemoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A9B67B851C3AAEA200373FFD /* DemoViewController.m */; }; + A9B53B2F1C3AC15200ABC6F6 /* Icon.png in Resources */ = {isa = PBXBuildFile; fileRef = A9B67B761C3AAE9800373FFD /* Icon.png */; }; + A9B53B301C3AC15200ABC6F6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A9B67B771C3AAE9800373FFD /* Main.storyboard */; }; + A9B53B311C3AC15200ABC6F6 /* Default~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = A9B67B751C3AAE9800373FFD /* Default~ipad.png */; }; + A9B53B321C3AC15200ABC6F6 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A9B67B741C3AAE9800373FFD /* Default-568h@2x.png */; }; + A9B53B341C3AC15200ABC6F6 /* DemoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A9B67B6F1C3AAE9800373FFD /* DemoViewController.m */; }; + A9B53B351C3AC15200ABC6F6 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A9B67B6C1C3AAE9800373FFD /* AppDelegate.m */; }; + A9B53B361C3AC15200ABC6F6 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A9B67B711C3AAE9800373FFD /* main.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + A91F43A11EDDD48800733D01 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 6; + files = ( + A91F43A61EDDD61100733D01 /* libMoltenVK.dylib in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A9B2A5571D7E4FC400F66656 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 6; + files = ( + A91F43A71EDDD61D00733D01 /* libMoltenVK.dylib in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + A904B4641C99B5950008C013 /* Demos.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Demos.h; sourceTree = ""; }; + A904B52E1C9A08C90008C013 /* cube-frag.spv */ = {isa = PBXFileReference; lastKnownFileType = file; path = "cube-frag.spv"; sourceTree = ""; }; + A904B52F1C9A08C90008C013 /* cube-vert.spv */ = {isa = PBXFileReference; lastKnownFileType = file; path = "cube-vert.spv"; sourceTree = ""; }; + A904B5301C9A08C90008C013 /* cube.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cube.c; sourceTree = ""; }; + A904B5311C9A08C90008C013 /* cube.frag */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; path = cube.frag; sourceTree = ""; }; + A904B5331C9A08C90008C013 /* cube.vert */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; path = cube.vert; sourceTree = ""; }; + A904B5351C9A08C90008C013 /* lunarg.ppm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = lunarg.ppm; sourceTree = ""; }; + A9096E501F7EF11A00DFBEA6 /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.0.sdk/System/Library/Frameworks/IOSurface.framework; sourceTree = DEVELOPER_DIR; }; + A91F439F1EDDD46B00733D01 /* libMoltenVK.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libMoltenVK.dylib; path = ../../../MoltenVK/iOS/libMoltenVK.dylib; sourceTree = ""; }; + A91F43A41EDDD5CF00733D01 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/usr/lib/libc++.tbd"; sourceTree = DEVELOPER_DIR; }; + A9A262E71CF0C60500A87A75 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; + A9B2A5551D7E4FB300F66656 /* libMoltenVK.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libMoltenVK.dylib; path = ../../../MoltenVK/macOS/libMoltenVK.dylib; sourceTree = ""; }; + A9B53B271C3AC0BE00ABC6F6 /* Cube.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Cube.app; sourceTree = BUILT_PRODUCTS_DIR; }; + A9B53B431C3AC15200ABC6F6 /* Cube.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Cube.app; sourceTree = BUILT_PRODUCTS_DIR; }; + A9B67B6B1C3AAE9800373FFD /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + A9B67B6C1C3AAE9800373FFD /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + A9B67B6E1C3AAE9800373FFD /* DemoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoViewController.h; sourceTree = ""; }; + A9B67B6F1C3AAE9800373FFD /* DemoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoViewController.m; sourceTree = ""; }; + A9B67B701C3AAE9800373FFD /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A9B67B711C3AAE9800373FFD /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + A9B67B721C3AAE9800373FFD /* Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Prefix.pch; sourceTree = ""; }; + A9B67B741C3AAE9800373FFD /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; + A9B67B751C3AAE9800373FFD /* Default~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default~ipad.png"; sourceTree = ""; }; + A9B67B761C3AAE9800373FFD /* Icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Icon.png; sourceTree = ""; }; + A9B67B771C3AAE9800373FFD /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; + A9B67B821C3AAEA200373FFD /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + A9B67B831C3AAEA200373FFD /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + A9B67B841C3AAEA200373FFD /* DemoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoViewController.h; sourceTree = ""; }; + A9B67B851C3AAEA200373FFD /* DemoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoViewController.m; sourceTree = ""; }; + A9B67B861C3AAEA200373FFD /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A9B67B871C3AAEA200373FFD /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + A9B67B881C3AAEA200373FFD /* Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Prefix.pch; sourceTree = ""; }; + A9B67B8A1C3AAEA200373FFD /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; + A9B67B8B1C3AAEA200373FFD /* macOS.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = macOS.xcassets; sourceTree = ""; }; + A9E34B841CECF28700E40A7F /* MoltenVK.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVK.framework; path = ../../../iOS/MoltenVK.framework; sourceTree = ""; }; + A9E34B871CECF29800E40A7F /* MoltenVK.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVK.framework; path = ../../../macOS/MoltenVK.framework; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + A9B53B1B1C3AC0BE00ABC6F6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A9B2A5561D7E4FB300F66656 /* libMoltenVK.dylib in Frameworks */, + A9A262E91CF0C60F00A87A75 /* libc++.tbd in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A9B53B371C3AC15200ABC6F6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A91F43A01EDDD46B00733D01 /* libMoltenVK.dylib in Frameworks */, + A9096E511F7EF11A00DFBEA6 /* IOSurface.framework in Frameworks */, + A91F43A51EDDD5CF00733D01 /* libc++.tbd in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + A9B53B271C3AC0BE00ABC6F6 /* Cube.app */, + A9B53B431C3AC15200ABC6F6 /* Cube.app */, + ); + name = Products; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { + isa = PBXGroup; + children = ( + A904B4641C99B5950008C013 /* Demos.h */, + A904B52C1C9A08C80008C013 /* demos */, + A9B67B6A1C3AAE9800373FFD /* iOS */, + A9B67B811C3AAEA200373FFD /* macOS */, + A9295EAA1CECEF0900EFC483 /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = CustomTemplate; + sourceTree = ""; + }; + A904B52C1C9A08C80008C013 /* demos */ = { + isa = PBXGroup; + children = ( + A904B52E1C9A08C90008C013 /* cube-frag.spv */, + A904B52F1C9A08C90008C013 /* cube-vert.spv */, + A904B5301C9A08C90008C013 /* cube.c */, + A904B5311C9A08C90008C013 /* cube.frag */, + A904B5331C9A08C90008C013 /* cube.vert */, + A904B5351C9A08C90008C013 /* lunarg.ppm */, + ); + name = demos; + path = ../VulkanSamples/demos; + sourceTree = ""; + }; + A9295EAA1CECEF0900EFC483 /* Frameworks */ = { + isa = PBXGroup; + children = ( + A9096E501F7EF11A00DFBEA6 /* IOSurface.framework */, + A91F439F1EDDD46B00733D01 /* libMoltenVK.dylib */, + A9B2A5551D7E4FB300F66656 /* libMoltenVK.dylib */, + A9E34B841CECF28700E40A7F /* MoltenVK.framework */, + A9E34B871CECF29800E40A7F /* MoltenVK.framework */, + A9A262E71CF0C60500A87A75 /* libc++.tbd */, + A91F43A41EDDD5CF00733D01 /* libc++.tbd */, + ); + name = Frameworks; + sourceTree = ""; + }; + A9B67B6A1C3AAE9800373FFD /* iOS */ = { + isa = PBXGroup; + children = ( + A9B67B6B1C3AAE9800373FFD /* AppDelegate.h */, + A9B67B6C1C3AAE9800373FFD /* AppDelegate.m */, + A9B67B6E1C3AAE9800373FFD /* DemoViewController.h */, + A9B67B6F1C3AAE9800373FFD /* DemoViewController.m */, + A9B67B701C3AAE9800373FFD /* Info.plist */, + A9B67B711C3AAE9800373FFD /* main.m */, + A9B67B721C3AAE9800373FFD /* Prefix.pch */, + A9B67B731C3AAE9800373FFD /* Resources */, + ); + path = iOS; + sourceTree = ""; + }; + A9B67B731C3AAE9800373FFD /* Resources */ = { + isa = PBXGroup; + children = ( + A9B67B741C3AAE9800373FFD /* Default-568h@2x.png */, + A9B67B751C3AAE9800373FFD /* Default~ipad.png */, + A9B67B761C3AAE9800373FFD /* Icon.png */, + A9B67B771C3AAE9800373FFD /* Main.storyboard */, + ); + path = Resources; + sourceTree = ""; + }; + A9B67B811C3AAEA200373FFD /* macOS */ = { + isa = PBXGroup; + children = ( + A9B67B821C3AAEA200373FFD /* AppDelegate.h */, + A9B67B831C3AAEA200373FFD /* AppDelegate.m */, + A9B67B841C3AAEA200373FFD /* DemoViewController.h */, + A9B67B851C3AAEA200373FFD /* DemoViewController.m */, + A9B67B861C3AAEA200373FFD /* Info.plist */, + A9B67B871C3AAEA200373FFD /* main.m */, + A9B67B881C3AAEA200373FFD /* Prefix.pch */, + A9B67B891C3AAEA200373FFD /* Resources */, + ); + path = macOS; + sourceTree = ""; + }; + A9B67B891C3AAEA200373FFD /* Resources */ = { + isa = PBXGroup; + children = ( + A9B67B8A1C3AAEA200373FFD /* Main.storyboard */, + A9B67B8B1C3AAEA200373FFD /* macOS.xcassets */, + ); + path = Resources; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + A9B53B0F1C3AC0BE00ABC6F6 /* Cube-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = A9B53B241C3AC0BE00ABC6F6 /* Build configuration list for PBXNativeTarget "Cube-macOS" */; + buildPhases = ( + A9B53B141C3AC0BE00ABC6F6 /* Resources */, + A9B53B171C3AC0BE00ABC6F6 /* Sources */, + A9B53B1B1C3AC0BE00ABC6F6 /* Frameworks */, + A9B2A5571D7E4FC400F66656 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Cube-macOS"; + productName = foo; + productReference = A9B53B271C3AC0BE00ABC6F6 /* Cube.app */; + productType = "com.apple.product-type.application"; + }; + A9B53B291C3AC15200ABC6F6 /* Cube-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = A9B53B401C3AC15200ABC6F6 /* Build configuration list for PBXNativeTarget "Cube-iOS" */; + buildPhases = ( + A9B53B2E1C3AC15200ABC6F6 /* Resources */, + A9B53B331C3AC15200ABC6F6 /* Sources */, + A9B53B371C3AC15200ABC6F6 /* Frameworks */, + A91F43A11EDDD48800733D01 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Cube-iOS"; + productName = foo; + productReference = A9B53B431C3AC15200ABC6F6 /* Cube.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0900; + TargetAttributes = { + A9B53B0F1C3AC0BE00ABC6F6 = { + DevelopmentTeam = VU3TCKU48B; + }; + A9B53B291C3AC15200ABC6F6 = { + DevelopmentTeam = VU3TCKU48B; + }; + }; + }; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Demos" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); + mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + A9B53B291C3AC15200ABC6F6 /* Cube-iOS */, + A9B53B0F1C3AC0BE00ABC6F6 /* Cube-macOS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + A9B53B141C3AC0BE00ABC6F6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A904B5761C9A08C90008C013 /* cube-vert.spv in Resources */, + A904B5721C9A08C90008C013 /* cube-frag.spv in Resources */, + A904B58A1C9A08C90008C013 /* lunarg.ppm in Resources */, + A9B53B151C3AC0BE00ABC6F6 /* macOS.xcassets in Resources */, + A9B53B161C3AC0BE00ABC6F6 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A9B53B2E1C3AC15200ABC6F6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A904B5751C9A08C90008C013 /* cube-vert.spv in Resources */, + A9B53B2F1C3AC15200ABC6F6 /* Icon.png in Resources */, + A9B53B301C3AC15200ABC6F6 /* Main.storyboard in Resources */, + A904B5891C9A08C90008C013 /* lunarg.ppm in Resources */, + A9B53B311C3AC15200ABC6F6 /* Default~ipad.png in Resources */, + A9B53B321C3AC15200ABC6F6 /* Default-568h@2x.png in Resources */, + A904B5711C9A08C90008C013 /* cube-frag.spv in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + A9B53B171C3AC0BE00ABC6F6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A9B53B181C3AC0BE00ABC6F6 /* AppDelegate.m in Sources */, + A9B53B191C3AC0BE00ABC6F6 /* main.m in Sources */, + A9B53B1A1C3AC0BE00ABC6F6 /* DemoViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A9B53B331C3AC15200ABC6F6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A9B53B341C3AC15200ABC6F6 /* DemoViewController.m in Sources */, + A9B53B351C3AC15200ABC6F6 /* AppDelegate.m in Sources */, + A9B53B361C3AC15200ABC6F6 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + A9B53B251C3AC0BE00ABC6F6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COMBINE_HIDPI_IMAGES = YES; + GCC_PREFIX_HEADER = "$(SRCROOT)/macOS/Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + MVK_SAMP_CUBE, + ); + INFOPLIST_FILE = "$(SRCROOT)/macOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "@executable_path"; + LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/../../../MoltenVK/macOS\""; + MACOSX_DEPLOYMENT_TARGET = 10.11; + PRODUCT_NAME = Cube; + SDKROOT = macosx; + }; + name = Debug; + }; + A9B53B261C3AC0BE00ABC6F6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COMBINE_HIDPI_IMAGES = YES; + GCC_PREFIX_HEADER = "$(SRCROOT)/macOS/Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + MVK_SAMP_CUBE, + ); + INFOPLIST_FILE = "$(SRCROOT)/macOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "@executable_path"; + LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/../../../MoltenVK/macOS\""; + MACOSX_DEPLOYMENT_TARGET = 10.11; + PRODUCT_NAME = Cube; + SDKROOT = macosx; + }; + name = Release; + }; + A9B53B411C3AC15200ABC6F6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + DEVELOPMENT_TEAM = VU3TCKU48B; + GCC_PREFIX_HEADER = "$(SRCROOT)/iOS/Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + MVK_SAMP_CUBE, + ); + INFOPLIST_FILE = "$(SRCROOT)/iOS/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "@executable_path"; + LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/../../../MoltenVK/iOS\""; + PRODUCT_NAME = Cube; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = arm64; + }; + name = Debug; + }; + A9B53B421C3AC15200ABC6F6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + DEVELOPMENT_TEAM = VU3TCKU48B; + GCC_PREFIX_HEADER = "$(SRCROOT)/iOS/Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + MVK_SAMP_CUBE, + ); + INFOPLIST_FILE = "$(SRCROOT)/iOS/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "@executable_path"; + LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/../../../MoltenVK/iOS\""; + PRODUCT_NAME = Cube; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = arm64; + }; + name = Release; + }; + C01FCF4F08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = NO; + COPY_PHASE_STRIP = NO; + ENABLE_BITCODE = NO; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + _DEBUG, + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + HEADER_SEARCH_PATHS = ( + "\"$(SRCROOT)/../../../MoltenVK/include\"", + "\"$(SRCROOT)/../VulkanSamples/include\"", + ); + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_BUNDLE_IDENTIFIER = "com.moltenvk.${PRODUCT_NAME:identifier}"; + }; + name = Debug; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = NO; + ENABLE_BITCODE = NO; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + HEADER_SEARCH_PATHS = ( + "\"$(SRCROOT)/../../../MoltenVK/include\"", + "\"$(SRCROOT)/../VulkanSamples/include\"", + ); + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_BUNDLE_IDENTIFIER = "com.moltenvk.${PRODUCT_NAME:identifier}"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + A9B53B241C3AC0BE00ABC6F6 /* Build configuration list for PBXNativeTarget "Cube-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A9B53B251C3AC0BE00ABC6F6 /* Debug */, + A9B53B261C3AC0BE00ABC6F6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A9B53B401C3AC15200ABC6F6 /* Build configuration list for PBXNativeTarget "Cube-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A9B53B411C3AC15200ABC6F6 /* Debug */, + A9B53B421C3AC15200ABC6F6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Demos" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/Demos/LunarG-VulkanSamples/Demos/Demos.xcodeproj/xcshareddata/xcschemes/Cube-iOS.xcscheme b/Demos/LunarG-VulkanSamples/Demos/Demos.xcodeproj/xcshareddata/xcschemes/Cube-iOS.xcscheme new file mode 100644 index 00000000..deb6976a --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/Demos.xcodeproj/xcshareddata/xcschemes/Cube-iOS.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demos/LunarG-VulkanSamples/Demos/Demos.xcodeproj/xcshareddata/xcschemes/Cube-macOS.xcscheme b/Demos/LunarG-VulkanSamples/Demos/Demos.xcodeproj/xcshareddata/xcschemes/Cube-macOS.xcscheme new file mode 100644 index 00000000..f80c0340 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/Demos.xcodeproj/xcshareddata/xcschemes/Cube-macOS.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demos/LunarG-VulkanSamples/Demos/iOS/AppDelegate.h b/Demos/LunarG-VulkanSamples/Demos/iOS/AppDelegate.h new file mode 100644 index 00000000..c80540c6 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/iOS/AppDelegate.h @@ -0,0 +1,26 @@ +/* + * AppDelegate.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + +@end + diff --git a/Demos/LunarG-VulkanSamples/Demos/iOS/AppDelegate.m b/Demos/LunarG-VulkanSamples/Demos/iOS/AppDelegate.m new file mode 100644 index 00000000..26b9a3eb --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/iOS/AppDelegate.m @@ -0,0 +1,55 @@ +/* + * AppDelegate.m + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. +} + +- (void)applicationDidEnterBackground:(UIApplication *)application { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. +} + +- (void)applicationWillEnterForeground:(UIApplication *)application { + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. +} + +- (void)applicationWillTerminate:(UIApplication *)application { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. +} + +@end diff --git a/Demos/LunarG-VulkanSamples/Demos/iOS/DemoViewController.h b/Demos/LunarG-VulkanSamples/Demos/iOS/DemoViewController.h new file mode 100644 index 00000000..a9b01a40 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/iOS/DemoViewController.h @@ -0,0 +1,36 @@ +/* + * DemoViewController.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + + +#pragma mark - +#pragma mark DemoViewController + +/** The main view controller for the demo storyboard. */ +@interface DemoViewController : UIViewController +@end + + +#pragma mark - +#pragma mark DemoView + +/** The Metal-compatibile view for the demo Storyboard. */ +@interface DemoView : UIView +@end + diff --git a/Demos/LunarG-VulkanSamples/Demos/iOS/DemoViewController.m b/Demos/LunarG-VulkanSamples/Demos/iOS/DemoViewController.m new file mode 100644 index 00000000..e5ddee2d --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/iOS/DemoViewController.m @@ -0,0 +1,69 @@ +/* + * DemoViewController.m + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "DemoViewController.h" + +#include "../Demos.h" // The LunarG Vulkan SDK demo code + + +#pragma mark - +#pragma mark DemoViewController + +@implementation DemoViewController { + CADisplayLink* _displayLink; + struct demo demo; +} + +-(void) dealloc { + demo_cleanup(&demo); + [_displayLink release]; + [super dealloc]; +} + +/** Since this is a single-view app, init Vulkan when the view is loaded. */ +-(void) viewDidLoad { + [super viewDidLoad]; + + self.view.contentScaleFactor = UIScreen.mainScreen.nativeScale; + + demo_main(&demo, self.view); + demo_update_and_draw(&demo); + + uint32_t fps = 60; + _displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector(renderLoop)]; + [_displayLink setFrameInterval: 60 / fps]; + [_displayLink addToRunLoop: NSRunLoop.currentRunLoop forMode: NSDefaultRunLoopMode]; +} + +-(void) renderLoop { + demo_update_and_draw(&demo); +} + +@end + + +#pragma mark - +#pragma mark DemoView + +@implementation DemoView + +/** Returns a Metal-compatible layer. */ ++(Class) layerClass { return [CAMetalLayer class]; } + +@end + diff --git a/Demos/LunarG-VulkanSamples/Demos/iOS/Info.plist b/Demos/LunarG-VulkanSamples/Demos/iOS/Info.plist new file mode 100644 index 00000000..dcbf9e84 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/iOS/Info.plist @@ -0,0 +1,44 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + iOS/Resources/Icon.png + CFBundleIcons~ipad + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleVersion + 1.0 + LSApplicationCategoryType + + UIMainStoryboardFile + Main + UIRequiresFullScreen + + UIStatusBarHidden + + UISupportedInterfaceOrientations + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/Demos/LunarG-VulkanSamples/Demos/iOS/Prefix.pch b/Demos/LunarG-VulkanSamples/Demos/iOS/Prefix.pch new file mode 100644 index 00000000..e41a7241 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/iOS/Prefix.pch @@ -0,0 +1,5 @@ +#import + +#ifdef __OBJC__ + #import +#endif diff --git a/Demos/LunarG-VulkanSamples/Demos/iOS/Resources/Default-568h@2x.png b/Demos/LunarG-VulkanSamples/Demos/iOS/Resources/Default-568h@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..1669d7b6845195d9e11a1af63165c359f7041fc6 GIT binary patch literal 48497 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 PlX?;)X>95|%p}7R{?x}+qCcdCM$GDH! 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 literal 0 HcmV?d00001 diff --git a/Demos/LunarG-VulkanSamples/Demos/iOS/Resources/Icon.png b/Demos/LunarG-VulkanSamples/Demos/iOS/Resources/Icon.png new file mode 100755 index 0000000000000000000000000000000000000000..dc3b10f5ea97460001efe8fb45e8264812ba68ab GIT binary patch literal 5914 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 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demos/LunarG-VulkanSamples/Demos/iOS/main.m b/Demos/LunarG-VulkanSamples/Demos/iOS/main.m new file mode 100644 index 00000000..b958eb21 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/iOS/main.m @@ -0,0 +1,26 @@ +/* + * main.m + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, @"AppDelegate"); + } +} + diff --git a/Demos/LunarG-VulkanSamples/Demos/macOS/AppDelegate.h b/Demos/LunarG-VulkanSamples/Demos/macOS/AppDelegate.h new file mode 100644 index 00000000..8675ee1b --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/macOS/AppDelegate.h @@ -0,0 +1,23 @@ +/* + * AppDelegate.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +@interface AppDelegate : NSObject + +@end diff --git a/Demos/LunarG-VulkanSamples/Demos/macOS/AppDelegate.m b/Demos/LunarG-VulkanSamples/Demos/macOS/AppDelegate.m new file mode 100644 index 00000000..3e405efe --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/macOS/AppDelegate.m @@ -0,0 +1,39 @@ +/* + * AppDelegate.m + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + // Insert code here to initialize your application +} + +- (void)applicationWillTerminate:(NSNotification *)aNotification { + // Insert code here to tear down your application +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender { + return YES; +} + +@end diff --git a/Demos/LunarG-VulkanSamples/Demos/macOS/DemoViewController.h b/Demos/LunarG-VulkanSamples/Demos/macOS/DemoViewController.h new file mode 100644 index 00000000..adf00728 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/macOS/DemoViewController.h @@ -0,0 +1,36 @@ +/* + * DemoViewController.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + + +#pragma mark - +#pragma mark DemoViewController + +/** The main view controller for the demo storyboard. */ +@interface DemoViewController : NSViewController +@end + + +#pragma mark - +#pragma mark DemoView + +/** The Metal-compatibile view for the demo Storyboard. */ +@interface DemoView : NSView +@end + diff --git a/Demos/LunarG-VulkanSamples/Demos/macOS/DemoViewController.m b/Demos/LunarG-VulkanSamples/Demos/macOS/DemoViewController.m new file mode 100644 index 00000000..b53f2d0c --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/macOS/DemoViewController.m @@ -0,0 +1,88 @@ +/* + * DemoViewController.m + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "DemoViewController.h" +#import + +#include "../Demos.h" // The LunarG Vulkan SDK demo code + + +#pragma mark - +#pragma mark DemoViewController + +@implementation DemoViewController { + CVDisplayLinkRef _displayLink; + struct demo demo; +} + +-(void) dealloc { + demo_cleanup(&demo); + CVDisplayLinkRelease(_displayLink); + [super dealloc]; +} + +/** Since this is a single-view app, initialize Vulkan during view loading. */ +-(void) viewDidLoad { + [super viewDidLoad]; + + self.view.wantsLayer = YES; // Back the view with a layer created by the makeBackingLayer method. + + demo_main(&demo, self.view); + + CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink); + CVDisplayLinkSetOutputCallback(_displayLink, &DisplayLinkCallback, &demo); + CVDisplayLinkStart(_displayLink); +} + + +#pragma mark Display loop callback function + +/** Rendering loop callback function for use with a CVDisplayLink. */ +static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, + const CVTimeStamp* now, + const CVTimeStamp* outputTime, + CVOptionFlags flagsIn, + CVOptionFlags* flagsOut, + void* target) { + demo_update_and_draw((struct demo*)target); + return kCVReturnSuccess; +} + +@end + + +#pragma mark - +#pragma mark DemoView + +@implementation DemoView + +/** Indicates that the view wants to draw using the backing layer instead of using drawRect:. */ +-(BOOL) wantsUpdateLayer { return YES; } + +/** Returns a Metal-compatible layer. */ ++(Class) layerClass { return [CAMetalLayer class]; } + +/** If the wantsLayer property is set to YES, this method will be invoked to return a layer instance. */ +-(CALayer*) makeBackingLayer { + CALayer* layer = [self.class.layerClass layer]; + CGSize viewScale = [self convertSizeToBacking: CGSizeMake(1.0, 1.0)]; + layer.contentsScale = MIN(viewScale.width, viewScale.height); + return layer; +} + +@end diff --git a/Demos/LunarG-VulkanSamples/Demos/macOS/Info.plist b/Demos/LunarG-VulkanSamples/Demos/macOS/Info.plist new file mode 100644 index 00000000..646b7832 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/macOS/Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSApplicationCategoryType + + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + NSHumanReadableCopyright + Copyright (c) 2015-2017 The Brenwill Workshop Ltd. All rights reserved. + NSMainStoryboardFile + Main + NSPrincipalClass + NSApplication + + diff --git a/Demos/LunarG-VulkanSamples/Demos/macOS/Prefix.pch b/Demos/LunarG-VulkanSamples/Demos/macOS/Prefix.pch new file mode 100644 index 00000000..1abd7a8c --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/macOS/Prefix.pch @@ -0,0 +1,3 @@ +// +// Prefix header for all source files of the project +// diff --git a/Demos/LunarG-VulkanSamples/Demos/macOS/Resources/Main.storyboard b/Demos/LunarG-VulkanSamples/Demos/macOS/Resources/Main.storyboard new file mode 100644 index 00000000..d21c1493 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/macOS/Resources/Main.storyboard @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demos/LunarG-VulkanSamples/Demos/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Contents.json b/Demos/LunarG-VulkanSamples/Demos/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..4124516b --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,63 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "Icon-16.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "Icon-32.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "Icon-128.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "Icon-256.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "Icon-512.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Demos/LunarG-VulkanSamples/Demos/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-128.png b/Demos/LunarG-VulkanSamples/Demos/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-128.png new file mode 100644 index 0000000000000000000000000000000000000000..8c55f1bea07d81a0366425784d928608f325c5c8 GIT binary patch literal 12449 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 literal 0 HcmV?d00001 diff --git a/Demos/LunarG-VulkanSamples/Demos/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-16.png b/Demos/LunarG-VulkanSamples/Demos/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-16.png new file mode 100644 index 0000000000000000000000000000000000000000..583d636d60253168d1632b648a8ae5d34eaf4843 GIT binary patch literal 671 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 literal 0 HcmV?d00001 diff --git a/Demos/LunarG-VulkanSamples/Demos/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-32.png b/Demos/LunarG-VulkanSamples/Demos/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-32.png new file mode 100644 index 0000000000000000000000000000000000000000..d73e1f60edb674197e45a25e24457f2c6d4dbfb6 GIT binary patch literal 1740 zcmV;-1~d7IP)Px*hDk(0R9Fe^R#|LRWfcDIojdz5vI(MKz(`Ac#rUQW zV=ysBjSn?|(N{x!5+g+01ceqN*a{M%?6oZdWtrA?x=p9kS?;}af6u*Lrll=tBI%1K zxw-#x{&T+ntUtGU^%{m5Bw=W1Xa>~-LL@T70HV?8%vwMc;}ZZ#l1y_3yY(CGvyN&=agnOKyUhx6yoLli}AfAmly zA%o|6s328-?q4DlWH|{yk|b1A?uKuk50#a>G-ay7?b~0)y7e2-c&QQL@E{mtno>nk zuyNCixPI*_oGuscwcVpLaRRLn4+WT@(|FafdJz?<`i&=EMV|a*GPR_cxuCERYJ8-n zrD;~_?d{R_bUGdK^Yh^%g|S!+Ma1Xzda-BEo5;!WX=k!#XCXH?2l04ZbEVmALLOPr z?sP(=OD6RtAgGg3N+cAxh^fB*7reZ6D^8y}g$)}v!0-2K0zDoNs;a6G2m~-^&Kw*# zaDZ;ap}4phD@%%T_s(qsk%Q9GGA(9WTU#(R6v4J_+tAX|j4T$#d!K!Q9$Mo^T*WI2 zBl8S@s%hhK5e*Fuuvo3|crr+M32JI;G(np;Z^5-|*RgNkJ{&xF5Ic74q#MU@{P>SJ zbLK3L9QjsrS8ZJ#jvhOP(z3Pi5ulT`C$WG3eq67qhRg)<2(A|v_ zKb^p?iV8fW+h4hI8UFTmgb8_b^B)?!y51iQA{ZPXvx?~K?9zPy(BH0^J(XNk{lgDP zvDt9v&sOb>HP#OU7o#K_2B0oZj0jp#(@h{h*VoshtgIXtFJ6QioT_^+TsV({fkTMf9U1^REe6Gs40>&m(uR$G zw7?cFTEy&jyV@VvvSrJe#bRL;x$N2JmNE)J=Jn2FTVC4AN>;Aa&XLcV)oNpNyUf zUo-!B9+SmF`zczg2K)M8rMa5#GPx-j8XV6GyHO7v6*a1#h>@#;#PcXk0+5}Zjf#rh zu%)D+y83&ne&;5f*|Dx1C5snArY82`;cu{KO%d`_ooM!V;L|hb$9UaT6*t*ak@rvnCjvR8&&+!=hXS^3XFr%qLe4Qi|_=(1u&$^c{Cn8mdxn{?!9lFpgZA$5OL( z40oB`0jEWPWCR-H!|w=_k%xoUj0O%iO5e|YK9tR}!))SF)!B|~Pk3)k4M2<_Vo+c+ z>rn1=XaMwd%W{_;HWJ*Hh{8Y8#?7SIV&dR3>2NT29=3W?scm!kgj`t{>>mR^-C&Xn zRK{*H`7Jq$g$^^yIR^}W4)ZN$EOl66)${nZBQ%z~CIb(nkwcNuhBZ1KijI*bc+7Db zNYHd@lj~DjK;3Sjpi*zLm_X!NEhtO1gNg_wlY+CMWWed2^my?oFlJIY$&wLqi{P_m zyz{&pMHxx=B~?xwO%EUxLdYh;k;&l|pA#?4Pk|vjffn;1T8ENB#k*zbvL)J+@a6pu zym9U}l=L{#=P5X}%>xTr=y5ddZX0DlvvCmIY!N#O9ax=D4AR8)ICRub4k!= ziMS~b;Dd$^oNgP$*R25vvqU;0;NA7EQPwH{(*jU+)OGiv%MyVxos`fgIwPpPpESQJ zkvG#D(kda$L{Pud=%d;m+!=@=CM59IMmvgpls41Qv;b6o^)~tx38|?H%r+UV!6>e_ zPb?^$nUGSV@Q&nbH4sYR{n~yA3k*= zM-h{f=2zMDxpcb~W*8QjMota?zH12K=&cyst5UG8G7Wp*&d~bD1SXn1u^SeUU|*I2 zoDR6q6vcziiS>N#>J)l8=@ICWaJFG$GxB8Uv8qIEY@o)LKt1gzsB(^{CjuDH{4~ao ib-~k6_^8bP0sIX#q`?IJsIZCv00007MlP2R#U zAlEkvGCwl2CvxvD#-=OixcuD zM)rRo$3tofhuEY1ZWT!+_{$j@!ZWyaq5WU$LV%#M8hm8?_awA%{AeBi*L-RJD6L{5 zxBq|a$qV7@|Fxb!g8s*I_h z>HnAlC`b5g0k1@GAH1!K z4KXQV|Wg3?_@U6Af>s2WxDnSfBkk?MLn>)3c@~3h`N60-V?7!iS?S`wHyOx}LhiTr$Lvk8%lVy*N53!} zDwdMtp<=4hlU+!LSciXsazW(5o*DWV?CB$j8gm*48{( ztiEDm-YH2ARxz=xa#@#8=C#7%-nvArv^lf%xtz7K*`KM@)YO!7n6Jx)lfKE&gE28P zuW}h^Hng;SyhPMGnw<2-%W-Vlkr(me%79en5wq*!g%m4sw!U6|?EbwJ8kc)Yijt&1sy3J=2A&AAC92;oU0KDk>lWR1WVteWS0p4 z9qwpPTd<{AFagmzCv69lf#->8u3GP$mg|%Y%b>Fp?cUXvlT{WD}C|}v0F>_}DglN5Rvjr{G$S<&g z=Iz$aavk)?X4~?2D6|v-c8jg@2lMs$+-AKKx7ZZ%ora%?*>~#Z4>N~L_+MI@6dShQ zs#Bakf9`E)d71*J@TrF#-&_RXMp(N>^r5N^DwKiwbum*y|EZ#A2 zhYoad;X#pvhTJ03&<$LAh`y$!IkX*oSRTvMcN;esF%Ps*Natz1OZS~c$&6vF_$>3U zH05(YJPdTy^7@=xH{B-N6@Vy!u5y;~p8@r5Mk^@K8p(if;_+@O7OmATks8{#|934qgw#u5sIW^Y+`cp{Ie|W()+6 zrOE9u=MuYPp`L_ESUA+N<%D5il(T~E(1-dW_$CJc#RI{C0y{W)C{!9C0Wc)3EII9yy!nt! zj)p@&{UH#@Yo+Zs@d4b?ag#IWYV8>1QJ#~)oyQ6qJWzIZgl>She~01HP%~(5E99#t zs@Yw#0vP}_&mT|37~2aW<|84ap(0m6&C}s~&gT2~B175!uR+E?mQ!(O5IPuu>Et%^ zEaTa$WhlRWs!dyY#-Q*6T8Vz+8yjneJ3sxEd59Y1Ee~hgvEM!KGv+@FAQ$hr1TePp zmk<%4jHj~(RmiS{?>{c7B*(ujW?4o?q>&5|D>bO6GZ)A_Jer@L!{*X5X49{L$j6Xn zhXfnRhqTt9^+=c_$c#twO{N!<_z*qaz5A_g(g>JBH_k$s7P8&~EVG%qb6qUIZx zylAFj%R@U)2>!`rp~gcP^aH_AAf40- z5ZKtkk?GVPU~4NikDAmycy#>~SXUNm_jzo+(ro?lhSik{lkB(>l>LS`H%F&jHvo1a zIP-Io!By?%Pp}sFBO@MqidrO1(c!OB-HA&oQTi8I>1Go5{p#094H1R`Hx_zFn)Oo5 z%!LCp_)0Xg>L)+rY!4*muW2K<12m@AQ)T=YGMBPvKlf_y};=62)inz zn?~Hlbg%GSme9Sr-Ft=BuwS+9;T9Rf+M<{)YMhCY5NkN!{$zZ&6{SV|yv6%W4yZs- zI7COl$2)ETAi2YW=^ zmP_ziLMdHk%VQww*H=bnX@zbPc}_Ax=vvF*D{_xLmXRk>O zxA^ZJ$H%L(e0IM`O31N~u*WF*?Lh&zP(9h`&%-9gP^S`D-w0SiR_C!>G}RE4Ifxv8 zT0JUJ%n7q9fQ>hn>oxxZt3HlJDjE$6x1mYFaC^&ZxKhtmXBN*!9h*yK7Ms#oYvPTo znn)<+%iviWEe|~Flj|*zL9;);5*ZB+n-`_M&J7BZseO%ew#5ITto2g&l_`72&KH6~ z(*t7=AORRlM9OvV`oWwP3M3XtNR)2 z`7;J$(=Qa#v?ut-C5%#<*e#&2?3mfT*$8}kmWGEOUo{hwV~H#0c=C@o-`3@m>Kyb~ z?QU=-3dkftnGfx|MaB2o;K<8Fws$i5emG!`rfbvPo=ebiYC)ogfN|;1NAVky?l>CE zH=Q9POOoaLz7aN{611@}2v^CIa@WrOMqh_ob0T*{x>8Pi?xc?UyW1}>yry@5d^eya zYbF2vte*2aX6)I4G(DY_bO#wi`ZUmyGsJ~d;?;J&_Q~sO2GOy8_a_`&UC8al9IR6Jiz+5LwNjrlNv_O~w;~Q~gQjD<@+U z!~wzZX)J0D6)Dx)lJ^$&+XK0|;Nk)$b85LvOXu%QEF+S-l|MBl9=6SbU%Kcf2ex0% z*-b`}B^!wNoF|>yheww%;Qg`-zCqO}UuvC+gC@7ESb7C5(jXa3pqq(`W8#MCsLErIy*|Xh^BAf_?uCBPKPKGM!hTqSF?HBR_^=J)yI)xj8|kQI6Je47*wq6hL2t0eBpu zqA5+DM}{CURbuMmi%eop*QpD$t^GXP-qa-t53vxt8q}J~%5^TPuj=W7o}bt&(Gk*R zI?fQJ-n`-QWatGQ^0@ZC_PKZ~>yC|*m$Bo@g?hW49W%)e6Pvi1sa!`g`7Rz~S;Q+1 zGwRQNZ?N?JJN@K$Z_H#a6NJQa)`OY0?nf+>-Kk=Qj4JMo*@noTG60QWOw5 z@_v2`B?P^XvUC$O7>?4Ej%gnnj@r5 zQh(2btK{!?F(sKfDtV{RencT=UXg1{ldZ$s=>)Ta=0gj{v(bkZap)FQ!M7Ssae|H9 zymF#P3^Jrcccuqm2~LN}>rO9KjcG@N3)RO)br0FwKFNpG_Fmis%=@Dhnhvd%rve>6 z@LzXcqe>>)dEynseY1Hdm}Tl&ZCEn*g4Ltl;RWMNPt6}!C>IXN=Lep*@Qs!@hS;7= z403Ex?Z~4Rku`eqhGT}2+aCD1SCZ3m=@;0Mj+mUK?(Px-cjO(9k|88tDu& z6KLqY?MqwyZcc&i$k<%*p`c2v2t^LlAStfR*s{fe5U9thRue%C?Fv7V7(1{p>HPVd z*3A>CoOLt2X@#TfY*g2+?thJtpBg+#1T(+o&a`a1aE& zDBpHrMvSQW#I?V_|M|?gZ6UICpNjto z6&EB^eB9!LTUa@g1uH(1{uZL`F$F&TI+S@%gveyl#%P;t7C(GUrdfZ`Lh|9;7wa}w^VS`jLf$iU^G{2P1 zw?@+TslCb$uDQU`&m)pl%T=x zUnwqs^xd&CPfoabaWB89q-{!o)Di}%M+R~q>Lmv{3rMKUmOFq!ceq%FalMzZS_|nYF>y1u@PY?jw%Y?3?(qX~;qc-E zc2LDqE8JBU$yQ&yAwp@|uNtIUjo+bwWeB_W@=TAfWo)Ok%MonLpnp!FeLJpP$vT0!=ucLJU+})^@)xBfvIMy#qlo z0aAY9EazV%3el8VvqpZ>GtYWBOvqQPB=6pJ-628Lcmuk;6uhR2^%5UO^g!WH>rZgw zRZ0%C=_k_g#pu_rj#s;3Kfi(3HwWTbr@p*|QDE6GwRpeE4jEw`7vW-DmTx%f!i^ZF zQHuzD)vTQID?qy=7!|kbe9DCP8w+Nb2ZEU{<%|$3E349m#r9Yh(kiiUSSIJYX2;T| znB~10Yyo|f^?GeZu+eSvz|WuR!Oy?8YnK}aEI2z{IGDH-GvT+h$5$d#4==(r!!|9(*k|hOCeQX?+A4OIhX5{bSj8H;1w;45?>7ONpI&8m)Lq2hGIZx?ly%rv7 zaZ4c+^Fo_430~#3dZp>hyjrKxOP**Pq&;j5-+7R*>((ez@mh^2k_Jjm>p2Iw9OMMx z;o_jl9}8g6kuQ_Bv97$;)!md#fp*m+Px$I=JJ}5<^S|ov^$ayod_l^UE&tmFlnJk# z>_?3lfB>2;$Mi(|#U^*!3A(aZ-oL|JhsT9qrwFji6%@6LUknsen!L?-q`E!Jcobx# z55|VfU-w}!bNSn^h4>_kdGmNGKDWp6PFAUoLcD(|l%JyJ~X z+4bsjH1bthgVzyJ&t%(3?g0nXoc7X9cHN2EX+s6c`&Hcd;i;5+1M#!{>KgyPXqLOO zpfmmW>2zWMi|(&(f!khb{b@u^oh|PS((5>A%kEXli1W`VMA&c~*gI&+oAELTm@8@& z)4J(u<@<#&M`wSI+>0xXZYZl-VbEu?q$j6Q2ob{lmZW(+x(P07>~|2bN*o}_a7M3Gu`)o;SY=^TOOWApE4ct6*EN%@2XwA zo4qUHv&ainsfgfc{7rBRFPwOVT+sw^5qub*W6wx)RvEzGZ61^P6xZ-HY~x2zcgvuR z;GDk8$xn>Ypzbvh{^vdJY~z*zSIx_X0%xC$?qzDpCh2#fEVzzKcGG3-uw|piSD(BU zRAvZ%eA%x9`z^1cIglMt=t4!oXB43%@`n}{TvDYveJY1Lmj!b)mfqI*K;c)6;b}Oe5+LHr-9g8e2tpQ%f~12V zG)MA$E^Bi07Si9iZ>P^3R!_;J=reWJgRk>nf0NmpF3(SKY_TEb`G0I*Bl2)B8_<{{{ZZI zJ%$xqJG(b3-iO*`u>)}@aI08d@)07{dB)KeoR8+!;hu~f_;rH!d`R|c{K%qUb6TU^LEVaX!-C7-`O-gy*UMumrB0fabfvFey4+u*-(Hj-Y0~Gl`QPah zbx!%J3=kYPcQ6gT)X(aoHfx5=8V%t@6o1Y)OZQrPncUTNHX7i;dT}((x#C^W&;`dJ zA~{w$__fym+q_||5F#yE-OtpWl4efKlFn*!Ag3EPJnWD0ZBALU62-7W9r_fM6rKZT7CU8Hm+Ak2C`<^D?8lR%Wpgb?uZI;y+#lvG4`l_F$uOy|) zTbfFhUk*q~7Iad{?^M*sV|toP zHhdO_O;NBTbk54fweNmlE z!}9n{BI27QcQxu6>Uuu3WHnf-Ll;z{a#QZoZ9Ts0ett7Crj*#dktHJGDsW#K{TwT9 zYrq4+!wX7z|$W*<-ix z$GR^j>rn0E-LmQsi0b^v%_%XspgKdvXeuV*M{$kNycuA6^WJ&F57Evet z8aaFqCo$l$3X|@q$gUTSjY}sK2TwEzj2|4oSU%tZ@DR$m!Hi6LAI}5jG~;h~;i|R@ zi&;kM9@4O%?v{}vQ!S0>KI05F0LqWO_Z2jli&gScW|g)Z;5q(}CdcnAc66{>-RK>#FLzHr69MQ17qCz6BvG#u>R?2V(%_Laugm5sCKJp-k4cqHN+ zvOlgaK?uL+=hZdrE37nT59<(@Ol>B~_DmV|RnP9SqbLkc(T1#VlDoXbLo^4d2Z*Qv zqBe{j$d{~HXdc2zmL(WLnb;0YZT!t4M~EA!#JBUH!`qZDvgPEgm*!f%~I8uYY=6lkU*vQ-Z$~~kZ z3=dHKqIeXqZP-~#uDtF&vW$}@9`;>vxoyffsXc6N8G93#BKmEyR$-$z7Qwlc^cJRu zXj5s&`m=lI?K*;2It~jB_!kExb3#uJaAi9BW9eZWg{1>Km*Q+ju;oXX(Ub~(t;_KF ziL!KduoL?76C9ZGw{lubj2=wW-PfN{ ziWe2kkD4oH;cU}>cs{La2_3nYb(D>DKKTIPeSbx(JZcJBxttFm=08)IR6zzJS-6fK zC=5yTbhd}QUqyVEbD!VYrlDaYHRghTVmTpg4;djU=Hn_toBAd{5JdgWII8V(4t4oq zJ9UWat1$KCR-#crAAP-ZA(5c)EXfms?5K#N5iqEQLvLXctn;7{%huYW^1oyos4 zA+c?ar0I3Czi+sg7DA}5R??{|Y{j1QM_uKE)}G<)QrLpM3O;FtVuw8r@s2Vfs?4Ch zi**!*pLD%x05!K)_Q@=2aV6r0g?oI@FR3VILOaM(+8WUszde$!2v5Lq;P{nuhRRN% zPZQpaz0s{ma}jaS9jj1d;g>-kr*<>NQIipHYh&T&KedrK&gs||$nz%7%h-u4Ao3wD z=|+~YV}&^kcdFg=;TDUIQUg^i>Ug<(BsV;77$e?1s&TX#)YbDrkXgUGOU#UEGv;c4 zthm(IfEsbTAcD>Rri?)a`Uxb&)F|MC&Cg~ zw)*OBGzLZWg7;S1`INSi!hUASY347I)8MP+M=AE{fuA<{zg}c7jjQjDKz9vF5qhc( znmwv_Q@#Sg1&e$EA_|tK=*GKNyQ_3)6Z)01zH?`&N8H<0i934{cp31=rD#F;%~?A- z8MQm{$NjH_<&u{(W|OVHo(e4XX$(kF84u^bUg@k+_}7eUNvP3#HLQiw;zbwko(GR! z*TIueOJdRt^Z@fc5z6#Vd}DLNL_uWCL{%*6j(d|)1jIGja4cyNp|bvI3o&aExktZ; z7d~ePp4;F6e^CRM2Wyp-Z<3-BZ-$lyPy3#LA|E5i+ns75s(4Hr4_6aQ2HRIOL%*Fw z-xo~@%kfA6<n#Lvb`dZ*RSw)4_6DdjN4=V#pzE@Kb<*dB1Uxg;<$1*K6O^&@HNZ z_2QPN5@{+qyj{9I_`|J=Hpr3+j8q&{vc`M~;mD6TSLp#L@xt;;#-^P%;mz_?GOW;Q zD^7m2v6Hu(Se+yXjtdQEQb z7#mj*O1@Ge$N88nc%_A;=~S7@RJ*I)>)7OWiiSFyFP%UQ3Jjgsc4lPU@Iw0vjAnrX z-hhm(CYzsb0n-Zw#~)CjMLr6Vz__b0EuVA#`A^0Skz`j{Mr6BLc2ExAKVil=W@$jF1sns;fvL zcHo$>Er!)1q@?|C@I5{OYr#r<%Q~%P}hgxPE~u^<07)|bh=%lrwzB473Je4dJtzw@BTYZf>ph1XvV`6=A;c*#*5%f z>lfG2m0yW8a`5ubPU~1K?T7^Ce79qk(j`6xz_!}eS&A>F{Mv0sXu3K$miqUeiOxJN zp**$$GKrWrme)n=V+3|=&(hHVfntodUb;eg*`%Xjyp@oSoWq87kA&Z8EpKlswn@uc zf6dN;3y#mXmmP6fy893E@F{Vqs0X&bjx~Ni?7VM(4DcR8rW$&LpLqd6oBeD^YaR&{~b4#q#@)i&^?D0=cFbPSBOt&vRF*N;n{CMog+E>S=mG1ej~P=9crTlqcP5i-()M^sVg!wR zv8;}9Rmk}|rrklHq{uX!h;&QBNzLYeV;|_pCj}#gQvt{{h z>u0_1xi@V=qZf0F)ltavcCN- zi@j*MU2mAWw-`3}I7c=S!h)g1ZPzExH+i~6iTDIOLs#9-#4O;NDX+2ejDQyh#xs>K zpzpgSEl&k2&N2cU*^rorA)VJ*AjP$Z`nQW%wrLj2r2Nzy&Z5}xei)TNIaI9fN^A7j zJPqqOBqS18Iq0k>UIdr)&N8GhSr`pwd_$a{90ul8yM|17^!Ngiwti>W%M-H)kqm3Z z_Du)FN_0HuuoIeuJkvHlN|SSB5W!G!lX?sClG-&b4X&7Td)GKeTvq{cu_qTxjG#1H zl8AeeZMh|pxE7j}>ywXV?GGXrM})hLad%ul_WOLfLsG(|M0*FmQ8+fxIS$cSFq!f{ zDrJo3j<+`5CW)s$el3P|9&_*ZZA+qu4Ylq4y4Vv6%$y6ofFBp)icba_^e*{diC<98 zcXic+Ew*>^AIW&wi^Vbt!vToZ32o$;N~A#+&aA_%>cCq`T?`_pg1hmY;P(cei?z`M zq37dSs%bvY9TuC|lg$mhQHjU7aDJRJ8Astz1wBKGQzLblsgM>A)?gMg{*WNkO!Ar%cKAHB@1xJU_K|7vUWREWIB+iqBAj zJ04(nH{}=p{_-?u&CB8|LI$w58!^9Yt{^?jv(}Ym>psB+XR~-LQp05a(Te-&$P5LT z+8yiWktD^?a)W0A${C-OG6Vd;W0i%3-dX2eH?Y@B;q)f#-D;PDPuNAN3K|OEc1VSz zIQ2R2lJYRR$q2p$Lfk9!_qrTkk?DzE&--0WEwFIT!DD9wY_TBW73^%M9XdKIfHVg& zp6Ci(dV+e;S18XYq|!X?@Cgx7;RMJ9R4aXSezfrtmoKknASjx4OYu)=4`slx z3##extU@~cFIvc#{Cm#XGFUn2ps9}Zco3oZ0TdrRW3iG#(THNpNLKM(fColuT zRM67V)}`ZCgM_e%J4a~&(qE79Nqw%L(0)5`+|C&^c z&+<68%IPs6K7hck{8(997C}0<$a*|X3|c^gAk~OM`)yCp4slWwF!CHMGkW_jN~tNG z1PFSosWq)*h0RF2`|f%1hK3=w@gHmYJZADCa;q}uCyBC0%O#D*Q|AXl_D~krnL8Xp z*+#A=bP7f@yMX9%!p--^rc09gC&o`AT0owZN#Au_tSbk6373ekIKkej`66ZLOqc`i z?5bcnST(N3$G|K#eBluNE@=@nMq9a zW7JCkPjO7E$>ZQv5S{ZWRx$z2dCJKs`YBK;oq9CxZ|gW$ z6bGQu#i!9Kg+B&$j&Typ0`l4s8v<3H1sSMdU^4b^osG6;CUFITyTg2(>$1;ccyE(F zHAD%0Ly@8ss?Z)lyn0mWv5O{bcb(f9TagZqsuALhuOxbpJf^9rl-a?MPB&EM=MV2iWcrq=g~vn^BqOPOAMofwu+#H)#XF$EG9 z<>EWFJrflktq@!q7OCQz?nT$8%Vn0w)wO!RO+0Y^#w&Q8n?AlMb+ESU<#Mnwg5VG5 zCsUcy2_kGRJhgk52d-XbWL8i>k@HQ<<`&-O!ZX(e6L52?V=xLftl1?@c{-$=VmUBn zhK{3w_7Pr$niT|&9IrZ@cDgV*SH`b#mViBPpWwt5D7q}@5$8Xj#t=Kn044!<42e_S?C{zA4JDs*z8`Hj%@3j?GOTI zX<9pB&0D|b;1x?9an~S84|Thlimdgj*4y3ov}c6V6-NFS@wy2@E^DFD1N^2x6#H&8 z$;qKtfg9$Gs>FBDo+C0|%OX3$8)xSxtpllLSG{9W;rAh3FZ*bAu$t55&I3UpVgZVN zFf^~EarLald$}U99}}IzA2|^-G;GkUd9uwvI+=LtWSh^qln=`<;PhvEkw1r;!b}CD zNr2qof{g<}&1PYxBA zI%ld_8%n4uHi9G2iyG3o1dV_0MoJ;*`2Kfj@!R5GuVlixljhx~aLX{5%yO;Nm#`{lx-~p@z~%sy#yPI;OjP zvd3Pj$H9BgpIK|J?`dA+If4(qGV=4kh^i`{C}7v0mAKuOwQL%_scbnND12BUI<`Q@ zL9#@9?=hgU>JJ{0gLi~!s#NzzQpv(*IRR2hq-i-id!+NV1>_oAR6E3^8&`U5Z?Sj! zuSSQBUX|`m(z2LygsdQ zeD?YkE)Mn7g@&s53tZSLZryTej|xkLKB~N@hlE-)es@EqS)VJ*SKfuY*_~R0lu^uH zLH#DLO{7NDQmGM9H5$;rfOBU8oGOf)B( z4R;y7Wj7PQoVe}?gcMHHNd%aJA3@e&pVNULao;NzL|HTvOcIXCP8NTO!7y!)d1S1` zg$W5M0Etj+WUyzS4OB8nar|tuUjWT}=6PnBZoN%0ss7kHMj|}co5-$EYE3y6AG2Ne zzSPPHryA!?;F6LP&vOOGD(Zfzer{C&sckmCFWsHaw!hSGu$?x1rTQ}WLz=6NVzK4< zT-DpJ!w*jiLTZuulf*cIOxf!1`}^{?&BH5OD)O0HC%eATeARs4qh?hjc`g7P|(^89xN>A$7^w8q9|Go zC2_Mn3s9)8eedkEF>L{|U}-lxAxWx8L7M*vhi&#FfeoioTbI^M5n4zf0;$udgi?D4 zGc5VVd{rks1OO=q8I!moF6#mGV!oSIL$?Z%g?Cu5l_e|0*8aRY1)H?Gk+a!-MyjZr z^~smptMkGK@52lj;=*LU=yNkL z&#^U}qME4hV-uR`?cGw!)y&ar%|SX3KL!&b0M0M#Ior>xs_-7koYd2P{8}6%r8~TO zr-VWWgtU)jzRX?3ynRHRtND)Q zINHkrvA@B!#NCpz@@tO=)FhCJV_JIP**hj+-LKLgZ9C@ZS8X|xv)ZjNgRO#!yUCgX zR3PCN2UL@-TOYRAO{-{Ag`{a459Svscb=0>_kdCM1g#n+H-G2;v;3KpXQ)uJh_Pymbc@L&ro^wP#Fz z8EQ7;%pG|!JZgp;kzA2;Xhuz{e-TV`Em+Ls{0b^fur+kxScb*KN?sl4 zJjM`#!iYQY&@BJ>(ol#+@iS1Re=(kdl z=EopWX0Vxn2=C;h7Pq|T=*wHhPL7A+WIegq`x$mFx2|6xp7x+csD%GZr zp)W}vY$O_f4-gy0G#htzBc>|&WL|F&Vg~p4!ORe|AvSJtR8`2P7@y;Ujstp>kPA)U zEp|#O1OCWkOH*HH79Ff59NMq%Y_ zXz}E?Z+?AY@4^N$?p$jxmpyx(A|JRPW5(O75DPR-KftQcfQ9X}&+}0?2lY$e(Fr*! z(n9a=5j=LeV*@UV2OPwLgzjmIq9(Z$QHcuX<7!-E+`gq_-<*{3e6 z`_TyeIB1f7*Us)XDwu=jXLa^j(!r>EnRRPc}c=!^QtWFVjIYoFY(!kGiRIfOo2w zh`kW`JP^9yVoGLtAA%lDsyHuDIy-$&sPEyKC&d!nK5p5)zFe&Bq_8o#k}DPFOekTf z#8te_AYbG$k6ks>=FjmMr7tmwJ?F)8*J8cYm2}j*$@AjHiz2f=>m&Qm5DPTR*!5_n zc&JC9U(=1aOl#Dsf!As%OR>sbxZ;M=`4$&($aH_B>sHth%W+t*RnO@L2nu+C5P%TG zYjr83K$~X`Z|k6kG7Q_Nh-kpa9oM#d*9WUvGQ6#K&7j`1oF|T>HrCL1r)dt7&f4ua z2hj!kO5A}@-(Y`ZokP#!UKba1N#XS+F3*36bqK4qn|8d!68hF^-+J_H@1a768pU$O zd`SS_LieZ5SbLC){bAeq>1s?N0^^2kD={(gHFpt?WX10y^t-KkB{H&a_%2WvGKW-A zpXDo;1wpE@w8Wp%zLV{bjSp5{s2BK!DsBt0BPw&q)!^xgu<21JVz%h7t>iFDFZtUx zxA_hp%|(f!%#Mm{EYW%H-ILcpu7{3#*XvCWNpH+1M-Q~p8qEeq=xV}9+EAW_Yz8nn(A8z>~3evpKC8Ba|&B+fZraBcA&h9 z=4ow1mqSA1`TzANr)9(hjQ z`EePCd37xRP&+%I;Ql0LR7W`I?Nb)4dBm@k479O|rND{}z>d~27GwKO{(j}7EeixD zwx<851OP0|Csb5pG>6DjvEJ?(c|Y1O9eTpTqKLk9G&yOQUu4gQtHI)}+=ywlxs?Hiyu5P>$<>R?Hquk&+bOZUq?bP8If*2~$Px zn)N*rWwQ(W54@M*Z#Jn@zs@{|PS~=VNr}3iYvk!$l6f*|>YZ*s?tt&bC(S$$QR5lk zXr{LbHyq|Tt!y-fPuLT zWTFC?Af>3xa<$gg+7n{H_{ZqEY3?@t8TiUMl+_I^_i;y&!;XE1OXHs1t3`jntV z$Tc=nVIAp9>@2F>m(HO2@fBk|-RQYrP>~oi+2?sCj=|kdvE!L0@*)2VznR4YHm|WhQ}{X`O+M z{-ifVC#Ng3EIvr)|NIw#(U2(}ZOqY0Vjs&RPr<`z6(?|s+VKD(#0WGOf~mVc$gR%_ zW)ICV*Q{>GejLgO1@Vi2R%`SWN2@;2ljG`}n zg26k5KW!7PtT88EFOMIEiB50a{De2|{xCmiZyZ`ZHNF{Qcz}OB=c#F!gN!ZZPG|pW zU9XGm>C zF_e*XQ!PjChnIf2#&6=1BX>?Pb-Z_#9$8?QxH`|8!Pmx`!{V?i;nZ zZ&Q-b#fPHQ8&~#|h<2*^?>V_sYr(=yNv%hUBEHFq^CwTFg3tjZOz_AqhsPZj-Aao< z6caCfkKolPHdzov6-~_pG?i~lxitx2z*o*R&rX9omgbvHwL z3_n=K{#Hr@+gAAcE?=cXRLdphDEDkr(qkV(hQanMP*@p8p;x{(HoaLrY>l36aOUd~ zP+x9I4k2Vz5n);1eT#EIk?Kd{!P;vnBzh6b<@korp?CPaSV{jCVu!7pI5J>TO6No* zVfzEE=RQtn1O73K=Hv4cbVWW81pCf#sUrTN#V;*%ZrS`koEeTZ(zZ9e1(mOX5k3EX z$jKy|$7HtVk;`X*>&UR4gVbZ&kNw(G!pXpP2-M9eb+*YaBXUeC?(-V{_YzQ>VGX?U zK4-Xfuwo&1n*BOIP!$&?%v=@&IVw@X)N%%<@*HETg@?SyZfhbKu8$ z^w}E$x7#tIhL<8sidKS9_X$h4HXk8AK6k?GqK|`PfHk9}7*)rw^ZnCN$x}w^n{_pE z2thhRho%$)zUCd{;K-OkuqXxmt2I<(U8zFYiJsB=kVSFlZ*NeCN_%aAM2q1$B(nXH{0QChZ)Tr5y;O>b;z^ z;oXcVg4V%wfzA~t(R##M zo1~uz013iS#%KWCS)rlDeH|Dw;sfGsSUx;D7O3MSAi+QGeiv1Nf=l&EgQM{)IiG#M z|Mx+06|{&I!c1Y_HM9LxB#KJocPS$=)c+rQZ~4$x6Se&YhvKEhwUpxSZY@xpLeb(a z?(V^%Xeq_L6!+pza4YT_+%-UukeuA_d!FZCIA6}!`7%3e?^&6(=DL0pm_GS{fgunC zyMIw<9?{bkc!$4TTkMPI>f|+ka-R6pisu!SwHbfo|GJ`saljy|M8WUwc#m)~%)4F1 zqxYA(tZaewWYTE#!8C_6-mRm4(SeYauC4yMySTBq<6w5;@s!Ehppv6pQRVYLMxgwe zGqR<3AGoN$kZo20_+@CyAIRhX4+c7Qwb@T}cMEv*Z$24z3iyqJ9ilU9^>2x-Bh1gR zg0xa$TZKJ+i>y95=3RW&R0Ih#>$@UIq5Ji%p8KtaCl{wmdF|WFm6K}5%2%~2OZUtR zDu~m4jKYNvxMWNCCJfYG)$YnufVYnV!7q=O&il9o?S55Ji!I|{2z+@b4gw`435H}h zWsLb^BrIYV1_@qsTQ+vnK0~6VN4($i_i0#BAw~W0k0nbLw#dcIb3Mxr5a`o`(!XNa zzaT-q7p}JF`wlQG^wO(xv61-3s4gy>Yr)L8bormEH5H^7NB!D`jxn%a15g^H{V5xV zTZvl>9Z)$&C-K!rhC;>Xt9e?J(?7^9It*y2w27OM+QxQ&_3Krl!{aKarxECf+~Fv4*M0A zgX;*6Je>8ZnAIL&wuPRrPiV_i3eAj^lZ8!p$9v?E4R26Y=yKH1`$YP=a|2D&e|{f| z$y|jQ<+S2lZ$8agHSMQ-r5$wi=i6 zbdcB13RY3SoPoM0mb*3N-H8v({5wM-NkBe+#8+}m3r_%gQkZ3|$DHI+yQNQu!eQt`Q#tM(OVSmjO z-;-N_SYwMdwdo&yTOiv5vHH)pz^*St7@RT<$W!OOGf1+AR9S=URCLd+9gn*1(O%Q! z$8=Vj3K-M2WPm%epvD=dB$vi>P2)0t>C%lkEP2#(hnvC3pTm&(w*oaiDfB_FKxpXisoyT)eswWg&tagdv;jYG!4TYP1K?n| z>Hqg_Gf+{a{3j2cX<{I1IOR7QF&%yLUwP}LbuUqWJ#Jder z^=5dbnGWuL-7JQ>Ncu3Oc^pZ=&m;s|Act zF}2o!xYvqaCA_0P#=sc^tGg+{Jsv#&PA?8wSkXRM-sGX*0z+*9qx0}u3^1%tp-1yc z?<@S{M-KEkvzLrLJSn)ZMH9*w(7}D-Y>P2q0cM{+FF0lRz3^ToXci-7Pmf`5V{W^- za1~nV9s3tE6b<)bkNi$ML)pvqj^X9x=-MongK-u2^b_7@>7Eyz(CO>fgXNeu?_F5L z2^LcGxd^WkQum*REe_NVh*Fc=WR)uT?m{ePFa?hJ+tB1EuA@c0%h z4HUUMZ$9-)iiU2+&)*`|gNjs+D+K1p&Y)0?CX5db*I^Gqh~d0d)9WJ(WW}qEwimaV zj%xNUODQ}_a`M?S2Z3Y5Hs{$&l&|nCajiEl&6>n_9%O>_6^F1=mx_4(wqP`Am!0~a zC0?j>K_lw+-cu>5_vm1XtKa>sTU~gLqTG?1o}4Wd#JQ8?DBsECR!Sznrnvqu1IeVq zE>T8|;0u>0NWY)s?Z%g{M%o_onjAN^WM?7EHyX>L^RAwzqL1dD7YrM80J&ER2u0s0 z{`SH)B@EeYkPu_iM%lz;ef8t zXwyhVJVyO70?@4nP=Log#A4EaS-tA#Kg(d)Oz5J>K8p)6yMCD%!I4mWa+6bcUuzQUL+votZbr}p@w1VLtA zLnb*ux^${5jdakA{=WkLJ#pc$LpsloP`wrzG{CKNHf0FUBVxWLXb;!GT_b75_nR_& zm@&jK7~G;>ZydbL;_`5vBH(v($o3jFzvvz`zsR!Fyqcn>1hAzAtnHpyDh6!-qqu0L5Ooys-ryI?#zK$1HifS8z;J%37IyYSiFQ6gM*M<z`(l=?;IMNoHsz%wDxhP9=45d`yv=roDfq2w(=C6GMWbO;&$(lViAz z>I#+6=6ypdiXoO`c&Q_C0}-G-Sf6Ac)@`{n>vvo4b7F1D_V!bz%jft2OR*CF=IWa< z)4_>gUfL!=dT+4RyO*!8JoGr?FV5RexQ=JVT~;&iKdsu8;OEpxs~n}wx0KU6Aw>f=A5duK8u9obIN?fF-(+xJ|Y&B z6B@!0Y+HWj_WBoAj6U8?P}4K@M^V2Px938isPdVN+e9@gmTkoq5T+?|2n#llMEZ2^ zsN3Ef1m||5Qdm^p4Fzi!R)UAgLQ^wMhjveEiu7qZ6_x zu`Tp^h)Ew=yF=RAmu_zK=!xl=KFKCaFaE&Qzb}wQ^6%0$H5TDQ$B@$w+Pmp$4 z_wy{*`Rou8h>~Xe{OCDX=57s?>P{DoHM`L@fQ4+u>}^&s8H=6i&tJO&HSZiv-W$uu=lk4q;!pg=-LF8!V}^16>;0s z#^$wf&e5b%d-mPj?{6xI#?Kp5x@L$SpPNLnUDjZ&9Pueq6UVuRAY;4PQU)5ccA8Rn z4GnMj%yMy$CL(RdaHGh>KIEBIIZN%28;ECea%lWE!fvuLN-g~LK)+~APLO+mNzk)J^5>hN7Ley1`&3M}l+vAp?v>nF`$&h$arE**U z7yHRwQ9jyo1&i)Iq}={Oe@`-YEPRLDNZ;89`5;M02&RmEfirb^rM1s1LV~Tn!?L?EnLsJB0 zMclXgzsIvk59PC?Vz;H#j{o1nXSt{Lqv9-^{c!_!!!7Faj0aUD48LExstJg6N{ z;{C?{KU1X$hdAhjIKn(_yj>@g=sm-s2$PK;(%<_Y&1Wu=e|U}H&48Zfj~XG-u%yF$ z*}`D&A|m;1!Pds{dZNqOk~0nG*9apb`|fPs z`46EStq1bNhz!nQ+Li6pa#bAc6N18n(YH8P!OF?pr#x5 z>hu^Rd$qK%yvw0nwFHx({a9rVj<9Pka#v#f}`I#^TjFC%e zgY4++Eqp9t@@Lfeh<+e$wRm|x6d8O1y~>uJ4NNv-XP`&u;|Nx#DaW6<`~HlE`(Uhk zv+(<#q`cxCGo@y{>i`gyqp_QRhl4?3cIZB^epf`)sh9OZLv_L5ENP$=5;%5n_}POC zg!!@7Lvctq_wP3=^%wMx6RYQh zmbZ*4iz)Kh0CNw6-W6oiXdHdZroXnp1M*LJ)qBq_fqvW>9E|oL6BsNYbdL`Lpi*MF zrcAv9<^Qu~AhaO^L0#hgLO*v+6i&n$D9u1`mZ=lF$d2$FQSbz~TW|Ot@7gc=FuA#; zK0IJS43@^Uw&H)uRm-8c0_PUa4@HlQ#Qg4gV!L0&d|IOI>f3<)6%XQdX6{3^ zD0A5vlDZ1xM$IAd#2VUKGar?!@~Ssgmb9wcDK;te=8=wJ{He?ZukyXF4o+RVHt z>u2M)3SE{zaFH?xUAu_@E7*xXpYs@iPug1GCf4)JIX2b{f3 zm#hpD6tta5M@oRDAODU{Slxc(KL29gGgn}#D7`V9{zZmMSJc4t$4CYth_E3$S0mc#ZpM{ zDwRj-k5m-}Y<_%Tj6zCs6WCVO#rAm~SEp`6*54(0t8p6I4DM8;1Geyv{T+Ba%(moG zsL5F`!+yM0O-~4M{WC-UK+e_M^J#OfYy2UxiN^d9J5&I@vuKk`+hBR(!EvsXcwX=< z1{+j|1UsP|3EOXcIb)tGwAL`}k&_@7&NezbIP;TDS6FItta$lq3X|bJl&cf=;XDgF;yrOmYa9kfeZ#l6}I>&JBETY-BYRHy>pySHr=GQ@`w*;tWT>jceCpn>_$ z`r>&jFgpe1&Q@1dJxuDS&1W+&@%Ol`1Q2#J0KgPM5IVhjbOtb=NwZ`=&=gThVk?V= z9!iRRhJr_hib76*je??;C^7ANB2i=Znd|(-K5$SHoA@x;m5>sH`qB52VzSG)?UxkX zg|grbgu$usB=)7<%du3WhSqME4tKxi#pJ`>5rcHS^}id_9jRim{K);hY7&J~n;j(F zK^to(tnG^>ZuXVrG3@qqK8yZG`iY$MthHGp)WtYM6>&c#U0kytR7QTh(7`F-K6r35 zGDPDx!u4fYTJs%^ijdt?3-f)d>>T52ue|oefYo;8Q2^04WGc1m-;DxXm^8XO zc&us9upr#G=3hq9Y)Q#qe*f7s`#`=>CN+7x$oqHS8a~LsodkTD-tD|EoOFF*W%3@a zC!T`tdq$hbnKF$SIUWfVgh$HxCBI`9BTBHF47pW8^Es^>V%uH)_=wc7yyQ7J+%*>N zvy&*;9>T^i{O$7UY?$*)HW6xV1y#p+$h1Yj)Y0!)G!t2Plf!bQLV@E-(`R*W7iUPZ zTHFo;Qwtbl&8R zS5R}IQD963+yZp;c$8FU!Eg8k3U0&xl}NnSqb#i%dPPl=A`jTSPx1buHG{Ch^|W>| zy#$ruFB=WIgI+by{$^5;q2YsY%t4Q2m2ReKdnjx2@85kse_Z+~La95Lbem|9=Xs;? zNX1D0?(WxtT3-BuRqoo6eJznW7|rC-?hy9f8zL52+*S?QAp7}pwFKmj6|g`S2nSK#t+`4UMk^ z-TsOhaosYzXz|iy2j|_3eeAe@cCK?#zG$TXg4ZVS9Ns?GmEVA{=>DuOvjMNRol`J` zXJos2hIzdSGanwTl}V#J z|3HgNvQ&j1+Jn?_{fg1EM8R`O$C<4Ca0U)Ro4cN5Sj)(Y9~?Jde{_TkU8*%qp5>1= zhNRJ3nE37<@*azS|B#dKV5Xl}pR}4SYFgG7y!Yfr`injoL{7A1H?FBjlsO)LFtr5E z$aMp`+c+z?>#WUB!m4vNr#gCk2&n|kO9s~+rph3*!8z{clrp9oh+WHXKd{9yu+>@; zH#h526)VnoB?Zo5S31LX0s1FM4yxX>RPR}}e`0RsmW^zq-QvD&>1Vi4)%AC`KHgWU zNMzS))VX+7avKdKzeQM7Vx=6dBtcou&puj6`g6{4vtipn=jnEmy; zxy}4p2mMzny_AngZs+tEcXy@OR7%AH(SCZ}Ea-&H)oF?>rVQxn}!f++xP1tqiYTT{gc6H7jc5Xu&*x zN-sdQ4p?pb;-gCuin))(+Uc&uR4ij5M(6G41Q--f$mi+Ay>x87@-ce$RmF@Q4&sDD z_Y!RQ$Cjy>bsm?OmyZvJ+27Y5oBu1DFQ@jw>=gh=@LJgQJGkf5KEegQQboX@A6fdk zK^=3Go}9$|FeW2!SGbCeOO}-#TfX_3;as&j+DZ~I+79B<7U&hYg1HIc0KCm+mZ6=K zhR&ljqF=T<4&hTFJI@8zdRPL0aoD#PJ!=hi8_oegehf~J6edqKxDOG`n;#~jiIH>u zJK_24J9vmDh4O>j<82alq=Oj3-3my|>a&)6A`ziX6zNS*L!cgNp?1hqfFB%#v@l-m zNvcnMNUPgg5Ek$`)bx7UpwalcDs}N;xj)DKY3&Q`y0ccfHMXFQ9(@k5v|6^-#(6kL z^1P2OBq+i9fTzC4Qz-FGyT?)g0n(r#uHafnd&?N8Hstd?ZG!9zW1jh7d#2=D3sFiX z>9DZh`ot@u0wzm#&F%*YrvO{Vv)!U3ilZYGUzEtpl&Lrf&b(MGSCIF_u#s5Qr0r&| zdalILWV4a4LQanek?(&cqU~vV5<1BBVnfo75<8P#buT!0`TC+;82wcjp%EkblX<^% zaE4$pRxmd@3VD{(O3U=7`lXZuTE1*>lgv)yQ+}K0iDpeMjH{VUgxk$?&iSo5Y;O-3 zW+<#(@#*I>0YJ_dvj&rVzCg0m4@}fpn)Xrn?xH9F?aw6y=(MEZKyH>$UKe%Yp|GC4nIR%dokeGak#!HlZyA!sBGI>Bx2m!%KjEZt)b9FchxmeQs zH#R73(8CWW3sm+<*)l$)B-w}HnmbXNl+_0Liiow=UV8i>67!%YLI?kbt_20Mb|dLZ zsPsMs$wIUF4DxeD8;WMD3ldght^4%KE{6kMfS$_AQ37al>zVu9jO@5+_v3LzIb$O8 z&LRbY)oD5@|D^Sai|*dk)$y9+n)@MSOY>&OyX?1%r6w;Z;TE$*8w(rkf{MSnr z-gJBKaSHdqMIlp%TfQ9Fp^T?|;48wjy|NN`Vi(M<`tP-{QP zE-|IG2~QNG1$4jG(36o-(nS?;%VN<{5KeGXeo$2Lc`ZY%c9=7OjH$5Tg{jo_P$!=a ziX9JA9SUxwaPHf<{H^qX7>^+&>X?=W_mk`Ss-+L)uOD|VwDuhZV}hC3ARQfS*?D4> z5*Gc*;1lpZ5m@Td&E~WO0uGGE3~J?RAo>Fvi}3-$!r>@91;2jj3j zO{~U%Ox@R!E!*3?{f>QF|F7ULH73S;2$WE^6;+ADnd(*#gM&Lw>(Y6ENFRYrPR5p{ z5xB&4G8kb&6}pHm)eK&JTk)H&SwIvPlqp?0mJq848cO7O`%?4vO`1*q_3|BMKuxfW{Wz*s7xzF;l1(^7!rT?y?UK~ z*Wr+df548mVV{ao@}Djp5j8~rnJ!U*5=|=Lt3gyN2gnguC=rpfP36gpM+;Z~GTe^c z!z6%?_G7`SG&Wz_?1sKlWQtvD4*!7>TjyoS+aOc)VC7Jjn*U|n&*O(=I&NvM+sR+E z|6lAmJ$641k|ny0-td<3e@5QEM3O$=esWYY`5#JuhzJ8cs!Vop>ev4rc`qk}9{j!> ze-d#}*fBx^t%m3zo zStj(B`~M}RhstCV5uwnl%c$`E@5r0~=6^FEx%=V&0_A0hqDhbs*^guMQQ?0^l-?re zSNgN^qvL)wVH!#bkqg`3Y*Ih3M@BnZ+a1Yf+SO8 zpgS$LeSYwyK)ml4$tne3n%m9MtE}@%-yO(C2%D_5A^ zdCY4VFV{G--Q%F=LEjliXryKrNPbY97vGzV7rRv4eVXQB4ExJJ8_(Y1UmSi%EvmqY zv^wFLibXKT9ud(|l$9NKN%I8V|K`+C+{9I$^lt5LNxxH};-QbbcJ@6}q>xqfL-yQg z1-^|k!QTXSpNflp@<{i)IaQ0N6u#S>T>YD&TVpsfE}g89K&!&6PrlUbP1tC=s=f}v zin~gbk9u*m6@?(*(dnQFL>}@VcEeAGa7^y`1{6OJjdbom6Rw$-!3d5w)3~U`2gypf zoz~gi$3K?ua9+eAVH;sFF)=K$7=WoV?SY&A*-5STwL2u%QN?bdfmg6pFELY)w=0jP z-D|euLcLHN{v;Muk}F*mh=qH&c@e0TW7w1*I~0Gjsh4z}_jXslWb#0|`;NeIsY_cr z)twwYb6&Zyjg@qCV>@}S-j-d=1NL^E@AOMT$jMqb?{5yNf2tja(J_HR8w7^+7x^zr zobD0#$NS@~cB-}l`JZtgy`Uze!=)dNkg!o(!2yCm2RC`T8?X$RAEz3Nh{}-3DZTy#a9ETWN7JU1*JG z;YnGP0iw#OMBqPA54!5+S?uvB!}jv zY000@mEFX(T-6|gRKb^oOYeD(VghlqK-@cOu;x;pX1id8U}I4 zn$C8${4tsGc#q{I`FZzL7ia}6);6uI+Ka$HxL+4Or!*7p+ySA-vMSKiqQnI!9bukP z>AovD+~A$#iG!)?UdFj-!4J#S2eItBvq${awsKhub4d}hNS=wp#Uxhs{1%D2i1W}x z4zrL$v1OOU6CYCv3o?QBpc|T-7g!XVmD9(agXRSLhLjHCkUZx_0mzVNWeFP3S*`iA z7^Rp)i+V24rxaU&G zkm)~+G)__^vRw(6RsQ-piDdO}q#>RZ*8_6K@!+p2z9g%u8Zqrn3RlMk5FD+q4@a<8 z9#1_uVd7#JPMpbqe9Wl9llS*3V32H^in-02yP76*w;eq+_Xsb+=rZ{m=awa0kWKmz zuK@kxe$44D6OjGQWwO+|&6m~GV^B}3S_&Hi(Pg5;0FL(JWNk$cHP!6oAJkk#Z1xsa zPDmGKV(2`ilVTSqNO;ax$f6ymqF^qt?l zy^R0ElYx7F@Qmm~69vbs^0p7U9$ADi{9@oiYJ9`q?sqW9u;;7)5-|D-5_dFTO4&SJ zcaXUsvRJ!9w*pwB6S1lFBauTPSUZ`n*VA;U>kbF-xbMd;G4B?a+= zHb*b@7XVB(;*J@(EHq*4w(^o7?k2wDjUXd1NES3C6M8`gVc8(>eA?hmix`8Iq;3Au zdiJ0Fk!xJGDSB3BIz-JT<`?%#U&&^fC-(elT4xkYTsXsbzbQCV?T}%A{A8I?rZM6u z|1r~mf1=9w&t~CA#?c8SJ;(*b=fyX|9f?{4ZBjtb9EEmIOw&=HXkv+#;k-G1BHcOP zXm!qnBIn9>79}9EYKEWXNMHzbaaW%A;o#;-&AE2raO!B*!&9B0iO)5k$!5wE)#liHb^`Td98p!0%>3Ph0f}@E; zrh)&)5y*a~Sv1B2md$LNAP4$mF~QXLa{ZQrZ3If8@7lou=0op84Zbf5_mNipwDva5 zX6YKVnDmX1)4Bq)uZM|2=rIjr;D;_)a4Qy1K#C*d1t?A$oXLnnk|$&>m3Z%;mbme} zcb$@_VflEMpS>TC)_}Y0xs;B#f=!S&!ggvFFDn|P;I-#0ifX|6QJ~UQeqc_JQE?T; zO8R_REyoo|v?^#Go@X$6786mmKBWJ7()|Q2Q;2tkw)e8M)3eIM4g-^5TSO<@hVEZ0 z;mttB`D=NA4Eb-dfVCwdy=PrV#M?!tjV%Nm`p}%<{7F~a@Ay#H@RO!e#fw-Q&qwo7 zwHo;k4oXXs^|X!ZDkVVLCH8STPw*Qg#q}B&FPeykb0X&6eE1+;xCVAeHzI@fqhFQmo8H4simY+Pji(f99ceCggdt~_*v!qAu!(aj6l2u3zwpJtQSM?D31ZK`mSk#AXsd*; z4>%sG)G8B^jcbcOL-B`;L*h&_Ae>;aszTwUM=1E{u^QLFrk^EMp5Kiz7z4b$|Z-utLVIt^^!t!#`I4hG4-5B%+T+&@nn6R)Vet z2|#;29g?X)&wJqA2KrQeVL3_lxjNEs6}b7AnH)6)9liIb+IvkI*Y`Q*R5{Kb3Nq3( zQL-L`u3r@1gC;d-zP|~jvi0iy8868Ya{94Eyz}BI_XZYJay%#bx%nLNym1S z>3pgw*FMM=^E~hLv}^zD9?%zc1eLx<*Zt9_X@(&-`_&NzgCj-$uP7(`1hfE4jIa;h zUq~V!((%CUiXY`c=m;!Bp+pRj#d$1FE8(djIU3=~xa!?H z>9nz(Fsc;0xOGoV?kKAfG?r9&&Q3o6xPQL>^V0idv3-D1vAr`Yp>^_njr3v&w?x(3 zIir*3+5@z7QvVyO7Kk{#@3`J%pUS_#>B>3(mRwR8!aM3H<~m--dpRQSZ@81kUXi!$ zh?bu}%sib;rHl`#Mzb+S*nt14aHY0d@1;U@12Oj5XTqNDV(M-bV(b;TTyS{<#Afck z%KuZ$9*#{?cOA)Y6v(Djt&$bw8Y!Xaj@s;W?jDltGZlK*GORInSU-oEkd_hpG-@24 z>;`)@>K!J>|M=qqT?&g@cwY|sNG5H%pWV8>c`|Z|^WoJyY~V9iVw*UvPk$ho`GK_> zlw66*cOR?0IW+OKsWsIj7jP_$%YN#dEk3y}VomFJJhYAo0Au~mGX2u2uzw_Md9_9- z4Nrd}WY=v^Q_I<_CKq2`Z?X;Z-@arJ-szn_zvVw@*CMZ`bA=f|!5e`I_@O{y=kIF? z#EN^%0dnv@L>huq91RZijG!@_wDX3vmrvKgXZlSI$(Ei1FSf zY?kDdgy-e9%KSv)vCdYvbmM6*bS>`@!p#y}cfng5OXN_8l}-4p%qoE8@Fyc+O|z&Z z!VF`^9n*p_*sb||T)<`EgZ)6TBBPR5t?2E&tuW^6b0xv|mwKEA96Zk*oRk61A3%`t0;v* z@#2Cn`ruLPbk4pjG<}&rjt)blqghcWK$}fKtZmpUe$TwMdVJhFg;p~4n>c>PQrgL7Wew?&xaRS9bu7p=KXaWBR<_e}vm_;jd z&%7SAgS;{XRvGrN?zajM;crar+kb2SwMhH`bQDS)nl{*}Wg_HUofS5jN9zZ}X(ox~ z;f_Fo3~tVTj+}wm30yKzjc8oIa6bj{7`Bj=Z38z8HGcYbi%4UD{jYKvu9GSoh_@ko zJd#_p?_A8&xBp-UNl+?6CE*SJ)0y9F$bG~Hs^UaP0x8XR>WtR!KkBLA z(X|r(0Ucj1_~LVfR@Y(DVR~e@dr8FcPyxte5OCxTSs41?x?gnK9zXD}dbL>ulx0 zR8e=W?6vxdgJ1li0_Ps4Qnjm`sPT^Uu$6%7dR~I0p#)klx{bQ&=Y`+%=UIw`Z5IuL zn=CeFp~yupTa%4Rj$GqhU+mX2Y{8?k3-_NdYdNQtre7?#d?k(}WSjp^zAqJ1Rgk#l zzkd;MAq$16aKD6IYF}xIF{t6&jkA+$4}LF>On$v*yIV9ksh3@{&gC%O<{E|I`>>R^ zMMwo;e7U0%`Q!aJ(#_VAW(V&)*kj=NURt%Q@Rz2@-iT~Tu05dNiI?{fl+Jyzs;9&z zS2iN;5+X4ER`7m&1){b^%YBU}npd+C&ofSC-d|QBteSJ6Kn;?7e@}9l0<=V!-0UZ| zK_?)dedV}=W7%Ty_h^a)qf@Yd+B|YHQBg6GPtIPJ;zu?xZV!a(`6tAhFGogJ$(57a zpXactc0KBe9pJ?V5QE~|T8E$|*tDy!SqFF^UbLbt}n^m!pLVRg;z3Jl>kV0Uqb zc7()YsyG;a8;?-r@yJZ0ynjpU`ipe)gY*Kh6UdDvjB0T~#kH$9i}tg6P={ z*A#fvk>Lc_>Bg^}sa;#F(m$;>?OIBVw#f*)xV5;0NBW*V-A6T}Oq8KNCM=Z)j5HUCRE ztJxh^7PgQ>gWrH3j4A1kkG#lH-Zq5ho1-^|LrZ~iu|j!Nf_<+ zY%OTC!o(%zsdusaM9AXywqE}^=--Em^$4=?4T87~oq%J1g)cv#}5ooH=>F# zEh;aTx>q^-t%JL?z!z}wgG*XyFA{FgT}&k9XO<>TN|$SLX`}U28PbF@CF_Y?XLk6( z$S;RLxIupAv%PK`j(}AoEzjehLF{RA21-RVeW0sP&`Y+FG*jR2Pov&+9!5#C=qUwb+hSs!V(a=i_i#BrAAfxT7^DtWXcYinWxPVY zY^BNuyROiKxL+pfOX@0 zYsL0Qjy9zHlg2xZm|R|WisdvfwmhDC0%;=C6mrgA+^jTC_lV3&*Y17fz1#(DJ*=0g zw{&l`irTVNuC~uUk5^*2UeVePVC+or96(1B?a|Ze0#8ihG!Rmelh3dWVxb~@!ax6| zZmhHYC^S!*l(*-&Kxu;0jJONuR_(P{SBgVE8aLLN8|N>Uyz#3ermy-o%j)7Ie;}{1 z_!Q=}U28l$l!$ai7=XJH(R6jka~TwBqH6|Ko_~T;V7-atv*pvWCyzPIxmFh4ZN%id zH3SjUkp%UZkr_dAQNzE6;^PV&`hl~1_7M@dn9m1DpkRv)m>oXOHX`qdZVFDS!3IBM zD|WN<{(+O>wz&SJ`wM*7r4Z~JvrdRTuiPP$^1m0@U3f-QKoR*ex$#UnYokd$vmSDU zD#T(Hn!8Aoab;BqhBjC0y@bZF^8Z-0f#N9;3DHQi1*yIV?~pgPkj9djhDQP(q)@U= z`1@moR{8F-~Ji zfi?p(m(M^Rj&0wZcbGrMj-fi_{@m5B73ZRI5*NVOB!Ta->yCsyD z(N>0>aadXlvZE+MO0 zWZ(`T2PJu0hMYJHYB1!A9Wb%LJ>dOtQfol4PE4*!(2XdGUlKQ;W(%qFR^J&j5juc+ zD&?)Rb_a~ALJ4`g7N&a;%XjfJ?bbCgW!2Yfi5UKZQB-5J5>&X&>Un4I#nfBDR~P{} z+GqRVn`l?am*n&vnI zN(llCt~*F};(OmI80-~s58z2{f0Pe(RaU1CQ^^j*N^B9Y<2*Y_<@|mml^D$KAtNzK zisIIjJqTp?n?G@x?zEh+@ZPiPyzZG)=e!F5LM)Z z)_n}lfZFDX+ztV*T7w^gm-e;ZJx+6u)EI|VMqxFmhC7=^Rkz$h{{N~J38jaT1b|3&l>YKgsg@DTyszYw*hMz@1 zSZ>*=O~-I5CN5WRz^ev$mrupFD^xk9noGQdqSqeD)-Ry~gpMl-G6->7K)CrsdA1?f zZpmamidFTR8#SbwF)ae3cF$TBsHz*3&Hqa|!z&->g(y3qxP z(r#+fcMgyqo%=9QsL38YO~XQQ;y=xJAR!sGSLMGG z=ga3=i-nn6h<$Q*{-X?aFc0LEh_Hq8QG9QKg1hb?@{5|yuS*!$WrrIGP6$_WyXU8t z37heQX+3i32>K&J_+4UBJrkv_YpHSU&3*ObaP9t5hzNPn3jJ`E@cHmVzYkP^I@fY> zg)U;_*7wKtvH!2D99Q@K8)_m5Pis>(E`Eo{nVU3%n=PvA^X;4yjzX?qcic2%3*Tj~ z?lwxE$td?hjjJT|hk3r!Wpoy;Nz+`($-_)!KC zzaaawBdB6rOFNg6@wOxR5Fvd<>Q(nefiiKqaD`_ta-WFK*Z!~*R1iD!V-2>=A;NCx zok11wsO5X8I12tRDxQnY!akfHkM7u`ut|%|E%LnNg&ghw{;Ij=Cba;$_&pzjB(OHx z(4L^8xu^%GnKvPKDa`{?XIv(oFIKA_CyN7Kut{TXk7*9zFe|EQT+7H&5_;sfxFNka zC^9)-Tkp)6p7>~`3f(vV;6UYWxg6f$#iXsCKKFxhIWJUVwK6^g9$+Oe> zK|2)GgM5Gl{9s~cm~HSUg;ddDX1DZ~Vt=2{w1RLPsl!(ql<2#xX}$7NOPx-;uERaX zr!HNfWDdNH2SJcRb7t{ubGoR{Ah3p%V%$K&t zif6WUWH%;7gr{F*c$=rpfSoa+l+xp|a+@D5_J3Fa_JZg2a^H^7uE$;oP*N^bzw5HX z@l&cgQAX3VobL*Fw7y{u??7?U8q%o^K32#ORykM8*Ye}_%N^d-yvCKt^x2c#X9E(2 zA%vSwU82liVfBt+_uy2bO#S`)da@P@^7DPW+tnt=mwD`SzWL*0wcEzomr&3a?8&)!!b?~^^Bhho=~P{MfGSbF zYpszRA1poa3Xmsm%6*gfuGE32j3G=VB9TrJ+U6)830Zs;(EYi~a`6-PYJ2>q zAuGapitJy`SuSW}v+ATW#GZ5qB8^uQgWERmU)Xl%YBohQgV7y_l^w3ZFLm|Pf7FMS z5bbGjKji4SUZ1fxb}f2mYffr^#H}aIO<81^4E&}Whq-0?FgdEgQsEB{m#_){^v?5V z2jg}pg!;04u0mec+7wHU1_51C+r#|^kXQE_G1!~(mnV_^#-Me@vN z97l71p+NX7zbe!sZ5UKn>;K9f>fkX2yxJ{0U$AvYul6)*_JuQaY5G(O!q>}BD#4A& zPi;P6a^1VVP=v|;?WH@~r1qAH-^7k+cWlahaL{D zZrHvk@aa^hb$wBS5}CZqW_iB%%79hluug@7_?8iH8CLm|VasKn|6*svIjZ0FF!1`O z1%}=l9z?EKT#%X}Z1HE&bc)0uQT|tWBAXrePJ~*c53yi?YhGJ{5km91P*v=Wc>%2g zwN#^ZR67(<{QaYR2^%KKp8`{!giD#HH8nRv; zTfMquOl)C-9JJ#s-KDG!JZ{x!&K6u#g*{iBK87B&bFce0`y+vg>jM?K_sVNVgkZF^ z!vL+|Np7rSHOj=~-Q1rI%!@VN9UXm<9N2Mm!s-NBN$Yj`pcw7GzUTuA)8XL0dx$QV zJL0j?U*hAF(Oi#UZ0)0IQ{A_}$MYuwiI66yPWSu)f41Fx!mc%Ie`lYQn~lRjo%Fpv zf!@!l9PnL`ywlTk(BU3$Y$#O-h8LqNX~%F+NBx#BHx>2=1!k<#0JjTW=w|Pr0M3{h z(M$bk4<)t&MSatwH(?A!`T<+y(3c#Kg`fjwuOdG81h}y~*;r{$)vQ&Y(gg$;MP=Cd zS4KRxSaqG*y;LPP*ar1PfYR-RMO&*hxL=#z_Fg*j2(4{VJ#Pn2r9fi1L-$HxxC2>% z?BOrh`qmo>0dup087%_|rF9ktdlF@xp9dz}*Ce0EttgnLVm`5X>=rV&P2X=m{JUr& zGxlutM^_xt^)VqAJRIYRt1+6@9UMG3*}SDkBUh3jCyQzD*cT8cd8&c*w=ARxUdlPF z6h8eUk;jwpU=~IXQah0Ch6hDp%7^2a$7A7l$D#j-W@&7h)NmPAS^C-%avqB&0@Gk! zV;zpc@H!S-A$hmGZN(4Y(k?e_P?m{S92>oAz!4}sO0Q$y4Z7+3SJi9l1N>`Nv||nQ zfH@?%!#z$T+z<1fu4g%EW6^>O(9R1Gtd(n%7a{bX4~5!Tv2|ng@lbqi>o%y=bE*G2 zUiT8WwRYyy!I=;G^DnA6;!8*v%Z3o3t%IUuN_(xcS4tFQ?Q;~5vYlJuydJ)3Q*6VV~oh9`;FXgl8*SC@ZuWA?Q zu^(Sco;-s)={tr*1Tp*HeF^cGN-_;DqlSXfyFxqyiec{G&~B+;A1G%dCMu{DDV;?6 z3N3&*SW`<8-@f_f#qNI|q>$32L@cUh@84M{yKJXxE%_XzKS`ecfDUFr{`@dqYc`U4 zvdqSXl%{c8nB>aeJnt<*fV1w0yFJl}eTzBs`B^hLTPJ~QVAKXYFp3}=gv?>K=Lc6A z`)@pcor|<=2I5h@YIM4^%Xb9aY;T;3yG;Gd5c@z0&@k(U4hcJW2zTy&_`H|6V$9~e z<-2mWwqrVBFzXUZir4>8uzVsNcnSS($xr{kbVg$$`(^>cX{A74ho zY^GvBtDY?MHXuT241iU0T;vx#xmjWwzoY;3)GspPO%CH2IvnnpK2(cnB>g>R(_dzE zICU)CkE4p$5I@Fw>A@-;fsuRp3;6#q^_5X=c1_p8f(Cb|Sdrpxf#OyuZE>d*C=_>h zio1KEKyiu`cXxMpcL|ofxu3PZwZ8xPF}dcPnb~{h%%;Rcqwf9k-IK4QHy+K%+UBF*T)_=IfNt?U16Qg_fE73zcDsw>zC$1VU*2fuq$Y*&GCiByA~#_8P1 z*S}P|ty0W0Gq%_gID|-J6HsGG1vd<$N0!gKoZ2nZ6)gvPTui=$$#G z_folBu2|&c(9F{w`Pde?$68Q$y+?9e`vQt*!}vZ><0iDmdqSsC5Y|1LfVOF|eSVPL zcLHg>fK1>Q)F95|;&Y^EQm1dYma~=z?#qd4oBHq6!Uky7NW{A1y(ikCCr66DA-PN| z8J&3X6-G50t2sicwL2UxtM4^6E5mxPwC8+Jo9NGbR&wKi*f?cOGU3`MSb!WL0H!WW zU$nB9+@2PwmkAwBbUPK)!fMHV`|?TUG-fcec^ggA_Jt^xueX5P;v%c<*><(Vcx$6F z(bouJ9jV_)%sgW^d&cQlvKZ&>N(%{xTW#FI+4DnPKUiy?HX2^<8?L4yK`@L5#@c1C~0dy%uLIZ~NvT_aQ^H`Azr*bZ%Y7fB*fU0tpO2 z4WnhyIvlJ%t>g#&Ay^!mil zC`=Fjp(lurFN~H+Vs_hvU7k1P9+vj{hwYA*9bPoVul2T;mjB(_Yl$`3;EpYe=XQ8L zGY)y!ASQn!slR$+WNOcco)S){4foq2h2MM+;P&;-4=vUK|0pA$wvQZ*zkpsborZE{ z-UM-*F0iL>2mn}rC|P35X=yrMkqfpBMGMSX8Mos}YIk)CZpjtoPSHJO7lheUIoo9l zOo(wR3>w^VHT{6Bo`e%vINcEDHsR65$E37vyvWx6ZRnWwe2~vW zv(cuCoy)RCc=HZXc~%aT~1Eu9<&1ZB&PvGY|vh;9)chhEU&k1FO5hm3{H^%E~ zn?;u+83OIP+);FpT2PJa2Ed9;`$=i-)J6LXNMX4m1LN%L0nxCx zXdc^g-n~(b+)Q+1OabbctTSB4JE)zs?R&GqBsWzU1l;I?B(RyzWEEMXJ$XNF73^oS zj{nde4aq<|$9#5e@>^@`dA-8u2#*C^ncdYBM^{JMPjzm%L1?e3Swvwqcvo(sZS39> z=?^Un;C(~PR;_QjNUTP8RxcavR}OCK^Xx2jcc?A@ky@M=ll@dJt2sW;tD}lg+Nd_t zUkBR~+=!d?7n68oe~Y;femjhxbZmuaHBoupH;sw9wUpH$QLSOzo_f5Vt!-2qYLMc! zS99t&Emouj&GP<5HRPJ+Od6RVy=|l{`z?_Q9fFE(ko(>B-Guj`y}?ZC^Y=K=<1-K z*Ha;?dybQsN>Uu0bAsyYHFW$H(xFg*wN)E`_4~DD+nX1(nLTi4Nf{(06ZlUh=Nvk+ z$~eVJ(Dqq5Qz8HL^)2i!^4bV2bijffz^PgvpR=dd+$1c=_Sg0jj;2<;vNO0o|Li zdU@3Xm?Onv@p+xSDz8%Tt+o9J1sXtrsj@`7@~VLVo^v#LfO@zUXYNs{GO{T7Z|rcY zgg|5$HLI8&+|&N$xW|&oxvR^hmhovc$%FS^;g+P|KVYgWm)w@fcTK0+*Sl9yLY&OS zNCsZnKa}VN8&a7wU{fxmnFa_5sP6&Z3jb;eVC-sb*g6(0*r|CJ~m&=SFi~2 z@blb5!NK^+Cok9L4TFZ|cU=~4V6a>M=nX-*HJVVVWNy!E)3dO5>43182>F$PW!meu z7}kL`(4QAldNhajOYu$mWSifNCp$#+?FkX`A{2||KZZkMwQ}w7u6Dr|6dG-Lu~I8> zCRZTqha!+o7LfDuH3}Jxiq%u?BKnR9ZdJg$m8qRvCTXZs4X6L@%iP;8^&w1BthV1o za{ZUqSr9(^g}{dm@ZvdY=d;7!Vz^^rd50jV!$Z|#QiOr5oG)#XA)fHUDeS)akt9kA z$vBDu_pXvd%dR-q|g!J7GOL zkl&b}oTDQch()q}Wm=UT6AG>8-EZ_;ZZjBuY-qwP)B@w(0jvBld!UK62SW;-F#j%d z&TMhNX@_7*Do{K2+(;6lzzrRBF^*83n)HMrY+?ez&l{1gbYOzy;oKD#Wfjz+>+|QY zl6TS7-x!Zt>uaiVqr8pg{6GISCBmK?KrJWP-48n?rYj?drrEooyWpusO{e9r+)yW` zolM``7n~iTEf*Y#a}Fb}6$3uhjd`FMG(Cgl=^ZaYXW>6q zvN!nGWuDd4-wt5O-}-v5Yy}#))&n>($KBkhhz*wvW9GtYWQkYJgz}Z&s1{NW{gUu} z`MDCPCPR{kV7%p)Q?U+~N%sD+k73O!CwSR;VUSkL5kpN%P0cU12$Y}<er(JiO8!p{*09X*^dR#9Nac7=Sn}ooF2)y`s9#0 zTD7A-2Ew@f6nCp`t1<+T${x#FqqKQ=EQ0cBRIY3*}t0ZAa>(GllL7@Js4wxfr>t=U&;uY|IzMO^VxXgmd{Jls3xR z5c7kriykc`iy6kfGbS_XxYP`%g;;9QUE9tg0X``F|5vr`8b}L1&QAmBHuv6wv9Vof zdjCOB!&7^;dU7KtwmhaM{X6jcGd>)G2tbako6Rhr){tkp{c3y?PcLAD@jZVx)wJ%| zD))O>($~%lRd`p!4#Kp^ir~Jkypc$9td>eX&bv!-3E%KY*iV9Oky`n6ayMui3+RPDvZ-)jg7(=_}{ zKZ5Wg;D;`U4z(PSlke$pAFVxqRo3htXbxw`QGTYNc+o-*7UzN5+W=b0TL9394gKYAY#I3=OHl4T$O z25svPB;)T8&6e0bD(tTdwIs{=__yY_XyU*2Fwl|PibfzR0&;LZwf30yF8HT#C>EygQF*TDq@ub4}T3r3j-w1Wm&QV}uL$_I}{k=Qs zOcIzcTr|z|0sj*CcYD0VFx=!EnwW^=oH^b%F4j{EYL)y%56*9_Ib+x5ro%G^%5xT@tB zyHrNOL8e7Uhmd)NmAK3L+EqXv>5vwC^5LC&(5Q>dbvgY4T__L3w56k={y{%k@fIcP zxlfv1hvzStej2?RiNR*roam6zG-lW#bClEtkZ5ePe2wyEhpCU$NoXv*_UR;B9l5aE*sI)=6YcsrkpD1= zTe5!{k8ZzMnF2E3#q^)l22_$Cw!zy+pLY7297r}B;+PFKRa17|C`E&PTxSGG{W{E< zx4f{aos{Lb`WVse#lWHB(cM)&Mf3;ONX8tl8kt&)s?n_T5ntG(1MaqpWngHm6ZJyP z^}9+C5tK{h!||m{KgDVd1jO9+u#v0l5HjN5I@BCbb_Tl+9vm1Yj#kuQ!_zt5yri zT{4aG8;q1+ju9iCr?iw9&7@dEvzs_6{b=HYa5T>QfX@M~t=FX)He%pJ6u%Lz{=y+B z-&B&1ppL(#Y?8SKVvbEtK&4JeE?(%*#{VYv#OgdQw=G;sGIs^@Qg zX+W?CJ~%zco7uje``Cu~Hp>@SV>Oc+5mb5Ze@~ktHHJFKizvr(MGI3PJFiw?ukV@l zw^UJc4|F41^oAd-4i>n}hAWdP$dicO9W-Iqph6pKTdP`0_Vi)vLM&&|$-CSxhROQK zPrKNPfz^+@4uWLNoBSd@lAtrt)~D*+C5Qt3k|d)#<*-R_sl^<3P0TuaLT!RO_Gzy% zZqX~+ym+f!R{}NTNl_XeGcaRm`VhADt-};H@NK+*Ht6;JA4w8-U*~7dhb8frH8-w` z%M<04xgu>SN+b?A0{H$Uv=}Hd`=O_{^VOiV%FJOysbSFSr_gbh);6BDo6vT!9W7r9XY1ucbgtt%KR^gJd{G@)DZRH$ z_h*n5bv^igvD!LQrWApG zP&~Z#rUdHFL}7H^ zYiH=)IgCFVKn^qV`z9?r85)`m92J>0(Q2sG$n$bL>%V+v0VC6RV-FDYzwY}$bkJjm zrfwD#!G;>GS?*1&haqz^^a&|#>nRA#Pr0T~5JRU0Fz7<}q%KoHisEwp1GwwN z+<*>UhPIub-HSZwAo9LnEBHZtLWpp%6|=L@MEPMlp^+RiqT)p(t)uzG&zuf#3r zObj4vi^>=O8R1mSes*jRdba$0VDCeTA_)wB;1Ziit(+plm*05@Gb#V>N5*kZXC%q; zcZU!_(tGuD>87s@Ig+MjPHi@v?s8wWXcm!()z0&{UqLmui!G2)xVJqt=*xKhh-?!4 z(BaU5CN!v-gojjTGbG0if;SjnQ0{jQ1YzP$Mb`|@z(0iqk;(c#dPqUl@76YCDcyY^ zPM3G&Hto~#$E>LNkeU0>4%0Y?R z3$Z(_)!rb)XKsS|p;f~pTH{BRcxe9KvD$@H@p=pQb*P(TEun3a(cYHyN_AZ1C%{4X zN3pZ6m4gry7I~v!4P8igV%v=Um9^GbYt{S+xWcEgPBO`ZoS$C_QN~NWaVUZAg@etN4NFPmqy88 zV68_jt!bh@0gRIjtiu>S+fG$WZ{dgbmw}HvY*x)bGEbl4`s|EptWoS~dVHqSyJ8K0 zI-6#cnHj(55E z{J+W!6HKdOz|+?Jb>qCWkWImkKyBM#7^j!BBE$PyfB9n!qNG(En70FGv==Os+OxRZ ztxV?9I7HXc+_LjD?Qr$%8FfmIO->U%Lr6JMR=E_H^r}&348L4Q(F#`?AzF+tKlM;a zfEH@^PB+lthG}>%6-&u{D-Ig=&&IJTm%pVb6JsN6(fT5>j8yh|9wPrGODjM^Lh2wn zrkVA0%@Vowo9RSuM!b$U%rvi6io#CVDir_otD3%G6*Py?zL)|nSDd4AbSIIO;o68xCxP-~{%mVp<{xwe}=i)C3L z|J^m^H`zR_=@}m&3Ye`e<8RCclCOH&5IWUh`$P=_U%n*Ot$)m=B1pD&^Bs@4a%zLb z@+9rDroJ`aM>gL@I|GVu_ewI*5C-J-yWNAyqdM5LR%(?sC!I1)eU&@DW^I%VU}O^i zzTNe($=QA|O5t$B2|4q57fbpfUDloj_oFmzKN62%=~LgGT73_caW5OS8jkza1k+~# z^m6R1_BU1f&}|~zPwG9GnQ!hW+4eCvAFFXEtDtK9as526E-It;s?NOoeio+B-&#m{ zE&7_4a#po!oHmtmhchUuy~(aUMEPzZXOZ?)u*iKOj!E3yvD8eIyME@R z|Fbw|Yopu3xueFJjCha#R5X83RRUvJ__d7r;ibv;S0b*W@ozD#b%eoZJ8HjC@u()) ze0J`C@axriqsi{sKzny~?xop7S+!a%f$%6&a>6iif2X$Z%OM@uH%2q-;ZJoN!;=YR zRoNcVG{dy=H{jaet?F0s*~*@wum@|y%h|K$$nH&Y(82u-n^mE+<)1}I3Kxd1p9DyX zj%I~y&Ou)Tqs5*@w;lwZ`vjW}(Q@+MbVG{WE*weJ z(%zOqtvyH)3Ci>FX!~I`^AM6`Cu#(E;R{|reYS@-@d3J57G{(eKg~zqMx(c#)H#qF zHwpmu^8;#|W813r+wdm>w)R!!%we8c>Fxf{lDG3@IU9R10YRN)@e$L#x1?`>&G{ST zqyCrw_+8t)CNX#0P%5Yd*M01dBI=~lxu=?*u~tc=`$k6AB(LUtyx=oLy8)o64gH4I zckfjA`Qqj~VbDL!D>;6uYzH)XuUOSI7mo%)?a5Sc+b6E}yKdvz3XMsCNB2j38sI|v^HzzW>* zBYqcQGhF4FM}rqd&ECjDd6ZN&B@6Y?@8$Pcq`eW!v_+VYf4Rf3xh0VJ7kQnGVT~L) zls_6hL1tN~63}_9*8>Og%XNy4H<^BF#yr+&`#fvqxXH#x5i=iQB|ZEh!px^g}?|b8&W<_-fKP?gP@?0#ZMA~&*K(< zADm{fVUb(M7Eliq_ENRm4TDf6$I37m%MU}~yMW=6t6z3;84$uF49T9-oMm{Gw5lSQ^uVb zL=rQ7!u2q`@OZL%h+RCZa;X58{Ku=nNf;8OWsN7pobgv8=mV& zi<9aXv@%HrGObfIi6#)?e^OUd^RZ(%B1@eS^=tcHzjNuwDjzUUn`Vqq%pc+dc$R`KefBf+#bipty9rz*XQ}aS6)~(~qSHVV;WA5aj=% z2Uc?`M}t4QgI7b{NYztA5>0WSxk;!XokI8Kw=j1X6;_)c-p@Cm_Br-GVBo!>Qp5~r zDJd>)ALjmDw)sl-l+CE@klWKe9%tgWPvOxgY2ZnH^9H%; z-t@Cunud326@RqC+ZYeLoh<&Su~ab|&PJ$6EyU7i4H{=f88;U{Qh^ffXX2?$6VJc3 zS-`85VX#j=1;KLiOp7&#*_S2qwx3og7J^^TdVc?j#w9O-HhAJvI2qWAq* zEV>Q%2_|8pQ83V_iPN>8Zw4d{h%@VMdeI-4;d0s!m?X_QCa?(QuZ*&mBbq1Ajh}_@uvv=68-oqo`oEn?dH zjN*>4D}lZPJHI<|Iu=;}F3D#PB$%!MX7XLG6@;yVxJnn)(xMicdWjC5^_f^ww=xfmb51 z+OINRAjcXW9|rlSaU5)qhNA;gV$Y#YRAVX0(&xvxK~vmQn=WXs{1)p0zFdrJA}&Ba zwBNTHoNIaVS)_-RyV;XX>dw_d)|!`g_hN^76!vzoq_)XbVm}Rue^CX1&6(l_2RI<$GVok;g??h=$DbSQJ-mN zCUdcLALGuriZZ7?VUd|>?=g2=ZM-|+TGYa~>5??GAgtMo%ypNIg^$zEaGXIrd^gRi zmF;s7Ll^Ew)Yk61Mqn&&pl&{SC$Ve~I_n!n{rGY_T$M@FtmI4Coq-lujkA^1`e)Xh zPC*C#5`mdQrE$boK}Odmx0C!-=o2BLgG#Ta{kO$h~ zz0};#>(J|+eE}>s{VPO2U#MUsWzp}?awimY^rQ$WBNApiodwz$eiB?%pBa()}+Gb7;23m7??CzOX@ z!U91Hz3zp78WFvL0^S+YHkCyi9E? zHtEDL(09kF3IA2{r}%;(>tV*Dt6#>1I(KF+alu5fjRwsn7|8gB9+WR{Y>Cf#%3>_} zp#Lfic8H_;O5(>vMVJ3jT4#F-@TwHb9H&xK;5&+k6Erao9JcSRV>_I22|pjzU@;n!}~K40M@IYHi+7SWnhxsC2jEVWmJq@-860z`;YX; zI@La|`O%GCbA~9w<9)(6U2Vbts=+nOKe0k^*)Q7r-RChf!woU-!8O0#YCc3mVe^Pg zpRN!C3$sNAKF*$)kX-U#AizDe&z+x$gT+dGI=(zEEl|jMRrH}k>hEK0a&sn+OcrT1 zrFWxrf>foMtO6FPGssoxQx?a|hC%yioBeem2NzqC*ohcNnyhhO0ioy2sxYH{-}q%x zp4D$Ep}TS~`q0;=%l{I-XW4%lUoZL&F}J5R*2JQ7Zp%K8TG5)*Q`RH_xx{PWLd0dd z0_-a$0B~##mUjA;H-zyNVXP361IBBEVeg&9eJ)hK^0M0`*!w;DbpytEMq`5sXzTqr zORAvsCLUW6*c3xV$89`GX5%mD&%1qI&{5+a?}a3BP+OTKk3P>(us4@ky=%ggz&zHQ z>5gX(j)JL)-Tw4Cw6}iAyq)b9wT8F-o#!XWiXC&^tN>}Lx`9mNDt|4_+9L*h4&AyT z9T?&pA%B0o)!?g%7g2qi??kvc5_Gt7`+0jmEegQLNP?jo;9U|S$Zh5S5UBnUwe0HP z#!Ms?grZrU(m#->jsMXb0?#pD(J;CU2@1v5xtxlgg>M@vmiU+oy_>_)%c62^BQv-TCMj9%ZC%Q=bLDS6$vw#T?8uWK7vOX<2;zts#W-XuJdr$qK%D-y9 z04q-u$+Ow>9Zrm8>=4J0?O6X9VMr<-?>XrE7(}2gElJ{lYrF?@v?&msT^%V_N&d2` zOPW&9JgvrkSF3Vg_maCHWtLtLSpHXK5m#wdk1bsAFou0tJ{`xA92 zN^(6qXS9V7kn&^HlQ$Rb-iw+35=;`F1xeu?2Fp~g#zjbq0ZFONR` zrqZs4zQ>Vj6OfsU71Pq&MJE{D+Ils|W2IlyH+e}s06RsFC}2+iF& zoDv5ocG)7T>sGnq`Sx8m@#7s2CQv_iU1F_h!AJlmL%cbbQ2sNrgp}7vln#GH8S}lZ zVO{?wC7?2$3*B{quv(DkHjw>SsAQDj-h%(bm~_y;ZCm(4bixJ%{rR6#0$n_|z_WM0 zavYJb(2)dL;YKt4Yd*5)Mg%@}`6z17%X)bM{GuqQOHOdj`fE>&0gDEOHUA{)|ES!z z75^BrfRI0AwFEpzP0Vqg41QNcK}G<31lnl7-f)CIC7GS+hvZ7*30_ zeY4D8fRvOvI5JL*c`4i8LlC-@gK@(r_N5%B;_VQbhl5pNg}_as=To*l`PPh*!_UOQSv-_UjdhqR9)uRf5jn*q(J0eXZRz<)x~sLxrv_3pb_tcY^*Li zxJK()uqf7RShMKxR%yUMd{?B;J>jEFl#MAncbE>tpK2uqywozDj_f2EsvGSg<%Z;$L%Aj z$zrQTT=tjTfLE0jubFZV;r7ni%3$(He$wq2~g?ohW|Ny?m>t5p5Vc6<8}5$ z_^;^ZKWr*TiVr!ZLvx6_pxdzj%=5FX!K4;@{Lvf8NIlbfgU@%xp)H#f^EITGkin;P zkPW95aRvYrgY#$$D#y^skit{NLRJr?#`iz-333hEZYp>8T=~#Y(dX@^{lhl89 z(IhRt!^A#V0!l)+nY}T>i3Y<;-QO=c6)L~?1#zWmRdhJ&E-M^WYP*!9g$JmFfG&ad za;F7BzE?(Zy2D9C-^s(U%K+?&j+ZzwHuyPN$cY^kzUIKwv&v&2UCAB$>ZRUnI3Zn)F2GoJJ)2y+vWBy z10pB2#d|QZSh+r6js*SKBqWsUms0ytOo$-X_D8@lu{n_pxcPRb zT_Bz6<#gfC{Ts}V0D*iYl^vr+(LpU(ZB&vQz^$s*Q?u=-(IjX6@t2HMsm_;NHO3UT zg%9bwP-CK~j$=wTm>i$JQi1TK8Z=3MKhdofkpS~M#=r~Wdkxo4Q_7#GkN^6?-^)Q* z)&I*Jn$_kLU+!Mob2ch#ISy-2P#k1H0i<@rtD9|kBu=NQr$7v-l2umqGByX0(D>!O z?*Fo|xBSJQ zEsB&Sh#^HrCaFKS@*|Sz0wjde1&AW#nON@L^O<-xlcq~|qRO8{T9 z;XEx~+X_aBo*3HOvQxT~9(mLaE3>Ms(2At`%VUb)#F9+)xThh8>@GQK)Xe83_h7m>bSy8OSJjz85)0u8=s$ zuwXZWN;g8^*BNdh(qaV25e?gmc4g_J?n|A@XO+e`swAG=U*C_XGIk&jTuytJ!@{$J zo}GqE%GEbz9ipkOTX9T(Qdcr04Mp zHzp}ZFxl|*z7@ewKmHM6(5GydCB86tj56ry~-dW8xUCjZoJ6{ANvJ< z_$NCan&*VFEcBwU|sq>A=MQF<=>x>5Xo`9Z891>Ca?I401F*? z(^7K=pwR8pW$4&>gX&7~El<+Hnldr5qn#A|Ivf3WonbD^^aGq`9Z3VCQS^6ugM-REJ|f0ZDEWLe=j3}J#x zXY#8dYLUCVd-$`>F^g_~oALB62ZONaTQS-A=!E7v zpVO`f4VxhnaOqAw+Tr8H?ai0lTpGkOSOJE1B`;YHYW;EF)6iH%`lCggGYt{(N2Jc{ z!hk3TTKwP4#oh!oczg6JJV+a#F0$nevBY?^+CO5WlyJ7jm-_m5m~ZJ1L}C%%1zE5B z@h=$=;4=QtN~EIYZ~bf3=w- zmaqnYTc{gLbtFgaslQVLu&c#?@;dLj80r+fUuSurlTYq?cXNw~{*h2zV!_`a1~~4O z((nmEUG@a1j^w?s>xRvD$(V&fa)OF%Bm3Lrjiu1`drgY_UwS`r)@ltK%n~DD zP6zyIZ|6K_)AtD~rG1L{u+hy(322}s_vI`?^ES4d;}W{xg7b(5|^W zv_68av(@r$cD55$VmS6t_vCA#ZWG_$2|SF4x^hJ!s`RIyXOq*v;qa}C5(iCSzKI}@ zTvA4&amhN;y@9Rfbagz&wr$J?HJ1X#;Hruw1X;i5nO064>_LG_*GuyP6c|RyccasM=L#Hr2T&>^(cB zsIKP5LF@Rp7yi{(9G9=Y$@?24Aw10{kF?&tHDc5$Q9S@+sN)_vFo95W3a72luYL9R zvsHxl5vJsq5WwCRLUsOwuUyX!p(M?RJE#0@pPA>-eKUDb6P<@=ETS2PjNPCse;`i5vuyO!M0F*^Li9hp}|V? zzp{VA3WQP9ACYRJxX=%>P*EfHGuy(zZP+3yr}hgMDcfNL6g0aORglN%&FZxRfk|gkM;3W{Rx8 z-ZMtxSs9cCKxY6n9rM}rpi6J}^ID_w657OhMNP5)%tghWB@yyXviSN7`B<+=JUIEF zY+`ExP?Zxu4zuPSZs$H$mwfs3oA4*YkxF|2Dw6p`HY}&Bjmj~`^Qa&|Z8+mZ3;t1W zoN*6m@j|wP%TloYk6R8=b1qv)$lmVs{zT%BB~UX40VTHSk&a`!Wsq=mhnRUUDZ3-& zsD+YL_de#9`W(G%?DpR7a>8W4;V?Q<}ra4E+uAK6LJMAj`` z4++WMI@$r^E*kJS1n$U**)(STcpzs76y9jene&F@kNSzBu&&F=MJ`o#`u67cy^*&g`3%ZelqNid3!~5ay?QzQgTVc?RI4 z7}23=bMfd9S*#66sc!*MmDC%2+iHIP#Ob$j1{Us}Pfate>*^{%Xb^-)1O85+zrfQ` z?Rm90InHG8X3t6Vh)2%Qfdgd(cqL|_je}ei=qaVR8;Gp&_Cjm(JoF%0CNB`?jU=n#>Sa^ZP`y%r)qPd?D zi++syU$)-@wa`_t5iaZfIv%swQ3a}m5}UJM=pCCqi5lh3 zy4T0&FMzuFX5UEw&DMOjG|0ZueI}g1ig6OtqY!GmXUYMe2!DmXh@H-%S>I?Rh0 zpGDq6ta#4vd4=<3*SbV1oL%ZYB|hu3_E>X~_%*CiSWIB(D212A-{Kf2Pdq6y>hDN$ z?6Kwb%Z%`%qdxtN?X+;^-%ySeB<<0pX)^e0HH(m(df46X5J*@jFW3G^8>!yI$> zW5S@v^b)D^ewigMbgzj&^b^R(c9SJD8GWm_k$3#B`DK4t@t<;x)K&~f^XrNbT97u)@b#ca}{?)`#V4N(pqZHb0$Q4S@<9^rdPdXCq{tjz5 zmyb$>%kW`3v@}HbR4>5;{*u?kQid+hJ47_O39>QNi zbmpEt$rMg3H1BSZp47#ruQw86T0q*%OE$p;obd`(RHXFYVCzacS0w$13W1 zPWd1x4^EsuRU%0I{m%=V;SQ*|KG;MY8_t|q;{3bwu*@GVFNm}aNbcyruFM#bNqfFp zaRy~1etZ2M7YgR8zPZqk_iI><)-iNR|3p-xuNGzkDLh3Hf9soQG&*sK_Zm4a-)O;= zeDm5P?N&?d^HMkZiy*qBY9f{VcGaS}n}IUQWT=rqEn0+kp&FusS$E6~Og;+1w3$_i z$Q@8DF#p(rY?iT?+Z&`GF6Q6Yn6G+>CAMRbLT0V2?rK;+4B&)`E|{_!yNVJ4DjF)` z+o3uT;k49K=IM>L(EU#Bw;~%I4^qL+u=KtEw+p~^R^frb>04usu2g6p)|mQdso4KW z2c~+p_b6XiBa2k{Y63v(gJ*uqI>^S{Ac$RPPP2pT6Xn}03{q*ScjXU95~9IjFdy_w zg|85x1)b})Fo!WFFaf9>^CiDD7vNT;_J{oxIasm9;MbH!YOu|*qNx!u_5RNNLWIw zdTSS}K~+OE=5^N@)$s>jUf3;0?QU&)VjgwRb0aH%0G5ATd?qDD5hqc9gOG;~s&Vau z^)$bZJ*+RdhJ4M{R;VT{*(IkUD^!7VDu2@KEupALVyk0fMfMLJKrO;|s7hieN-AFv z!Kyz7qc-tP{m%y4VY&TKnDlZDJKwBxUF&vW)yII3R;8W(a-3Z(z39UZO3uML3n0~A zZbw;d$@3)i!7n@}5C7T%<< zUS~CKDnd$Wn?M7El-t0Y#_4#?D*Pxb%?CxXL}l}+YxBBjZ_k8BEU%03Q4r6bkcYmD z*$8g8)+qJGy5HFwzgYEqaGUR+%x<6*yxV>I9?3gPS5SeE7G7r~ch6-92qM#}K;sJg zr=Ny5h(-7CWq+2a#A4%BjvnlbAsrctfmU+*P0YVW*sB2zqguEx!@WJ|ZX)>AP#v&M zR}-wBio<$dI^Jc0!VsDJskl6f4l~P2xV|Jj1sK^V^tO^mwKOdEE{0LyT8)B+On&Mu zP1}PhAYyiUa-au{fkQ-`dN>hcv~9JNq5@a>`*QXfkP|w82e*<>4{qh9Fc#fPlRGD| zb@cJ^P$$J~;b$V$kqQj?U4Kgota|&nLs_95w>(M;(#V>#YS+tIuolp8Hjw5xP-NVN zXZHZ+L9fFxi*_}sm8(J=NlQD)Bt^LjR(nOU)UisYvO);j(1o_SzcY z#hh+_5}FExC^Xo{S^A?6z5qo1W$9y6MFjbVlej((~sM8|&f5$88o@JH`>HktYShY5aw^t#p7tX9x( zhdU>fe)ph&SRf5~M594)ypj1Vd@{|)oFSgZV@0`xLtW?Sfk~}z?C4tPN{K5U4i{^& z=7?xmuXkigy2Bv)Ec)h=?L%Q8mcuPIKX*k@qD}rqsB3lIKLK0Xos`K>PE4of*ueSEObu;(Kyq*@Z45nTR`!cy`>RLy@fkKE}_v=x8{P z@R3@7v?OPay^w&979$(TCx~Iu6&WKprF4L}nLUT;UHD~Mo0Op}!2C7`vYXaj0D-Wc zhL6lvX{Vl0htI9x3CjR`)%oT}YkF4h=B#YO-3y z@n9Br+wUsl6o+u^DV{ro%NZBF#0|sig>8~AZv>qB_L|?pLZd(Ghq;pAKzK)JEg8C&pe6(^S)V&YS zE89@R1}?!7>=;^oBR6=?A2Dux0n}BbRZ-u@IwKvzpu3?NFG??>T?)Cy2i0QKS6)1q zMJk0ge|gF|!sMZnan(ceM%2$d@YR<=xg%Kz`Hu&ejCk2TtvPVr|4A>6MK zBu3wz9wSfyW@(<*n~84*qTUyI^d%QlED7oR=`?(!+gckz6NEOU7hrhguqN_Pk{3-l zpU4NrMCe3|v8nz3xW)*i7Q2qf;zN%Tq66Eo^oq`&X~3A>5W>3mg(wVdPz<|%-$Q$HYE?{2~B9Z2ia*$$LpY? z+XIuI`{F^)@&R_Px8`wZ2G-z>f!3Jg0;gp$V#z&xRmUU;BKyf+*83)GW0=!~DM{<` zeKj^*6-H>DfKL0mff{_YAKf&`oQ$hn9rY3YH!}T_DsOQM>i+?}KtsO|_0J(V?EQ<8 z&NH)d2{$N#wWT=q5s5)6us|2nJ!MYLcw}o(9z1ZwZ9H<^K!!mu*F|sRR6$Ntg5@|8 z=l!W%P3JjHg_?$Pykb1ft0r?!!yHPmH0L@F4ZL*)9tBW=k8>aV);9NvNA}1GfpZ_Y z&^@vDn0xQY`R+9{UzLi~jZzxBvJs_)5jXoBDNQjhi~SJ{eE&c%>l2b z-n;hTMbIFW_Tpj5(eeQ-mEVpBlPuF^yuIF zV!zoBjX&vDP9GcO3qmHDopy_-UWi7(VlZl5tAV-(sxD?dt%aOJ4cxQuAWpQKyPNR<+59e-KJ;mL!a*DhCiT%vEb*%=X2G|a6-5}J! zr+4GiGVM+ArPj*?6-3{5XqLkm(``F(GWI@5pL7mv9Ju5W9V2>K#&P}}=^75c4BbZt zeG6PSW3pelT0&_ui4_d-JK@YdWXMnuq#U=P_*b%?6*%Tf&sbTq2)|tIV1QeL5Q# znv6H{`pM5dDaN1Zx3Ayl);+yHeQ(GHwDwA5(YzD+vV2&ETo%hv!p!Kj`?t40g<*e= z=8>l2bKR0j?!DJzG(gG$T+Oe>mwcT2v)3$czY2gVT9;~ok4@_ap$53K+-EQipzpK3 z-}DtGK7MT7@$ivjZrmtf0WJ*ygD=HQhyk&TGozj^stdTfpw9ZJoNk)SdB`(!!o$iF z3Y!~qDKCo{u2dnAAuprGEpNhNJXEZvf{jK;U~w+FzB}){V}rYM^C4tfx%b?) z$xj8;$^jgO6`vcVN7+KaW=5KvE{S{oe|X~(>G4;2S-u^@DjUlWzEj4Hbbode4i9Qw zs{x|{J|?Xjq#EG<@>*vcJAK-%KUUZu0J5`sQMY{DXt!>cH%`Z%d# zZ<>Es8l7{eU>b%mL-#5~AN^bA;q*VSRU@N*Rgr=*3rm^l=NI96DW1fGAxJ7%TLBw7 zrSIf}_uoFf3q-@rp%hR0BL=PT%`&$4|Jw*u2v{gtG!7&{m|u zK##p}r2B&BmA z(S7Lto$i5MTy&?*0j$Ymb-*xjaA`goC(K@mCh?qx&vq#Aa(}SN`yxJ;wj^*9y}|R*fZBKDh3$!=S?S` z4@>Vw8v!-rj#@)1C*U?2Qd@~-VQVOFMi0KPPH2^PQVDjDD^`riRmu>2yzyDn`wbG_D|?ZR zi3`vC9y5OnE0uvb;rh_`c6K!Wz!dk+&u=jA2S7Ft4I947zvwHz>L8bq@pvIG@U`U@ zU!x>`Yuz2~c&L*YAn59~bZe&_4k(r1JuNnRlynIF|5oz2Xt0%5)RLpK3|3BFEwa^ z1!yh2q~AKMkXIs4#E= z{i@O&Faxh;R6i-J*TG#o4k9p2o!gTYcaSF65i)AWd zrf2db@sMvVuZpi2|Icj%pm&aY1ia(3pksb=fgbs!A~qQKs8i5TABlXSJdtnU-*IHh z&_vz=7UNZ3sf^A|9XCoE0l;Y8d8`3Gime;u8n}D!fnsG^Gm@-~a7Xp(Hv^wku%U92 zN(PGK6J#p1n-^jzznAV7&}fgSs$4r%f=T5W@>e-}w+fy54`72IG2#IC#of8_ zko(&o?1|4SpWC!S0ooKEP)Q`w_Y^a z-HPde)}6l^;3L|)L9T(jcl*OX)Uc~xtL(Jmks1JnQa@Hi0T$z-a>*;*7zcjD&BElJ zrvY~PbnFVq)%?A1*UiE3zslOcx0Y8S?*yk;d`u;avTI{Re2GIm#$h`AkNahf zNTSPJ*BXz{iFuCfeY6Z?*yy@s;K;k_-dWy!zHGckgM2di9@urn-Sn|1NO?x?-Cx?` z_MgDs`l1J0_K+v@O)g}ova0TC4F|(V^a7foi>NyAXbRWvm*%H(#L~X_4Bb>4(~@jsy=8%*RRT8 zR+Vd2f!NREv|_qtQTNjT*Uv31{-b=MtWnlH9&t>iiV8O|?$Cvgr{z*PqmS%jnE5p1 zHJp3@R6(kPpoql4hx;yuCFngda^TVMY^A*Oyh1$En&&IeD>Lwkcn$aeft^R(jUVeD z;}1#2-Tv4C$LWCb+8m&0S)j&5MkFQihG(tPm(fKmyS)!-UdcP!WuxHMizi_^pzmMs zh(g+*A*lgArmY(S8n|m0*8%t!e;PLOSTnL768UdGvNoh)Cd$ScvB|QshsNb-fGzTNglw_Jk)1p ztKs0^xpf#X0HmJivNES}rWY=&SBB|w7~_#PJZpJg(ZE&om9fNgTDbq|NJXGC`RqS& z%DwrM8ytJ>{q5fIsm(H?P{|mIjj~!;Rc=T?*`jWO9!iEfK7S1~QCES7#$(xYx{xou zOg_dx@`iRQ0IE6f93Pj~4FL_T+k(YsxLY*%+~SvwfKP1R=e8dAQv=bk14qJq$t8S- z2A-7_pAx=A@teCC1-Ojs1-Q+r8iKrnm$;V0`&H!`@`YD}sjTXYe3?OKg{k?{ z&&!~+!GCbiQU3`T^Bb;?I>_~)Fzd7YHlEXnJ~RC)Obk_^rw3YpH5mAKkATd3k>nNf z5T3k;;R*(otM+fa?J2imKR(?X0JmY^3HPCI?ylYqeP*I=QhB0$@!5k=O*C4T+*f?n zMa&Dl7^Vk5xqxfy2cKuqUC=M50zUHN^E`w0_q@>nAEVX{0S)ksavnb`X965KAusfA zUOv|?hXLgX+&T@VWKUiSE`*+Npp6d|U<{Yd$Ki8zLIGYv(~XZOlk_ z@l>v|*UAJzyu5qSK=|K}?R0nVI_y624Au*1#nY3|$|c9iVR<~_rro)4)p4sL+8;@Kc=v47fG$HT|*7x!y_!-s-$WLmqG+`H8u*U9d#jpJA^7J{GMT zA{vk#=u9PIPy6GPz}r^Okc#A}Jq3^zi=nA1kPL2dVi=WzRI0v`(TZJsR2a5m%6spN zu||PI{7p{MXChtlGWlrbr1?s`3t6*@R4de)Z~2zOtZ?mxte?T|Z?XeiQ(=Y+-0Bcz zs`Sv%dyht!_m2d)@@FSEPB9+vHBU>I@D0yKgoQlpJ<7uG_}9(ol@DysA5uiey#D0t zou&eS%5u`nWG%i!`Ewa{=HJ88!rR(=)C~<~%JO<(%Q1K9?9n1?`!oDCz}K#ILqr1` z594qkotI9Po?tu5^^SH#wn!IjV z8hZEz{mKYI%)cO`;Un@>W_yvRjY~L`C-TkWQm(8LCL7S&E7#ZU>-RIa;qPEH>~j#G z54{)Ev4*XVt+9zcU*PKfDbXl>%@28G_r%^G4GaIs+voT8f7#?d^#~Tso@MueJFrdg zzLUvwByvJNYvjHD%yVgVr(w#jx>G;blj5ph?Dd;`NUyloujO^xUA=g$Ob4806}H)( z0}b%?Y26Ugz&b4a(@OXi3nsb${qQbX6sWL^r;l}GN8`5I3Ii{^t=Ow@RXNov99BGZ zS^=qVE8G}16%8wwkoPUin%4o`ybO(lNMFdOn0Lin@-TV%JRvUROfIrEu$s}J3@lqB zOPbGZkDLed|Elk(2k5{Wids16Ezw=7H+>F-k317b&*q-U{UN&ymfRD)Pt30-i-?{3E6UM1MvPxvWJ)O}!WzhHG>zcXss$O_ta7tH!(EdEIRHkvGqE zOQvIiZtI4%2KczPZU|}M_Bg-qD~zp2Pq>d@f!eX>o~?{LDi7Q1$cN{pB4L?p6$)U{ z0BE>SG1-cT6;)0{zJOgeYnWKT)Ll}Kfy{Rwqs*bX!0cmT`e@4q(H4pXsLZh_mgDsB@{>Yo> zFo)I+V-4`pY~2vjz=L~^xMwgGpa%YJ7tL@hri^y`um~`z3|J<8q1Cq)C&Pr>3L{pq zwz48l50~;U_}9;`F9y7Yz5k*i0T?{uMG6YGB{w8BS(uNNt5gQU5_u3w7&D_%*)5DXd0x)lxe2?yoi9$yUs7KNOYRBp z4{77Y`_XXa&z#U0++H;bob>wN{*PM*%^li*^jY^Sw{Ne%XSqHoanx4`%X4m@Nu!rY zU!cqRkhd}FHf#<2|9{VZx9z~GVw8m6CD9!>EY-@Kv%E6^qv*uUrbv+xq(mO z$lvCn;e;=6vN-qszx|2xWC5TSA3C6FYx;`12}Y@8sH4sUeNGdNSm2>?y&vjFx*!xi z!1C)h^!nfa$-!g%A*Q&GetW;W^GPh$&C8L$Lt1|IMWlP`_5pX_GYwx$ggj+~pr34~0*u;*RgkY->xP&HZr^}IY4mU)tu&i_TX{5wGo#B2 zsa9EK6$*5gSID{1*mCrg`wr&MZ&_Y_4RATu1#o^b@|Vhw%=8L@gn2=pL_W}#${%?K z-tek?OgPd8!xVVm{^Z~oR;Jiz#fiRx4oU`I^a4p#Us1n-Mh#i+D?a^5W5Nsqo?O7y zX)amAB0Q?+fJ6Ro8-&ImMG^P5k8dkX1t6(ZehUlq)~IW^GVUCr&-4}Ik-Vn>@IUt- z7rC1*n^=~~(6>_oMj68_$k(cMLr?>31V}|}D-VY0a#(KA^EfM8&?FbxWUyHAOJyAL z(J=i`@$W^?`M-aAm;0mpcDs+`&=l$MBbn}}E}ot-2CybiRnhx~0&#F9c?Fm)D?qif zTN)a$QeHvsNJ}>0d;8;O|8p`H;hedmj*W3IH3~$ZkXZF>??(vd&ymD|-^sx1vlRWT zcl^=s4(`U;hCL_U2k+cle;(!LLZ>WGcJ*Z7g|yqB!hML(H`99Qe*HD=R6s9PHYCvb z+O}>8YT&Ldn48v@Jn0RiVyVTwK}#xPl-b6GmsaBHlfQ=P7mX+3dLaO&)5eW*?|s2c zck4vinoG7pXp>X5b{ooixJ7>s6jlxgRFK%)raN>O>>*4(^{1d~UbPaR=f^ z;J&aAn&k7(@eKkfhS9HOld?3)D^cz&Z?1TVOBFlar5%t$Ubn978S7m4-67Rf)1uRVA&8FxRlaqYT9+ zp8`!vFYab}`S2sAk8_)L>~d$$o#xJ*$K!t?eM9-!sF&o$FnIzUtyoNdT&YsQ=kf}9 zM83*DwK4sVrHigJifd89%l8v-i{DL(7u+<&Iem)XN)hui1z$x&9;3)Cd32Mz?&pQ& ziWH+V_prVn@hf?vY!Ka|Obh;GhMfu+R{88+=B(OgMkxYId-OB_UVs%iG@d`k)b#`F zF!k6_kr9w-&k&!{#i`lY2!k)b5b_ZvkoX&ThjeLpp!w>(>)c!C?d#t4is`Y8F|T67 zLpHIPO%!~~@`d+_eBq~ClnvcUIpaGeG=cZxZtMU6nnt-Ob_s68P_PTQsXQ7mFVG0x z5YRBcQDhckDPZ-wZP~XsY-JC!8iazcDw>=Mc$X~&RN*vQ;#rN&j8X({eGEs`qCyJ} zXwO+w#=0Xg?VffDv~!RT!;9k!*t8QrC5(-GQOQT1h;QXi6(}2jRRn4y?7O&^y=TLA znfm84K&ITm8u7)^yG+q5z4B)n)Xlh}qRIJ_GN&|=XHrhmNqE8^c`aSDq5G!)0fEOg z;3wEAJ|&o!jow902~C--B%hFnep^M^FE#)Ss-m;)!cN#jv-DP30J zs0f3*P^o+Sx4X~Z{X)L+$kCwDiOLdg&9B(-Fgx)6@#7ohdGNU{?r!{2+DWq}Mn#|J zQ!HQL9r=cQbN)b+d#@Jdld@*-o$rA7@)pBx{aqJM{{tegJOY+w3SQ-|c1|uzr+gAy z)2m&ii+LwBg>RHeyg@!u32NBLw-?{n@A$Mf>ANNFh!?1fxX|B;DWgxr+T>+R0c`>` z!joACV1x^(s(80?79dsDX=QHZPH3i2-pgHa3V%TmjgCIC(LtE@BNoiuxbd*`v(`7ike{yE;Ep_4FXkUT{iEn9;P8m0wpR=#q#@K4@7mtQE? zt$X0t%gSnKHO!vFsxnEj6Mx-msdqPITi43TMDRvX~ajf`kEQV2;90VT?W95Xl0K| zjtxtAw1K9rVX&v;VxCFk#>B?eH?o~i&vUB(6Te*NzOmvZcf^db?yRHsaqm2aM*uay zCgn@Z)!+t=z6;(>6@b`eR|u2$*Yva%53#izCAQ@2+={iE-D>=-Ygd{aRsp-BJd*E5 zzM-<2f+C*?CU7q(clt90-zZPYw=RZD0SkXvm}%?e{ff92{lf-Sx!mT@o#-xkLw3p6 zuw`$IhYbNXk!D6Q0xO=`gkP-N0wsw`nm)0i#><6Lnh?QR4kwPQNEz4^W_W0M%mNw*cnhI9~;3Er5-U*Y_hyOCXF=wVVSNqDPHO( z7!l(p?g$w0EGYO)xo4yIRGxIVZPIeIO*FC+@L_-nSD7Pp_`3{gr zS$Pn);0&G$7&{b&FdlmV0qX$l0nmp!OTDi?Ygogm3JNyeWH%rMFC#oEN|ZW%Q~^_o zvyp(k4o4H2=9q)KBl zE|;CiQ|d!f1}%%)6=jm8fOBmrpaP~58_lNA%qT}->6(5~^kqsnQ;cW?R9UA=Fe+4l zpp`x<#)Kv+^bi*PpbH9=qklFkT>lqV`19a1Tix9~`1xYE*KOXJbqErr>l1@-TCUVB zd4+s(bb%(mLxD$pkL2C-9bp$cBy0M%xfQ)lejEiXy2Aj&MA2s>_(r^lUq>dvK7MOy zSVzo8Z(?2(eC_|mQoxXyyu4D`4TXY_MNH*XaLcKHH`-D_#A*~d*hHEcH7srMv&b_g{urh^n@t+>o@HyP8Ya zq*#a!!0H!JXDbcrLDC`1xr_C>4EIX@Awd~!8dkwamI7wlQa}YvBQBcFpP3pUAU|BA z6;_lQUW$q>Ds#AlqRYaV1`p`U8vkOL&L-Ua0FE<(ZqtsP?ty2wx*y@EPx;UsITO?W zse(@wH9G^Da!3?>nqJ-U9l$h{F{72N8q17ys@!QydVOcLmA~53x$tSfbvHItnkh#> zzpZ9=In@w_mMOs@V!a31eDK?Ak$c&@g)sR>kz^<^Z^lEz2Z@873K%4Sdx)IPq?sBZ zz{_i*f>&in=>|nuROUs$SC%2YDo<@3a>{a-I}LY&Q^|k!SAFird)B)BTA6d=DNWL$iPr7XLb>5`SWCirJfPVw-4J26FkivCV`wUxF} zZu95iRDdl7w1wVCNn~?2BSjK}EXQJ8Uh|bPhh$ue4M@=+Q?eolQ%JLE`!3h>(hjWY z?=8m!HQDsZV_eUA?D)sACNY8T1+4M&P61K!A@QbdyWE%WS}#WpfAiE`n z?xaQ9rK^+Q8UQKHW-9d1C~(A1Kb3#a`W^1%LnpXXkH8IuxNqR*6_~9E=}0#NorYy> zFw%0xG9y7O7u-@_mLWXt(2!o!t6iX5{D5@;fYJaAr(So@n(U{GOJ%0zQM*aGZm?4U zO^_LR!E8>=)C7U0xPmvQL`B)*`?{KTuZFhf#(aDkf zW1=Y1da$ChM>ww?J`E#?E8jFNPSG>ZFWtFb6#i9j+SeUBYb??^ci|~huuovDTF_I{rsU3M#I8vHs?g|#8J$f-Z#C(R~gF48c*iZ0MB z=B7q70RhOM49kJyGnBjrT82?TciH!!lONe>xfBrJBh-`VFLAVq`&DPoaNoVF-%Xw9 z+XZ$Cda*1pYg$kZMLzJyAaO*W6k=EKUiGIl+@gCox)o30k}@+R76C8y5nDVfSSJYZ z3R_-f8%O+W^Y?XpPB?smn>`)(c(Cg3jqAbpN*?$f{JZ$TEjH5a;)8xZAI2-)4ezrH zg4z_r9!mz%nW+;3-@ET6_YElURI%&M`SVW3^t(*wdqq4KZeHF)h5w~6`X?5v&=}Eh zCV=bnvKbKT`04ulk^7^zL&;bJHEy_jNSR z&3IS6d0%%nrs${R(w?^+JIS4TWcUf;&|vT~ACBNpI&7T#=JtRMv^2{=oyA+)cm5=sNJce=W-ZwDD}hvcjt8vCkkpDV=xwbeg`@7Qs;& zG(M?<&vk~7H~nh(6Rq5cWhNjHFpU|0D@b($*_`}dhm>H1>*dBlpDVBj3YUQ(A+$(4MO?x+E4UUSOqkd7?RM0S- zp6A9rHoEIJ`lEYqJ7yxL^C!CdpWY_B_m4T47y2Lx8e{IzKlWho+^ey_D8~fRQQ)-x zX@5A8?1hHb{JG<0vmciaRz0`FEq!b&IvU^$BOM0VJQZ;EiJ)U%rN)=J7?mpVVq7Qw z#rF^O=BrL}uWQsNIL@10|BME`V7JVd@&dM?%1msLR6!&?rp6t#$d9pj{&~Z?-V8x1>JikF> zvkV&pr8m#c0iI*gdEls@odk~HX_o$;NAwa}q+=L20aB53rhwD>>@3KoKX+|HM`AlV z9b?44`u2WzBf?%lXCv|v&X@_+- z^k=*ZKiM5d?CA);e=FMr2tjxQX{0^j2q=H`(^fY%N$OAB$NiTHZoxZjDPSait2P5L z5~VW8GA(^y8C;#xt_iqGTNto2x#x?*^UzeV-SWt23%8QV?PixP&(wb0vjCp z!z^Y}1X_PH4c|&i+NpaGvg3qDx>5e{;U$pPJLDET7&|PPSG62l2u#X4_0h(ryo55GY4{Zu)rbl7Rr*u4BGk4ySn?we@X!yD!hk^PiZ0RAGqX^ zP3~LRn0xLYWBQ%-Q}E2{ZC0-lQS3dZ*r#s7mikk78dn+~0!!ma3{#9lz%1PLu^gQM znmPNAa}W1$w$IE+L4XrCW@?Cl+-%2beVLL?Y;8wb=d-DLye?aDeTq}^X}HEE)AK?neh5^i z`-4q)&X1}Z=w-hE_;R@+D@Vc2IHaGC-v*f?*Mp-Sm@vkjfjdLYj2r~`d6=mo0!#So ze5iv|>fC%M_x8gt3O2m&s^_-5OK*8e9!||a20z2NCV%6d8{O5nt#@Dg#YWi_m@0ab zb5q|Jmu_^+*0h*H*Zi84ntlFUTsU|z39Hp?gw}{ znr9=bqxW^2c7&;VCsXrWm*)n+x!BZq)AG$SWzRzO^z9Ty@9DE*isENu4Ab+{PIt${ zTjfY!ZUpb6hv9DvMGUn2#X{5sL;WOiaq)Rjdmzf1&=gJzvRJ9 z?uxHJE1MP@Iv}7q1J{ZF=#z8Yf^!cL{x^Jd)~J<7-3o3UfVxREybm`L{rGnqWQ5MC zcXk4Lp{O}(UvO)`yXo%Da>);;l03IWxf?xIoM*5*){`#HR z<&QOePV1jM#~5dgi=VK|_QT{;m!ppZ@# ze6%HBro#go&H$|J-RiFSm*?F3acogL4!QD4o)~!G1i~0BN%A>=&p zCkQl=naZ*W);Srpn_ZC?TB+=U8;m@uRG6kq%xYY~v+(=4COF{W6+QqEg$x^tyEr}1 zG`ZP=bo4GtIZy9-fkx?I`ze0UdnHcttjD#%v6CR3h%r$9&%3FZ=0Ee4eWHT*mk@DgwTkG&~#v%nW(S3j=^Zf>Y7O3Y9;+JoS=c!mA=Plot~#18b1f@O#MuyIiK7ho>}FBjrQL~a}` zyJ%=&MQQSGWES3Uk)8c-4Q=5&h|{-%VKEX41L1;tAxiP)9CR)!dO#)aL}oD#4spkT zC_gED65RS_>KD84JI}kb{%ES3bC92E&&h|cs{z0W>OYZ9igV`Hf1%JltrbanUnZn+ib5m*SFw-ZOthAiucNvxt-u*wG^x^h* zw<2I2fZYoB?$1oGyPbm@xrW4~l{jIzZ1r|I8E`r-_o1>s=T&~KlAQr6`H#MNvMBs( zf4D(54KkdDM-SUGvFRyK-lSZ`#EbFEh-R7~01Rb=U5IYy$7R^a=b(=s$V=Rb++rLY zIn9F%V_d<_vXsT8VNBB{=EV&=-1(pDb$5K~FuXAoe8fRjb%ij8hDt_jYs-2)QflUsjRj0HE!Haw7xPRXa!^K?hK z3Eq?)pyMNcbqAT|_@%t@{eVW7Df)SY5ozPnj=E6}?loBcyUp$ZY%r7?18XIzk^eFj zt(lr5uwIaK%Hs^o3*YT#n6yLmpheGDsdyDmVuk3-M$|=gsW@?t>LsakJ7l1upthL1z}Vc-8=)p*MG5pr@Qn^&$t^f%^r>7rFM*f zL&Fqz1PP8zgSeHr{(IH|#ZfpSB32NT>cf4jl`?e%LViTxRV1grxv{1}gj92Q~i``p)H zlg@xlY$_V4kK7+xBdN^A1gE{+-xSD>!XX&>f9${dGsMMo;%~vHG<}DGi|)1c|CW#u ze~#EwlgcD@GmZMyRyy&Az|uIMP?b58c%_~sPMk3xrql35k!iNwW`FaCeeT>d_QNr} z$=RzXxOhyns1!#D4QZF)y5TE6zt(;DT{GP|r-z;YN~U;&IAZUKGx^gW(>eE#x4ht% zKeXBX`{(DlUw>zAF3yu?iOKU+{6+wuCCmEVCGS4K{p>DWe+;*o;YYwa0K<>*h{$0n z&gnMq05n(dX~iDQi*RXR()6t24~AD~wEo?etlW$vfVv#f8HcV(P4Va?Q7Wp`O)$hC z0!!mOVnqc%1_AL)1D(2aiY4VI?j#^_`W)=<`8a!j=Z#0WX<^rW4w0lY5S;}K*P~8c z^N$#%BYgd49{u+>SXsk`Ve#C`a@16=9%#)`DqTwT1Nxb%Uan0Q9A_7|7i{a+483K#s_B%GYg{Nx$y%QW*sarjr@M=44 z;e8t3&zH%j|JwkRvSW)tC7^5Wsax0IB@UtvYyI5F#q?bFP3iR9w_ZZy*Nn+OeKH!Q z7aP3fM1t@R>7{IRg9iW2IJX*C^j`P97yO!gkb$nhQ!a%wk01a57g$L|K~#P(2fzYm ze*GSHSbOLGfL>e)e)ga2=idL08ScbmFgqu*WqP0+@lM z3Y2ajWjR4E<%4@Ye)72Y`EBk4XX74$BPO~n_yyAsy%#?mWMa+YhIo{8#U%;2FNt9%5r3&nS~g^pY_Il9dFk220%ru5R$r?Ao0&TVgi(IawmU5 z&LR`fG)JdApvrx0!czh`uuzl4d6_aGqnk2ytb6}CxDhZ(_u#56ZXT{ZUWv<(%nTm_ z)&UqkY)2eto(5>z0Z1y}?kjlGNHoiI2+FkrJCIi=mzgxns_Vl!y5q<$#k{3EiAlA7J^rRC3Q0>0k?Sv zi|IUl=L%jkgso1=*YlIV8ZLGr9TN27K7dayIK*wmzMQDsQGs z5Rg$YD_z+$EEur4Zt;|rqZrn1xr%i7vi%gJbN17}ZGuP)b5p+vV26NHwwxKDO@DC9 z6tPSRgHAdRqz^Q(b?RSuFkIrwuBQ#MlBEIAArG0ZhCg>bZMbtljwgbhnGAsyk8F0= z-Gu9T3;5@b@=+#`lVB=x_VSczdc@_X>qGsC%K)Sk+8~>_jF8HUvPYRjj%p)pY>++6 zmOdiqz&GK4FpdNAISkhdU-jWx*-M&h$Pu8p%ybC?tMQ9oE1n#xDYX`gU6nrSdueAw z!#lxUY1GD-s|sG$JcDx5#*9iw6$v*0nrQ<8j1C*t-$P@rXTC+L1KVOl9{o_#`3q|3 z_p=KTZ3OPTk*tDb$Z%;p;4L+unBenTi@V&EVITxQP8UFshHW4h+-%x zc#(@&@VqfFDtB>%jc#>f`QF@P(;IZ3v{H*M=ZzVAu{Iu58rrI|)=MP&6D( z;#q`?Ma;B`0QD-d3-}R3Rm!wQIjCIV_oH{(6$)PXk~es;#fiKq*F*^kZ+|k(YEB;PG-28Qom_~|D40I6mVH`;Zeg|TfjgOuuXsiMQ)VR zaS34c3)|d~{WuVDK;6z6_+wDA=}6KbLc1PWqCl-goQlpdghv8EskX+)gm8#E28jP# zxQf(Fn+O0pDvUhFin-?z#hJh?F{xkN{DY2xX9k|$pKe0S(WUf0t56PjVy6Dv@zG@j zO4!}@2d`Y?9zNye7&W7LPhl#!Sr4O;xR9WbE}|~s`R_q8P+HiHJbex-LZ4LrhKN{# zd|4Ts@Vn^31Kr{uzUY=(eNF%yyp#QLGhK#2M@QmVR}0U8rsp||;HXhXfQ1pf_V^L! zN1SLz!U#UD2?n}2>d=t~ZE>TVOrsC-;`PV(uCeL=wm)wi-HM}s;3u-6t#W}oj^Kg0 zUwVNJ`RRx~PA@YaQ_b1P60}H{%11_wl$V5iIi@4l=tdf!S4hkB_;02h0q&Y#c-_;n zz6o}ao!}{Qp}aia4WoC==EOQDZsb9{!xNVcl#lRb8axQ|-%L`!K_~l4n4j{Y%th{* zTzMhUZFls!pZ^Ls07|-)pZ^BGa{b|M+B9ZlW~dOb8GxZeauo65rnpPqh$}y7iVZ3J zF-W`ZKBfHS(IM&O$3!7A`cdJdyBG&bi4zV!GNy&2NEqXgGj@2>ejy>5NKKkoPI zpC1)CMr!fTIc&VU>WbO!@++Uv9L*ji0@eW-B!Huld@=6C@7uD|?Kip5n?~(qm6K|h z)MI(a=x?FmW(xa5Z2J|07S$B%voqGtCjW;L_FZ!CxPX1Vt=;kW#7~ z1X}elGQ~P|hqx#kPl!--8HJuPo`4Fzx!L7J{wKaN`CWq&!i)b3O98iIDc~2j zv{wR8$3dKkG1G+zEW&93omS1J2mwTxvWvU!qaRfo{RK`Dmru`wRz{Gh_&VLEe)cnT z+AmD~MSoeGhN-QzoEnTg6L&5@Urye9xac=F1JKc@%{E)*qu3{y$0>P)Wo|V4py88E z@q$tOZu}Im-_)hw5N$3Iga`8TyV8>lfFX}e-sGQ@k7YH}5`opY0PEV%_6FP_2goSh zT&HegyznzmyNfS-!hP<`&%|$lGX42~_PZ*j`H~EfM{xD-@_RSJ#+?~bzbIpepY_E({cE>LyU?iM2aoIW zd$ME6AO7nD-MqQUwZu)>>rz0IXbbKD0e%YYocTXnSs%xVSW9g+qjsZ}!E~aecBPi0 z_EP&gB*YSHFtyY&c48|^ZMC*^N>L(B38k^CDr%1<8q^XrmIhDE^R$1%^TXU<&M)_z z_vf5@&+B#1_xrsc7=|mR=RosTZLCLp{yTAR&kE;<(o3%bE~Y0Q#{Zsdc=F614&i(? zT9(R_6t%!A?g#hr#4A-^!}N?aCW87r`r>Tg#@fhlifK|~jml%0->KXkyoAlyttLEq z|8dR+W%=`&{#h_tU-4h3><{Own;dV1uY$SD}%04Qra_JbKXZNj>%qIpq%ZyetL zUXL)rOT%f;BrHH8N{G~Imi^%n5ZxSLB0!Sqcs;5)q8$0~eUw<;tZa2T4H8!n4> zWuZ7sstIob&8xIk6sY?a++hz4+V8?jUtr^+zn-c3o{07$VX_Q_TrJ$qUJ2=gE=IY6 z*-Yczp>98e_6sA1AF`cS)}Yr?MAr8IN*eq%DR4LC=5gn36|v*LkrbF?aiQMl_QQKd zo2`#ZO68#7HDCnc&9~P0@qWEl^cl@;0%dU+cl`@pa4q#lM+K_n3k7EH0>iO1s1&T- z1?EvNTEtNS`v!t!!hG|b@=fz+OHMjF*wK)p`dLB1F=cF(qUK z*AjZFop;BBr6O-Ph1qt5XLaZys^x)bVCF2>&(NZN2r^Y}xILbUXre4(EY$f{nD2fj zbl*@YMMf>o*lqRQF|LW7 z;W}PKz3+smEYGav1)--`WqjXL3YhFW+fpZy4c!FnEN#I1+ra}ObBkN|KbnqQ3-@^) zzTQ*3g;sW(+m|xy!lliGqn8yCd#oz}55rI%R#0S#bwk`jeqUk$#(J+$_}(3xfZ&RG z-Hw*gj@~C@8o*4p5=lOzojUd_PGgk{NtR3n5|yhY3u7bTkygt*&I-iPKg;vehbnSR zob3>I*=`462|s7Npwjqfxq=(wbP|b@XL1mDgxohCr7DUCvx z4fqHCP_jL!Ep?=5Wp((SKUX?gT>c=i{y~q@L7sJbXb|Vr%vWqu%EY_67$>^-n-PC41~1`PGn3O&*ek^~ouOi` zULYgofy`>t^1&&Am~n4^MLG&Y#QJh#i-`@-07v-Gk)Fo0&G@S5qx#2kBxrQ@^FpRb z$^~!Yt{6gV#RzB$ZZOJF!%S_aIM7 zhRb8rrzmpNvdGwCvc!>WAQ(0OM4w6$CNSA`6d3A-Zl1*!iLeg#=cda+#|lN%+zPS-$*LfMM(Kf;5K;q)_J1~q; zOi$<&o|<%J0sF6h)9nK%_}#w|1USXJch5Zvw5CeH83 zs(ov3%wc$V{V)qW(78=drH1DHW+;|k#Bz(~i!9-PlxrGdG^l!f6tO$4{a4=Bli3Tn z%kjbYJ8W?K6F7*82Y~WeVL}-Bu@cfh%g54Ccv7DN`N=T|C~3mKJb#q%OzvqmQ$7cH z+7m^GPZwNP@SP#9x{NK@AP%9+&rS6uApYUgE}*D_7kmPc9l6LxQ6X^l>6s1`uDg&>ZA!-Lqr=Z1tV43p$%BdK0bAX?k^?t^sN?a$qU#gIyM%T643{P(AM|GFWt=M@imGg!~a1=m*UTpl+0{)?&e* zdq>$cqurr!t2RC>GOwm;kQ42cyw#r}^da*aM83X7HNuJ##tP7TC085^6ZRqf z7E6cB@+Mtr%8aoMiYwPV)a9PSePAB%+L#}FW5d@W^=cRGG>q$HV%x4~-Bttz<}8oa zWBqWCh2w4WsV|MA9e*L!PExzBfzgwDUPG1SaRtQ+4z`=kJ+?{?@H*!-@Ua2^TWp1E zoXzhX>hqOT`uc>i#L*b32fA<<%6d%;rpnuxU+;y3zV|Ex(5i_o>D)*E*Cjzyn7iSkrf`+l zw)^*iEYE8T*rI=%{sEJqefmGGu-q*EhfR|Z!$u2|n$c(K3VvrJb!Y)bN0_24~d#$@e?^BR5UoNAp^W$q#{y=Wx2-a*p$P zpR-lY^BF34#^?Kf2s}@2^U>Kh|6uE#>(hHSt$O*pz<(F#-^Ka&&G~=XR)=Sn7msBf T1Z`O8XqSPmiOvfxm)QRR$Cw~? literal 0 HcmV?d00001 diff --git a/Demos/LunarG-VulkanSamples/Demos/macOS/Resources/macOS.xcassets/Contents.json b/Demos/LunarG-VulkanSamples/Demos/macOS/Resources/macOS.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/macOS/Resources/macOS.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Demos/LunarG-VulkanSamples/Demos/macOS/main.m b/Demos/LunarG-VulkanSamples/Demos/macOS/main.m new file mode 100644 index 00000000..9da3591d --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Demos/macOS/main.m @@ -0,0 +1,23 @@ +/* + * main.m + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +int main(int argc, const char * argv[]) { + return NSApplicationMain(argc, argv); +} diff --git a/Demos/LunarG-VulkanSamples/Hologram/HelpersDispatchTable.cpp b/Demos/LunarG-VulkanSamples/Hologram/HelpersDispatchTable.cpp new file mode 100644 index 00000000..96357d39 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/HelpersDispatchTable.cpp @@ -0,0 +1,525 @@ +// This file is generated. +#include "HelpersDispatchTable.h" + +namespace vk { + +PFN_vkCreateInstance CreateInstance; +PFN_vkDestroyInstance DestroyInstance; +PFN_vkEnumeratePhysicalDevices EnumeratePhysicalDevices; +PFN_vkGetPhysicalDeviceFeatures GetPhysicalDeviceFeatures; +PFN_vkGetPhysicalDeviceFormatProperties GetPhysicalDeviceFormatProperties; +PFN_vkGetPhysicalDeviceImageFormatProperties GetPhysicalDeviceImageFormatProperties; +PFN_vkGetPhysicalDeviceProperties GetPhysicalDeviceProperties; +PFN_vkGetPhysicalDeviceQueueFamilyProperties GetPhysicalDeviceQueueFamilyProperties; +PFN_vkGetPhysicalDeviceMemoryProperties GetPhysicalDeviceMemoryProperties; +PFN_vkGetInstanceProcAddr GetInstanceProcAddr; +PFN_vkGetDeviceProcAddr GetDeviceProcAddr; +PFN_vkCreateDevice CreateDevice; +PFN_vkDestroyDevice DestroyDevice; +PFN_vkEnumerateInstanceExtensionProperties EnumerateInstanceExtensionProperties; +PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties; +PFN_vkEnumerateInstanceLayerProperties EnumerateInstanceLayerProperties; +PFN_vkGetDeviceQueue GetDeviceQueue; +PFN_vkQueueSubmit QueueSubmit; +PFN_vkQueueWaitIdle QueueWaitIdle; +PFN_vkDeviceWaitIdle DeviceWaitIdle; +PFN_vkAllocateMemory AllocateMemory; +PFN_vkFreeMemory FreeMemory; +PFN_vkMapMemory MapMemory; +PFN_vkUnmapMemory UnmapMemory; +PFN_vkFlushMappedMemoryRanges FlushMappedMemoryRanges; +PFN_vkInvalidateMappedMemoryRanges InvalidateMappedMemoryRanges; +PFN_vkGetDeviceMemoryCommitment GetDeviceMemoryCommitment; +PFN_vkBindBufferMemory BindBufferMemory; +PFN_vkBindImageMemory BindImageMemory; +PFN_vkGetBufferMemoryRequirements GetBufferMemoryRequirements; +PFN_vkGetImageMemoryRequirements GetImageMemoryRequirements; +PFN_vkGetImageSparseMemoryRequirements GetImageSparseMemoryRequirements; +PFN_vkGetPhysicalDeviceSparseImageFormatProperties GetPhysicalDeviceSparseImageFormatProperties; +PFN_vkQueueBindSparse QueueBindSparse; +PFN_vkCreateFence CreateFence; +PFN_vkDestroyFence DestroyFence; +PFN_vkResetFences ResetFences; +PFN_vkGetFenceStatus GetFenceStatus; +PFN_vkWaitForFences WaitForFences; +PFN_vkCreateSemaphore CreateSemaphore; +PFN_vkDestroySemaphore DestroySemaphore; +PFN_vkCreateEvent CreateEvent; +PFN_vkDestroyEvent DestroyEvent; +PFN_vkGetEventStatus GetEventStatus; +PFN_vkSetEvent SetEvent; +PFN_vkResetEvent ResetEvent; +PFN_vkCreateQueryPool CreateQueryPool; +PFN_vkDestroyQueryPool DestroyQueryPool; +PFN_vkGetQueryPoolResults GetQueryPoolResults; +PFN_vkCreateBuffer CreateBuffer; +PFN_vkDestroyBuffer DestroyBuffer; +PFN_vkCreateBufferView CreateBufferView; +PFN_vkDestroyBufferView DestroyBufferView; +PFN_vkCreateImage CreateImage; +PFN_vkDestroyImage DestroyImage; +PFN_vkGetImageSubresourceLayout GetImageSubresourceLayout; +PFN_vkCreateImageView CreateImageView; +PFN_vkDestroyImageView DestroyImageView; +PFN_vkCreateShaderModule CreateShaderModule; +PFN_vkDestroyShaderModule DestroyShaderModule; +PFN_vkCreatePipelineCache CreatePipelineCache; +PFN_vkDestroyPipelineCache DestroyPipelineCache; +PFN_vkGetPipelineCacheData GetPipelineCacheData; +PFN_vkMergePipelineCaches MergePipelineCaches; +PFN_vkCreateGraphicsPipelines CreateGraphicsPipelines; +PFN_vkCreateComputePipelines CreateComputePipelines; +PFN_vkDestroyPipeline DestroyPipeline; +PFN_vkCreatePipelineLayout CreatePipelineLayout; +PFN_vkDestroyPipelineLayout DestroyPipelineLayout; +PFN_vkCreateSampler CreateSampler; +PFN_vkDestroySampler DestroySampler; +PFN_vkCreateDescriptorSetLayout CreateDescriptorSetLayout; +PFN_vkDestroyDescriptorSetLayout DestroyDescriptorSetLayout; +PFN_vkCreateDescriptorPool CreateDescriptorPool; +PFN_vkDestroyDescriptorPool DestroyDescriptorPool; +PFN_vkResetDescriptorPool ResetDescriptorPool; +PFN_vkAllocateDescriptorSets AllocateDescriptorSets; +PFN_vkFreeDescriptorSets FreeDescriptorSets; +PFN_vkUpdateDescriptorSets UpdateDescriptorSets; +PFN_vkCreateFramebuffer CreateFramebuffer; +PFN_vkDestroyFramebuffer DestroyFramebuffer; +PFN_vkCreateRenderPass CreateRenderPass; +PFN_vkDestroyRenderPass DestroyRenderPass; +PFN_vkGetRenderAreaGranularity GetRenderAreaGranularity; +PFN_vkCreateCommandPool CreateCommandPool; +PFN_vkDestroyCommandPool DestroyCommandPool; +PFN_vkResetCommandPool ResetCommandPool; +PFN_vkAllocateCommandBuffers AllocateCommandBuffers; +PFN_vkFreeCommandBuffers FreeCommandBuffers; +PFN_vkBeginCommandBuffer BeginCommandBuffer; +PFN_vkEndCommandBuffer EndCommandBuffer; +PFN_vkResetCommandBuffer ResetCommandBuffer; +PFN_vkCmdBindPipeline CmdBindPipeline; +PFN_vkCmdSetViewport CmdSetViewport; +PFN_vkCmdSetScissor CmdSetScissor; +PFN_vkCmdSetLineWidth CmdSetLineWidth; +PFN_vkCmdSetDepthBias CmdSetDepthBias; +PFN_vkCmdSetBlendConstants CmdSetBlendConstants; +PFN_vkCmdSetDepthBounds CmdSetDepthBounds; +PFN_vkCmdSetStencilCompareMask CmdSetStencilCompareMask; +PFN_vkCmdSetStencilWriteMask CmdSetStencilWriteMask; +PFN_vkCmdSetStencilReference CmdSetStencilReference; +PFN_vkCmdBindDescriptorSets CmdBindDescriptorSets; +PFN_vkCmdBindIndexBuffer CmdBindIndexBuffer; +PFN_vkCmdBindVertexBuffers CmdBindVertexBuffers; +PFN_vkCmdDraw CmdDraw; +PFN_vkCmdDrawIndexed CmdDrawIndexed; +PFN_vkCmdDrawIndirect CmdDrawIndirect; +PFN_vkCmdDrawIndexedIndirect CmdDrawIndexedIndirect; +PFN_vkCmdDispatch CmdDispatch; +PFN_vkCmdDispatchIndirect CmdDispatchIndirect; +PFN_vkCmdCopyBuffer CmdCopyBuffer; +PFN_vkCmdCopyImage CmdCopyImage; +PFN_vkCmdBlitImage CmdBlitImage; +PFN_vkCmdCopyBufferToImage CmdCopyBufferToImage; +PFN_vkCmdCopyImageToBuffer CmdCopyImageToBuffer; +PFN_vkCmdUpdateBuffer CmdUpdateBuffer; +PFN_vkCmdFillBuffer CmdFillBuffer; +PFN_vkCmdClearColorImage CmdClearColorImage; +PFN_vkCmdClearDepthStencilImage CmdClearDepthStencilImage; +PFN_vkCmdClearAttachments CmdClearAttachments; +PFN_vkCmdResolveImage CmdResolveImage; +PFN_vkCmdSetEvent CmdSetEvent; +PFN_vkCmdResetEvent CmdResetEvent; +PFN_vkCmdWaitEvents CmdWaitEvents; +PFN_vkCmdPipelineBarrier CmdPipelineBarrier; +PFN_vkCmdBeginQuery CmdBeginQuery; +PFN_vkCmdEndQuery CmdEndQuery; +PFN_vkCmdResetQueryPool CmdResetQueryPool; +PFN_vkCmdWriteTimestamp CmdWriteTimestamp; +PFN_vkCmdCopyQueryPoolResults CmdCopyQueryPoolResults; +PFN_vkCmdPushConstants CmdPushConstants; +PFN_vkCmdBeginRenderPass CmdBeginRenderPass; +PFN_vkCmdNextSubpass CmdNextSubpass; +PFN_vkCmdEndRenderPass CmdEndRenderPass; +PFN_vkCmdExecuteCommands CmdExecuteCommands; +PFN_vkDestroySurfaceKHR DestroySurfaceKHR; +PFN_vkGetPhysicalDeviceSurfaceSupportKHR GetPhysicalDeviceSurfaceSupportKHR; +PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR GetPhysicalDeviceSurfaceCapabilitiesKHR; +PFN_vkGetPhysicalDeviceSurfaceFormatsKHR GetPhysicalDeviceSurfaceFormatsKHR; +PFN_vkGetPhysicalDeviceSurfacePresentModesKHR GetPhysicalDeviceSurfacePresentModesKHR; +PFN_vkCreateSwapchainKHR CreateSwapchainKHR; +PFN_vkDestroySwapchainKHR DestroySwapchainKHR; +PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR; +PFN_vkAcquireNextImageKHR AcquireNextImageKHR; +PFN_vkQueuePresentKHR QueuePresentKHR; +PFN_vkGetPhysicalDeviceDisplayPropertiesKHR GetPhysicalDeviceDisplayPropertiesKHR; +PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR GetPhysicalDeviceDisplayPlanePropertiesKHR; +PFN_vkGetDisplayPlaneSupportedDisplaysKHR GetDisplayPlaneSupportedDisplaysKHR; +PFN_vkGetDisplayModePropertiesKHR GetDisplayModePropertiesKHR; +PFN_vkCreateDisplayModeKHR CreateDisplayModeKHR; +PFN_vkGetDisplayPlaneCapabilitiesKHR GetDisplayPlaneCapabilitiesKHR; +PFN_vkCreateDisplayPlaneSurfaceKHR CreateDisplayPlaneSurfaceKHR; +PFN_vkCreateSharedSwapchainsKHR CreateSharedSwapchainsKHR; +#ifdef VK_USE_PLATFORM_XLIB_KHR +PFN_vkCreateXlibSurfaceKHR CreateXlibSurfaceKHR; +PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR GetPhysicalDeviceXlibPresentationSupportKHR; +#endif +#ifdef VK_USE_PLATFORM_XCB_KHR +PFN_vkCreateXcbSurfaceKHR CreateXcbSurfaceKHR; +PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR GetPhysicalDeviceXcbPresentationSupportKHR; +#endif +#ifdef VK_USE_PLATFORM_WAYLAND_KHR +PFN_vkCreateWaylandSurfaceKHR CreateWaylandSurfaceKHR; +PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR GetPhysicalDeviceWaylandPresentationSupportKHR; +#endif +#ifdef VK_USE_PLATFORM_MIR_KHR +PFN_vkCreateMirSurfaceKHR CreateMirSurfaceKHR; +PFN_vkGetPhysicalDeviceMirPresentationSupportKHR GetPhysicalDeviceMirPresentationSupportKHR; +#endif +#ifdef VK_USE_PLATFORM_ANDROID_KHR +PFN_vkCreateAndroidSurfaceKHR CreateAndroidSurfaceKHR; +#endif +#ifdef VK_USE_PLATFORM_WIN32_KHR +PFN_vkCreateWin32SurfaceKHR CreateWin32SurfaceKHR; +PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR GetPhysicalDeviceWin32PresentationSupportKHR; +#endif +PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT; +PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT; +PFN_vkDebugReportMessageEXT DebugReportMessageEXT; + +void init_dispatch_table_top(PFN_vkGetInstanceProcAddr get_instance_proc_addr) +{ + GetInstanceProcAddr = get_instance_proc_addr; + + CreateInstance = reinterpret_cast(GetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance")); + EnumerateInstanceExtensionProperties = reinterpret_cast(GetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties")); + EnumerateInstanceLayerProperties = reinterpret_cast(GetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceLayerProperties")); +} + +void init_dispatch_table_middle(VkInstance instance, bool include_bottom) +{ + GetInstanceProcAddr = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetInstanceProcAddr")); + + DestroyInstance = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroyInstance")); + EnumeratePhysicalDevices = reinterpret_cast(GetInstanceProcAddr(instance, "vkEnumeratePhysicalDevices")); + GetPhysicalDeviceFeatures = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures")); + GetPhysicalDeviceFormatProperties = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceFormatProperties")); + GetPhysicalDeviceImageFormatProperties = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceImageFormatProperties")); + GetPhysicalDeviceProperties = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties")); + GetPhysicalDeviceQueueFamilyProperties = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceQueueFamilyProperties")); + GetPhysicalDeviceMemoryProperties = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceMemoryProperties")); + CreateDevice = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateDevice")); + EnumerateDeviceExtensionProperties = reinterpret_cast(GetInstanceProcAddr(instance, "vkEnumerateDeviceExtensionProperties")); + GetPhysicalDeviceSparseImageFormatProperties = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSparseImageFormatProperties")); + DestroySurfaceKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroySurfaceKHR")); + GetPhysicalDeviceSurfaceSupportKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceSupportKHR")); + GetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR")); + GetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceFormatsKHR")); + GetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfacePresentModesKHR")); + GetPhysicalDeviceDisplayPropertiesKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceDisplayPropertiesKHR")); + GetPhysicalDeviceDisplayPlanePropertiesKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR")); + GetDisplayPlaneSupportedDisplaysKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetDisplayPlaneSupportedDisplaysKHR")); + GetDisplayModePropertiesKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetDisplayModePropertiesKHR")); + CreateDisplayModeKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateDisplayModeKHR")); + GetDisplayPlaneCapabilitiesKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetDisplayPlaneCapabilitiesKHR")); + CreateDisplayPlaneSurfaceKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateDisplayPlaneSurfaceKHR")); +#ifdef VK_USE_PLATFORM_XLIB_KHR + CreateXlibSurfaceKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR")); +#endif +#ifdef VK_USE_PLATFORM_XLIB_KHR + GetPhysicalDeviceXlibPresentationSupportKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR")); +#endif +#ifdef VK_USE_PLATFORM_XCB_KHR + CreateXcbSurfaceKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR")); +#endif +#ifdef VK_USE_PLATFORM_XCB_KHR + GetPhysicalDeviceXcbPresentationSupportKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR")); +#endif +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + CreateWaylandSurfaceKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR")); +#endif +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + GetPhysicalDeviceWaylandPresentationSupportKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR")); +#endif +#ifdef VK_USE_PLATFORM_MIR_KHR + CreateMirSurfaceKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateMirSurfaceKHR")); +#endif +#ifdef VK_USE_PLATFORM_MIR_KHR + GetPhysicalDeviceMirPresentationSupportKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceMirPresentationSupportKHR")); +#endif +#ifdef VK_USE_PLATFORM_ANDROID_KHR + CreateAndroidSurfaceKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateAndroidSurfaceKHR")); +#endif +#ifdef VK_USE_PLATFORM_WIN32_KHR + CreateWin32SurfaceKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR")); +#endif +#ifdef VK_USE_PLATFORM_WIN32_KHR + GetPhysicalDeviceWin32PresentationSupportKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR")); +#endif + CreateDebugReportCallbackEXT = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT")); + DestroyDebugReportCallbackEXT = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT")); + DebugReportMessageEXT = reinterpret_cast(GetInstanceProcAddr(instance, "vkDebugReportMessageEXT")); + + if (!include_bottom) + return; + + GetDeviceProcAddr = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetDeviceProcAddr")); + DestroyDevice = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroyDevice")); + GetDeviceQueue = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetDeviceQueue")); + QueueSubmit = reinterpret_cast(GetInstanceProcAddr(instance, "vkQueueSubmit")); + QueueWaitIdle = reinterpret_cast(GetInstanceProcAddr(instance, "vkQueueWaitIdle")); + DeviceWaitIdle = reinterpret_cast(GetInstanceProcAddr(instance, "vkDeviceWaitIdle")); + AllocateMemory = reinterpret_cast(GetInstanceProcAddr(instance, "vkAllocateMemory")); + FreeMemory = reinterpret_cast(GetInstanceProcAddr(instance, "vkFreeMemory")); + MapMemory = reinterpret_cast(GetInstanceProcAddr(instance, "vkMapMemory")); + UnmapMemory = reinterpret_cast(GetInstanceProcAddr(instance, "vkUnmapMemory")); + FlushMappedMemoryRanges = reinterpret_cast(GetInstanceProcAddr(instance, "vkFlushMappedMemoryRanges")); + InvalidateMappedMemoryRanges = reinterpret_cast(GetInstanceProcAddr(instance, "vkInvalidateMappedMemoryRanges")); + GetDeviceMemoryCommitment = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetDeviceMemoryCommitment")); + BindBufferMemory = reinterpret_cast(GetInstanceProcAddr(instance, "vkBindBufferMemory")); + BindImageMemory = reinterpret_cast(GetInstanceProcAddr(instance, "vkBindImageMemory")); + GetBufferMemoryRequirements = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetBufferMemoryRequirements")); + GetImageMemoryRequirements = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetImageMemoryRequirements")); + GetImageSparseMemoryRequirements = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetImageSparseMemoryRequirements")); + QueueBindSparse = reinterpret_cast(GetInstanceProcAddr(instance, "vkQueueBindSparse")); + CreateFence = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateFence")); + DestroyFence = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroyFence")); + ResetFences = reinterpret_cast(GetInstanceProcAddr(instance, "vkResetFences")); + GetFenceStatus = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetFenceStatus")); + WaitForFences = reinterpret_cast(GetInstanceProcAddr(instance, "vkWaitForFences")); + CreateSemaphore = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateSemaphore")); + DestroySemaphore = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroySemaphore")); + CreateEvent = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateEvent")); + DestroyEvent = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroyEvent")); + GetEventStatus = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetEventStatus")); + SetEvent = reinterpret_cast(GetInstanceProcAddr(instance, "vkSetEvent")); + ResetEvent = reinterpret_cast(GetInstanceProcAddr(instance, "vkResetEvent")); + CreateQueryPool = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateQueryPool")); + DestroyQueryPool = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroyQueryPool")); + GetQueryPoolResults = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetQueryPoolResults")); + CreateBuffer = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateBuffer")); + DestroyBuffer = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroyBuffer")); + CreateBufferView = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateBufferView")); + DestroyBufferView = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroyBufferView")); + CreateImage = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateImage")); + DestroyImage = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroyImage")); + GetImageSubresourceLayout = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetImageSubresourceLayout")); + CreateImageView = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateImageView")); + DestroyImageView = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroyImageView")); + CreateShaderModule = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateShaderModule")); + DestroyShaderModule = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroyShaderModule")); + CreatePipelineCache = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreatePipelineCache")); + DestroyPipelineCache = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroyPipelineCache")); + GetPipelineCacheData = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetPipelineCacheData")); + MergePipelineCaches = reinterpret_cast(GetInstanceProcAddr(instance, "vkMergePipelineCaches")); + CreateGraphicsPipelines = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateGraphicsPipelines")); + CreateComputePipelines = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateComputePipelines")); + DestroyPipeline = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroyPipeline")); + CreatePipelineLayout = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreatePipelineLayout")); + DestroyPipelineLayout = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroyPipelineLayout")); + CreateSampler = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateSampler")); + DestroySampler = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroySampler")); + CreateDescriptorSetLayout = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateDescriptorSetLayout")); + DestroyDescriptorSetLayout = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroyDescriptorSetLayout")); + CreateDescriptorPool = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateDescriptorPool")); + DestroyDescriptorPool = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroyDescriptorPool")); + ResetDescriptorPool = reinterpret_cast(GetInstanceProcAddr(instance, "vkResetDescriptorPool")); + AllocateDescriptorSets = reinterpret_cast(GetInstanceProcAddr(instance, "vkAllocateDescriptorSets")); + FreeDescriptorSets = reinterpret_cast(GetInstanceProcAddr(instance, "vkFreeDescriptorSets")); + UpdateDescriptorSets = reinterpret_cast(GetInstanceProcAddr(instance, "vkUpdateDescriptorSets")); + CreateFramebuffer = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateFramebuffer")); + DestroyFramebuffer = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroyFramebuffer")); + CreateRenderPass = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateRenderPass")); + DestroyRenderPass = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroyRenderPass")); + GetRenderAreaGranularity = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetRenderAreaGranularity")); + CreateCommandPool = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateCommandPool")); + DestroyCommandPool = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroyCommandPool")); + ResetCommandPool = reinterpret_cast(GetInstanceProcAddr(instance, "vkResetCommandPool")); + AllocateCommandBuffers = reinterpret_cast(GetInstanceProcAddr(instance, "vkAllocateCommandBuffers")); + FreeCommandBuffers = reinterpret_cast(GetInstanceProcAddr(instance, "vkFreeCommandBuffers")); + BeginCommandBuffer = reinterpret_cast(GetInstanceProcAddr(instance, "vkBeginCommandBuffer")); + EndCommandBuffer = reinterpret_cast(GetInstanceProcAddr(instance, "vkEndCommandBuffer")); + ResetCommandBuffer = reinterpret_cast(GetInstanceProcAddr(instance, "vkResetCommandBuffer")); + CmdBindPipeline = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdBindPipeline")); + CmdSetViewport = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdSetViewport")); + CmdSetScissor = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdSetScissor")); + CmdSetLineWidth = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdSetLineWidth")); + CmdSetDepthBias = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdSetDepthBias")); + CmdSetBlendConstants = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdSetBlendConstants")); + CmdSetDepthBounds = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdSetDepthBounds")); + CmdSetStencilCompareMask = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdSetStencilCompareMask")); + CmdSetStencilWriteMask = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdSetStencilWriteMask")); + CmdSetStencilReference = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdSetStencilReference")); + CmdBindDescriptorSets = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdBindDescriptorSets")); + CmdBindIndexBuffer = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdBindIndexBuffer")); + CmdBindVertexBuffers = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdBindVertexBuffers")); + CmdDraw = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdDraw")); + CmdDrawIndexed = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdDrawIndexed")); + CmdDrawIndirect = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdDrawIndirect")); + CmdDrawIndexedIndirect = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdDrawIndexedIndirect")); + CmdDispatch = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdDispatch")); + CmdDispatchIndirect = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdDispatchIndirect")); + CmdCopyBuffer = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdCopyBuffer")); + CmdCopyImage = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdCopyImage")); + CmdBlitImage = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdBlitImage")); + CmdCopyBufferToImage = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdCopyBufferToImage")); + CmdCopyImageToBuffer = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdCopyImageToBuffer")); + CmdUpdateBuffer = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdUpdateBuffer")); + CmdFillBuffer = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdFillBuffer")); + CmdClearColorImage = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdClearColorImage")); + CmdClearDepthStencilImage = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdClearDepthStencilImage")); + CmdClearAttachments = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdClearAttachments")); + CmdResolveImage = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdResolveImage")); + CmdSetEvent = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdSetEvent")); + CmdResetEvent = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdResetEvent")); + CmdWaitEvents = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdWaitEvents")); + CmdPipelineBarrier = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdPipelineBarrier")); + CmdBeginQuery = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdBeginQuery")); + CmdEndQuery = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdEndQuery")); + CmdResetQueryPool = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdResetQueryPool")); + CmdWriteTimestamp = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdWriteTimestamp")); + CmdCopyQueryPoolResults = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdCopyQueryPoolResults")); + CmdPushConstants = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdPushConstants")); + CmdBeginRenderPass = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdBeginRenderPass")); + CmdNextSubpass = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdNextSubpass")); + CmdEndRenderPass = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdEndRenderPass")); + CmdExecuteCommands = reinterpret_cast(GetInstanceProcAddr(instance, "vkCmdExecuteCommands")); + CreateSwapchainKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateSwapchainKHR")); + DestroySwapchainKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkDestroySwapchainKHR")); + GetSwapchainImagesKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetSwapchainImagesKHR")); + AcquireNextImageKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkAcquireNextImageKHR")); + QueuePresentKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkQueuePresentKHR")); + CreateSharedSwapchainsKHR = reinterpret_cast(GetInstanceProcAddr(instance, "vkCreateSharedSwapchainsKHR")); +} + +void init_dispatch_table_bottom(VkInstance instance, VkDevice dev) +{ + GetDeviceProcAddr = reinterpret_cast(GetInstanceProcAddr(instance, "vkGetDeviceProcAddr")); + GetDeviceProcAddr = reinterpret_cast(GetDeviceProcAddr(dev, "vkGetDeviceProcAddr")); + + DestroyDevice = reinterpret_cast(GetDeviceProcAddr(dev, "vkDestroyDevice")); + GetDeviceQueue = reinterpret_cast(GetDeviceProcAddr(dev, "vkGetDeviceQueue")); + QueueSubmit = reinterpret_cast(GetDeviceProcAddr(dev, "vkQueueSubmit")); + QueueWaitIdle = reinterpret_cast(GetDeviceProcAddr(dev, "vkQueueWaitIdle")); + DeviceWaitIdle = reinterpret_cast(GetDeviceProcAddr(dev, "vkDeviceWaitIdle")); + AllocateMemory = reinterpret_cast(GetDeviceProcAddr(dev, "vkAllocateMemory")); + FreeMemory = reinterpret_cast(GetDeviceProcAddr(dev, "vkFreeMemory")); + MapMemory = reinterpret_cast(GetDeviceProcAddr(dev, "vkMapMemory")); + UnmapMemory = reinterpret_cast(GetDeviceProcAddr(dev, "vkUnmapMemory")); + FlushMappedMemoryRanges = reinterpret_cast(GetDeviceProcAddr(dev, "vkFlushMappedMemoryRanges")); + InvalidateMappedMemoryRanges = reinterpret_cast(GetDeviceProcAddr(dev, "vkInvalidateMappedMemoryRanges")); + GetDeviceMemoryCommitment = reinterpret_cast(GetDeviceProcAddr(dev, "vkGetDeviceMemoryCommitment")); + BindBufferMemory = reinterpret_cast(GetDeviceProcAddr(dev, "vkBindBufferMemory")); + BindImageMemory = reinterpret_cast(GetDeviceProcAddr(dev, "vkBindImageMemory")); + GetBufferMemoryRequirements = reinterpret_cast(GetDeviceProcAddr(dev, "vkGetBufferMemoryRequirements")); + GetImageMemoryRequirements = reinterpret_cast(GetDeviceProcAddr(dev, "vkGetImageMemoryRequirements")); + GetImageSparseMemoryRequirements = reinterpret_cast(GetDeviceProcAddr(dev, "vkGetImageSparseMemoryRequirements")); + QueueBindSparse = reinterpret_cast(GetDeviceProcAddr(dev, "vkQueueBindSparse")); + CreateFence = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreateFence")); + DestroyFence = reinterpret_cast(GetDeviceProcAddr(dev, "vkDestroyFence")); + ResetFences = reinterpret_cast(GetDeviceProcAddr(dev, "vkResetFences")); + GetFenceStatus = reinterpret_cast(GetDeviceProcAddr(dev, "vkGetFenceStatus")); + WaitForFences = reinterpret_cast(GetDeviceProcAddr(dev, "vkWaitForFences")); + CreateSemaphore = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreateSemaphore")); + DestroySemaphore = reinterpret_cast(GetDeviceProcAddr(dev, "vkDestroySemaphore")); + CreateEvent = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreateEvent")); + DestroyEvent = reinterpret_cast(GetDeviceProcAddr(dev, "vkDestroyEvent")); + GetEventStatus = reinterpret_cast(GetDeviceProcAddr(dev, "vkGetEventStatus")); + SetEvent = reinterpret_cast(GetDeviceProcAddr(dev, "vkSetEvent")); + ResetEvent = reinterpret_cast(GetDeviceProcAddr(dev, "vkResetEvent")); + CreateQueryPool = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreateQueryPool")); + DestroyQueryPool = reinterpret_cast(GetDeviceProcAddr(dev, "vkDestroyQueryPool")); + GetQueryPoolResults = reinterpret_cast(GetDeviceProcAddr(dev, "vkGetQueryPoolResults")); + CreateBuffer = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreateBuffer")); + DestroyBuffer = reinterpret_cast(GetDeviceProcAddr(dev, "vkDestroyBuffer")); + CreateBufferView = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreateBufferView")); + DestroyBufferView = reinterpret_cast(GetDeviceProcAddr(dev, "vkDestroyBufferView")); + CreateImage = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreateImage")); + DestroyImage = reinterpret_cast(GetDeviceProcAddr(dev, "vkDestroyImage")); + GetImageSubresourceLayout = reinterpret_cast(GetDeviceProcAddr(dev, "vkGetImageSubresourceLayout")); + CreateImageView = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreateImageView")); + DestroyImageView = reinterpret_cast(GetDeviceProcAddr(dev, "vkDestroyImageView")); + CreateShaderModule = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreateShaderModule")); + DestroyShaderModule = reinterpret_cast(GetDeviceProcAddr(dev, "vkDestroyShaderModule")); + CreatePipelineCache = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreatePipelineCache")); + DestroyPipelineCache = reinterpret_cast(GetDeviceProcAddr(dev, "vkDestroyPipelineCache")); + GetPipelineCacheData = reinterpret_cast(GetDeviceProcAddr(dev, "vkGetPipelineCacheData")); + MergePipelineCaches = reinterpret_cast(GetDeviceProcAddr(dev, "vkMergePipelineCaches")); + CreateGraphicsPipelines = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreateGraphicsPipelines")); + CreateComputePipelines = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreateComputePipelines")); + DestroyPipeline = reinterpret_cast(GetDeviceProcAddr(dev, "vkDestroyPipeline")); + CreatePipelineLayout = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreatePipelineLayout")); + DestroyPipelineLayout = reinterpret_cast(GetDeviceProcAddr(dev, "vkDestroyPipelineLayout")); + CreateSampler = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreateSampler")); + DestroySampler = reinterpret_cast(GetDeviceProcAddr(dev, "vkDestroySampler")); + CreateDescriptorSetLayout = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreateDescriptorSetLayout")); + DestroyDescriptorSetLayout = reinterpret_cast(GetDeviceProcAddr(dev, "vkDestroyDescriptorSetLayout")); + CreateDescriptorPool = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreateDescriptorPool")); + DestroyDescriptorPool = reinterpret_cast(GetDeviceProcAddr(dev, "vkDestroyDescriptorPool")); + ResetDescriptorPool = reinterpret_cast(GetDeviceProcAddr(dev, "vkResetDescriptorPool")); + AllocateDescriptorSets = reinterpret_cast(GetDeviceProcAddr(dev, "vkAllocateDescriptorSets")); + FreeDescriptorSets = reinterpret_cast(GetDeviceProcAddr(dev, "vkFreeDescriptorSets")); + UpdateDescriptorSets = reinterpret_cast(GetDeviceProcAddr(dev, "vkUpdateDescriptorSets")); + CreateFramebuffer = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreateFramebuffer")); + DestroyFramebuffer = reinterpret_cast(GetDeviceProcAddr(dev, "vkDestroyFramebuffer")); + CreateRenderPass = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreateRenderPass")); + DestroyRenderPass = reinterpret_cast(GetDeviceProcAddr(dev, "vkDestroyRenderPass")); + GetRenderAreaGranularity = reinterpret_cast(GetDeviceProcAddr(dev, "vkGetRenderAreaGranularity")); + CreateCommandPool = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreateCommandPool")); + DestroyCommandPool = reinterpret_cast(GetDeviceProcAddr(dev, "vkDestroyCommandPool")); + ResetCommandPool = reinterpret_cast(GetDeviceProcAddr(dev, "vkResetCommandPool")); + AllocateCommandBuffers = reinterpret_cast(GetDeviceProcAddr(dev, "vkAllocateCommandBuffers")); + FreeCommandBuffers = reinterpret_cast(GetDeviceProcAddr(dev, "vkFreeCommandBuffers")); + BeginCommandBuffer = reinterpret_cast(GetDeviceProcAddr(dev, "vkBeginCommandBuffer")); + EndCommandBuffer = reinterpret_cast(GetDeviceProcAddr(dev, "vkEndCommandBuffer")); + ResetCommandBuffer = reinterpret_cast(GetDeviceProcAddr(dev, "vkResetCommandBuffer")); + CmdBindPipeline = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdBindPipeline")); + CmdSetViewport = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdSetViewport")); + CmdSetScissor = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdSetScissor")); + CmdSetLineWidth = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdSetLineWidth")); + CmdSetDepthBias = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdSetDepthBias")); + CmdSetBlendConstants = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdSetBlendConstants")); + CmdSetDepthBounds = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdSetDepthBounds")); + CmdSetStencilCompareMask = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdSetStencilCompareMask")); + CmdSetStencilWriteMask = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdSetStencilWriteMask")); + CmdSetStencilReference = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdSetStencilReference")); + CmdBindDescriptorSets = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdBindDescriptorSets")); + CmdBindIndexBuffer = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdBindIndexBuffer")); + CmdBindVertexBuffers = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdBindVertexBuffers")); + CmdDraw = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdDraw")); + CmdDrawIndexed = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdDrawIndexed")); + CmdDrawIndirect = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdDrawIndirect")); + CmdDrawIndexedIndirect = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdDrawIndexedIndirect")); + CmdDispatch = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdDispatch")); + CmdDispatchIndirect = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdDispatchIndirect")); + CmdCopyBuffer = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdCopyBuffer")); + CmdCopyImage = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdCopyImage")); + CmdBlitImage = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdBlitImage")); + CmdCopyBufferToImage = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdCopyBufferToImage")); + CmdCopyImageToBuffer = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdCopyImageToBuffer")); + CmdUpdateBuffer = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdUpdateBuffer")); + CmdFillBuffer = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdFillBuffer")); + CmdClearColorImage = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdClearColorImage")); + CmdClearDepthStencilImage = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdClearDepthStencilImage")); + CmdClearAttachments = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdClearAttachments")); + CmdResolveImage = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdResolveImage")); + CmdSetEvent = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdSetEvent")); + CmdResetEvent = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdResetEvent")); + CmdWaitEvents = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdWaitEvents")); + CmdPipelineBarrier = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdPipelineBarrier")); + CmdBeginQuery = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdBeginQuery")); + CmdEndQuery = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdEndQuery")); + CmdResetQueryPool = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdResetQueryPool")); + CmdWriteTimestamp = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdWriteTimestamp")); + CmdCopyQueryPoolResults = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdCopyQueryPoolResults")); + CmdPushConstants = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdPushConstants")); + CmdBeginRenderPass = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdBeginRenderPass")); + CmdNextSubpass = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdNextSubpass")); + CmdEndRenderPass = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdEndRenderPass")); + CmdExecuteCommands = reinterpret_cast(GetDeviceProcAddr(dev, "vkCmdExecuteCommands")); + CreateSwapchainKHR = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreateSwapchainKHR")); + DestroySwapchainKHR = reinterpret_cast(GetDeviceProcAddr(dev, "vkDestroySwapchainKHR")); + GetSwapchainImagesKHR = reinterpret_cast(GetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR")); + AcquireNextImageKHR = reinterpret_cast(GetDeviceProcAddr(dev, "vkAcquireNextImageKHR")); + QueuePresentKHR = reinterpret_cast(GetDeviceProcAddr(dev, "vkQueuePresentKHR")); + CreateSharedSwapchainsKHR = reinterpret_cast(GetDeviceProcAddr(dev, "vkCreateSharedSwapchainsKHR")); +} + +} // namespace vk diff --git a/Demos/LunarG-VulkanSamples/Hologram/HelpersDispatchTable.h b/Demos/LunarG-VulkanSamples/Hologram/HelpersDispatchTable.h new file mode 100644 index 00000000..78ef3177 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/HelpersDispatchTable.h @@ -0,0 +1,219 @@ +// This file is generated. +#ifndef HELPERSDISPATCHTABLE_H +#define HELPERSDISPATCHTABLE_H + +#include + +namespace vk { + +// VK_core +extern PFN_vkCreateInstance CreateInstance; +extern PFN_vkDestroyInstance DestroyInstance; +extern PFN_vkEnumeratePhysicalDevices EnumeratePhysicalDevices; +extern PFN_vkGetPhysicalDeviceFeatures GetPhysicalDeviceFeatures; +extern PFN_vkGetPhysicalDeviceFormatProperties GetPhysicalDeviceFormatProperties; +extern PFN_vkGetPhysicalDeviceImageFormatProperties GetPhysicalDeviceImageFormatProperties; +extern PFN_vkGetPhysicalDeviceProperties GetPhysicalDeviceProperties; +extern PFN_vkGetPhysicalDeviceQueueFamilyProperties GetPhysicalDeviceQueueFamilyProperties; +extern PFN_vkGetPhysicalDeviceMemoryProperties GetPhysicalDeviceMemoryProperties; +extern PFN_vkGetInstanceProcAddr GetInstanceProcAddr; +extern PFN_vkGetDeviceProcAddr GetDeviceProcAddr; +extern PFN_vkCreateDevice CreateDevice; +extern PFN_vkDestroyDevice DestroyDevice; +extern PFN_vkEnumerateInstanceExtensionProperties EnumerateInstanceExtensionProperties; +extern PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties; +extern PFN_vkEnumerateInstanceLayerProperties EnumerateInstanceLayerProperties; +extern PFN_vkGetDeviceQueue GetDeviceQueue; +extern PFN_vkQueueSubmit QueueSubmit; +extern PFN_vkQueueWaitIdle QueueWaitIdle; +extern PFN_vkDeviceWaitIdle DeviceWaitIdle; +extern PFN_vkAllocateMemory AllocateMemory; +extern PFN_vkFreeMemory FreeMemory; +extern PFN_vkMapMemory MapMemory; +extern PFN_vkUnmapMemory UnmapMemory; +extern PFN_vkFlushMappedMemoryRanges FlushMappedMemoryRanges; +extern PFN_vkInvalidateMappedMemoryRanges InvalidateMappedMemoryRanges; +extern PFN_vkGetDeviceMemoryCommitment GetDeviceMemoryCommitment; +extern PFN_vkBindBufferMemory BindBufferMemory; +extern PFN_vkBindImageMemory BindImageMemory; +extern PFN_vkGetBufferMemoryRequirements GetBufferMemoryRequirements; +extern PFN_vkGetImageMemoryRequirements GetImageMemoryRequirements; +extern PFN_vkGetImageSparseMemoryRequirements GetImageSparseMemoryRequirements; +extern PFN_vkGetPhysicalDeviceSparseImageFormatProperties GetPhysicalDeviceSparseImageFormatProperties; +extern PFN_vkQueueBindSparse QueueBindSparse; +extern PFN_vkCreateFence CreateFence; +extern PFN_vkDestroyFence DestroyFence; +extern PFN_vkResetFences ResetFences; +extern PFN_vkGetFenceStatus GetFenceStatus; +extern PFN_vkWaitForFences WaitForFences; +extern PFN_vkCreateSemaphore CreateSemaphore; +extern PFN_vkDestroySemaphore DestroySemaphore; +extern PFN_vkCreateEvent CreateEvent; +extern PFN_vkDestroyEvent DestroyEvent; +extern PFN_vkGetEventStatus GetEventStatus; +extern PFN_vkSetEvent SetEvent; +extern PFN_vkResetEvent ResetEvent; +extern PFN_vkCreateQueryPool CreateQueryPool; +extern PFN_vkDestroyQueryPool DestroyQueryPool; +extern PFN_vkGetQueryPoolResults GetQueryPoolResults; +extern PFN_vkCreateBuffer CreateBuffer; +extern PFN_vkDestroyBuffer DestroyBuffer; +extern PFN_vkCreateBufferView CreateBufferView; +extern PFN_vkDestroyBufferView DestroyBufferView; +extern PFN_vkCreateImage CreateImage; +extern PFN_vkDestroyImage DestroyImage; +extern PFN_vkGetImageSubresourceLayout GetImageSubresourceLayout; +extern PFN_vkCreateImageView CreateImageView; +extern PFN_vkDestroyImageView DestroyImageView; +extern PFN_vkCreateShaderModule CreateShaderModule; +extern PFN_vkDestroyShaderModule DestroyShaderModule; +extern PFN_vkCreatePipelineCache CreatePipelineCache; +extern PFN_vkDestroyPipelineCache DestroyPipelineCache; +extern PFN_vkGetPipelineCacheData GetPipelineCacheData; +extern PFN_vkMergePipelineCaches MergePipelineCaches; +extern PFN_vkCreateGraphicsPipelines CreateGraphicsPipelines; +extern PFN_vkCreateComputePipelines CreateComputePipelines; +extern PFN_vkDestroyPipeline DestroyPipeline; +extern PFN_vkCreatePipelineLayout CreatePipelineLayout; +extern PFN_vkDestroyPipelineLayout DestroyPipelineLayout; +extern PFN_vkCreateSampler CreateSampler; +extern PFN_vkDestroySampler DestroySampler; +extern PFN_vkCreateDescriptorSetLayout CreateDescriptorSetLayout; +extern PFN_vkDestroyDescriptorSetLayout DestroyDescriptorSetLayout; +extern PFN_vkCreateDescriptorPool CreateDescriptorPool; +extern PFN_vkDestroyDescriptorPool DestroyDescriptorPool; +extern PFN_vkResetDescriptorPool ResetDescriptorPool; +extern PFN_vkAllocateDescriptorSets AllocateDescriptorSets; +extern PFN_vkFreeDescriptorSets FreeDescriptorSets; +extern PFN_vkUpdateDescriptorSets UpdateDescriptorSets; +extern PFN_vkCreateFramebuffer CreateFramebuffer; +extern PFN_vkDestroyFramebuffer DestroyFramebuffer; +extern PFN_vkCreateRenderPass CreateRenderPass; +extern PFN_vkDestroyRenderPass DestroyRenderPass; +extern PFN_vkGetRenderAreaGranularity GetRenderAreaGranularity; +extern PFN_vkCreateCommandPool CreateCommandPool; +extern PFN_vkDestroyCommandPool DestroyCommandPool; +extern PFN_vkResetCommandPool ResetCommandPool; +extern PFN_vkAllocateCommandBuffers AllocateCommandBuffers; +extern PFN_vkFreeCommandBuffers FreeCommandBuffers; +extern PFN_vkBeginCommandBuffer BeginCommandBuffer; +extern PFN_vkEndCommandBuffer EndCommandBuffer; +extern PFN_vkResetCommandBuffer ResetCommandBuffer; +extern PFN_vkCmdBindPipeline CmdBindPipeline; +extern PFN_vkCmdSetViewport CmdSetViewport; +extern PFN_vkCmdSetScissor CmdSetScissor; +extern PFN_vkCmdSetLineWidth CmdSetLineWidth; +extern PFN_vkCmdSetDepthBias CmdSetDepthBias; +extern PFN_vkCmdSetBlendConstants CmdSetBlendConstants; +extern PFN_vkCmdSetDepthBounds CmdSetDepthBounds; +extern PFN_vkCmdSetStencilCompareMask CmdSetStencilCompareMask; +extern PFN_vkCmdSetStencilWriteMask CmdSetStencilWriteMask; +extern PFN_vkCmdSetStencilReference CmdSetStencilReference; +extern PFN_vkCmdBindDescriptorSets CmdBindDescriptorSets; +extern PFN_vkCmdBindIndexBuffer CmdBindIndexBuffer; +extern PFN_vkCmdBindVertexBuffers CmdBindVertexBuffers; +extern PFN_vkCmdDraw CmdDraw; +extern PFN_vkCmdDrawIndexed CmdDrawIndexed; +extern PFN_vkCmdDrawIndirect CmdDrawIndirect; +extern PFN_vkCmdDrawIndexedIndirect CmdDrawIndexedIndirect; +extern PFN_vkCmdDispatch CmdDispatch; +extern PFN_vkCmdDispatchIndirect CmdDispatchIndirect; +extern PFN_vkCmdCopyBuffer CmdCopyBuffer; +extern PFN_vkCmdCopyImage CmdCopyImage; +extern PFN_vkCmdBlitImage CmdBlitImage; +extern PFN_vkCmdCopyBufferToImage CmdCopyBufferToImage; +extern PFN_vkCmdCopyImageToBuffer CmdCopyImageToBuffer; +extern PFN_vkCmdUpdateBuffer CmdUpdateBuffer; +extern PFN_vkCmdFillBuffer CmdFillBuffer; +extern PFN_vkCmdClearColorImage CmdClearColorImage; +extern PFN_vkCmdClearDepthStencilImage CmdClearDepthStencilImage; +extern PFN_vkCmdClearAttachments CmdClearAttachments; +extern PFN_vkCmdResolveImage CmdResolveImage; +extern PFN_vkCmdSetEvent CmdSetEvent; +extern PFN_vkCmdResetEvent CmdResetEvent; +extern PFN_vkCmdWaitEvents CmdWaitEvents; +extern PFN_vkCmdPipelineBarrier CmdPipelineBarrier; +extern PFN_vkCmdBeginQuery CmdBeginQuery; +extern PFN_vkCmdEndQuery CmdEndQuery; +extern PFN_vkCmdResetQueryPool CmdResetQueryPool; +extern PFN_vkCmdWriteTimestamp CmdWriteTimestamp; +extern PFN_vkCmdCopyQueryPoolResults CmdCopyQueryPoolResults; +extern PFN_vkCmdPushConstants CmdPushConstants; +extern PFN_vkCmdBeginRenderPass CmdBeginRenderPass; +extern PFN_vkCmdNextSubpass CmdNextSubpass; +extern PFN_vkCmdEndRenderPass CmdEndRenderPass; +extern PFN_vkCmdExecuteCommands CmdExecuteCommands; + +// VK_KHR_surface +extern PFN_vkDestroySurfaceKHR DestroySurfaceKHR; +extern PFN_vkGetPhysicalDeviceSurfaceSupportKHR GetPhysicalDeviceSurfaceSupportKHR; +extern PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR GetPhysicalDeviceSurfaceCapabilitiesKHR; +extern PFN_vkGetPhysicalDeviceSurfaceFormatsKHR GetPhysicalDeviceSurfaceFormatsKHR; +extern PFN_vkGetPhysicalDeviceSurfacePresentModesKHR GetPhysicalDeviceSurfacePresentModesKHR; + +// VK_KHR_swapchain +extern PFN_vkCreateSwapchainKHR CreateSwapchainKHR; +extern PFN_vkDestroySwapchainKHR DestroySwapchainKHR; +extern PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR; +extern PFN_vkAcquireNextImageKHR AcquireNextImageKHR; +extern PFN_vkQueuePresentKHR QueuePresentKHR; + +// VK_KHR_display +extern PFN_vkGetPhysicalDeviceDisplayPropertiesKHR GetPhysicalDeviceDisplayPropertiesKHR; +extern PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR GetPhysicalDeviceDisplayPlanePropertiesKHR; +extern PFN_vkGetDisplayPlaneSupportedDisplaysKHR GetDisplayPlaneSupportedDisplaysKHR; +extern PFN_vkGetDisplayModePropertiesKHR GetDisplayModePropertiesKHR; +extern PFN_vkCreateDisplayModeKHR CreateDisplayModeKHR; +extern PFN_vkGetDisplayPlaneCapabilitiesKHR GetDisplayPlaneCapabilitiesKHR; +extern PFN_vkCreateDisplayPlaneSurfaceKHR CreateDisplayPlaneSurfaceKHR; + +// VK_KHR_display_swapchain +extern PFN_vkCreateSharedSwapchainsKHR CreateSharedSwapchainsKHR; + +#ifdef VK_USE_PLATFORM_XLIB_KHR +// VK_KHR_xlib_surface +extern PFN_vkCreateXlibSurfaceKHR CreateXlibSurfaceKHR; +extern PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR GetPhysicalDeviceXlibPresentationSupportKHR; +#endif + +#ifdef VK_USE_PLATFORM_XCB_KHR +// VK_KHR_xcb_surface +extern PFN_vkCreateXcbSurfaceKHR CreateXcbSurfaceKHR; +extern PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR GetPhysicalDeviceXcbPresentationSupportKHR; +#endif + +#ifdef VK_USE_PLATFORM_WAYLAND_KHR +// VK_KHR_wayland_surface +extern PFN_vkCreateWaylandSurfaceKHR CreateWaylandSurfaceKHR; +extern PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR GetPhysicalDeviceWaylandPresentationSupportKHR; +#endif + +#ifdef VK_USE_PLATFORM_MIR_KHR +// VK_KHR_mir_surface +extern PFN_vkCreateMirSurfaceKHR CreateMirSurfaceKHR; +extern PFN_vkGetPhysicalDeviceMirPresentationSupportKHR GetPhysicalDeviceMirPresentationSupportKHR; +#endif + +#ifdef VK_USE_PLATFORM_ANDROID_KHR +// VK_KHR_android_surface +extern PFN_vkCreateAndroidSurfaceKHR CreateAndroidSurfaceKHR; +#endif + +#ifdef VK_USE_PLATFORM_WIN32_KHR +// VK_KHR_win32_surface +extern PFN_vkCreateWin32SurfaceKHR CreateWin32SurfaceKHR; +extern PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR GetPhysicalDeviceWin32PresentationSupportKHR; +#endif + +// VK_EXT_debug_report +extern PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT; +extern PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT; +extern PFN_vkDebugReportMessageEXT DebugReportMessageEXT; + +void init_dispatch_table_top(PFN_vkGetInstanceProcAddr get_instance_proc_addr); +void init_dispatch_table_middle(VkInstance instance, bool include_bottom); +void init_dispatch_table_bottom(VkInstance instance, VkDevice dev); + +} // namespace vk + +#endif // HELPERSDISPATCHTABLE_H diff --git a/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/project.pbxproj b/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/project.pbxproj new file mode 100644 index 00000000..5aac2a50 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/project.pbxproj @@ -0,0 +1,677 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + A9096E531F7EF13000DFBEA6 /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9096E521F7EF13000DFBEA6 /* IOSurface.framework */; }; + A99789AF1CD3D4E2005E7DAC /* Hologram.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A99789831CD3D4E2005E7DAC /* Hologram.cpp */; }; + A99789B01CD3D4E2005E7DAC /* Hologram.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A99789831CD3D4E2005E7DAC /* Hologram.cpp */; }; + A99789B11CD3D4E2005E7DAC /* Hologram.frag in Resources */ = {isa = PBXBuildFile; fileRef = A99789841CD3D4E2005E7DAC /* Hologram.frag */; }; + A99789B21CD3D4E2005E7DAC /* Hologram.frag in Resources */ = {isa = PBXBuildFile; fileRef = A99789841CD3D4E2005E7DAC /* Hologram.frag */; }; + A99789B31CD3D4E2005E7DAC /* Hologram.push_constant.vert in Resources */ = {isa = PBXBuildFile; fileRef = A99789861CD3D4E2005E7DAC /* Hologram.push_constant.vert */; }; + A99789B41CD3D4E2005E7DAC /* Hologram.push_constant.vert in Resources */ = {isa = PBXBuildFile; fileRef = A99789861CD3D4E2005E7DAC /* Hologram.push_constant.vert */; }; + A99789B51CD3D4E2005E7DAC /* Hologram.vert in Resources */ = {isa = PBXBuildFile; fileRef = A99789871CD3D4E2005E7DAC /* Hologram.vert */; }; + A99789B61CD3D4E2005E7DAC /* Hologram.vert in Resources */ = {isa = PBXBuildFile; fileRef = A99789871CD3D4E2005E7DAC /* Hologram.vert */; }; + A99789B71CD3D4E2005E7DAC /* Main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A99789881CD3D4E2005E7DAC /* Main.cpp */; }; + A99789B81CD3D4E2005E7DAC /* Main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A99789881CD3D4E2005E7DAC /* Main.cpp */; }; + A99789B91CD3D4E2005E7DAC /* Meshes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A99789891CD3D4E2005E7DAC /* Meshes.cpp */; }; + A99789BA1CD3D4E2005E7DAC /* Meshes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A99789891CD3D4E2005E7DAC /* Meshes.cpp */; }; + A99789BD1CD3D4E2005E7DAC /* Shell.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A997898D1CD3D4E2005E7DAC /* Shell.cpp */; }; + A99789BE1CD3D4E2005E7DAC /* Shell.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A997898D1CD3D4E2005E7DAC /* Shell.cpp */; }; + A99789C51CD3D4E2005E7DAC /* Simulation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A99789951CD3D4E2005E7DAC /* Simulation.cpp */; }; + A99789C61CD3D4E2005E7DAC /* Simulation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A99789951CD3D4E2005E7DAC /* Simulation.cpp */; }; + A99789C91CD3D819005E7DAC /* ShellMVK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A99789C71CD3D819005E7DAC /* ShellMVK.cpp */; }; + A99789CA1CD3D819005E7DAC /* ShellMVK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A99789C71CD3D819005E7DAC /* ShellMVK.cpp */; }; + A9B67B781C3AAE9800373FFD /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A9B67B6C1C3AAE9800373FFD /* AppDelegate.m */; }; + A9B67B7A1C3AAE9800373FFD /* DemoViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9B67B6F1C3AAE9800373FFD /* DemoViewController.mm */; }; + A9B67B7C1C3AAE9800373FFD /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A9B67B711C3AAE9800373FFD /* main.m */; }; + A9B67B7D1C3AAE9800373FFD /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A9B67B741C3AAE9800373FFD /* Default-568h@2x.png */; }; + A9B67B7E1C3AAE9800373FFD /* Default~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = A9B67B751C3AAE9800373FFD /* Default~ipad.png */; }; + A9B67B7F1C3AAE9800373FFD /* Icon.png in Resources */ = {isa = PBXBuildFile; fileRef = A9B67B761C3AAE9800373FFD /* Icon.png */; }; + A9B67B801C3AAE9800373FFD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A9B67B771C3AAE9800373FFD /* Main.storyboard */; }; + A9B67B8C1C3AAEA200373FFD /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A9B67B831C3AAEA200373FFD /* AppDelegate.m */; }; + A9B67B8D1C3AAEA200373FFD /* DemoViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9B67B851C3AAEA200373FFD /* DemoViewController.mm */; }; + A9B67B8F1C3AAEA200373FFD /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A9B67B871C3AAEA200373FFD /* main.m */; }; + A9B67B901C3AAEA200373FFD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A9B67B8A1C3AAEA200373FFD /* Main.storyboard */; }; + A9B67B911C3AAEA200373FFD /* macOS.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A9B67B8B1C3AAEA200373FFD /* macOS.xcassets */; }; + A9D516F81CD575E300097D96 /* HelpersDispatchTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D516F61CD575E300097D96 /* HelpersDispatchTable.cpp */; }; + A9D516F91CD575E300097D96 /* HelpersDispatchTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D516F61CD575E300097D96 /* HelpersDispatchTable.cpp */; }; + A9F4FB461CF68822003FA0C3 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = A9F4FB451CF68822003FA0C3 /* libc++.tbd */; }; + A9F4FB481CF6882E003FA0C3 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = A9F4FB471CF6882E003FA0C3 /* libc++.tbd */; }; + A9F4FB4A1CF688A5003FA0C3 /* MoltenVK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9F4FB491CF688A5003FA0C3 /* MoltenVK.framework */; }; + A9F4FB4C1CF688B5003FA0C3 /* MoltenVK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9F4FB4B1CF688B5003FA0C3 /* MoltenVK.framework */; }; + A9F4FB4F1CF68C79003FA0C3 /* MoltenVKGLSLToSPIRVConverter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9F4FB4E1CF68C79003FA0C3 /* MoltenVKGLSLToSPIRVConverter.framework */; }; + A9F4FB511CF68C83003FA0C3 /* MoltenVKGLSLToSPIRVConverter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9F4FB501CF68C83003FA0C3 /* MoltenVKGLSLToSPIRVConverter.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 1D6058910D05DD3D006BFB54 /* Hologram.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Hologram.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 2D500B990D5A79CF00DBA0E3 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + A900B1A21B5EAA1C00150D60 /* libc++.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libc++.dylib"; path = "usr/lib/libc++.dylib"; sourceTree = SDKROOT; }; + A90941BB1C582DF40094110D /* MoltenVKGLSLToSPIRVConverter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVKGLSLToSPIRVConverter.framework; path = "../../../../../../../../Library/Developer/Xcode/DerivedData/VulkanSamples-emusmkeclqfwfncmwqpcvdzumhpu/Build/Products/Release/MoltenVKGLSLToSPIRVConverter.framework"; sourceTree = ""; }; + A9096E521F7EF13000DFBEA6 /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.0.sdk/System/Library/Frameworks/IOSurface.framework; sourceTree = DEVELOPER_DIR; }; + A93CC3701CD56B8F00EB8A56 /* generate-dispatch-table */ = {isa = PBXFileReference; explicitFileType = text.script.python; path = "generate-dispatch-table"; sourceTree = ""; }; + A93CC3711CD56FD600EB8A56 /* CMakeLists.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + A94A67231B7BDE9B00F6D7C4 /* MetalGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalGL.framework; path = ../../MetalGL/macOS/MetalGL.framework; sourceTree = ""; }; + A94A67241B7BDE9B00F6D7C4 /* MetalGLShaderConverter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalGLShaderConverter.framework; path = ../../MetalGLShaderConverter/macOS/MetalGLShaderConverter.framework; sourceTree = ""; }; + A977BCFE1B66BB010067E5BF /* Hologram.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Hologram.app; sourceTree = BUILT_PRODUCTS_DIR; }; + A977BD211B67186B0067E5BF /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/AppKit.framework; sourceTree = DEVELOPER_DIR; }; + A977BD221B67186B0067E5BF /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; }; + A977BD231B67186B0067E5BF /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + A977BD251B67186B0067E5BF /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/Metal.framework; sourceTree = DEVELOPER_DIR; }; + A977BD261B67186B0067E5BF /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; }; + A997897F1CD3D4E2005E7DAC /* Game.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Game.h; sourceTree = ""; }; + A99789821CD3D4E2005E7DAC /* Helpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Helpers.h; sourceTree = ""; }; + A99789831CD3D4E2005E7DAC /* Hologram.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Hologram.cpp; sourceTree = ""; }; + A99789841CD3D4E2005E7DAC /* Hologram.frag */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; path = Hologram.frag; sourceTree = ""; }; + A99789851CD3D4E2005E7DAC /* Hologram.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Hologram.h; sourceTree = ""; }; + A99789861CD3D4E2005E7DAC /* Hologram.push_constant.vert */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; path = Hologram.push_constant.vert; sourceTree = ""; }; + A99789871CD3D4E2005E7DAC /* Hologram.vert */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; path = Hologram.vert; sourceTree = ""; }; + A99789881CD3D4E2005E7DAC /* Main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Main.cpp; sourceTree = ""; }; + A99789891CD3D4E2005E7DAC /* Meshes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Meshes.cpp; sourceTree = ""; }; + A997898A1CD3D4E2005E7DAC /* Meshes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Meshes.h; sourceTree = ""; }; + A997898B1CD3D4E2005E7DAC /* Meshes.teapot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Meshes.teapot.h; sourceTree = ""; }; + A997898D1CD3D4E2005E7DAC /* Shell.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Shell.cpp; sourceTree = ""; }; + A997898E1CD3D4E2005E7DAC /* Shell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Shell.h; sourceTree = ""; }; + A99789951CD3D4E2005E7DAC /* Simulation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Simulation.cpp; sourceTree = ""; }; + A99789961CD3D4E2005E7DAC /* Simulation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Simulation.h; sourceTree = ""; }; + A99789C71CD3D819005E7DAC /* ShellMVK.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ShellMVK.cpp; sourceTree = SOURCE_ROOT; }; + A99789C81CD3D819005E7DAC /* ShellMVK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShellMVK.h; sourceTree = SOURCE_ROOT; }; + A99789E01CD4186E005E7DAC /* MoltenVKGLSLToSPIRVConverter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVKGLSLToSPIRVConverter.framework; path = "../../../../MoltenShaderConverter/build/Debug-iphoneos/MoltenVKGLSLToSPIRVConverter.framework"; sourceTree = ""; }; + A99789E21CD4187A005E7DAC /* MoltenVKGLSLToSPIRVConverter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVKGLSLToSPIRVConverter.framework; path = ../../../../MoltenShaderConverter/build/Debug/MoltenVKGLSLToSPIRVConverter.framework; sourceTree = ""; }; + A9A222171B5D69F40050A5F9 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; }; + A9B67B6B1C3AAE9800373FFD /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + A9B67B6C1C3AAE9800373FFD /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + A9B67B6E1C3AAE9800373FFD /* DemoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoViewController.h; sourceTree = ""; }; + A9B67B6F1C3AAE9800373FFD /* DemoViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DemoViewController.mm; sourceTree = ""; }; + A9B67B701C3AAE9800373FFD /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A9B67B711C3AAE9800373FFD /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + A9B67B721C3AAE9800373FFD /* Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Prefix.pch; sourceTree = ""; }; + A9B67B741C3AAE9800373FFD /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; + A9B67B751C3AAE9800373FFD /* Default~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default~ipad.png"; sourceTree = ""; }; + A9B67B761C3AAE9800373FFD /* Icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Icon.png; sourceTree = ""; }; + A9B67B771C3AAE9800373FFD /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; + A9B67B821C3AAEA200373FFD /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + A9B67B831C3AAEA200373FFD /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + A9B67B841C3AAEA200373FFD /* DemoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoViewController.h; sourceTree = ""; }; + A9B67B851C3AAEA200373FFD /* DemoViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DemoViewController.mm; sourceTree = ""; }; + A9B67B861C3AAEA200373FFD /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A9B67B871C3AAEA200373FFD /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + A9B67B881C3AAEA200373FFD /* Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Prefix.pch; sourceTree = ""; }; + A9B67B8A1C3AAEA200373FFD /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; + A9B67B8B1C3AAEA200373FFD /* macOS.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = macOS.xcassets; sourceTree = ""; }; + A9B6B75F1C0F795000A9E33A /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.1.sdk/System/Library/Frameworks/CoreAudio.framework; sourceTree = DEVELOPER_DIR; }; + A9B6B7641C0F795D00A9E33A /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; }; + A9BF67C21C582EC900B8CF77 /* MoltenVKGLSLToSPIRVConverter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVKGLSLToSPIRVConverter.framework; path = "../../../MoltenShaderConverter/build/Debug-iphoneos/MoltenVKGLSLToSPIRVConverter.framework"; sourceTree = ""; }; + A9CDEA271B6A782C00F7B008 /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = System/Library/Frameworks/GLKit.framework; sourceTree = SDKROOT; }; + A9D516F61CD575E300097D96 /* HelpersDispatchTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HelpersDispatchTable.cpp; sourceTree = SOURCE_ROOT; }; + A9D516F71CD575E300097D96 /* HelpersDispatchTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HelpersDispatchTable.h; sourceTree = SOURCE_ROOT; }; + A9E264761B671B0A00FE691A /* libc++.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libc++.dylib"; path = "Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libc++.dylib"; sourceTree = DEVELOPER_DIR; }; + A9F4FB451CF68822003FA0C3 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; + A9F4FB471CF6882E003FA0C3 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/lib/libc++.tbd"; sourceTree = DEVELOPER_DIR; }; + A9F4FB491CF688A5003FA0C3 /* MoltenVK.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVK.framework; path = ../../../../Package/Latest/MoltenVK/iOS/MoltenVK.framework; sourceTree = ""; }; + A9F4FB4B1CF688B5003FA0C3 /* MoltenVK.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVK.framework; path = ../../../../Package/Latest/MoltenVK/macOS/MoltenVK.framework; sourceTree = ""; }; + A9F4FB4E1CF68C79003FA0C3 /* MoltenVKGLSLToSPIRVConverter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVKGLSLToSPIRVConverter.framework; path = ../../../../Package/Latest/MoltenShaderConverter/MoltenVKGLSLToSPIRVConverter/iOS/MoltenVKGLSLToSPIRVConverter.framework; sourceTree = ""; }; + A9F4FB501CF68C83003FA0C3 /* MoltenVKGLSLToSPIRVConverter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVKGLSLToSPIRVConverter.framework; path = ../../../../Package/Latest/MoltenShaderConverter/MoltenVKGLSLToSPIRVConverter/macOS/MoltenVKGLSLToSPIRVConverter.framework; sourceTree = ""; }; + BA240AC10FEFE77A00DE852D /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1D60588F0D05DD3D006BFB54 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A9F4FB4A1CF688A5003FA0C3 /* MoltenVK.framework in Frameworks */, + A9F4FB4F1CF68C79003FA0C3 /* MoltenVKGLSLToSPIRVConverter.framework in Frameworks */, + A9096E531F7EF13000DFBEA6 /* IOSurface.framework in Frameworks */, + A9F4FB481CF6882E003FA0C3 /* libc++.tbd in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A977BCF11B66BB010067E5BF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A9F4FB4C1CF688B5003FA0C3 /* MoltenVK.framework in Frameworks */, + A9F4FB511CF68C83003FA0C3 /* MoltenVKGLSLToSPIRVConverter.framework in Frameworks */, + A9F4FB461CF68822003FA0C3 /* libc++.tbd in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 1D6058910D05DD3D006BFB54 /* Hologram.app */, + A977BCFE1B66BB010067E5BF /* Hologram.app */, + ); + name = Products; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { + isa = PBXGroup; + children = ( + A99789681CD3D4E2005E7DAC /* Hologram */, + A9B67B6A1C3AAE9800373FFD /* iOS */, + A9B67B811C3AAEA200373FFD /* macOS */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = CustomTemplate; + sourceTree = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + A9096E521F7EF13000DFBEA6 /* IOSurface.framework */, + A9F4FB501CF68C83003FA0C3 /* MoltenVKGLSLToSPIRVConverter.framework */, + A9F4FB4E1CF68C79003FA0C3 /* MoltenVKGLSLToSPIRVConverter.framework */, + A9F4FB4B1CF688B5003FA0C3 /* MoltenVK.framework */, + A9F4FB491CF688A5003FA0C3 /* MoltenVK.framework */, + A9F4FB471CF6882E003FA0C3 /* libc++.tbd */, + A9F4FB451CF68822003FA0C3 /* libc++.tbd */, + A99789E21CD4187A005E7DAC /* MoltenVKGLSLToSPIRVConverter.framework */, + A99789E01CD4186E005E7DAC /* MoltenVKGLSLToSPIRVConverter.framework */, + A9BF67C21C582EC900B8CF77 /* MoltenVKGLSLToSPIRVConverter.framework */, + A90941BB1C582DF40094110D /* MoltenVKGLSLToSPIRVConverter.framework */, + A9B6B7641C0F795D00A9E33A /* CoreAudio.framework */, + A9B6B75F1C0F795000A9E33A /* CoreAudio.framework */, + A9ADEC601B6EC2EB00DBA48C /* iOS */, + A9ADEC611B6EC2F300DBA48C /* macOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + A99789681CD3D4E2005E7DAC /* Hologram */ = { + isa = PBXGroup; + children = ( + A9D516F51CD575B000097D96 /* Generated */, + A93CC3711CD56FD600EB8A56 /* CMakeLists.txt */, + A93CC3701CD56B8F00EB8A56 /* generate-dispatch-table */, + A997897F1CD3D4E2005E7DAC /* Game.h */, + A99789821CD3D4E2005E7DAC /* Helpers.h */, + A99789831CD3D4E2005E7DAC /* Hologram.cpp */, + A99789841CD3D4E2005E7DAC /* Hologram.frag */, + A99789851CD3D4E2005E7DAC /* Hologram.h */, + A99789861CD3D4E2005E7DAC /* Hologram.push_constant.vert */, + A99789871CD3D4E2005E7DAC /* Hologram.vert */, + A99789881CD3D4E2005E7DAC /* Main.cpp */, + A99789891CD3D4E2005E7DAC /* Meshes.cpp */, + A997898A1CD3D4E2005E7DAC /* Meshes.h */, + A997898B1CD3D4E2005E7DAC /* Meshes.teapot.h */, + A997898D1CD3D4E2005E7DAC /* Shell.cpp */, + A997898E1CD3D4E2005E7DAC /* Shell.h */, + A99789C71CD3D819005E7DAC /* ShellMVK.cpp */, + A99789C81CD3D819005E7DAC /* ShellMVK.h */, + A99789951CD3D4E2005E7DAC /* Simulation.cpp */, + A99789961CD3D4E2005E7DAC /* Simulation.h */, + ); + name = Hologram; + path = "../VulkanSamples/Sample-Programs/Hologram"; + sourceTree = ""; + }; + A9ADEC601B6EC2EB00DBA48C /* iOS */ = { + isa = PBXGroup; + children = ( + A9A222171B5D69F40050A5F9 /* Metal.framework */, + BA240AC10FEFE77A00DE852D /* OpenGLES.framework */, + 1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */, + 2D500B990D5A79CF00DBA0E3 /* QuartzCore.framework */, + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, + 1D30AB110D05D00D00671497 /* Foundation.framework */, + A9CDEA271B6A782C00F7B008 /* GLKit.framework */, + A900B1A21B5EAA1C00150D60 /* libc++.dylib */, + ); + name = iOS; + sourceTree = ""; + }; + A9ADEC611B6EC2F300DBA48C /* macOS */ = { + isa = PBXGroup; + children = ( + A94A67231B7BDE9B00F6D7C4 /* MetalGL.framework */, + A94A67241B7BDE9B00F6D7C4 /* MetalGLShaderConverter.framework */, + A9E264761B671B0A00FE691A /* libc++.dylib */, + A977BD251B67186B0067E5BF /* Metal.framework */, + A977BD261B67186B0067E5BF /* QuartzCore.framework */, + A977BD211B67186B0067E5BF /* AppKit.framework */, + A977BD221B67186B0067E5BF /* CoreGraphics.framework */, + A977BD231B67186B0067E5BF /* Foundation.framework */, + ); + name = macOS; + sourceTree = ""; + }; + A9B67B6A1C3AAE9800373FFD /* iOS */ = { + isa = PBXGroup; + children = ( + A9B67B6B1C3AAE9800373FFD /* AppDelegate.h */, + A9B67B6C1C3AAE9800373FFD /* AppDelegate.m */, + A9B67B6E1C3AAE9800373FFD /* DemoViewController.h */, + A9B67B6F1C3AAE9800373FFD /* DemoViewController.mm */, + A9B67B701C3AAE9800373FFD /* Info.plist */, + A9B67B711C3AAE9800373FFD /* main.m */, + A9B67B721C3AAE9800373FFD /* Prefix.pch */, + A9B67B731C3AAE9800373FFD /* Resources */, + ); + path = iOS; + sourceTree = ""; + }; + A9B67B731C3AAE9800373FFD /* Resources */ = { + isa = PBXGroup; + children = ( + A9B67B741C3AAE9800373FFD /* Default-568h@2x.png */, + A9B67B751C3AAE9800373FFD /* Default~ipad.png */, + A9B67B761C3AAE9800373FFD /* Icon.png */, + A9B67B771C3AAE9800373FFD /* Main.storyboard */, + ); + path = Resources; + sourceTree = ""; + }; + A9B67B811C3AAEA200373FFD /* macOS */ = { + isa = PBXGroup; + children = ( + A9B67B821C3AAEA200373FFD /* AppDelegate.h */, + A9B67B831C3AAEA200373FFD /* AppDelegate.m */, + A9B67B841C3AAEA200373FFD /* DemoViewController.h */, + A9B67B851C3AAEA200373FFD /* DemoViewController.mm */, + A9B67B861C3AAEA200373FFD /* Info.plist */, + A9B67B871C3AAEA200373FFD /* main.m */, + A9B67B881C3AAEA200373FFD /* Prefix.pch */, + A9B67B891C3AAEA200373FFD /* Resources */, + ); + path = macOS; + sourceTree = ""; + }; + A9B67B891C3AAEA200373FFD /* Resources */ = { + isa = PBXGroup; + children = ( + A9B67B8A1C3AAEA200373FFD /* Main.storyboard */, + A9B67B8B1C3AAEA200373FFD /* macOS.xcassets */, + ); + path = Resources; + sourceTree = ""; + }; + A9D516F51CD575B000097D96 /* Generated */ = { + isa = PBXGroup; + children = ( + A9D516F61CD575E300097D96 /* HelpersDispatchTable.cpp */, + A9D516F71CD575E300097D96 /* HelpersDispatchTable.h */, + ); + name = Generated; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1D6058900D05DD3D006BFB54 /* Hologram-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "Hologram-iOS" */; + buildPhases = ( + A99789D71CD41746005E7DAC /* ShellScript */, + A9006C4A1CDAB49000C6BC7B /* ShellScript */, + 1D60588D0D05DD3D006BFB54 /* Resources */, + 1D60588E0D05DD3D006BFB54 /* Sources */, + 1D60588F0D05DD3D006BFB54 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Hologram-iOS"; + productName = foo; + productReference = 1D6058910D05DD3D006BFB54 /* Hologram.app */; + productType = "com.apple.product-type.application"; + }; + A977BCBD1B66BB010067E5BF /* Hologram-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = A977BCFB1B66BB010067E5BF /* Build configuration list for PBXNativeTarget "Hologram-macOS" */; + buildPhases = ( + A99789CD1CD3FFEB005E7DAC /* Run Script */, + A9D516F41CD5752C00097D96 /* ShellScript */, + A977BCBE1B66BB010067E5BF /* Resources */, + A977BCC91B66BB010067E5BF /* Sources */, + A977BCF11B66BB010067E5BF /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Hologram-macOS"; + productName = foo; + productReference = A977BCFE1B66BB010067E5BF /* Hologram.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0900; + TargetAttributes = { + 1D6058900D05DD3D006BFB54 = { + DevelopmentTeam = VU3TCKU48B; + }; + A977BCBD1B66BB010067E5BF = { + DevelopmentTeam = VU3TCKU48B; + }; + }; + }; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Hologram" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); + mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1D6058900D05DD3D006BFB54 /* Hologram-iOS */, + A977BCBD1B66BB010067E5BF /* Hologram-macOS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 1D60588D0D05DD3D006BFB54 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A99789B51CD3D4E2005E7DAC /* Hologram.vert in Resources */, + A9B67B7F1C3AAE9800373FFD /* Icon.png in Resources */, + A99789B31CD3D4E2005E7DAC /* Hologram.push_constant.vert in Resources */, + A9B67B801C3AAE9800373FFD /* Main.storyboard in Resources */, + A9B67B7E1C3AAE9800373FFD /* Default~ipad.png in Resources */, + A99789B11CD3D4E2005E7DAC /* Hologram.frag in Resources */, + A9B67B7D1C3AAE9800373FFD /* Default-568h@2x.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A977BCBE1B66BB010067E5BF /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A9B67B911C3AAEA200373FFD /* macOS.xcassets in Resources */, + A99789B21CD3D4E2005E7DAC /* Hologram.frag in Resources */, + A9B67B901C3AAEA200373FFD /* Main.storyboard in Resources */, + A99789B41CD3D4E2005E7DAC /* Hologram.push_constant.vert in Resources */, + A99789B61CD3D4E2005E7DAC /* Hologram.vert in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + A9006C4A1CDAB49000C6BC7B /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "../VulkanSamples/Sample-Programs/Hologram/generate-dispatch-table HelpersDispatchTable.cpp"; + }; + A99789CD1CD3FFEB005E7DAC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "", + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "../VulkanSamples/Sample-Programs/Hologram/generate-dispatch-table HelpersDispatchTable.h"; + }; + A99789D71CD41746005E7DAC /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "../VulkanSamples/Sample-Programs/Hologram/generate-dispatch-table HelpersDispatchTable.h"; + }; + A9D516F41CD5752C00097D96 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "../VulkanSamples/Sample-Programs/Hologram/generate-dispatch-table HelpersDispatchTable.cpp"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D60588E0D05DD3D006BFB54 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A99789B71CD3D4E2005E7DAC /* Main.cpp in Sources */, + A9B67B7A1C3AAE9800373FFD /* DemoViewController.mm in Sources */, + A99789AF1CD3D4E2005E7DAC /* Hologram.cpp in Sources */, + A99789C51CD3D4E2005E7DAC /* Simulation.cpp in Sources */, + A99789C91CD3D819005E7DAC /* ShellMVK.cpp in Sources */, + A99789B91CD3D4E2005E7DAC /* Meshes.cpp in Sources */, + A9B67B781C3AAE9800373FFD /* AppDelegate.m in Sources */, + A9B67B7C1C3AAE9800373FFD /* main.m in Sources */, + A9D516F81CD575E300097D96 /* HelpersDispatchTable.cpp in Sources */, + A99789BD1CD3D4E2005E7DAC /* Shell.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A977BCC91B66BB010067E5BF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A99789B81CD3D4E2005E7DAC /* Main.cpp in Sources */, + A9B67B8C1C3AAEA200373FFD /* AppDelegate.m in Sources */, + A99789B01CD3D4E2005E7DAC /* Hologram.cpp in Sources */, + A99789C61CD3D4E2005E7DAC /* Simulation.cpp in Sources */, + A99789CA1CD3D819005E7DAC /* ShellMVK.cpp in Sources */, + A99789BA1CD3D4E2005E7DAC /* Meshes.cpp in Sources */, + A9B67B8F1C3AAEA200373FFD /* main.m in Sources */, + A9B67B8D1C3AAEA200373FFD /* DemoViewController.mm in Sources */, + A9D516F91CD575E300097D96 /* HelpersDispatchTable.cpp in Sources */, + A99789BE1CD3D4E2005E7DAC /* Shell.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1D6058940D05DD3E006BFB54 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + DEVELOPMENT_TEAM = VU3TCKU48B; + FRAMEWORK_SEARCH_PATHS = ( + "\"$(SRCROOT)/../../../MoltenVK/iOS\"", + "\"$(SRCROOT)/../../../MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/iOS\"", + ); + GCC_PREFIX_HEADER = "$(SRCROOT)/iOS/Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "$(SRCROOT)/iOS/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = arm64; + }; + name = Debug; + }; + 1D6058950D05DD3E006BFB54 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + DEVELOPMENT_TEAM = VU3TCKU48B; + FRAMEWORK_SEARCH_PATHS = ( + "\"$(SRCROOT)/../../../MoltenVK/iOS\"", + "\"$(SRCROOT)/../../../MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/iOS\"", + ); + GCC_PREFIX_HEADER = "$(SRCROOT)/iOS/Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "$(SRCROOT)/iOS/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = arm64; + }; + name = Release; + }; + A977BCFC1B66BB010067E5BF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "\"$(SRCROOT)/../../../MoltenVK/macOS\"", + "\"$(SRCROOT)/../../../MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/macOS\"", + ); + GCC_PREFIX_HEADER = "$(SRCROOT)/macOS/Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "$(SRCROOT)/macOS/Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + SDKROOT = macosx; + }; + name = Debug; + }; + A977BCFD1B66BB010067E5BF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "\"$(SRCROOT)/../../../MoltenVK/macOS\"", + "\"$(SRCROOT)/../../../MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/macOS\"", + ); + GCC_PREFIX_HEADER = "$(SRCROOT)/macOS/Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "$(SRCROOT)/macOS/Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + SDKROOT = macosx; + }; + name = Release; + }; + C01FCF4F08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = NO; + COPY_PHASE_STRIP = NO; + ENABLE_BITCODE = NO; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + _DEBUG, + GLM_FORCE_RADIANS, + MVK_USE_MOLTENVK_SHADER_CONVERTER, + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + HEADER_SEARCH_PATHS = ( + "\"$(SRCROOT)/../../../MoltenVK/include\"", + "\"$(SRCROOT)/../VulkanSamples/libs\"", + ); + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_BUNDLE_IDENTIFIER = "com.moltenvk.${PRODUCT_NAME:identifier}"; + PRODUCT_NAME = Hologram; + }; + name = Debug; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = NO; + ENABLE_BITCODE = NO; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + GLM_FORCE_RADIANS, + MVK_USE_MOLTENVK_SHADER_CONVERTER, + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + HEADER_SEARCH_PATHS = ( + "\"$(SRCROOT)/../../../MoltenVK/include\"", + "\"$(SRCROOT)/../VulkanSamples/libs\"", + ); + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_BUNDLE_IDENTIFIER = "com.moltenvk.${PRODUCT_NAME:identifier}"; + PRODUCT_NAME = Hologram; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "Hologram-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D6058940D05DD3E006BFB54 /* Debug */, + 1D6058950D05DD3E006BFB54 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A977BCFB1B66BB010067E5BF /* Build configuration list for PBXNativeTarget "Hologram-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A977BCFC1B66BB010067E5BF /* Debug */, + A977BCFD1B66BB010067E5BF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Hologram" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/xcshareddata/xcschemes/Hologram-iOS.xcscheme b/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/xcshareddata/xcschemes/Hologram-iOS.xcscheme new file mode 100644 index 00000000..4f4b1420 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/xcshareddata/xcschemes/Hologram-iOS.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/xcshareddata/xcschemes/Hologram-macOS.xcscheme b/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/xcshareddata/xcschemes/Hologram-macOS.xcscheme new file mode 100644 index 00000000..017b4bb7 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/xcshareddata/xcschemes/Hologram-macOS.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demos/LunarG-VulkanSamples/Hologram/ShellMVK.cpp b/Demos/LunarG-VulkanSamples/Hologram/ShellMVK.cpp new file mode 100644 index 00000000..263f0042 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/ShellMVK.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2016 The Brenwill Workshop Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "ShellMVK.h" +#include +#include +#include +#include +#include "Helpers.h" +#include "Game.h" + +PosixTimer::PosixTimer() +{ + _tsBase = mach_absolute_time(); + mach_timebase_info_data_t timebase; + mach_timebase_info(&timebase); + _tsPeriod = (double)timebase.numer / (double)timebase.denom; +} + +double PosixTimer::get() +{ + return (double)(mach_absolute_time() - _tsBase) * _tsPeriod / 1e9; +} + +ShellMVK::ShellMVK(Game& game) : Shell(game) +{ + _timer = PosixTimer(); + _current_time = _timer.get(); + _profile_start_time = _current_time; + _profile_present_count = 0; + +#ifdef VK_USE_PLATFORM_IOS_MVK + instance_extensions_.push_back(VK_MVK_IOS_SURFACE_EXTENSION_NAME); +#endif +#ifdef VK_USE_PLATFORM_MACOS_MVK + instance_extensions_.push_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME); +#endif + + init_vk(); +} + +ShellMVK::~ShellMVK() +{ + destroy_context(); + cleanup_vk(); +} + +PFN_vkGetInstanceProcAddr ShellMVK::load_vk() +{ + return vkGetInstanceProcAddr; +} + +bool ShellMVK::can_present(VkPhysicalDevice phy, uint32_t queue_family) +{ + return true; +} + +VkSurfaceKHR ShellMVK::create_surface(VkInstance instance) { + VkSurfaceKHR surface; + + VkResult err; +#ifdef VK_USE_PLATFORM_IOS_MVK + VkIOSSurfaceCreateInfoMVK surface_info; + surface_info.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK; + surface_info.pNext = NULL; + surface_info.flags = 0; + surface_info.pView = _view; + err = vkCreateIOSSurfaceMVK(instance, &surface_info, NULL, &surface); +#endif +#ifdef VK_USE_PLATFORM_MACOS_MVK + VkMacOSSurfaceCreateInfoMVK surface_info; + surface_info.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; + surface_info.pNext = NULL; + surface_info.flags = 0; + surface_info.pView = _view; + err = vkCreateMacOSSurfaceMVK(instance, &surface_info, NULL, &surface); +#endif + assert(!err); + + return surface; +} + +void ShellMVK::update_and_draw() { + + acquire_back_buffer(); + + double t = _timer.get(); + add_game_time(static_cast(t - _current_time)); + + present_back_buffer(); + + _current_time = t; + + _profile_present_count++; + if (_current_time - _profile_start_time >= 5.0) { + const double fps = _profile_present_count / (_current_time - _profile_start_time); + std::stringstream ss; + ss << _profile_present_count << " presents in " << + _current_time - _profile_start_time << " seconds " << + "(FPS: " << fps << ")"; + log(LOG_INFO, ss.str().c_str()); + + _profile_start_time = _current_time; + _profile_present_count = 0; + } +} + +void ShellMVK::run(void* view) { + _view = view; // not retained + create_context(); + resize_swapchain(settings_.initial_width, settings_.initial_height); +} diff --git a/Demos/LunarG-VulkanSamples/Hologram/ShellMVK.h b/Demos/LunarG-VulkanSamples/Hologram/ShellMVK.h new file mode 100644 index 00000000..5948b06a --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/ShellMVK.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016 The Brenwill Workshop Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef SHELL_MVK_H +#define SHELL_MVK_H + +#include +#include "Shell.h" +#include + +class PosixTimer { +public: + double get(); + PosixTimer(); + +protected: + uint64_t _tsBase; + double _tsPeriod; +}; + +class ShellMVK : public Shell { +public: + ShellMVK(Game &game); + ~ShellMVK(); + + void run(void* view); + void update_and_draw(); + + void run() { run(nullptr); }; + void quit() { } + +protected: + void* _view; + PosixTimer _timer; + double _current_time; + double _profile_start_time; + int _profile_present_count; + + PFN_vkGetInstanceProcAddr load_vk(); + bool can_present(VkPhysicalDevice phy, uint32_t queue_family); + + VkSurfaceKHR create_surface(VkInstance instance); +}; + +#endif // SHELL_MVK_H diff --git a/Demos/LunarG-VulkanSamples/Hologram/iOS/AppDelegate.h b/Demos/LunarG-VulkanSamples/Hologram/iOS/AppDelegate.h new file mode 100644 index 00000000..c80540c6 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/iOS/AppDelegate.h @@ -0,0 +1,26 @@ +/* + * AppDelegate.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + +@end + diff --git a/Demos/LunarG-VulkanSamples/Hologram/iOS/AppDelegate.m b/Demos/LunarG-VulkanSamples/Hologram/iOS/AppDelegate.m new file mode 100644 index 00000000..26b9a3eb --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/iOS/AppDelegate.m @@ -0,0 +1,55 @@ +/* + * AppDelegate.m + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. +} + +- (void)applicationDidEnterBackground:(UIApplication *)application { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. +} + +- (void)applicationWillEnterForeground:(UIApplication *)application { + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. +} + +- (void)applicationWillTerminate:(UIApplication *)application { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. +} + +@end diff --git a/Demos/LunarG-VulkanSamples/Hologram/iOS/DemoViewController.h b/Demos/LunarG-VulkanSamples/Hologram/iOS/DemoViewController.h new file mode 100644 index 00000000..a9b01a40 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/iOS/DemoViewController.h @@ -0,0 +1,36 @@ +/* + * DemoViewController.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + + +#pragma mark - +#pragma mark DemoViewController + +/** The main view controller for the demo storyboard. */ +@interface DemoViewController : UIViewController +@end + + +#pragma mark - +#pragma mark DemoView + +/** The Metal-compatibile view for the demo Storyboard. */ +@interface DemoView : UIView +@end + diff --git a/Demos/LunarG-VulkanSamples/Hologram/iOS/DemoViewController.mm b/Demos/LunarG-VulkanSamples/Hologram/iOS/DemoViewController.mm new file mode 100644 index 00000000..4c391e50 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/iOS/DemoViewController.mm @@ -0,0 +1,77 @@ +/* + * DemoViewController.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "DemoViewController.h" + +#include "ShellMVK.h" +#include "Hologram.h" + + +#pragma mark - +#pragma mark DemoViewController + +@implementation DemoViewController { + CADisplayLink* _displayLink; + ShellMVK* _shell; + Game* _game; +} + +-(void) dealloc { + delete _shell; + delete _game; + [_displayLink release]; + [super dealloc]; +} + +/** Since this is a single-view app, init Vulkan when the view is loaded. */ +-(void) viewDidLoad { + [super viewDidLoad]; + + self.view.contentScaleFactor = UIScreen.mainScreen.nativeScale; + + std::vector args; + args.push_back("-p"); // Use push constants +// args.push_back("-s"); // Use a single thread + _game = new Hologram(args); + + _shell = new ShellMVK(*_game); + _shell->run(self.view); + + uint32_t fps = 60; + _displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector(renderLoop)]; + [_displayLink setFrameInterval: 60 / fps]; + [_displayLink addToRunLoop: NSRunLoop.currentRunLoop forMode: NSDefaultRunLoopMode]; +} + +-(void) renderLoop { + _shell->update_and_draw(); +} + +@end + + +#pragma mark - +#pragma mark DemoView + +@implementation DemoView + +/** Returns a Metal-compatible layer. */ ++(Class) layerClass { return [CAMetalLayer class]; } + +@end + diff --git a/Demos/LunarG-VulkanSamples/Hologram/iOS/Info.plist b/Demos/LunarG-VulkanSamples/Hologram/iOS/Info.plist new file mode 100644 index 00000000..dcbf9e84 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/iOS/Info.plist @@ -0,0 +1,44 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + iOS/Resources/Icon.png + CFBundleIcons~ipad + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleVersion + 1.0 + LSApplicationCategoryType + + UIMainStoryboardFile + Main + UIRequiresFullScreen + + UIStatusBarHidden + + UISupportedInterfaceOrientations + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/Demos/LunarG-VulkanSamples/Hologram/iOS/Prefix.pch b/Demos/LunarG-VulkanSamples/Hologram/iOS/Prefix.pch new file mode 100644 index 00000000..e41a7241 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/iOS/Prefix.pch @@ -0,0 +1,5 @@ +#import + +#ifdef __OBJC__ + #import +#endif diff --git a/Demos/LunarG-VulkanSamples/Hologram/iOS/Resources/Default-568h@2x.png b/Demos/LunarG-VulkanSamples/Hologram/iOS/Resources/Default-568h@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..1669d7b6845195d9e11a1af63165c359f7041fc6 GIT binary patch literal 48497 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 PlX?;)X>95|%p}7R{?x}+qCcdCM$GDH! z0FNvt%-3@t`SsXNV+5-WE@E^K0Vs-~;Y0~__hb4f*C=MY$EZsJ6_ zF5G}0 z08Jwx^dX6CaV%v19&&H^tdg8|$RBYZ&02F(>M6 znZVs~8vW+bOcXC1w0ORm@;xQ@B_s#LaE9j)@BBlU`Z18hS!j-PihMY?@TH<_S#V-+ z1E_hQ;fZML9te4~Pi=X?6-vKK*K2@KdWZ5+hdZT1d%as6u+>(h3%Rf)A|DZ5+Pa%;WRtn)Ija1GB-eX@8;q9R)^uot-8 z;8>JNbI{1%&S?1BdS7Ndt3*;2|I4xlewbN74-%h3uT#Q(ZQ%D@rEsnl}R$#eW z(|3b5HWUn-WcJ*N^5^-$B`uG+(;k9R)wNIedo857hrTXWmuUO>50wLS(>?iY3z<@@ zFmKW*7;x^WIBUGUwC7`dIO{hLt*VTHK*)VkvYRCpvqbyBSbmJ}>BOfqN7c)t8k#(v8Fea%Z|$=)iPJO?$$=DSVM+!sdo ztWRnA264Il|1fHq9={L7%%A3ObvOpQm5*~7N>3OTEK(yWDRNO7K# z?VYmN_yHfFeP5#_~NZx~iWM zGz`OiC<5uu;k_q3mV>A^XW^>jLYaO9y4xlclp)sgX{Rph0FKiT?%PwPz4a#gXaenD zt?Cbar{*-2=jV4Y=A0vRQ*T56tG@xcTdX~L&$17j?pZj$bMmd<4?WahZ$_(EY0A8_ znB3kP!&9wZZGLvDFHL=jOF$Eh$Ba;Kv{Y*KhB9Nta_TB5OnK{D*U{Rap+d7C?-&gic!5|br5#lqV9Woi)M z5?d0NL#)ZqyR25mIUGzDVmqody9`cT1zh;?k`}+I>Z-dem!kzGxz@`K!4HC-J>Ng0 zLsCEn(9kA^TRWa{zZrDkTMO?Tb#L8Rl*eS_p^;F@P^K5|P7Qbfy$`e8mVYGw zz1;m1J1Uav&&__kUDQO>+@-O?2aU}!4$1~&{#S)EI5&ovpXqC+iQ!0_i_3wF$@Axg zrtCD-WZs;=tw#Sg-{bEY--dlYpW?loTy#61?Du^vPOXw?uI~FJv1Z|Vs?*@><+yWO z;~s4V#dvEoMN|9@Ittei)gn9~kbr*x6Yj49^xz zILC7=>G!kDJ$6EE;d3otxy_L8ChGQYDFV=hj+l_D&6{RFtIEDx&s0j9$f^6+FLljK z1}MCpLlk>gkG$IVXBJu^MxlQ7gzt z(RCfI;zR`}ior-nHrH>SI|5~W^2NCfy|j4YUE0@vSdU@9n9tlT&xW_dLaHD7`@4E0 z2K+1#ectRT$YRxo7j@vm&E?G_HYiew)fSnmT3{6UPrXi0x?)xaX||AhwuX?(pFb_r@VBTGt%FHNCpY>F~9z zy)RB$(83>q@iKOuKY~sl*;G2WoN$PJsiq0leh(hxo2ikfzuTOXw&VBHsil-%}tBOHKA(sw$bLn8Hv$uO(FCf zTkYXJ#wJg$crVP_rMMv&g~qBo@9NyQX1YMW>*IV?TRKe*L4M7kL{8jPoO_?+=8)w? z%kIbI9^boA^sT2)JHPeXE{;fWiK~()e)#lDLf&rQ?!c{0%NQ;mLg}+CRJST8fJ9aa zvY=OVBAlB2pGq0T^bl}qAjWovz_1wHXAqt|i*1g0Lz_#_1{&H3xfxaO$$2O_S)ppH zh)Y8{oXJt}cn{1{wE>+`?Tfd*6N6dTrLahgm87b*0dJ8PIUq|Y%`Tl87Qf6{G4Fct z#yxR;B2s2iS}7yG?8tf^@#0!b4+WyrebnrB=QKp+8d;^5M45PBK3d|7mh?@Tio@%- zjg)zpBnxP;N<0U@aJjE}hdVoWvir5mvtx{b48~#BnvdFnPM$J>%7O`ve-9{7_UZre zrdsOZy|1irvaBcjKb5Co@%r7ITTI|0Wsp(tEa@`iJQ1>?=mCE_7IlXRWFzqzfl z{e$lvNA~RFCpT-)RQcj!0*m`Efs5tS$tTkuadH+TTJoX7+g~F)>K$%a)vd50^--v5 zfL@y{Lc)wRea~z0rPwW}KefOrxwbsR?6LUA11i4D_zPcg%#L$!u}Qi8wEo7@L#Fmf z*{*^^EYhz@DL>@Yhmonk(h!tKv#c@_0wg!#3epAI|3OpAbDipa#_WU zBka;iRiF5FFF;!hqJ0qGlr#KI_r>Oc#;avit%gzEwrau`Oc%tOz?W0W+|JHcTwY9- z-sOmG(I4V>N_6{dT${S?A+|$x{Tp*h4@VVo(6Pj4OJT{ci;%>yTULoK+d>=iCMOG1RejZr`I*s)1(<{O)=FPAQz)RpA?(F zNMCG4ro_o=myDX!FZC@*AE&?bm(3v9`o}-C6CUix%#7`p0VPVji-M!FHAYO}d`^3% z8o0S)B|oddn|T^oHFWm&-1hT8qF&^uxuSb7Li7;m$(mf<7=HdPl&H9);xF@F0OveZO)2c0o{=>YEsB3Ppr;@^3Acz)}7znTpQC1 z8=S~>v|GDtbC5tc6$;!8Lj0N^&dl&*#kRxt`cPY+G*VF3vrny02~9LrQ}|*7gW+ab z{i(IgkD_gJp*C%>J5flUk}Rpc&UQe0J1h-=`l&=BWv%xCb1go3+v#5hknVAdJ-&bU z@UQ}8$KQ9)Ig(WV1ykZzy>2WO6fij+m!4E2*n-dB1S`7&?Al$GJm+KSdz zQq;2kZU4L?ij#_ar*2zi#txN@pOf#&9Tky6Z9DXBXWx-?uxCoyF$+Z* zb!MqJfhV2((d|LRnT>k>$iNqNv15|&#YBg*0)BdFy1@NE>Qd_dT-iHyfOpBrTO_G2 zMM2rc6gRvu^vp9e{Z^QZ^=6QEjH}VsD6z$Nqh9?;<%NqvWyNuxqpcD2JfdIw-!Vbh zUfNEL54;o?UD&~_34y2Yl$*;}RMKfDjSQCmmOD@u~1}Fe#Hq);mjk8A|&aL&W zsG%Jwc~-7_6XpD!SsO!!VCGwfVDEHQ{U~vHNU}v{h+0lQ>(3q@>d8cIUu{bL z?PY8JKG=Euo6O+g$HlG_`5nYYeaBjCHPtP&bDT`F#mdH zstQFiYhr!8d`Q4Jr~_z2kX6IfUER-?7EHB82SVgwCHCB9qigAQ0qG18qge?Uj6K{p#SLQjitn| zjxNAZB+gVkhK%3JHiCUTl1KgyK`Q7!Z2N18q9jAbl*D0)1)Y@Uw*yRNVJxOkX=UkL zd|z;2>`qr6WgrpQhJQfh`SNMPBbf@U*=6FQqJFCwC&16X9jV}sj(|1EZYw=F$d!V> z4lg+TFRyGOZ#j$h>?0_^5@@da$#)#@VNqsoqj7S0Wc-8pUmNLYt+larqVMN6Z13xe z<#L@=^$Xvin=45(8S0l?TdV0#M$wj5b{G0YdtW1Ii_nKvzV+Ef{{-CRZgTT|;8WQj zMA$!7?=HcuG15k<5VUj-`RD5eWC1k;EakbEhD4}E%X=!8tR;H9J-=L#W`7{haziFe&o}aO!0k?^}6jhzMiIU-EDD4b+tf_hk z(N69tkjN16tl^?QC+zB7c^I=VApOn-?yG?s$fbwf)`-{!ajx?@ao95opS|2zWYoxH z*eXFN=%JTcjJ}S-)JP|K%!RFVZ_xy-U6b=@By0~QTN4uUrm^VqZR5O_(7MGuaV+!K z1q+tL)_2--yXbv_Lh~U2fjIrjQHd)6zgMI`^1|;TN94+;5q3H7EyQ$RC$wof5v-0k z-CfulleMAfQof(9u3c)a(Wo-n9V%>DtR91|aY(V9^SpEyIyjdCtbpb%Zg<~r#{Iy> z3~3A=FSM?>j+d8q<9Bc2HPDja9GlIakI^7Y+1Nm(KM2{~^^(N$sS)iw#(M(I?j`q*9ej0!-jQ)z=zaMabrzIV|lR zve~sJzPB!%C*8N204m}P#X?Aut=*KSE8VMl?PQ?QVmshU#M_*F2VCM+ZSubE%EfW@ z_?Wzl&j7YMP&5X1&5lhBXOYE&Y!qSLeVZEM<;p9-;0uj_(>3JM6%W+?iXC@iD$jYL z&jI(PZgfuXq*AK(nwR#irHZf^bi_!}=yT2;ZoB;J<-nG#Z?CO?10E|ml!bc!cz1b4 zb#9~++S`^toH)HJaHJcHGBcL_i^jC8S=!|o&7sBff@~# zPS__=W5=gX`v#T!*vsbOV@b(9lU}bnaM~K5-5XYN8+PUx-A)S6$}0>18&qZd+GlE} zh)2$_^b2LCcfUGDcGYDYporO$^iGQ6|65Fxid#Q3m8rU40KnZlE&{A60YuXXee$n(Wb-br~~N4 z*wG;xV5vAdo&_loM+g7FqmrW|rQoq;|LK$sqs$ literal 0 HcmV?d00001 diff --git a/Demos/LunarG-VulkanSamples/Hologram/iOS/Resources/Icon.png b/Demos/LunarG-VulkanSamples/Hologram/iOS/Resources/Icon.png new file mode 100755 index 0000000000000000000000000000000000000000..dc3b10f5ea97460001efe8fb45e8264812ba68ab GIT binary patch literal 5914 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 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demos/LunarG-VulkanSamples/Hologram/iOS/main.m b/Demos/LunarG-VulkanSamples/Hologram/iOS/main.m new file mode 100644 index 00000000..b958eb21 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/iOS/main.m @@ -0,0 +1,26 @@ +/* + * main.m + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, @"AppDelegate"); + } +} + diff --git a/Demos/LunarG-VulkanSamples/Hologram/macOS/AppDelegate.h b/Demos/LunarG-VulkanSamples/Hologram/macOS/AppDelegate.h new file mode 100644 index 00000000..8675ee1b --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/macOS/AppDelegate.h @@ -0,0 +1,23 @@ +/* + * AppDelegate.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +@interface AppDelegate : NSObject + +@end diff --git a/Demos/LunarG-VulkanSamples/Hologram/macOS/AppDelegate.m b/Demos/LunarG-VulkanSamples/Hologram/macOS/AppDelegate.m new file mode 100644 index 00000000..3e405efe --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/macOS/AppDelegate.m @@ -0,0 +1,39 @@ +/* + * AppDelegate.m + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + // Insert code here to initialize your application +} + +- (void)applicationWillTerminate:(NSNotification *)aNotification { + // Insert code here to tear down your application +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender { + return YES; +} + +@end diff --git a/Demos/LunarG-VulkanSamples/Hologram/macOS/DemoViewController.h b/Demos/LunarG-VulkanSamples/Hologram/macOS/DemoViewController.h new file mode 100644 index 00000000..adf00728 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/macOS/DemoViewController.h @@ -0,0 +1,36 @@ +/* + * DemoViewController.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + + +#pragma mark - +#pragma mark DemoViewController + +/** The main view controller for the demo storyboard. */ +@interface DemoViewController : NSViewController +@end + + +#pragma mark - +#pragma mark DemoView + +/** The Metal-compatibile view for the demo Storyboard. */ +@interface DemoView : NSView +@end + diff --git a/Demos/LunarG-VulkanSamples/Hologram/macOS/DemoViewController.mm b/Demos/LunarG-VulkanSamples/Hologram/macOS/DemoViewController.mm new file mode 100644 index 00000000..e0c8c035 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/macOS/DemoViewController.mm @@ -0,0 +1,130 @@ +/* + * DemoViewController.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "DemoViewController.h" +#import + +#include "ShellMVK.h" +#include "Hologram.h" + + +#pragma mark - +#pragma mark DemoViewController + +@implementation DemoViewController { + CVDisplayLinkRef _displayLink; + ShellMVK* _shell; + Game* _game; +} + +-(void) dealloc { + delete _shell; + delete _game; + CVDisplayLinkRelease(_displayLink); + [super dealloc]; +} + +/** Since this is a single-view app, initialize Vulkan during view loading. */ +-(void) viewDidLoad { + [super viewDidLoad]; + + self.view.wantsLayer = YES; // Back the view with a layer created by the makeBackingLayer method. + + std::vector args; +// args.push_back("-p"); // Uncomment to use push constants +// args.push_back("-s"); // Uncomment to use a single thread + _game = new Hologram(args); + + _shell = new ShellMVK(*_game); + _shell->run(self.view); + + CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink); + CVDisplayLinkSetOutputCallback(_displayLink, &DisplayLinkCallback, _shell); + CVDisplayLinkStart(_displayLink); +} + + +#pragma mark Display loop callback function + +/** Rendering loop callback function for use with a CVDisplayLink. */ +static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, + const CVTimeStamp* now, + const CVTimeStamp* outputTime, + CVOptionFlags flagsIn, + CVOptionFlags* flagsOut, + void* target) { + ((ShellMVK*)target)->update_and_draw(); + return kCVReturnSuccess; +} + +-(void) viewDidAppear { + self.view.window.initialFirstResponder = self.view; +} + +// Delegated from the view as first responder. +-(void) keyDown:(NSEvent*) theEvent { + Game::Key key; + switch (theEvent.keyCode) { + case 53: + key = Game::KEY_ESC; + break; + case 126: + key = Game::KEY_UP; + break; + case 125: + key = Game::KEY_DOWN; + break; + case 49: + key = Game::KEY_SPACE; + break; + case 3: + key = Game::KEY_F; + break; + default: + key = Game::KEY_UNKNOWN; + break; + } + + _game->on_key(key); +} + +@end + + +#pragma mark - +#pragma mark DemoView + +@implementation DemoView + +/** Indicates that the view wants to draw using the backing layer instead of using drawRect:. */ +-(BOOL) wantsUpdateLayer { return YES; } + +/** Returns a Metal-compatible layer. */ ++(Class) layerClass { return [CAMetalLayer class]; } + +/** If the wantsLayer property is set to YES, this method will be invoked to return a layer instance. */ +-(CALayer*) makeBackingLayer { + CALayer* layer = [self.class.layerClass layer]; + CGSize viewScale = [self convertSizeToBacking: CGSizeMake(1.0, 1.0)]; + layer.contentsScale = MIN(viewScale.width, viewScale.height); + return layer; +} + +-(BOOL) acceptsFirstResponder { return YES; } + +@end diff --git a/Demos/LunarG-VulkanSamples/Hologram/macOS/Info.plist b/Demos/LunarG-VulkanSamples/Hologram/macOS/Info.plist new file mode 100644 index 00000000..646b7832 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/macOS/Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSApplicationCategoryType + + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + NSHumanReadableCopyright + Copyright (c) 2015-2017 The Brenwill Workshop Ltd. All rights reserved. + NSMainStoryboardFile + Main + NSPrincipalClass + NSApplication + + diff --git a/Demos/LunarG-VulkanSamples/Hologram/macOS/Prefix.pch b/Demos/LunarG-VulkanSamples/Hologram/macOS/Prefix.pch new file mode 100644 index 00000000..1abd7a8c --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/macOS/Prefix.pch @@ -0,0 +1,3 @@ +// +// Prefix header for all source files of the project +// diff --git a/Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/Main.storyboard b/Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/Main.storyboard new file mode 100644 index 00000000..2aca0bbb --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/Main.storyboard @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Contents.json b/Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..4124516b --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,63 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "Icon-16.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "Icon-32.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "Icon-128.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "Icon-256.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "Icon-512.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-128.png b/Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-128.png new file mode 100644 index 0000000000000000000000000000000000000000..8c55f1bea07d81a0366425784d928608f325c5c8 GIT binary patch literal 12449 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 literal 0 HcmV?d00001 diff --git a/Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-16.png b/Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-16.png new file mode 100644 index 0000000000000000000000000000000000000000..583d636d60253168d1632b648a8ae5d34eaf4843 GIT binary patch literal 671 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 literal 0 HcmV?d00001 diff --git a/Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-32.png b/Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/macOS.xcassets/AppIcon.appiconset/Icon-32.png new file mode 100644 index 0000000000000000000000000000000000000000..d73e1f60edb674197e45a25e24457f2c6d4dbfb6 GIT binary patch literal 1740 zcmV;-1~d7IP)Px*hDk(0R9Fe^R#|LRWfcDIojdz5vI(MKz(`Ac#rUQW zV=ysBjSn?|(N{x!5+g+01ceqN*a{M%?6oZdWtrA?x=p9kS?;}af6u*Lrll=tBI%1K zxw-#x{&T+ntUtGU^%{m5Bw=W1Xa>~-LL@T70HV?8%vwMc;}ZZ#l1y_3yY(CGvyN&=agnOKyUhx6yoLli}AfAmly zA%o|6s328-?q4DlWH|{yk|b1A?uKuk50#a>G-ay7?b~0)y7e2-c&QQL@E{mtno>nk zuyNCixPI*_oGuscwcVpLaRRLn4+WT@(|FafdJz?<`i&=EMV|a*GPR_cxuCERYJ8-n zrD;~_?d{R_bUGdK^Yh^%g|S!+Ma1Xzda-BEo5;!WX=k!#XCXH?2l04ZbEVmALLOPr z?sP(=OD6RtAgGg3N+cAxh^fB*7reZ6D^8y}g$)}v!0-2K0zDoNs;a6G2m~-^&Kw*# zaDZ;ap}4phD@%%T_s(qsk%Q9GGA(9WTU#(R6v4J_+tAX|j4T$#d!K!Q9$Mo^T*WI2 zBl8S@s%hhK5e*Fuuvo3|crr+M32JI;G(np;Z^5-|*RgNkJ{&xF5Ic74q#MU@{P>SJ zbLK3L9QjsrS8ZJ#jvhOP(z3Pi5ulT`C$WG3eq67qhRg)<2(A|v_ zKb^p?iV8fW+h4hI8UFTmgb8_b^B)?!y51iQA{ZPXvx?~K?9zPy(BH0^J(XNk{lgDP zvDt9v&sOb>HP#OU7o#K_2B0oZj0jp#(@h{h*VoshtgIXtFJ6QioT_^+TsV({fkTMf9U1^REe6Gs40>&m(uR$G zw7?cFTEy&jyV@VvvSrJe#bRL;x$N2JmNE)J=Jn2FTVC4AN>;Aa&XLcV)oNpNyUf zUo-!B9+SmF`zczg2K)M8rMa5#GPx-j8XV6GyHO7v6*a1#h>@#;#PcXk0+5}Zjf#rh zu%)D+y83&ne&;5f*|Dx1C5snArY82`;cu{KO%d`_ooM!V;L|hb$9UaT6*t*ak@rvnCjvR8&&+!=hXS^3XFr%qLe4Qi|_=(1u&$^c{Cn8mdxn{?!9lFpgZA$5OL( z40oB`0jEWPWCR-H!|w=_k%xoUj0O%iO5e|YK9tR}!))SF)!B|~Pk3)k4M2<_Vo+c+ z>rn1=XaMwd%W{_;HWJ*Hh{8Y8#?7SIV&dR3>2NT29=3W?scm!kgj`t{>>mR^-C&Xn zRK{*H`7Jq$g$^^yIR^}W4)ZN$EOl66)${nZBQ%z~CIb(nkwcNuhBZ1KijI*bc+7Db zNYHd@lj~DjK;3Sjpi*zLm_X!NEhtO1gNg_wlY+CMWWed2^my?oFlJIY$&wLqi{P_m zyz{&pMHxx=B~?xwO%EUxLdYh;k;&l|pA#?4Pk|vjffn;1T8ENB#k*zbvL)J+@a6pu zym9U}l=L{#=P5X}%>xTr=y5ddZX0DlvvCmIY!N#O9ax=D4AR8)ICRub4k!= ziMS~b;Dd$^oNgP$*R25vvqU;0;NA7EQPwH{(*jU+)OGiv%MyVxos`fgIwPpPpESQJ zkvG#D(kda$L{Pud=%d;m+!=@=CM59IMmvgpls41Qv;b6o^)~tx38|?H%r+UV!6>e_ zPb?^$nUGSV@Q&nbH4sYR{n~yA3k*= zM-h{f=2zMDxpcb~W*8QjMota?zH12K=&cyst5UG8G7Wp*&d~bD1SXn1u^SeUU|*I2 zoDR6q6vcziiS>N#>J)l8=@ICWaJFG$GxB8Uv8qIEY@o)LKt1gzsB(^{CjuDH{4~ao ib-~k6_^8bP0sIX#q`?IJsIZCv00007MlP2R#U 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 T1Z`O8XqSPmiOvfxm)QRR$Cw~? literal 0 HcmV?d00001 diff --git a/Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/macOS.xcassets/Contents.json b/Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/macOS.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/macOS/Resources/macOS.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Demos/LunarG-VulkanSamples/Hologram/macOS/main.m b/Demos/LunarG-VulkanSamples/Hologram/macOS/main.m new file mode 100644 index 00000000..9da3591d --- /dev/null +++ b/Demos/LunarG-VulkanSamples/Hologram/macOS/main.m @@ -0,0 +1,23 @@ +/* + * main.m + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +int main(int argc, const char * argv[]) { + return NSApplicationMain(argc, argv); +} diff --git a/Demos/LunarG-VulkanSamples/VulkanSamples b/Demos/LunarG-VulkanSamples/VulkanSamples new file mode 120000 index 00000000..7a26cfe4 --- /dev/null +++ b/Demos/LunarG-VulkanSamples/VulkanSamples @@ -0,0 +1 @@ +../../../../Khronos/Vulkan/LunarG-VulkanSamples/VulkanSamples \ No newline at end of file diff --git a/Demos/README_MoltenVK_Demos.md b/Demos/README_MoltenVK_Demos.md new file mode 100755 index 00000000..e73a05c7 --- /dev/null +++ b/Demos/README_MoltenVK_Demos.md @@ -0,0 +1,322 @@ + + + + +#MoltenVK Demo Projects + +Copyright (c) 2014-2017 [The Brenwill Workshop Ltd.](http://www.brenwill.com) + +*This document is written in [Markdown](http://en.wikipedia.org/wiki/Markdown) format. +For best results, use a Markdown reader.* + + + +Table of Contents +----------------- + +- [Introduction](#intro) +- [LunarG Vulkan Samples](#lunarg-vulkan-samples) + - [Installing the LunarG `VulkanSamples` Library](#lunarg-vulkan-samples-install) + - [*Demos*](#lunarg-vulkan-samples-demos) + - [*API-Samples*](#lunarg-vulkan-samples-api) + - [*Hologram*](#lunarg-vulkan-samples-hologram) +- [Khronos Vulkan Samples](#khronos-vulkan-samples) + - [Installing the Khronos `Vulkan-Samples` Library](#khronos-vulkan-samples-install) + - [*AsynchronousTimeWarp*](#khronos-vulkan-samples-atw) +- [Sascha Willems Vulkan Samples](#sascha-willems-vulkan-samples) + - [Installing the *Sascha Willems* Library](#sascha-willems-install) +- [Cinder Vulkan Samples](#cinder-vulkan-samples) + - [Installing the *Cinder* Library](#cinder-install) + - [*Fish Tornado*](#cinder-vulkan-samples-fish-tornado) + + + +Introduction +------------ + +The *Xcode* projects in this folder are a set of demo applications that demonstrate +how to integrate *Vulkan* into an *Xcode* project, and demonstrate the features and +capabilities of *Vulkan* when using using **MoltenVK** on the *iOS* and *macOS* platforms. + +Although the demo projects are provided with this `MoltenVK` distribution, +the source code and resources for the demo applications come from publicly-available +open-source repositories. Follow the instructions for each section below to learn +how to download the demo application source code and resources for each set of +demo applications. + +To review and run all of the available demo apps, open the `Demos.xcworkspace` +*Xcode* workspace in *Xcode*. + + + + +LunarG Vulkan Samples +--------------------- + +[LunarG](https://lunarg.com), who have been involved in *Vulkan* development from the +beginning, and are one of the original developers of *Vulkan* tools and SDK's, provides +a suite of demo apps, that demonstrate a wide range of basic *Vulkan* features. + +These demo apps can be found in the `LunarG-VulkanSamples` folder of this `Demos` +folder, and in the `LunarG-VulkanSamples` group in the *Xcode Project Navigator* +in the `Demos.xcworkspace` *Xcode* workspace. + +These **MoltenVK** demos use a modified version of the *LunarG Vulkan Samples*, that allows +the demo apps to run under *iOS* and *macOS*. To download these modified *LunarG Vulkan Samples*, +and link them to the demo *Xcode* projects in this `MoltenVK` distribution, follow the instructions +in the [Installing the LunarG `VulkanSamples` Library](#lunarg-vulkan-samples-install) section next. + + + +###Installing the LunarG *VulkanSamples* Library + +To run the *LunarG Vulkan Samples* demo apps, **MoltenVK** uses a modified version of the +*LunarG* `VulkanSamples` library. To install this modified *LunarG* `VulkanSamples` library, +open a *Terminal* session and perform the following command-line steps: + +1. In a folder outside this `MoltenVK` distribution, clone the modified `VulkanSamples` repo: + + git clone https://github.com/brenwill/VulkanSamples.git + +2. In the `MoltenVK/Demos/LunarG-VulkanSamples` folder, replace the `VulkanSamples` symlink as follows: + + ln -sfn path-to-VulkanSamples-repo-folder VulkanSamples + +3. Run the `MoltenVKShaderConverter` tool to convert the *GLSL* shaders in the `VulkanSamples` + library to *SPIR-V*: + + cd path-to-MoltenVK-package + MoltenVKShaderConverter/Tools/MoltenVKShaderConverter -gi -so -xs "-" -d Demos/LunarG-VulkanSamples/VulkanSamples/demos + + + +###LunarG Vulkan Samples: *Demos* + +This demo is a simple renderings that originally were included in the *Vulkan* SDK. + +The demo can be found in the `MoltenVK/Demos/LunarG-VulkanSamples/Demos` folder, +and in the `LunarG-VulkanSamples/Demos` group in the *Xcode Project Navigator* +in the `Demos.xcworkspace` *Xcode* workspace. + +####Cube + +A basic textured cube that spins in place. + +To run this demo, run either the `Cube-iOS` or `Cube-macOS` *Scheme* from within *Xcode*. + +This demo is a simple example of installing **MoltenVK** as a dynamic library, instead of as +a statically-linked framework. In this demo, the **MoltenVK** dynamic library is embedded in +the application, but it could have been installed as a system library instead. + + + + +###LunarG Vulkan Samples: *API-Samples* + +This *Xcode* project actually contains a large number of modular demos, with each demo +demonstrating a particular *Vulkan* feature, or suite of calls. + +> **Note:** For simplicity, the `API-Samples` demos are bare-bones. Each of the `API-Samples` +> demos renders a single frame during app startup, and then leaves the rendered image static. +> There is no display loop or motion in any of these demos. +> **This is normal for these demos, and the demo has not "hung" or "crashed" when this occurs.** + +This demo can be found in the `MoltenVK/Demos/LunarG-VulkanSamples/API-Samples` folder, +and in the `LunarG-VulkanSamples/API-Samples` group in the *Xcode Project Navigator* in +the `Demos.xcworkspace` *Xcode* workspace. + +To run this demo, run either the `API-Samples-iOS` or `API-Samples-macOS` *Scheme* from within *Xcode*. + +To specify which of the many modular demos to run, open the `Samples.h` in the `API-Samples` +project in the *Xcode Project Navigator* in the `Demos.xcworkspace` *Xcode* workspace, and +follow the instructions in the comments within that file. + +To see descriptions and screenshots of each of the demos, open +[this summary document](LunarG-VulkanSamples/VulkanSamples/samples_index.html#AdditionalVulkan), +after you have [installed](#lunarg-vulkan-samples-install) the `LunarG Vulkan Samples` repository. + + + +###LunarG Vulkan Samples: *Hologram* + +> **Note:** In order to build the `Hologram` demo, you must have *Python3* installed +> on your build computer. + +This is a sophisticated particle demo that populates command buffers from multiple threads. + +This demo can be found in the `MoltenVK/Demos/LunarG-VulkanSamples/Hologram` folder, and in the +`LunarG-VulkanSamples/Hologram` group in the *Xcode Project Navigator* in the `Demos.xcworkspace` +*Xcode* workspace. + +To run this demo, run either the `Hologram-iOS` or `Hologram-macOS` *Scheme* from within *Xcode*. + +On *macOS*, once the demo is open, you can use the *Up-arrow* and *Down-arrow* keys on the +keyboard to zoom the camera in and out of the scene. Zooming out will show more items on screen. + +The demo allows some customization, by modifying the arguments passed to the demo at startup. +To customize, modify the arguments created in the `DemoViewController viewDidLoad` method +found in the `iOS/DemoViewController.mm` or `macOS/DemoViewController.mm` file. + +This demo illustrates the use of the **MoltenVK** API `vkGetMoltenVKDeviceConfigurationMVK()` +and `vkSetMoltenVKDeviceConfigurationMVK()` functions to enable **MoltenVK** debugging, including +logging the conversion of shaders from *SPIR-V* to *Metal Shading Language*. See the use of these +functions in the `Hologram/Hologram.cpp` file. To see the effect, modify the `Hologram-iOS` or +`Hologram-macOS` *Scheme* from within *Xcode* to use the **Debug** *Build Configuration* setting. + + + + +Khronos Vulkan Samples +---------------------- + +[Khronos](https://khronos.org), the standards organization that developed *Vulkan* provides +a suite of demo apps, that demonstrate a range of sophisticated *Vulkan* features. + +These demo apps can be found in the `Khronos-Vulkan-Samples` folder of this `Demos` +folder, and in the `Khronos-Vulkan-Samples` group in the *Xcode Project Navigator* +in the `Demos.xcworkspace` *Xcode* workspace. + +These **MoltenVK** demos use a modified version of the *Khronos Vulkan Samples*, that allows +the demo apps to run under *iOS* and *macOS*. To download these modified *Khronos Vulkan Samples*, +and link them to the demo *Xcode* projects in this `MoltenVK` distribution, follow the instructions +in the [Installing the Khronos `Vulkan-Samples` Library](#khronos-vulkan-samples-install) section next. + + + +###Installing the Khronos `Vulkan-Samples` Library + +To run the *Khronos Vulkan Samples* demo apps, **MoltenVK** uses a modified version of the +*Khronos* `Vulkan-Samples` library. To install this modified *Khronos* `Vulkan-Samples` library, +open a *Terminal* session and perform the following command-line steps: + +1. In a folder outside this `MoltenVK` distribution, clone the modified `Vulkan-Samples` repo: + + git clone https://github.com/brenwill/Vulkan-Samples.git + +2. In the `MoltenVK/Demos/Khronos-Vulkan-Samples` folder, replace the `Vulkan-Samples` symlink as follows: + + ln -sfn path-to-Vulkan-Samples-repo-folder Vulkan-Samples + + + +###Khronos Vulkan Samples: *AsynchronousTimeWarp* + +This demo was contributed by *Oculus VR, LLC*, and demonstrates a variety of critical tests for +evaluating accurate synchronization between the two scene images in a virtual reality headset. + +This demo can be found in the `MoltenVK/Demos/Khronos-Vulkan-Samples/AsynchronousTimeWarp` folder, +and in the `Khronos-Vulkan-Samples/AsynchronousTimeWarp` group in the *Xcode Project Navigator* +in the `Demos.xcworkspace` *Xcode* workspace. + +You can make a large number of configuration changes to this demo, to increase or decrease the +rendering and computational load of the scene. You can set these configuration values by passing +command-line arguments to the demo at start-up. You pass these command-line arguments by setting +them in the `Arguments` tab of the `AsynchronousTimeWarp-VK-iOS` or `AsynchronousTimeWarp-VK-macOS` +*Xcode* schemes. + +For example, the following command-line arguments can be used to set the scene complexity: + +- `-q [0-3]` : controls whether a minimal, small, medium, or large quantity of objects + will be rendered. +- `-w [0-3]` : controls whether each object will be rendered with a minimal, small, + medium, or large quantity of triangles. +- `-e [0-3]` : controls whether a minimal, small, medium, or large number of lights + will be used to illuminate the scene. + +On *macOS*, once the demo is open, you can also tap the `Q`, `W`, or `E` keys on the keyboard +to cycle each of these same configuration parameters through their range of possible values. + +For the full instructions for this demo, including a list and explanation of all of +the configuration options, read the notes at the top of the `atw/atw_vulkan.c` file. + +This demo illustrates the use of the **MoltenVK** API `vkGetMoltenVKDeviceConfigurationMVK()` +and `vkSetMoltenVKDeviceConfigurationMVK()` functions to enable performance tracking and logging, +and to enable **MoltenVK** debugging, including logging the conversion of shaders from *SPIR-V* +to *Metal Shading Language*. See the use of these functions in the `atw/atw_vulkan.c` file. +To see the effect of shader conversion logging, modify the `AsynchronousTimeWarp-VK-iOS` or +`AsynchronousTimeWarp-VK-macOS` *Scheme* from within *Xcode* to use the +**Debug** *Build Configuration* setting. + + + + +Sascha Willems Vulkan Samples +----------------------------- + +[*Sascha Willems*](https://github.com/brenwill/Vulkan) provides an open-source library containing +a large number of sophisticated *Vulkan* examples. The library contains support for running these +examples on *iOS* and *macOS* in *Xcode*, using **MoltenVK**. + + + +###Installing the *Sascha Willems* Library + +To install the *Sascha Willems Vulkan* samples, open a *Terminal* session and perform +the following command-line steps: + +1. In a folder outside this `MoltenVK` distribution, clone the modified *Sascha Willems* `Vulkan` repo: + + git clone https://github.com/brenwill/Vulkan.git + +2. Follow the instructions in the `Vulkan\xcode\README_MoltenVK_Examples.md` document + within the *Sascha Willems* `Vulkan` repository. + + + + +Cinder Vulkan Samples +--------------------- + +[*Cinder*](https://libcinder.org) is a cross-platform 3D graphics engine built in C++. +*Cinder* supports *Vulkan*, and includes several *Vulkan* demos. + +These demo apps are included as part of the *Cinder* code repository. + +These **MoltenVK** demos use a modified version of *Cinder*, that allows *Vulkan* to run under +*iOS* and *macOS*. To download the modified version of *Cinder*, and link it to **MoltenVK**, +follow the instructions in the [Installing the `Cinder` Library](#cinder-vulkan-samples-install) +section next. + + + +###Installing the *Cinder* Library + +To run the *Cinder Vulkan Samples* demo apps, **MoltenVK** uses a modified version of the +*Cinder* library. To install this modified *Cinder* library, and link it to **MoltenVK**, +open a *Terminal* session and perform the following command-line steps: + +1. In a folder outside this `MoltenVK` distribution, clone the modified `Cinder` repo, + including required submodules: + + git clone --recursive https://github.com/brenwill/Cinder.git + +2. Build the core *Cinder* library: + + Cinder/xcode/fullbuild.sh + +3. By default, the *Cinder Vulkan* samples expect **MoltenVK** to be installed in a directory + beside the `Cinder` repository: + + Cinder/ + MoltenVK/ + + + If you have installed **MoltenVK** somewhere else, you can redirect the *Cinder Vulkan* + samples to the location of your **MoltenVK** installation as follows: + + cd Cinder/samples/_vulkan_explicit + ln -sfn path-to-the-MoltenVK-distribution/MoltenVK + + + + +###Cinder Vulkan Samples: *Fish Tornado* + +This is a sophisticated simulation of a *Fish Tornado*, a swirling school of thousands of fish. + +This demo can be found in the `samples/_vulkan_explicit/FishTornado` folder of the *Cinder* repository. +To build and run this demo for either *iOS* or *macOS*, open the `xcode-ios/FishTornado.xcodeproj` +or `xcode/FishTornado.xcodeproj` *Xcode* project, respectively. + diff --git a/Docs/MoltenVK_Runtime_UserGuide.md b/Docs/MoltenVK_Runtime_UserGuide.md new file mode 100755 index 00000000..cc73684f --- /dev/null +++ b/Docs/MoltenVK_Runtime_UserGuide.md @@ -0,0 +1,480 @@ + + + + +MoltenVK Runtime User Guide +=========================== + +Copyright (c) 2014-2017 [The Brenwill Workshop Ltd.](http://www.brenwill.com) + +*This document is written in [Markdown](http://en.wikipedia.org/wiki/Markdown) format. +For best results, use a Markdown reader.* + + + +Table of Contents +----------------- + +- [About This Document](#about_this) +- [About **MoltenVK**](#about_moltenvk) +- [Running the **MoltenVK** Demo Applications](#demos) +- [Installing **MoltenVK** in Your *Vulkan* Application](#install) + - [Install as Static Library Framework](#install_static_lib) + - [Install as Dynamic Library](#install_dynamic_lib) +- [Interacting with the **MoltenVK** Runtime](#interaction) + - [MoltenVK Extension](#moltenvk_extension) +- [*Metal Shading Language* Shaders](#shaders) + - [MoltenVKShaderConverter Shader Converter Tool](#shader_converter_tool) + - [Troubleshooting Shader Conversion](#spv_vs_msl) +- [Performance Considerations](#performance) + - [Shader Loading Time](#shader_load_time) + - [Xcode Configuration](#xcode_config) + - [Metal System Trace Tool](#trace_tool) +- [Known **MoltenVK** Limitations](#limitations) +- [Third-Party Credits](#credits) + + + + +About This Document +------------------- + +This document describes how to integrate the **MoltenVK** runtime distribution package into a game +or application, once **MoltenVK** has been built into a framework or library for *iOS* or *macOS*. + +To learn how to use the **MoltenVK** open-source repository to build a **MoltenVK** runtime +distribution package, see the main [**README**](../README.md) document in the `MoltenVK` repository. + + + + +About **MoltenVK** +------------------ + +**MoltenVK** is an implementation of the [*Vulkan*](https://www.khronos.org/vulkan) +graphics and compute API, that runs on Apple's [*Metal*](https://developer.apple.com/metal) +graphics and compute framework on both *iOS* and *macOS*. + +**MoltenVK** allows you to use the *Vulkan* graphics and compute API to develop modern, cross-platform, +high-performance graphical games and applications, and to run them across many platforms, including +both *iOS* and *macOS*. + +*Metal* uses a different shading language, the *Metal Shading Language (MSL)*, than +*Vulkan*, which uses *SPIR-V*. However, fear not, as **MoltenVK** will automatically convert +your *SPIR-V* shaders to their *MSL* equivalents. This can be performed transparently at run time, +using the **Runtime Shader Conversion** feature of **MoltenVK**, or at development time using the +[**MoltenVKShaderConverter**](#shader_converter_tool) tool provided with this **MoltenVK** distribution +package. + + + + +Running the **MoltenVK** Demo Applications +------------------------------------------ + +You can explore how **MoltenVK** provides *Vulkan* support on *iOS* and *macOS* by investigating +and running the demo applications that are supported with this **MoltenVK** distribution package. + +The **MoltenVK** demo apps are located in the `Demos` folder within the **MoltenVK** +distribution package. Each demo app is available as an *Xcode* project. + +To review and run many of the available demo apps, open the `Demos/Demos.xcworkspace` workspace +in *Xcode*. + +Please read the [Demos/README_MoltenVK_Demos.md](Demos/README_MoltenVK_Demos.md) document for a +description of each demo app, and instructions on downloading and running the demo apps. +Many of the **MoltenVK** demos make use of third-party demo examples, which must be downloaded +from an external repository. Several of the demo apps allow you to explore a variety of *Vulkan* +features by modifying *Xcode* build settings. All of this is explained in the +[README_MoltenVK_Demos.md](Demos/README_MoltenVK_Demos.md) document. + + + +Installing **MoltenVK** in Your *Vulkan* Application +---------------------------------------------------- + +>***Note:*** **MoltenVK** can be run on *iOS 9* and *macOS 11.0* devices, + but it does reference advanced OS frameworks during building. *Xcode 9* + or above is required to build and link **MoltenVK** projects. + + +###Install as Static Library Framework + +Installation of **MoltenVK** is straightforward and easy! + +For most applications, you can install **MoltenVK** as a *static library framework* that will be +embedded directly in your application executable, or a component library within your application. +This is simple and straightforward, and is the recommended installation approach for all applications. + +To add **MoltenVK** as a *static library framework* to your existing *Vulkan* application, +follow the steps in this section. If you're new to **MoltenVK**, it is recommended that you +start with a smaller project to help you understand the transition, and to reduce the possibility +of needing to make modifications to your [shaders](#shaders) to ensure their compatibility with +the *Metal* environment. + +1. Open your application in *Xcode* and select your application's target in the + *Project Navigator* panel. + +2. Open the *Build Settings* tab, and in the **Framework Search Paths** (aka `FRAMEWORK_SEARCH_PATHS`) + setting: + - If building for *iOS*, add an entry that points to the `MoltenVK/iOS` folder, + found in the **MoltenVK** distribution package. + - If building for *macOS*, add an entry that points to the `MoltenVK/macOS` folder, + found in the **MoltenVK** distribution package. + +3. On the *Build Phases* tab, open the *Link Binary With Libraries* list. + + 1. Drag the `MoltenVK/iOS/MoltenVK.framework` or `MoltenVK/macOS/MoltenVK.framework` folder, + found in the **MoltenVK** distribution package, to the *Link Binary With Libraries* list. + 2. Click the **+** button, and (selecting from the list of system libraries) add `libc++.tbd`. + 3. If you do not have the **Link Frameworks Automatically** (aka `CLANG_MODULES_AUTOLINK`) and + **Enable Modules (C and Objective-C** (aka `CLANG_ENABLE_MODULES`) settings enabled, click the + **+** button, and (selecting from the list of system frameworks) add `Metal.framework`, + `IOSurface.framework`, and `QuartzCore.framework`. + +4. When a *Metal* app is running from *Xcode*, the default ***Scheme*** settings reduce + performance. To improve performance and gain the benefits of *Metal*, perform the + following in *Xcode*: + + 1. Open the ***Scheme Editor*** for building your main application. You can do + this by selecting ***Edit Scheme...*** from the ***Scheme*** drop-down menu, or select + ***Product -> Scheme -> Edit Scheme...*** from the main menu. + 2. On the ***Info*** tab, set the ***Build Configuration*** to ***Release***, and disable the + ***Debug executable*** check-box. + 3. On the ***Options*** tab, disable both the ***Metal API Validation*** and ***GPU Frame Capture*** + options. For optimal performance, you may also consider disabling the other simulation + and debugging options on this tab. For further information, see the + [Xcode Scheme Settings and Performance](https://developer.apple.com/library/ios/documentation/Miscellaneous/Conceptual/MetalProgrammingGuide/Dev-Technique/Dev-Technique.html#//apple_ref/doc/uid/TP40014221-CH8-SW3) + section of Apple's *Metal Programming Guide* documentation. + + + + +###Install as Dynamic Library + +For some applications, you may prefer to install **MoltenVK** as a dynamic library. +This is only recommended for developers who are used to working with dynamic libraries, +and even then, the preferred approach is to link the **MoltenVK** +[*static library framework*](#install_static_lib) into a dynamic library of your own +creation, in order to give you the most flexibility for organizing your dynamic libraries. + +In order to install **MoltenVK** as its own dynamic library in your application, +follow these instructions: + +1. Open your application in *Xcode* and select your application's target in the + *Project Navigator* panel. + +2. On the *Build Settings* tab: + 1. In the **Header Search Paths** (aka `HEADER_SEARCH_PATHS`) setting, add an entry + that points to the `MoltenVK/include` folder, found in the **MoltenVK** distribution package. + 2. In the **Library Search Paths** (aka `HEADER_SEARCH_PATHS`) setting, add an entry + that points to either the `MoltenVK/iOS` or `MoltenVK/macOS` folder, found in the + **MoltenVK** distribution package. + 3. In the **Runpath Search Paths** (aka `LD_RUNPATH_SEARCH_PATHS`) setting, add a path + that matches the library destination you established in **Step 2** above. If the dynamic + library is to be embedded within your application, you would typically set this value to + either `@executable_path` or `@loader_path`. The `libMoltenVK.dylib` library is internally + configured to be located at `@rpath/libMoltenVK.dylib`. + +3. On the *Build Phases* tab, open the *Link Binary With Libraries* list. + + 1. Drag the `MoltenVK/iOS/libMoltenVK.dylib` or `MoltenVK/macOS/libMoltenVK.dylib` file, + found in the **MoltenVK** distribution package, to the *Link Binary With Libraries* list. + 2. Click the **+** button, and (selecting from the list of system libraries) add `libc++.tbd`. + 3. If you do not have the **Link Frameworks Automatically** (aka `CLANG_MODULES_AUTOLINK`) and + **Enable Modules (C and Objective-C** (aka `CLANG_ENABLE_MODULES`) settings enabled, click the + **+** button, and (selecting from the list of system frameworks) add `Metal.framework`, + `IOSurface.framework`, and `QuartzCore.framework`. + +4. Arrange to install the `libMoltenVK.dylib` file in your application environment: + + - To copy the `libMoltenVK.dylib` file into your application or component library: + 1. On the *Build Phases* tab, add a new *Copy Files* build phase. + 2. Set the *Destination* into which you want to place the `libMoltenVK.dylib` file. + Typically this will be *Executables*. + 3. Drag the `MoltenVK/iOS/libMoltenVK.dylib` or `MoltenVK/macOS/libMoltenVK.dylib` + file to the *Copy Files* list in this new build phase. + + - Alternately, you may create your own installation mechanism to install the + `MoltenVK/iOS/libMoltenVK.dylib` or `MoltenVK/macOS/libMoltenVK.dylib` file + into a standard *iOS* or *macOS* system library folder on the user's device. + + +The `Cube-iOS` and `Cube-macOS` **MoltenVK** demo apps, found in the `Demos.xcworkspace`, +located in the `Demos` folder within the **MoltenVK** distribution package, are simple +examples of installing *MoltenVK* as a dynamic library embedded within an *iOS* or *macOS* +application, respectively. + + + + +Interacting with the **MoltenVK** Runtime +----------------------------------------- + +You programmatically configure and interact with the **MoltenVK** runtime through function +calls, enumeration values, and capabilities, in exactly the same way you do with other +*Vulkan* implementations. The `MoltenVK.framework` contains several header files that define +access to *Vulkan* and **MoltenVK** function calls. + +In your application code, you access *Vulkan* features through the API defined in the standard +`vulkan.h` header file. This file is included in the **MoltenVK** framework, and can be included +in your source code files as follows: + + #include + +In addition to the core *Vulkan* API, **MoltenVK** also supports the following *Vulkan* extensions: + +- `VK_MVK_moltenvk` +- `VK_KHR_swapchain` +- `VK_KHR_surface` +- `VK_MVK_ios_surface` (iOS) +- `VK_MVK_macos_surface` (macOS) +- `VK_AMD_negative_viewport_height` +- `VK_IMG_format_pvrtc` (iOS) + +In order to visibly display your content on *iOS* or *macOS*, you must enable the `VK_MVK_ios_surface` +or `VK_MVK_macos_surface` extension, respectively, and use the functions defined for that extension +to create a *Vulkan* rendering surface. + +You can enable each of these extensions by defining the `VK_USE_PLATFORM_IOS_MVK` or +`VK_USE_PLATFORM_MACOS_MVK` guard macro in your compiler build settings. See the description +of the `mvk_vulkan.h` file below for a convenient way to enable these extensions automatically. + +The *Vulkan* API, including the `VK_MVK_ios_surface` and `VK_MVK_macos_surface` surface +extensions, and other *Vulkan* extensions supported by **MoltenVK** (except `VK_MVK_moltenvk`), +is described in the +[*Vulkan 1.0 Spec with MoltenVK Extensions*](Vulkan_1.0_Spec_with_MoltenVK_Extensions.html) +document found in the `MoltenVK/Vulkan` folder of the **MoltenVK** distribution. + + + +###MoltenVK Extension + +The `VK_MVK_moltenvk` *Vulkan* extension provides functionality beyond the standard *Vulkan* +API, to support configuration options, license registration, and behaviour that is specific +to the **MoltenVK** implementation of *Vulkan*. You can access this functionality by including +the `vk_mvk_moltenvk.h` header file in your code. The `vk_mvk_moltenvk.h` file also includes +the API documentation for this `VK_MVK_moltenvk` extension. + +The following API header files are included in the **MoltenVK** package, each of which +can be included in your application source code as follows: + + #include + +where `HEADER_FILE` is one of the following: + +- `vk_mvk_moltenvk.h` - Contains declarations and documenation for the functions, structures, + and enumerations that define the behaviour of the `VK_MVK_moltenvk` *Vulkan* extension. + +- `mvk_vulkan.h` - This is a convenience header file that loads the `vulkan.h` header file + with the appropriate **MoltenVK** *Vulkan* platform surface extension automatically + enabled for *iOS* or *macOS*. Use this header file in place of the `vulkan.h` header file, + where access to a **MoltenVK** platform surface extension is required. + + - When building for *iOS*, the `mvk_vulkan.h` header file automatically enables the + `VK_USE_PLATFORM_IOS_MVK` build setting and `VK_MVK_ios_surface` *Vulkan* extension. + - When building for *macOS*, the `mvk_vulkan.h` header file automatically enables the + `VK_USE_PLATFORM_MACOS_MVK` build setting and `VK_MVK_macos_surface` *Vulkan* extension. + +- `mvk_datatypes.h` - Contains helpful functions for converting between *Vulkan* and *Metal* data types. + You do not need to use this functionality to use **MoltenVK**, as **MoltenVK** converts between + *Vulkan* and *Metal* datatypes automatically (using the functions declared in this header). + These functions are exposed in this header for your own purposes such as interacting with *Metal* + directly, or simply logging data values. + + + + +*Metal Shading Language* Shaders +-------------------------------- + +*Metal* uses a different shader language than *Vulkan*. *Vulkan* uses the new +*SPIR-V Shading Language (SPIR-V)*, whereas *Metal* uses the *Metal Shading Language (MSL)*. + +**MoltenVK** provides several options for creating and running *MSL* versions of your +existing *SPIR-V* shaders. The following options are presented in order of increasing +sophistication and difficulty: + +- You can use the automatic **Runtime Shader Conversion** feature of **MoltenVK** to automatically + and transparently convert your *SPIR-V* shaders to *MSL* at runtime, by simply loading your + *SPIR-V* shaders as you always have, using the standard *Vulkan* `vkCreateShaderModule()` + function. **MoltenVK** will automatically convert the *SPIR-V* code to *MSL* at runtime. + +- You can use the standard *Vulkan* `vkCreateShaderModule()` function to provide your own *MSL* + shader code. To do so, set the value of the *magic number* element of the *SPIR-V* stream to one + of the values in the `MVKMSLMagicNumber` enumeration found in the `vk_mvk_moltenvk.h` header file. + + The *magic number* element of the *SPIR-V* stream is the first element of the stream, + and by setting the value of this element to either `kMVKMagicNumberMSLSourceCode` or + `kMVKMagicNumberMSLCompiledCode`, on *SPIR-V* code that you submit to the `vkCreateShaderModule()` + function, you are indicating that the remainder of the *SPIR-V* stream contains either + *MSL* source code, or *MSL* compiled code, respectively. + + You can use the `MoltenVKShaderConverter` command-line tool found in this **MoltenVK** distribution + package to convert your *SPIR-V* shaders to *MSL* source code, offline at development time, + in order to create the appropriate *MSL* code to load at runtime. The [section below](#shaders) + discusses how to use this tool in more detail. + +You can mix and match these options in your application. For example, a convenient approach is +to use **Runtime Shader Conversion** for most *SPIR-V* shaders, and provide pre-converted *MSL* +shader source code for the odd *SPIR-V* shader that proves problematic for runtime conversion. + + + + +###MoltenVKShaderConverter Shader Converter Tool + +The **MoltenVK** distribution package includes the `MoltenVKShaderConverter` command line tool, +which allows you to convert your *SPIR-V* shader source code to *MSL* at development time, and +then supply the *MSL* code to **MoltenVK** using one of the methods described in the +[*Metal Shading Language* Shaders](#shaders) section above. + +The `MoltenVKShaderConverter` tool uses the same conversion technology as the **Runtime Shader +Conversion** feature of **MoltenVK**. + +The `MoltenVKShaderConverter` tool has a number of options available from the command line: + +- The tool can be used to convert a single *SPIR-V* file to *MSL*, or an entire directory tree + of *SPIR-V* files to *MSL*. + +- The tool can be used to convert a single *OpenGL GLSL* file, or an entire directory tree + of *GLSL* files to either *SPIR-V* or *MSL*. + +To see a complete list of options, run the `MoltenVKShaderConverter` tool from the command +line with no arguments. + + + + +###Troubleshooting Shader Conversion +--------------------------------- + +The shader converter technology in **MoltenVK** is quite robust, and most *SPIR-V* shaders +can be converted to *MSL* without any problems. In the case where a conversion issue arises, +you can address the issue as follows: + +- Errors encountered during **Runtime Shader Conversion** are logged to the console. + +- To help understand conversion issues during **Runtime Shader Conversion**, you can enable + the logging of the *SPIR-V* and *MSL* shader source code during conversion as follows: + + #include + ... + MVKDeviceConfiguration mvkConfig; + vkGetMoltenVKDeviceConfigurationMVK(vkDevice, &mvkConfig); + mvkConfig.debugMode = true; + vkSetMoltenVKDeviceConfigurationMVK(vkDevice, &mvkConfig); + + Performing these steps will enable debug mode in **MoltenVK**, which includes shader conversion + logging, and causes both the incoming *SPIR-V* code and the converted *MSL* source code to be + logged to the console (in human-readable form). This allows you to manually verify the conversions, + and can help you diagnose issues that might occur during shader conversion. + +- For minor issues, you may be able to adjust your *SPIR-V* code so that it behaves the same + under *Vulkan*, but is easier to automatically convert to *MSL*. + +- For more significant issues, you can use the `MoltenVKShaderConverter` tool to convert the + shaders at development time, adjust the *MSL* code manually so that it compiles correctly, + and use the *MSL* shader code instead of the *SPIR-V* code, using the techniques described + in the [*Metal Shading Language* Shaders](#shaders) section above. + +- You are also encouraged to report issues with shader conversion to the + [*SPIRV-Cross*](https://github.com/KhronosGroup/SPIRV-Cross/issues) project. **MoltenVK** and + **MoltenVKShaderConverter** make use of *SPIRV-Cross* to convert *SPIR-V* shaders to *MSL* shaders. + + + + +Performance Considerations +-------------------------- + +This section discusses various options for improving performance when using **MoltenVK**. + + + +###Shader Loading Time + +*Metal* supports pre-compiled shaders, which can improve shader loading and set-up performance, +allowing you to reduce your scene loading time. See the [*Metal Shading Language* Shaders](#shaders) +and [MoltenVKShaderConverter Shader Converter Tool](#shader_converter_tool) sections above for +more information about how to use the `MoltenVKShaderConverter` tool to create and load pre-compiled +*Metal* shaders into **MoltenVK**. + + + +###Xcode Configuration + +When a *Metal* app is running from *Xcode*, the default ***Scheme*** settings reduce performance. +Be sure to follow the instructions for configuring your application's ***Scheme*** within *Xcode*, +found in the in the [installation](#install) section above. + + + +###Metal System Trace Tool + +To help you get the best performance from your graphics app, the *Xcode Instruments* profiling tool +includes the *Metal System Trace* template. This template can be used to provide detailed tracing of the +CPU and GPU behaviour of your application, allowing you unprecedented performance measurement +and tuning capabilities for apps using *Metal*. + + + + +Known **MoltenVK** Limitations +----------------------------- + +This section documents the known limitations in this version of **MoltenVK**. + +- **MoltenVK** is a Layer-0 driver implementation of *Vulkan*, and currently does not + support the loading of higher level *Vulkan Layers*. + +The following *Vulkan* features have not been implemented in this version of **MoltenVK**: + +- Tessellation and Geometry shader stages. + +- Events: + - vkCreateEvent() + - vkDestroyEvent() + - vkGetEventStatus() + - vkSetEvent() + - vkResetEvent() + - vkCmdSetEvent() + - vkCmdResetEvent() + - vkCmdWaitEvents() + +- Application-controlled memory allocations: + - VkAllocationCallbacks are ignored + +- Sparse memory: + - vkGetImageSparseMemoryRequirements() + - vkGetPhysicalDeviceSparseImageFormatProperties() + - vkQueueBindSparse() + +- Pipeline statistics query pool: + - vkCreateQueryPool(VK_QUERY_TYPE_PIPELINE_STATISTICS) + +- VkImageViewCreateInfo::VkComponentMapping supports only the following per-texture swizzles: + - VK_FORMAT_R8_UNORM: VkComponentMapping.r = VK_COMPONENT_SWIZZLE_R + - VK_FORMAT_R8G8B8A8_UNORM <-> VK_FORMAT_B8G8R8A8_UNORM + - VK_FORMAT_R8G8B8A8_SRGB <-> VK_FORMAT_B8G8R8A8_SRGB + + + + + +Third-Party Credits +------------------- + +**MoltenVK** uses technology from the following open-source frameworks: + +- [*Vulkan-Hpp*](https://github.com/KhronosGroup/Vulkan-Hpp) +- [*Vulkan-Docs*](https://github.com/KhronosGroup/Vulkan-Docs) +- [*tinyxml2*](https://github.com/leethomason/tinyxml2) + +**MoltenVKShaderConverter** uses technology from the following open-source frameworks: + +- [*SPIRV-Cross*](https://github.com/KhronosGroup/SPIRV-Cross) +- [*SPIRV-Tools*](https://github.com/KhronosGroup/SPIRV-Tools) +- [*glslang*](https://github.com/KhronosGroup/glslang) diff --git a/Docs/Vulkan_1.0_Spec_with_MoltenVK_Extensions.html b/Docs/Vulkan_1.0_Spec_with_MoltenVK_Extensions.html new file mode 120000 index 00000000..aeddd427 --- /dev/null +++ b/Docs/Vulkan_1.0_Spec_with_MoltenVK_Extensions.html @@ -0,0 +1 @@ +../MoltenVK/Vulkan-Hpp/Vulkan-Docs/out/1.0/html/vkspec.html \ No newline at end of file diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md new file mode 100644 index 00000000..3f935ffe --- /dev/null +++ b/Docs/Whats_New.md @@ -0,0 +1,22 @@ + + + + +#What's New in MoltenVK + +Copyright (c) 2014-2017 [The Brenwill Workshop Ltd.](http://www.brenwill.com) + +*This document is written in [Markdown](http://en.wikipedia.org/wiki/Markdown) format. +For best results, use a Markdown reader.* + + + +MoltenVK 0.20.0 +--------------- + +Released 2017/11/17 + +#### Initial open-source release! + diff --git a/Docs/images/MoltenVK-Logo-Banner.png b/Docs/images/MoltenVK-Logo-Banner.png new file mode 100755 index 0000000000000000000000000000000000000000..8bd880f9755d7c58f01302914672a1816b8aa003 GIT binary patch literal 163965 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
    j%9oB3M@ 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-macOS.xcscheme b/MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-macOS.xcscheme new file mode 100644 index 00000000..94566758 --- /dev/null +++ b/MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-macOS.xcscheme @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MoltenVK/MoltenVK/API/mvk_datatypes.h b/MoltenVK/MoltenVK/API/mvk_datatypes.h new file mode 100644 index 00000000..13bef661 --- /dev/null +++ b/MoltenVK/MoltenVK/API/mvk_datatypes.h @@ -0,0 +1,391 @@ +/* + * mvk_datatypes.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/* + * This file contains functions for converting between Vulkan and Metal data types. + * + * The functions here are used internally by MoltenVK, and are exposed here + * as a convenience for use elsewhere within applications using MoltenVK. + */ + +#ifndef __mvkDataTypes_h_ +#define __mvkDataTypes_h_ 1 + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#include + +#import +#import + + +#pragma mark - +#pragma mark Image properties + +#pragma mark Texture formats + +/** Enumerates the data type of a format. */ +typedef enum { + kMVKFormatNone, /**< Format type is unknown. */ + kMVKFormatColorFloat, /**< A floating point color. */ + kMVKFormatColorInt, /**< A signed integer color. */ + kMVKFormatColorUInt, /**< An unsigned integer color. */ + kMVKFormatDepthStencil, /**< A depth and stencil value. */ +} MVKFormatType; + +/** Returns the format type corresponding to the specified Vulkan VkFormat, */ +MVKFormatType mvkFormatTypeFromVkFormat(VkFormat vkFormat); + +/** Returns the format type corresponding to the specified Metal MTLPixelFormat, */ +MVKFormatType mvkFormatTypeFromMTLPixelFormat(MTLPixelFormat mtlFormat); + +/** + * Returns the Metal MTLPixelFormat corresponding to the specified Vulkan VkFormat, + * or returns MTLPixelFormatInvalid if no corresponding MTLPixelFormat exists. + * + * Not all MTLPixelFormats returned by this function are supported by all GPU's, + * and, internally, MoltenVK may substitute and use a different MTLPixelFormat than + * is returned by this function for a particular Vulkan VkFormat value. + * + * Not all macOS GPU's support the MTLPixelFormatDepth24Unorm_Stencil8 pixel format. + * Even though this function will return that value when passed the corresponding + * VkFormat value, internally, MoltenVK will use the MTLPixelFormatDepth32Float_Stencil8 + * instead when a GPU does not support the MTLPixelFormatDepth24Unorm_Stencil8 pixel format. + * On an macOS device that has more than one GPU, one of the GPU's may support the + * MTLPixelFormatDepth24Unorm_Stencil8 pixel format while another may not. + */ +MTLPixelFormat mvkMTLPixelFormatFromVkFormat(VkFormat vkFormat); + +/** + * Returns the Vulkan VkFormat corresponding to the specified Metal MTLPixelFormat, + * or returns VK_FORMAT_UNDEFINED if no corresponding VkFormat exists. + */ +VkFormat mvkVkFormatFromMTLPixelFormat(MTLPixelFormat mtlFormat); + +/** + * Returns the size, in bytes, of a texel block of the specified Vulkan format. + * For uncompressed formats, the returned value corresponds to the size in bytes of a single texel. + */ +uint32_t mvkVkFormatBytesPerBlock(VkFormat vkFormat); + +/** + * Returns the size, in bytes, of a texel block of the specified Metal format. + * For uncompressed formats, the returned value corresponds to the size in bytes of a single texel. + */ +uint32_t mvkMTLPixelFormatBytesPerBlock(MTLPixelFormat mtlFormat); + +/** + * 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. + */ +VkExtent2D mvkVkFormatBlockTexelSize(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. + */ +VkExtent2D mvkMTLPixelFormatBlockTexelSize(MTLPixelFormat mtlFormat); + +/** + * Returns the size, in bytes, of a texel of the specified Vulkan format. + * The returned value may be fractional for certain compressed formats. + */ +float mvkVkFormatBytesPerTexel(VkFormat vkFormat); + +/** + * Returns the size, in bytes, of a texel of the specified Metal format. + * The returned value may be fractional for certain compressed formats. + */ +float mvkMTLPixelFormatBytesPerTexel(MTLPixelFormat mtlFormat); + +/** + * Returns the size, in bytes, of a row of texels of the specified Vulkan format. + * For compressed formats, this takes into consideration the compression block size, + * and texelsPerRow should specify the width in texels, not blocks. + */ +size_t mvkVkFormatBytesPerRow(VkFormat vkFormat, uint32_t texelsPerRow); + +/** + * Returns the size, in bytes, of a row of texels of the specified Metal format. + * For compressed formats, this takes into consideration the compression block size, + * and texelsPerRow should specify the width in texels, not blocks. + */ +size_t mvkMTLPixelFormatBytesPerRow(MTLPixelFormat mtlFormat, uint32_t texelsPerRow); + +/** + * Returns the size, in bytes, of a texture layer of the specified Vulkan format. + * For compressed formats, this takes into consideration the compression block size, + * and texelRowsPerLayer should specify the height in texels, not blocks. + */ +size_t mvkVkFormatBytesPerLayer(VkFormat vkFormat, size_t bytesPerRow, uint32_t texelRowsPerLayer); + +/** + * Returns the size, in bytes, of a texture layer of the specified Metal format. + * For compressed formats, this takes into consideration the compression block size, + * and texelRowsPerLayer should specify the height in texels, not blocks. + */ +size_t mvkMTLPixelFormatBytesPerLayer(MTLPixelFormat mtlFormat, size_t bytesPerRow, uint32_t texelRowsPerLayer); + +/** + * Returns the default properties for the specified Vulkan format. + * + * Not all MTLPixelFormats returned by this function are supported by all GPU's, + * and, as a result, MoltenVK may return a different value from the + * vkGetPhysicalDeviceFormatProperties() function than is returned here. + * + * Not all macOS GPU's support the MTLPixelFormatDepth24Unorm_Stencil8 + * (VK_FORMAT_D24_UNORM_S8_UINT) pixel format. On an macOS device that has more + * than one GPU, one of the GPU's may support that format, while another may not. + * Use the vkGetPhysicalDeviceFormatProperties() function to return the properties + * for a particular GPU. + */ +VkFormatProperties mvkVkFormatProperties(VkFormat vkFormat); + +/** Returns the name of the specified Vulkan format. */ +const char* mvkVkFormatName(VkFormat vkFormat); + +/** Returns the name of the specified Metal pixel format. */ +const char* mvkMTLPixelFormatName(MTLPixelFormat mtlFormat); + +/** + * Returns the MTLClearColor value corresponding to the color value in the VkClearValue, + * extracting the color value that is VkFormat for the VkFormat. + */ +MTLClearColor mvkMTLClearColorFromVkClearValue(VkClearValue vkClearValue, + VkFormat vkFormat); + +/** Returns the Metal depth value corresponding to the depth value in the specified VkClearValue. */ +double mvkMTLClearDepthFromVkClearValue(VkClearValue vkClearValue); + +/** Returns the Metal stencil value corresponding to the stencil value in the specified VkClearValue. */ +uint32_t mvkMTLClearStencilFromVkClearValue(VkClearValue vkClearValue); + +/** Returns whether the specified Metal MTLPixelFormat can be used as a depth format. */ +bool mvkMTLPixelFormatIsDepthFormat(MTLPixelFormat mtlFormat); + +/** Returns whether the specified Metal MTLPixelFormat can be used as a stencil format. */ +bool mvkMTLPixelFormatIsStencilFormat(MTLPixelFormat mtlFormat); + +/** Returns the Metal texture type from the specified Vulkan image properties. */ +MTLTextureType mvkMTLTextureTypeFromVkImageType(VkImageType vkImageType, + uint32_t arraySize, + bool isMultisample); + +/** Returns the Vulkan image type from the Metal texture type. */ +VkImageType mvkVkImageTypeFromMTLTextureType(MTLTextureType mtlTextureType); + +/** Returns the Metal MTLTextureType corresponding to the Vulkan VkImageViewType. */ +MTLTextureType mvkMTLTextureTypeFromVkImageViewType(VkImageViewType vkImageViewType, bool isMultisample); + +/** Returns the Metal texture usage from the Vulkan image usage. */ +MTLTextureUsage mvkMTLTextureUsageFromVkImageUsageFlags(VkImageUsageFlags vkImageUsageFlags); + +/** Returns the Vulkan image usage from the Metal texture usage and format. */ +VkImageUsageFlags mvkVkImageUsageFlagsFromMTLTextureUsage(MTLTextureUsage mtlUsage, MTLPixelFormat mtlFormat); + +/** + * Returns the numeric sample count corresponding to the specified Vulkan sample count flag. + * + * The specified flags value should have only one bit set, otherwise an invalid numeric value will be returned. + */ +uint32_t mvkSampleCountFromVkSampleCountFlagBits(VkSampleCountFlagBits vkSampleCountFlag); + +/** Returns the Vulkan bit flags corresponding to the numeric sample count, which must be a PoT value. */ +VkSampleCountFlagBits mvkVkSampleCountFlagBitsFromSampleCount(NSUInteger sampleCount); + + +#pragma mark Mipmaps + +/** + * Returns the number of mipmap levels available to an image with the specified side dimension. + * + * If the specified dimension is a power-of-two, the value returned is (log2(dim) + 1). + * If the specified dimension is NOT a power-of-two, the value returned is 0, indicating + * that the image cannot support mipmaps. + */ +uint32_t mvkMipmapLevels(uint32_t dim); + +/** + * Returns the number of mipmap levels available to an image with the specified extent. + * + * If each dimension in the specified extent is a power-of-two, the value returned + * is MAX(log2(dim) + 1) across both dimensions. If either dimension in the specified + * extent is NOT a power-of-two, the value returned is 1, indicating that the image + * cannot support mipmaps, and that only the base mip level can be used. + */ +uint32_t mvkMipmapLevels2D(VkExtent2D extent); + +/** + * Returns the number of mipmap levels available to an image with the specified extent. + * + * If each dimension in the specified extent is a power-of-two, the value returned + * is MAX(log2(dim) + 1) across all dimensions. If either dimension in the specified + * extent is NOT a power-of-two, the value returned is 1, indicating that the image + * cannot support mipmaps, and that only the base mip level can be used. + */ +uint32_t mvkMipmapLevels3D(VkExtent3D extent); + +/** + * Returns the size of the specified zero-based mipmap level, + * when the size of the base level is the specified size. + */ +VkExtent2D mvkMipmapLevelSizeFromBaseSize(VkExtent2D baseSize, uint32_t level); + +/** + * Returns the size of the mipmap base level, when the size of + * the specified zero-based mipmap level is the specified size. + */ +VkExtent2D mvkMipmapBaseSizeFromLevelSize(VkExtent2D levelSize, uint32_t level); + + +#pragma mark Samplers + +/** + * Returns the Metal MTLSamplerAddressMode corresponding to the specified Vulkan VkSamplerAddressMode, + * or returns MTLSamplerAddressModeMirrorClampToEdge if no corresponding MTLSamplerAddressMode exists. + */ +MTLSamplerAddressMode mvkMTLSamplerAddressModeFromVkSamplerAddressMode(VkSamplerAddressMode vkMode); + +/** + * Returns the Metal MTLSamplerMinMagFilter corresponding to the specified Vulkan VkFilter, + * or returns MTLSamplerMinMagFilterNearest if no corresponding MTLSamplerMinMagFilter exists. + */ +MTLSamplerMinMagFilter mvkMTLSamplerMinMagFilterFromVkFilter(VkFilter vkFilter); + +/** + * Returns the Metal MTLSamplerMipFilter corresponding to the specified Vulkan VkSamplerMipmapMode, + * or returns MTLSamplerMipFilterNotMipmapped if no corresponding MTLSamplerMipFilter exists. + */ +MTLSamplerMipFilter mvkMTLSamplerMipFilterFromVkSamplerMipmapMode(VkSamplerMipmapMode vkMode); + + +#pragma mark - +#pragma mark Render pipeline + +/** Returns the Metal MTLColorWriteMask corresponding to the specified Vulkan VkColorComponentFlags. */ +MTLColorWriteMask mvkMTLColorWriteMaskFromVkChannelFlags(VkColorComponentFlags vkWriteFlags); + +/** Returns the Metal MTLBlendOperation corresponding to the specified Vulkan VkBlendOp. */ +MTLBlendOperation mvkMTLBlendOperationFromVkBlendOp(VkBlendOp vkBlendOp); + +/** Returns the Metal MTLBlendFactor corresponding to the specified Vulkan VkBlendFactor. */ +MTLBlendFactor mvkMTLBlendFactorFromVkBlendFactor(VkBlendFactor vkBlendFactor); + +/** + * Returns the Metal MTLVertexFormat corresponding to the specified + * Vulkan VkFormat as used as a vertex attribute format. + */ +MTLVertexFormat mvkMTLVertexFormatFromVkFormat(VkFormat vkFormat); + +/** Returns the Metal MTLVertexStepFunction corresponding to the specified Vulkan VkVertexInputRate. */ +MTLVertexStepFunction mvkMTLVertexStepFunctionFromVkVertexInputRate(VkVertexInputRate vkVtxStep); + +/** Returns the Metal MTLPrimitiveType corresponding to the specified Vulkan VkPrimitiveTopology. */ +MTLPrimitiveType mvkMTLPrimitiveTypeFromVkPrimitiveTopology(VkPrimitiveTopology vkTopology); + +#if MVK_MACOS +/** Returns the Metal MTLPrimitiveTopologyClass corresponding to the specified Vulkan VkPrimitiveTopology. */ +MTLPrimitiveTopologyClass mvkMTLPrimitiveTopologyClassFromVkPrimitiveTopology(VkPrimitiveTopology vkTopology); +#endif + +/** Returns the Metal MTLLoadAction corresponding to the specified Vulkan VkAttachmentLoadOp. */ +MTLLoadAction mvkMTLLoadActionFromVkAttachmentLoadOp(VkAttachmentLoadOp vkLoadOp); + +/** Returns the Metal MTLStoreAction corresponding to the specified Vulkan VkAttachmentStoreOp. */ +MTLStoreAction mvkMTLStoreActionFromVkAttachmentStoreOp(VkAttachmentStoreOp vkStoreOp); + +/** Returns the Metal MTLViewport corresponding to the specified Vulkan VkViewport. */ +MTLViewport mvkMTLViewportFromVkViewport(VkViewport vkViewport); + +/** Returns the Metal MTLScissorRect corresponding to the specified Vulkan VkRect2D. */ +MTLScissorRect mvkMTLScissorRectFromVkRect2D(VkRect2D vkRect); + +/** Returns the Metal MTLCompareFunction corresponding to the specified Vulkan VkCompareOp, */ +MTLCompareFunction mvkMTLCompareFunctionFromVkCompareOp(VkCompareOp vkOp); + +/** Returns the Metal MTLStencilOperation corresponding to the specified Vulkan VkStencilOp, */ +MTLStencilOperation mvkMTLStencilOperationFromVkStencilOp(VkStencilOp vkOp); + +/** Returns the Metal MTLCullMode corresponding to the specified Vulkan VkCullModeFlags, */ +MTLCullMode mvkMTLCullModeFromVkCullModeFlags(VkCullModeFlags vkCull); + +/** Returns the Metal MTLWinding corresponding to the specified Vulkan VkFrontFace, */ +MTLWinding mvkMTLWindingFromVkFrontFace(VkFrontFace vkWinding); + +/** Returns the Metal MTLTriangleFillMode corresponding to the specified Vulkan VkPolygonMode, */ +MTLTriangleFillMode mvkMTLTriangleFillModeFromVkPolygonMode(VkPolygonMode vkFillMode); + +/** Returns the Metal MTLIndexType corresponding to the specified Vulkan VkIndexType, */ +MTLIndexType mvkMTLIndexTypeFromVkIndexType(VkIndexType vkIdxType); + +/** Returns the size, in bytes, of a vertex index of the specified type. */ +size_t mvkMTLIndexTypeSizeInBytes(MTLIndexType mtlIdxType); + + +#pragma mark - +#pragma mark Geometry conversions + +/** Returns a VkExtent2D that corresponds to the specified CGSize. */ +static inline VkExtent2D mvkVkExtent2DFromCGSize(CGSize cgSize) { + VkExtent2D vkExt; + vkExt.width = cgSize.width; + vkExt.height = cgSize.height; + return vkExt; +} + +/** Returns a Metal MTLOrigin constructed from the specified VkOffset3D. */ +static inline MTLOrigin mvkMTLOriginFromVkOffset3D(VkOffset3D vkOffset) { + return MTLOriginMake(vkOffset.x, vkOffset.y, vkOffset.z); +} + +/** Returns a Metal MTLSize constructed from the specified VkExtent3D. */ +static inline MTLSize mvkMTLSizeFromVkExtent3D(VkExtent3D vkExtent) { + return MTLSizeMake(vkExtent.width, vkExtent.height, vkExtent.depth); +} + + +#pragma mark - +#pragma mark Memory options + +/** Macro indicating the Vulkan memory type bits corresponding to Metal private memory (not host visible). */ +#define MVK_VK_MEMORY_TYPE_METAL_PRIVATE (VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) + +/** Macro indicating the Vulkan memory type bits corresponding to Metal managed memory (host visible and non-coherent). */ +#define MVK_VK_MEMORY_TYPE_METAL_MANAGED (VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT) + +/** Macro indicating the Vulkan memory type bits corresponding to Metal shared memory (host visible and coherent). */ +#define MVK_VK_MEMORY_TYPE_METAL_SHARED (VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT) + +/** Returns the Metal storage mode corresponding to the specified Vulkan memory flags. */ +MTLStorageMode mvkMTLStorageModeFromVkMemoryPropertyFlags(VkMemoryPropertyFlags vkFlags); + +/** Returns the Metal CPU cache mode corresponding to the specified Vulkan memory flags. */ +MTLCPUCacheMode mvkMTLCPUCacheModeFromVkMemoryPropertyFlags(VkMemoryPropertyFlags vkFlags); + +/** Returns the Metal resource option flags corresponding to the specified Vulkan memory flags. */ +MTLResourceOptions mvkMTLResourceOptionsFromVkMemoryPropertyFlags(VkMemoryPropertyFlags vkFlags); + + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif diff --git a/MoltenVK/MoltenVK/API/mvk_vulkan.h b/MoltenVK/MoltenVK/API/mvk_vulkan.h new file mode 100644 index 00000000..bde77cca --- /dev/null +++ b/MoltenVK/MoltenVK/API/mvk_vulkan.h @@ -0,0 +1,48 @@ +/* + * mvk_vulkan.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * This is a convenience header file that loads vulkan.h with the appropriate MoltenVK + * Vulkan platform extensions automatically enabled for iOS or macOS. + * + * When building for iOS, this header automatically enables the VK_MVK_ios_surface Vulkan extension. + * When building for macOS, this header automatically enables the VK_MVK_macos_surface Vulkan extension. + * + * Use the following form when including this header file: + * + * #include + */ + +#ifndef __mvk_vulkan_h_ +#define __mvk_vulkan_h_ 1 + + +#include + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +# define VK_USE_PLATFORM_IOS_MVK 1 +#endif + +#ifdef __MAC_OS_X_VERSION_MAX_ALLOWED +# define VK_USE_PLATFORM_MACOS_MVK 1 +#endif + +#include + +#endif diff --git a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h new file mode 100644 index 00000000..d5c67d95 --- /dev/null +++ b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h @@ -0,0 +1,317 @@ +/* + * vk_mvk_moltenvk.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** Vulkan extension VK_MVK_moltenvk. */ + +#ifndef __vk_mvk_moltenvk_h_ +#define __vk_mvk_moltenvk_h_ 1 + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#include + +#ifdef __OBJC__ +#import +#import +#endif + + +/** + * The version number of MoltenVK is a single integer value, derived from the Major, Minor, + * and Patch version values, where each of the Major, Minor, and Patch components is allocated + * two decimal digits, in the format MjMnPt. This creates a version number that is both human + * readable and allows efficient computational comparisons to a single integer number. + * + * The following examples illustrate how the MoltenVK version number is built from its components: + * - 002000 (version 0.20.0) + * - 010000 (version 1.0.0) + * - 030104 (version 3.1.4) + * - 401215 (version 4.12.15) + */ +#define MVK_VERSION_MAJOR 0 +#define MVK_VERSION_MINOR 20 +#define MVK_VERSION_PATCH 0 + +#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) + + +#define VK_MVK_MOLTENVK_SPEC_VERSION 4 +#define VK_MVK_MOLTENVK_EXTENSION_NAME "VK_MVK_moltenvk" + +/** MoltenVK configuration settings. */ +typedef struct { + VkBool32 debugMode; /**< If enabled, several debugging capabilities will be enabled. Shader code will be logged during Runtime Shader Conversion. Improves support for Xcode GPU Frame Capture. Default value is determined at build time by the presence of a DEBUG build setting. By default the DEBUG build setting is defined when MoltenVK is compiled in Debug mode, and not defined when compiled in Release mode. */ + VkBool32 shaderConversionFlipVertexY; /**< If enabled, MSL vertex shader code created during Runtime Shader Conversion will flip the Y-axis of each vertex, as Vulkan coordinate system is inverse of OpenGL. Default is true. */ + VkBool32 supportLargeQueryPools; /**< Metal allows only 8192 occlusion queries per MTLBuffer. If enabled, MoltenVK allocates a MTLBuffer for each query pool, allowing each query pool to support 8192 queries, which may slow performance or cause unexpected behaviour if the query pool is not established prior to a Metal renderpass, or if the query pool is changed within a Metal renderpass. If disabled, one MTLBuffer will be shared by all query pools, which improves performance, but limits the total device queries to 8192. Default is false. */ + VkBool32 displayWatermark; /**< If enabled, a MoltenVK logo watermark will be rendered on top of the scene. This can be enabled for publicity during demos. Default value is determined at build time by the presence of a MVK_WATERMARK build setting. By default the MVK_WATERMARK build setting is defined when MoltenVK is compiled in Debug mode, and not defined when compiled in Release mode. */ + VkBool32 performanceTracking; /**< If enabled, per-frame performance statistics are tracked, optionally logged, and can be retrieved via the vkGetSwapchainPerformanceMVK() function, and various shader compilation performance statistics are tracked, logged, and can be retrieved via the vkGetShaderCompilationPerformanceMVK() function. Default is false. */ + uint32_t performanceLoggingFrameCount; /**< If non-zero, performance statistics will be periodically logged to the console, on a repeating cycle of this many frames per swapchain. The performanceTracking capability must also be enabled. Default is zero, indicating no logging. */ +} MVKDeviceConfiguration; + +/** Features provided by the current implementation of Metal on the current device. */ +typedef struct { + uint32_t mslVersion; /**< The version of the Metal Shading Language available on this device. The format of the integer is MMmmpp, with two decimal digts each for Major, minor, and patch version values (eg. MSL 1.2 would appear as 010200). */ + VkBool32 indirectDrawing; /**< If true, draw calls support parameters held in a GPU buffer. */ + VkBool32 baseVertexInstanceDrawing; /**< If true, draw calls support specifiying the base vertex and instance. */ + VkBool32 dynamicMTLBuffers; /**< If true, dynamic MTLBuffers for setting vertex, fragment, and compute bytes are supported. */ + VkBool32 shaderSpecialization; /**< If true, shader specialization (aka Metal function constants) is supported. */ + VkBool32 ioSurfaces; /**< If true, VkImages can be underlaid by IOSurfaces via the vkUseIOSurfaceMVK() function, to support inter-process image transfers. */ + VkBool32 texelBuffers; /**< If true, texel buffers are supported, allowing the contents of a buffer to be interpreted as an image via a VkBufferView. */ + VkBool32 depthClipMode; /**< If true, the device supports both depth clipping and depth clamping per the depthClampEnable flag of VkPipelineRasterizationStateCreateInfo in VkGraphicsPipelineCreateInfo. */ + uint32_t maxPerStageBufferCount; /**< The total number of per-stage Metal buffers available for shader uniform content and attributes. */ + uint32_t maxPerStageTextureCount; /**< The total number of per-stage Metal textures available for shader uniform content. */ + uint32_t maxPerStageSamplerCount; /**< The total number of per-stage Metal samplers available for shader uniform content. */ + VkDeviceSize maxMTLBufferSize; /**< The max size of a MTLBuffer (in bytes). */ + VkDeviceSize mtlBufferAlignment; /**< The alignment used when allocating memory for MTLBuffers. Must be PoT. */ + VkDeviceSize maxQueryBufferSize; /**< The maximum size of an occlusion query buffer (in bytes). */ + VkSampleCountFlags supportedSampleCounts; /**< A bitmask identifying the sample counts supported by the device. */ +} MVKPhysicalDeviceMetalFeatures; + +/** MoltenVK swapchain performance statistics. */ +typedef struct { + double lastFrameInterval; /**< The time interval between this frame and the immediately previous frame, in seconds. */ + double averageFrameInterval; /**< The rolling average time interval between frames, in seconds. This value has less volatility than the lastFrameInterval value. The inverse of this value is the rolling average frames per second. */ + double averageFramesPerSecond; /**< The rolling average number of frames per second. This is simply the inverse of the averageFrameInterval value. */ +} MVKSwapchainPerformance; + +/** MoltenVK performance of a particular type of shader compilation event. */ +typedef struct { + uint32_t count; /**< The number of compilation events of this type. */ + double averageInterval; /**< The average time interval consumed by the compilation event, in seconds. */ + double minimumInterval; /**< The minimum time interval consumed by the compilation event, in seconds. */ + double maximumInterval; /**< The maximum time interval consumed by the compilation event, in seconds. */ +} MVKShaderCompilationEventPerformance; + +/** MoltenVK performance of shader compilation events for a VkDevice. */ +typedef struct { + MVKShaderCompilationEventPerformance spirvToMSL; /** Convert SPIR-V to MSL source code. */ + MVKShaderCompilationEventPerformance mslCompile; /** Compile MSL source code into a MTLLibrary. */ + MVKShaderCompilationEventPerformance mslLoad; /** Load pre-compiled MSL code into a MTLLibrary. */ + MVKShaderCompilationEventPerformance functionRetrieval; /** Retrieve a MTLFunction from a MTLLibrary. */ + MVKShaderCompilationEventPerformance functionSpecialization; /** Specialize a retrieved MTLFunction. */ + MVKShaderCompilationEventPerformance pipelineCompile; /** Compile MTLFunctions into a pipeline. */ +} MVKShaderCompilationPerformance; + + +#pragma mark - +#pragma mark Function types + +typedef void (VKAPI_PTR *PFN_vkGetMoltenVKDeviceConfigurationMVK)(VkDevice device, MVKDeviceConfiguration* pConfiguration); +typedef VkResult (VKAPI_PTR *PFN_vkSetMoltenVKDeviceConfigurationMVK)(VkDevice device, MVKDeviceConfiguration* pConfiguration); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceMetalFeaturesMVK)(VkPhysicalDevice physicalDevice, MVKPhysicalDeviceMetalFeatures* pMetalFeatures); +typedef void (VKAPI_PTR *PFN_vkGetSwapchainPerformanceMVK)(VkDevice device, VkSwapchainKHR swapchain, MVKSwapchainPerformance* pSwapchainPerf); +typedef void (VKAPI_PTR *PFN_vkGetShaderCompilationPerformanceMVK)(VkDevice device, MVKShaderCompilationPerformance* pShaderCompPerf); +typedef void (VKAPI_PTR *PFN_vkGetVersionStringsMVK)(char* pMoltenVersionStringBuffer, uint32_t moltenVersionStringBufferLength, char* pVulkanVersionStringBuffer, uint32_t vulkanVersionStringBufferLength); + +#ifdef __OBJC__ +typedef void (VKAPI_PTR *PFN_vkGetMTLDeviceMVK)(VkPhysicalDevice physicalDevice, id* pMTLDevice); +typedef VkResult (VKAPI_PTR *PFN_vkSetMTLTextureMVK)(VkImage image, id mtlTexture); +typedef void (VKAPI_PTR *PFN_vkGetMTLTextureMVK)(VkImage image, id* pMTLTexture); +typedef VkResult (VKAPI_PTR *PFN_vkUseIOSurfaceMVK)(VkImage image, IOSurfaceRef ioSurface); +typedef void (VKAPI_PTR *PFN_vkGetIOSurfaceMVK)(VkImage image, IOSurfaceRef* pIOSurface); +#endif // __OBJC__ + +#pragma mark Deprecated license functions +typedef VkResult (VKAPI_PTR *PFN_vkActivateMoltenVKLicenseMVK)(const char* licenseID, const char* licenseKey, VkBool32 acceptLicenseTermsAndConditions); +typedef VkResult (VKAPI_PTR *PFN_vkActivateMoltenVKLicensesMVK)(); + + +#pragma mark - +#pragma mark Function prototypes + +#ifndef VK_NO_PROTOTYPES + +/** + * Populates the pConfiguration structure with the current MoltenVK configuration settings + * of the specified device. + * + * To change a specific configuration value, call vkGetMoltenVKDeviceConfigurationMVK() + * to retrieve the current configuration, make changes, and call + * vkSetMoltenVKDeviceConfigurationMVK() to update all of the values. + */ +VKAPI_ATTR void VKAPI_CALL vkGetMoltenVKDeviceConfigurationMVK( + VkDevice device, + MVKDeviceConfiguration* pConfiguration); + +/** + * Sets the MoltenVK configuration settings of the specified device to those found in the + * pConfiguration structure. + * + * To change a specific configuration value, call vkGetMoltenVKDeviceConfigurationMVK() + * to retrieve the current configuration, make changes, and call + * vkSetMoltenVKDeviceConfigurationMVK() to update all of the values. + */ +VKAPI_ATTR VkResult VKAPI_CALL vkSetMoltenVKDeviceConfigurationMVK( + VkDevice device, + MVKDeviceConfiguration* pConfiguration); + +/** + * Populates the pMetalFeatures structure with the Metal-specific features + * supported by the specified physical device. + */ +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMetalFeaturesMVK( + VkPhysicalDevice physicalDevice, + MVKPhysicalDeviceMetalFeatures* pMetalFeatures); + +/** + * Populates the specified MVKSwapchainPerformance structure with + * the current performance statistics for the specified swapchain. + */ +VKAPI_ATTR void VKAPI_CALL vkGetSwapchainPerformanceMVK( + VkDevice device, + VkSwapchainKHR swapchain, + MVKSwapchainPerformance* pSwapchainPerf); + +/** + * Populates the specified MVKShaderCompilationPerformance structure with the + * current shader compilation performance statistics for the specified device. + */ +VKAPI_ATTR void VKAPI_CALL vkGetShaderCompilationPerformanceMVK( + VkDevice device, + MVKShaderCompilationPerformance* pShaderCompPerf); + +/** + * Returns a human readable version of the MoltenVK and Vulkan versions. + * + * This function is provided as a convenience for reporting. Use the MVK_VERSION, + * VK_API_VERSION_1_0, and VK_HEADER_VERSION macros for programmatically accessing + * the corresponding version numbers. + */ +VKAPI_ATTR void VKAPI_CALL vkGetVersionStringsMVK( + char* pMoltenVersionStringBuffer, + uint32_t moltenVersionStringBufferLength, + char* pVulkanVersionStringBuffer, + uint32_t vulkanVersionStringBufferLength); + + +#ifdef __OBJC__ + +/** Returns, in the pMTLDevice pointer, the MTLDevice used by the VkPhysicalDevice. */ +VKAPI_ATTR void VKAPI_CALL vkGetMTLDeviceMVK( + VkPhysicalDevice physicalDevice, + id* pMTLDevice); + +/** + * Sets the VkImage to use the specified MTLTexture. + * + * Any differences in the properties of mtlTexture and this image will modify the + * properties of this image. + * + * If a MTLTexture has already been created for this image, it will be destroyed. + * + * Returns VK_SUCCESS. + */ +VKAPI_ATTR VkResult VKAPI_CALL vkSetMTLTextureMVK( + VkImage image, + id mtlTexture); + +/** Returns, in the pMTLTexture pointer, the MTLTexture currently underlaying the VkImage. */ +VKAPI_ATTR void VKAPI_CALL vkGetMTLTextureMVK( + VkImage image, + id* pMTLTexture); + +/** + * Indicates that a VkImage should use an IOSurface to underlay the Metal texture. + * + * If ioSurface is not null, it will be used as the IOSurface, and any differences + * in the properties of that IOSurface will modify the properties of this image. + * + * If ioSurface is null, this image will create and use an IOSurface + * whose properties are compatible with the properties of this image. + * + * If a MTLTexture has already been created for this image, it will be destroyed. + * + * Returns: + * - VK_SUCCESS. + * - VK_ERROR_FEATURE_NOT_PRESENT if IOSurfaces are not supported on the platform. + * - VK_ERROR_INITIALIZATION_FAILED if ioSurface is specified and is not compatible with this VkImage. + */ +VKAPI_ATTR VkResult VKAPI_CALL vkUseIOSurfaceMVK( + VkImage image, + IOSurfaceRef ioSurface); + + +/** + * Returns, in the pIOSurface pointer, the IOSurface currently underlaying the VkImage, + * as set by the useIOSurfaceMVK() function, or returns null if the VkImage is not using + * an IOSurface, or if the platform does not support IOSurfaces. + */ +VKAPI_ATTR void VKAPI_CALL vkGetIOSurfaceMVK( + VkImage image, + IOSurfaceRef* pIOSurface); + + +#endif // __OBJC__ + + +#pragma mark - +#pragma mark Shaders + +/** + * Enumerates the magic number values to set in the MVKMSLSPIRVHeader when + * submitting a SPIR-V stream that contains either Metal Shading Language source + * code or Metal Shading Language compiled binary code in place of SPIR-V code. + */ +typedef enum { + kMVKMagicNumberSPIRVCode = 0x07230203, /**< SPIR-V stream contains standard SPIR-V code. */ + kMVKMagicNumberMSLSourceCode = 0x19960412, /**< SPIR-V stream contains Metal Shading Language source code. */ + kMVKMagicNumberMSLCompiledCode = 0x19981215, /**< SPIR-V stream contains Metal Shading Language compiled binary code. */ +} MVKMSLMagicNumber; + +/** + * Describes the header at the start of an SPIR-V stream, when it contains either + * Metal Shading Language source code or Metal Shading Language compiled binary code. + * + * To submit MSL source code to the vkCreateShaderModule() function in place of SPIR-V + * code, prepend a MVKMSLSPIRVHeader containing the kMVKMagicNumberMSLSourceCode magic + * number to the MSL source code. The MSL source code must be null-terminated. + * + * To submit MSL compiled binary code to the vkCreateShaderModule() function in place of + * SPIR-V code, prepend a MVKMSLSPIRVHeader containing the kMVKMagicNumberMSLCompiledCode + * magic number to the MSL compiled binary code. + * + * In both cases, the pCode element of VkShaderModuleCreateInfo should pointer to the + * location of the MVKMSLSPIRVHeader, and the MSL code should start at the byte immediately + * after the MVKMSLSPIRVHeader. + * + * The codeSize element of VkShaderModuleCreateInfo should be set to the entire size of + * the submitted code memory, including the additional sizeof(MVKMSLSPIRVHeader) bytes + * taken up by the MVKMSLSPIRVHeader, and, in the case of MSL source code, including + * the null-terminator byte. + */ +typedef uint32_t MVKMSLSPIRVHeader; + + +#pragma mark Deprecated license functions +__attribute__((deprecated("MoltenVK no longer requires a runtime license."))) +VKAPI_ATTR VkResult VKAPI_CALL vkActivateMoltenVKLicenseMVK(const char* licenseID, const char* licenseKey, VkBool32 acceptLicenseTermsAndConditions); +__attribute__((deprecated("MoltenVK no longer requires a runtime license."))) +VKAPI_ATTR static inline void VKAPI_CALL vkActivateMoltenVKLicensesMVK() {} + +#endif // VK_NO_PROTOTYPES + + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.h b/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.h new file mode 100644 index 00000000..2784d3f9 --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.h @@ -0,0 +1,75 @@ +/* + * MVKMVKCmdDispatch.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKCommand.h" +#include "MVKMTLResourceBindings.h" +#include + +#import + +class MVKDevice; + + +#pragma mark - +#pragma mark MVKCmdDispatch + +/** Vulkan command to dispatch compute threadgroups. */ +class MVKCmdDispatch : public MVKCommand { + +public: + void setContent(uint32_t x, uint32_t y, uint32_t z); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdDispatch(MVKCommandTypePool* pool); + +protected: + MTLSize _mtlThreadgroupCount; +}; + + +#pragma mark - +#pragma mark MVKCmdDispatchIndirect + +/** Vulkan command to dispatch compute threadgroups. */ +class MVKCmdDispatchIndirect : public MVKCommand { + +public: + void setContent(VkBuffer buffer, VkDeviceSize offset); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdDispatchIndirect(MVKCommandTypePool* pool); + +protected: + id _mtlIndirectBuffer; + NSUInteger _mtlIndirectBufferOffset; +}; + + +#pragma mark - +#pragma mark Command creation functions + +/** Adds a compute threadgroup dispatch command to the specified command buffer. */ +void mvkCmdDispatch(VkCommandBuffer commandBuffer, uint32_t x, uint32_t y, uint32_t z); + +/** Adds an indirect compute threadgroup dispatch command to the specified command buffer. */ +void mvkCmdDispatchIndirect(VkCommandBuffer cmdBuffer, VkBuffer buffer, VkDeviceSize offset); + diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.mm b/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.mm new file mode 100644 index 00000000..fcd0ec94 --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.mm @@ -0,0 +1,90 @@ +/* + * MVKMVKCmdDispatch.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKCmdDispatch.h" +#include "MVKCommandBuffer.h" +#include "MVKCommandPool.h" +#include "MVKBuffer.h" +#include "MVKFoundation.h" +#include "mvk_datatypes.h" + + +#pragma mark - +#pragma mark MVKCmdDraw + +void MVKCmdDispatch::setContent(uint32_t x, uint32_t y, uint32_t z) { + _mtlThreadgroupCount.width = x; + _mtlThreadgroupCount.height = y; + _mtlThreadgroupCount.depth = z; +} + +void MVKCmdDispatch::encode(MVKCommandEncoder* cmdEncoder) { +// MVKLogDebug("vkCmdDispatch() dispatching (%d, %d, %d) threadgroups.", _x, _y, _z); + + cmdEncoder->finalizeDispatchState(); // Ensure all updated state has been submitted to Metal + [cmdEncoder->getMTLComputeEncoder() dispatchThreadgroups: _mtlThreadgroupCount + threadsPerThreadgroup: cmdEncoder->_mtlThreadgroupSize]; +} + +MVKCmdDispatch::MVKCmdDispatch(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) { +} + + +#pragma mark - +#pragma mark MVKCmdDispatchIndirect + +void MVKCmdDispatchIndirect::setContent(VkBuffer buffer, VkDeviceSize offset) { + MVKBuffer* mvkBuffer = (MVKBuffer*)buffer; + _mtlIndirectBuffer = mvkBuffer->getMTLBuffer(); + _mtlIndirectBufferOffset = mvkBuffer->getMTLBufferOffset() + offset; +} + +void MVKCmdDispatchIndirect::encode(MVKCommandEncoder* cmdEncoder) { +// MVKLogDebug("vkCmdDispatchIndirect() dispatching indirectly."); + + cmdEncoder->finalizeDispatchState(); // Ensure all updated state has been submitted to Metal + [cmdEncoder->getMTLComputeEncoder() dispatchThreadgroupsWithIndirectBuffer: _mtlIndirectBuffer + indirectBufferOffset: _mtlIndirectBufferOffset + threadsPerThreadgroup: cmdEncoder->_mtlThreadgroupSize]; +} + +MVKCmdDispatchIndirect::MVKCmdDispatchIndirect(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + + + +#pragma mark - +#pragma mark Command creation functions + +void mvkCmdDispatch(VkCommandBuffer cmdBuffer, uint32_t x, uint32_t y, uint32_t z) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)cmdBuffer; + MVKCmdDispatch* cmd = cmdBuff->_commandPool->_cmdDispatchPool.acquireObject(); + cmd->setContent(x, y, z); + cmdBuff->addCommand(cmd); +} + +void mvkCmdDispatchIndirect(VkCommandBuffer cmdBuffer, VkBuffer buffer, VkDeviceSize offset) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)cmdBuffer; + MVKCmdDispatchIndirect* cmd = cmdBuff->_commandPool->_cmdDispatchIndirectPool.acquireObject(); + cmd->setContent(buffer, offset); + cmdBuff->addCommand(cmd); +} + + diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDraw.h b/MoltenVK/MoltenVK/Commands/MVKCmdDraw.h new file mode 100644 index 00000000..33234e29 --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCmdDraw.h @@ -0,0 +1,211 @@ +/* + * MVKCmdDraw.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKCommand.h" +#include "MVKMTLResourceBindings.h" +#include + +#import + +class MVKDevice; + + +#pragma mark - +#pragma mark MVKCmdBindVertexBuffers + +/** Vulkan command to bind buffers containing vertex content. */ +class MVKCmdBindVertexBuffers : public MVKCommand { + +public: + void setContent(uint32_t startBinding, + uint32_t bindingCount, + const VkBuffer* pBuffers, + const VkDeviceSize* pOffsets); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdBindVertexBuffers(MVKCommandTypePool* pool); + +protected: + std::vector _bindings; +}; + + +#pragma mark - +#pragma mark MVKCmdBindIndexBuffer + +/** Vulkan command to bind a vertex index buffer. */ +class MVKCmdBindIndexBuffer : public MVKCommand { + +public: + void setContent(VkBuffer buffer, VkDeviceSize offset, VkIndexType indexType); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdBindIndexBuffer(MVKCommandTypePool* pool); + +protected: + MVKIndexMTLBufferBinding _binding; +}; + + +#pragma mark - +#pragma mark MVKCmdDraw + +/** Vulkan command to draw vertices. */ +class MVKCmdDraw : public MVKCommand { + +public: + void setContent(uint32_t vertexCount, + uint32_t instanceCount, + uint32_t firstVertex, + uint32_t firstInstance); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdDraw(MVKCommandTypePool* pool); + +protected: + uint32_t _firstVertex; + uint32_t _vertexCount; + uint32_t _firstInstance; + uint32_t _instanceCount; +}; + + +#pragma mark - +#pragma mark MVKCmdDrawIndexed + +/** Vulkan command to draw indexed vertices. */ +class MVKCmdDrawIndexed : public MVKCommand { + +public: + void setContent(uint32_t indexCount, + uint32_t instanceCount, + uint32_t firstIndex, + int32_t vertexOffset, + uint32_t firstInstance); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdDrawIndexed(MVKCommandTypePool* pool); + +protected: + uint32_t _firstIndex; + uint32_t _indexCount; + int32_t _vertexOffset; + uint32_t _firstInstance; + uint32_t _instanceCount; +}; + + +#pragma mark - +#pragma mark MVKCmdDrawIndirect + +/** Vulkan command to draw vertices indirectly. */ +class MVKCmdDrawIndirect : public MVKCommand { + +public: + void setContent(VkBuffer buffer, + VkDeviceSize offset, + uint32_t count, + uint32_t stride); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdDrawIndirect(MVKCommandTypePool* pool); + +protected: + id _mtlIndirectBuffer; + NSUInteger _mtlIndirectBufferOffset; + uint32_t _mtlIndirectBufferStride; + uint32_t _drawCount; +}; + + +#pragma mark - +#pragma mark MVKCmdDrawIndexedIndirect + +/** Vulkan command to draw indexed vertices indirectly. */ +class MVKCmdDrawIndexedIndirect : public MVKCommand { + +public: + void setContent(VkBuffer buffer, + VkDeviceSize offset, + uint32_t count, + uint32_t stride); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdDrawIndexedIndirect(MVKCommandTypePool* pool); + +protected: + id _mtlIndirectBuffer; + VkDeviceSize _mtlIndirectBufferOffset; + uint32_t _mtlIndirectBufferStride; + uint32_t _drawCount; +}; + + +#pragma mark - +#pragma mark Command creation functions + +/** Adds a vertex bind command to the specified command buffer. */ +void mvkCmdBindVertexBuffers(VkCommandBuffer cmdBuffer, + uint32_t startBinding, + uint32_t bindingCount, + const VkBuffer* pBuffers, + const VkDeviceSize* pOffsets); + +/** Adds a bind index buffer command to the specified command buffer. */ +void mvkCmdBindIndexBuffer(VkCommandBuffer cmdBuffer, + VkBuffer buffer, + VkDeviceSize offset, + VkIndexType indexType); + +/** Adds a vertex draw command to the specified command buffer. */ +void mvkCmdDraw(VkCommandBuffer cmdBuffer, + uint32_t vertexCount, + uint32_t instanceCount, + uint32_t firstVertex, + uint32_t firstInstance); + +/** Adds an indexed draw command to the specified command buffer. */ +void mvkCmdDrawIndexed(VkCommandBuffer cmdBuffer, + uint32_t indexCount, + uint32_t instanceCount, + uint32_t firstIndex, + int32_t vertexOffset, + uint32_t firstInstance); + +/** Adds an indirect draw command to the specified command buffer. */ +void mvkCmdDrawIndirect(VkCommandBuffer cmdBuffer, + VkBuffer buffer, + VkDeviceSize offset, + uint32_t count, + uint32_t stride); + +/** Adds an indirect indexed draw command to the specified command buffer. */ +void mvkCmdDrawIndexedIndirect(VkCommandBuffer cmdBuffer, + VkBuffer buffer, + VkDeviceSize offset, + uint32_t count, + uint32_t stride); + diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDraw.mm b/MoltenVK/MoltenVK/Commands/MVKCmdDraw.mm new file mode 100644 index 00000000..27f13c94 --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCmdDraw.mm @@ -0,0 +1,320 @@ +/* + * MVKCmdDraw.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKCmdDraw.h" +#include "MVKCommandBuffer.h" +#include "MVKCommandPool.h" +#include "MVKBuffer.h" +#include "MVKFoundation.h" +#include "mvk_datatypes.h" + + +#pragma mark - +#pragma mark MVKCmdBindVertexBuffers + +void MVKCmdBindVertexBuffers::setContent(uint32_t startBinding, + uint32_t bindingCount, + const VkBuffer* pBuffers, + const VkDeviceSize* pOffsets) { + + _bindings.clear(); // Clear for reuse + _bindings.reserve(bindingCount); + MVKMTLBufferBinding b; + for (uint32_t bindIdx = 0; bindIdx < bindingCount; bindIdx++) { + MVKBuffer* mvkBuffer = (MVKBuffer*)pBuffers[bindIdx]; + b.index = getDevice()->getMetalBufferIndexForVertexAttributeBinding(startBinding + bindIdx); + b.mtlBuffer = mvkBuffer->getMTLBuffer(); + b.offset = mvkBuffer->getMTLBufferOffset() + pOffsets[bindIdx]; + _bindings.push_back(b); + } +} + +void MVKCmdBindVertexBuffers::encode(MVKCommandEncoder* cmdEncoder) { + for (auto& b : _bindings) { cmdEncoder->_graphicsResourcesState.bindVertexBuffer(b); } +} + +MVKCmdBindVertexBuffers::MVKCmdBindVertexBuffers(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdBindIndexBuffer + +void MVKCmdBindIndexBuffer::setContent(VkBuffer buffer, + VkDeviceSize offset, + VkIndexType indexType) { + MVKBuffer* mvkBuffer = (MVKBuffer*)buffer; + _binding.mtlBuffer = mvkBuffer->getMTLBuffer(); + _binding.offset = mvkBuffer->getMTLBufferOffset() + offset; + _binding.mtlIndexType = mvkMTLIndexTypeFromVkIndexType(indexType); +} + +void MVKCmdBindIndexBuffer::encode(MVKCommandEncoder* cmdEncoder) { + cmdEncoder->_graphicsResourcesState.bindIndexBuffer(_binding); +} + +MVKCmdBindIndexBuffer::MVKCmdBindIndexBuffer(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdDraw + +void MVKCmdDraw::setContent(uint32_t vertexCount, + uint32_t instanceCount, + uint32_t firstVertex, + uint32_t firstInstance) { + _vertexCount = vertexCount; + _instanceCount = instanceCount; + _firstVertex = firstVertex; + _firstInstance = firstInstance; + + // Validate + clearConfigurationResult(); + if ((_firstInstance != 0) && !(getDevice()->_pMetalFeatures->baseVertexInstanceDrawing)) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdDraw(): The current device does not support drawing with a non-zero base instance.")); + } +} + +void MVKCmdDraw::encode(MVKCommandEncoder* cmdEncoder) { + + cmdEncoder->finalizeDrawState(); // Ensure all updated state has been submitted to Metal + + if (cmdEncoder->_pDeviceMetalFeatures->baseVertexInstanceDrawing) { + [cmdEncoder->_mtlRenderEncoder drawPrimitives: cmdEncoder->_mtlPrimitiveType + vertexStart: _firstVertex + vertexCount: _vertexCount + instanceCount: _instanceCount + baseInstance: _firstInstance]; + } else { + [cmdEncoder->_mtlRenderEncoder drawPrimitives: cmdEncoder->_mtlPrimitiveType + vertexStart: _firstVertex + vertexCount: _vertexCount + instanceCount: _instanceCount]; + } +} + +MVKCmdDraw::MVKCmdDraw(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) { +} + + +#pragma mark - +#pragma mark MVKCmdDrawIndexed + +void MVKCmdDrawIndexed::setContent(uint32_t indexCount, + uint32_t instanceCount, + uint32_t firstIndex, + int32_t vertexOffset, + uint32_t firstInstance) { + _indexCount = indexCount; + _instanceCount = instanceCount; + _firstIndex = firstIndex; + _vertexOffset = vertexOffset; + _firstInstance = firstInstance; + + // Validate + clearConfigurationResult(); + if ((_firstInstance != 0) && !(getDevice()->_pMetalFeatures->baseVertexInstanceDrawing)) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdDrawIndexed(): The current device does not support drawing with a non-zero base instance.")); + } + if ((_vertexOffset != 0) && !(getDevice()->_pMetalFeatures->baseVertexInstanceDrawing)) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdDrawIndexed(): The current device does not support drawing with a non-zero base vertex.")); + } +} + +void MVKCmdDrawIndexed::encode(MVKCommandEncoder* cmdEncoder) { + + cmdEncoder->finalizeDrawState(); // Ensure all updated state has been submitted to Metal + + MVKIndexMTLBufferBinding& ibb = cmdEncoder->_graphicsResourcesState._mtlIndexBufferBinding; + size_t idxSize = mvkMTLIndexTypeSizeInBytes(ibb.mtlIndexType); + VkDeviceSize idxBuffOffset = ibb.offset + (_firstIndex * idxSize); + + if (cmdEncoder->_pDeviceMetalFeatures->baseVertexInstanceDrawing) { + [cmdEncoder->_mtlRenderEncoder drawIndexedPrimitives: cmdEncoder->_mtlPrimitiveType + indexCount: _indexCount + indexType: ibb.mtlIndexType + indexBuffer: ibb.mtlBuffer + indexBufferOffset: idxBuffOffset + instanceCount: _instanceCount + baseVertex: _vertexOffset + baseInstance: _firstInstance]; + } else { + [cmdEncoder->_mtlRenderEncoder drawIndexedPrimitives: cmdEncoder->_mtlPrimitiveType + indexCount: _indexCount + indexType: ibb.mtlIndexType + indexBuffer: ibb.mtlBuffer + indexBufferOffset: idxBuffOffset + instanceCount: _instanceCount]; + } +} + +MVKCmdDrawIndexed::MVKCmdDrawIndexed(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdDrawIndirect + +void MVKCmdDrawIndirect::setContent(VkBuffer buffer, + VkDeviceSize offset, + uint32_t count, + uint32_t stride) { + MVKBuffer* mvkBuffer = (MVKBuffer*)buffer; + _mtlIndirectBuffer = mvkBuffer->getMTLBuffer(); + _mtlIndirectBufferOffset = mvkBuffer->getMTLBufferOffset() + offset; + _mtlIndirectBufferStride = stride; + _drawCount = count; + + // Validate + clearConfigurationResult(); + if ( !(getDevice()->_pMetalFeatures->indirectDrawing) ) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdDrawIndirect(): The current device does not support indirect drawing.")); + } +} + +void MVKCmdDrawIndirect::encode(MVKCommandEncoder* cmdEncoder) { + + cmdEncoder->finalizeDrawState(); // Ensure all updated state has been submitted to Metal + + VkDeviceSize mtlIndBuffOfst = _mtlIndirectBufferOffset; + for (uint32_t drawIdx = 0; drawIdx < _drawCount; drawIdx++) { + [cmdEncoder->_mtlRenderEncoder drawPrimitives: cmdEncoder->_mtlPrimitiveType + indirectBuffer: _mtlIndirectBuffer + indirectBufferOffset: mtlIndBuffOfst]; + mtlIndBuffOfst += _mtlIndirectBufferStride; + } +} + +MVKCmdDrawIndirect::MVKCmdDrawIndirect(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdDrawIndexedIndirect + +void MVKCmdDrawIndexedIndirect::setContent(VkBuffer buffer, + VkDeviceSize offset, + uint32_t count, + uint32_t stride) { + MVKBuffer* mvkBuffer = (MVKBuffer*)buffer; + _mtlIndirectBuffer = mvkBuffer->getMTLBuffer(); + _mtlIndirectBufferOffset = mvkBuffer->getMTLBufferOffset() + offset; + _mtlIndirectBufferStride = stride; + _drawCount = count; + + // Validate + clearConfigurationResult(); + if ( !(getDevice()->_pMetalFeatures->indirectDrawing) ) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdDrawIndexedIndirect(): The current device does not support indirect drawing.")); + } +} + +void MVKCmdDrawIndexedIndirect::encode(MVKCommandEncoder* cmdEncoder) { + + cmdEncoder->finalizeDrawState(); // Ensure all updated state has been submitted to Metal + + MVKIndexMTLBufferBinding& ibb = cmdEncoder->_graphicsResourcesState._mtlIndexBufferBinding; + + VkDeviceSize mtlIndBuffOfst = _mtlIndirectBufferOffset; + for (uint32_t drawIdx = 0; drawIdx < _drawCount; drawIdx++) { + [cmdEncoder->_mtlRenderEncoder drawIndexedPrimitives: cmdEncoder->_mtlPrimitiveType + indexType: ibb.mtlIndexType + indexBuffer: ibb.mtlBuffer + indexBufferOffset: ibb.offset + indirectBuffer: _mtlIndirectBuffer + indirectBufferOffset: mtlIndBuffOfst]; + mtlIndBuffOfst += _mtlIndirectBufferStride; + } +} + +MVKCmdDrawIndexedIndirect::MVKCmdDrawIndexedIndirect(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark Command creation functions + +void mvkCmdBindVertexBuffers(VkCommandBuffer cmdBuffer, + uint32_t startBinding, + uint32_t bindingCount, + const VkBuffer* pBuffers, + const VkDeviceSize* pOffsets) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)cmdBuffer; + MVKCmdBindVertexBuffers* cmd = cmdBuff->_commandPool->_cmdBindVertexBuffersPool.acquireObject(); + cmd->setContent(startBinding, bindingCount, pBuffers, pOffsets); + cmdBuff->addCommand(cmd); +} + +void mvkCmdDraw(VkCommandBuffer cmdBuffer, + uint32_t vertexCount, + uint32_t instanceCount, + uint32_t firstVertex, + uint32_t firstInstance) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)cmdBuffer; + MVKCmdDraw* cmd = cmdBuff->_commandPool->_cmdDrawPool.acquireObject(); + cmd->setContent(vertexCount, instanceCount, firstVertex, firstInstance); + cmdBuff->addCommand(cmd); +} + +void mvkCmdDrawIndexed(VkCommandBuffer cmdBuffer, + uint32_t indexCount, + uint32_t instanceCount, + uint32_t firstIndex, + int32_t vertexOffset, + uint32_t firstInstance) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)cmdBuffer; + MVKCmdDrawIndexed* cmd = cmdBuff->_commandPool->_cmdDrawIndexedPool.acquireObject(); + cmd->setContent(indexCount, instanceCount, firstIndex, vertexOffset, firstInstance); + cmdBuff->addCommand(cmd); +} + +void mvkCmdBindIndexBuffer(VkCommandBuffer cmdBuffer, + VkBuffer buffer, + VkDeviceSize offset, + VkIndexType indexType) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)cmdBuffer; + MVKCmdBindIndexBuffer* cmd = cmdBuff->_commandPool->_cmdBindIndexBufferPool.acquireObject(); + cmd->setContent(buffer, offset, indexType); + cmdBuff->addCommand(cmd); +} + +void mvkCmdDrawIndirect(VkCommandBuffer cmdBuffer, + VkBuffer buffer, + VkDeviceSize offset, + uint32_t count, + uint32_t stride) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)cmdBuffer; + MVKCmdDrawIndirect* cmd = cmdBuff->_commandPool->_cmdDrawIndirectPool.acquireObject(); + cmd->setContent(buffer, offset, count, stride); + cmdBuff->addCommand(cmd); +} + +void mvkCmdDrawIndexedIndirect(VkCommandBuffer cmdBuffer, + VkBuffer buffer, + VkDeviceSize offset, + uint32_t count, + uint32_t stride) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)cmdBuffer; + MVKCmdDrawIndexedIndirect* cmd = cmdBuff->_commandPool->_cmdDrawIndexedIndirectPool.acquireObject(); + cmd->setContent(buffer, offset, count, stride); + cmdBuff->addCommand(cmd); +} + + diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.h b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.h new file mode 100644 index 00000000..05cd801f --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.h @@ -0,0 +1,170 @@ +/* + * MVKCmdPipeline.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKCommand.h" +#include + +class MVKCommandBuffer; +class MVKPipeline; +class MVKPipelineLayout; +class MVKDescriptorSet; + + +#pragma mark - +#pragma mark MVKCmdPipelineBarrier + +/** Represents an abstract Vulkan command to add a pipeline barrier. */ +class MVKCmdPipelineBarrier : public MVKCommand { + +public: + void setContent(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkDependencyFlags dependencyFlags, + uint32_t memoryBarrierCount, + const VkMemoryBarrier* pMemoryBarriers, + uint32_t bufferMemoryBarrierCount, + const VkBufferMemoryBarrier* pBufferMemoryBarriers, + uint32_t imageMemoryBarrierCount, + const VkImageMemoryBarrier* pImageMemoryBarriers); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdPipelineBarrier(MVKCommandTypePool* pool); + +private: + VkPipelineStageFlags _srcStageMask; + VkPipelineStageFlags _dstStageMask; + VkDependencyFlags _dependencyFlags; + std::vector _memoryBarriers; + std::vector _bufferMemoryBarriers; + std::vector _imageMemoryBarriers; +}; + + +#pragma mark - +#pragma mark MVKCmdBindPipeline + +/** Vulkan command to bind the pipeline state. */ +class MVKCmdBindPipeline : public MVKCommand { + +public: + void setContent(VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdBindPipeline(MVKCommandTypePool* pool); + +private: + VkPipelineBindPoint _bindPoint; + MVKPipeline* _pipeline; + +}; + + +#pragma mark - +#pragma mark MVKCmdBindDescriptorSets + +/** Vulkan command to bind descriptor sets. */ +class MVKCmdBindDescriptorSets : public MVKCommand { + +public: + void setContent(VkPipelineBindPoint pipelineBindPoint, + VkPipelineLayout layout, + uint32_t firstSet, + uint32_t setCount, + const VkDescriptorSet* pDescriptorSets, + uint32_t dynamicOffsetCount, + const uint32_t* pDynamicOffsets); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdBindDescriptorSets(MVKCommandTypePool* pool); + +private: + VkPipelineBindPoint _pipelineBindPoint; + MVKPipelineLayout* _pipelineLayout; + std::vector _descriptorSets; + std::vector _dynamicOffsets; + uint32_t _firstSet; +}; + + +#pragma mark - +#pragma mark MVKCmdPushConstants + +/** Vulkan command to bind push constants. */ +class MVKCmdPushConstants : public MVKCommand { + +public: + void setContent(VkPipelineLayout layout, + VkShaderStageFlags stageFlags, + uint32_t offset, + uint32_t size, + const void* pValues); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdPushConstants(MVKCommandTypePool* pool); + +private: + MVKPipelineLayout* _pipelineLayout; + VkShaderStageFlags _stageFlags; + uint32_t _offset; + std::vector _pushConstants; +}; + + +#pragma mark - +#pragma mark Command creation functions + +/** Adds commands to the specified command buffer that insert the specified pipeline barriers. */ +void mvkCmdPipelineBarrier(VkCommandBuffer commandBuffer, + VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkDependencyFlags dependencyFlags, + uint32_t memoryBarrierCount, + const VkMemoryBarrier* pMemoryBarriers, + uint32_t bufferMemoryBarrierCount, + const VkBufferMemoryBarrier* pBufferMemoryBarriers, + uint32_t imageMemoryBarrierCount, + const VkImageMemoryBarrier* pImageMemoryBarriers); + +/** Adds a command to the specified command buffer that binds the specified pipeline. */ +void mvkCmdBindPipeline(VkCommandBuffer cmdBuffer, + VkPipelineBindPoint pipelineBindPoint, + VkPipeline pipeline); + +/** Adds commands to the specified command buffer that insert the specified descriptor sets. */ +void mvkCmdBindDescriptorSets(VkCommandBuffer cmdBuffer, + VkPipelineBindPoint pipelineBindPoint, + VkPipelineLayout layout, + uint32_t firstSet, + uint32_t setCount, + const VkDescriptorSet* pDescriptorSets, + uint32_t dynamicOffsetCount, + const uint32_t* pDynamicOffsets); + +/** Adds a vertex bind command to the specified command buffer. */ +void mvkCmdPushConstants(VkCommandBuffer cmdBuffer, + VkPipelineLayout layout, + VkShaderStageFlags stageFlags, + uint32_t offset, + uint32_t size, + const void* pValues); diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm new file mode 100644 index 00000000..b1627897 --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm @@ -0,0 +1,242 @@ +/* + * MVKCmdPipeline.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKCmdPipeline.h" +#include "MVKCommandBuffer.h" +#include "MVKCommandPool.h" +#include "MVKImage.h" +#include "MVKBuffer.h" +#include "MVKPipeline.h" +#include "MVKFoundation.h" +#include "mvk_datatypes.h" + + +#pragma mark - +#pragma mark MVKCmdPipelineBarrier + +void MVKCmdPipelineBarrier::setContent(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkDependencyFlags dependencyFlags, + uint32_t memoryBarrierCount, + const VkMemoryBarrier* pMemoryBarriers, + uint32_t bufferMemoryBarrierCount, + const VkBufferMemoryBarrier* pBufferMemoryBarriers, + uint32_t imageMemoryBarrierCount, + const VkImageMemoryBarrier* pImageMemoryBarriers) { + _srcStageMask = srcStageMask; + _dstStageMask = dstStageMask; + _dependencyFlags = dependencyFlags; + + _memoryBarriers.clear(); // Clear for reuse + _memoryBarriers.reserve(memoryBarrierCount); + for (uint32_t i = 0; i < memoryBarrierCount; i++) { + _memoryBarriers.push_back(pMemoryBarriers[i]); + } + + _bufferMemoryBarriers.clear(); // Clear for reuse + _bufferMemoryBarriers.reserve(bufferMemoryBarrierCount); + for (uint32_t i = 0; i < bufferMemoryBarrierCount; i++) { + _bufferMemoryBarriers.push_back(pBufferMemoryBarriers[i]); + } + + _imageMemoryBarriers.clear(); // Clear for reuse + _imageMemoryBarriers.reserve(imageMemoryBarrierCount); + for (uint32_t i = 0; i < imageMemoryBarrierCount; i++) { + _imageMemoryBarriers.push_back(pImageMemoryBarriers[i]); + } +} + +void MVKCmdPipelineBarrier::encode(MVKCommandEncoder* cmdEncoder) { + +#if MVK_MACOS + // Calls below invoke MTLBlitCommandEncoder so must apply this first + if ( !(_memoryBarriers.empty() && _imageMemoryBarriers.empty()) ) { + [cmdEncoder->_mtlRenderEncoder textureBarrier]; + } +#endif + + MVKCommandUse cmdUse = kMVKCommandUsePipelineBarrier; + + // Apply global memory barriers + for (auto& mb : _memoryBarriers) { + getDevice()->applyMemoryBarrier(_srcStageMask, _dstStageMask, &mb, cmdEncoder, cmdUse); + } + + // Apply specific buffer barriers + for (auto& mb : _bufferMemoryBarriers) { + MVKBuffer* mvkBuff = (MVKBuffer*)mb.buffer; + mvkBuff->applyBufferMemoryBarrier(_srcStageMask, _dstStageMask, &mb, cmdEncoder, cmdUse); + } + + // Apply specific image barriers + for (auto& mb : _imageMemoryBarriers) { + MVKImage* mvkImg = (MVKImage*)mb.image; + mvkImg->applyImageMemoryBarrier(_srcStageMask, _dstStageMask, &mb, cmdEncoder, cmdUse); + } +} + +MVKCmdPipelineBarrier::MVKCmdPipelineBarrier(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdBindPipeline + +void MVKCmdBindPipeline::setContent(VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline) { + _bindPoint = pipelineBindPoint; + _pipeline = (MVKPipeline*)pipeline; +} + +void MVKCmdBindPipeline::encode(MVKCommandEncoder* cmdEncoder) { + cmdEncoder->bindPipeline(_bindPoint, _pipeline); +} + +MVKCmdBindPipeline::MVKCmdBindPipeline(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdBindDescriptorSets + +void MVKCmdBindDescriptorSets::setContent(VkPipelineBindPoint pipelineBindPoint, + VkPipelineLayout layout, + uint32_t firstSet, + uint32_t setCount, + const VkDescriptorSet* pDescriptorSets, + uint32_t dynamicOffsetCount, + const uint32_t* pDynamicOffsets) { + _pipelineBindPoint = pipelineBindPoint; + _pipelineLayout = (MVKPipelineLayout*)layout; + _firstSet = firstSet; + + // Add the descriptor sets + _descriptorSets.clear(); // Clear for reuse + _descriptorSets.reserve(setCount); + for (uint32_t dsIdx = 0; dsIdx < setCount; dsIdx++) { + _descriptorSets.push_back((MVKDescriptorSet*)pDescriptorSets[dsIdx]); + } + + // Add the dynamic offsets + _dynamicOffsets.clear(); // Clear for reuse + _dynamicOffsets.reserve(dynamicOffsetCount); + for (uint32_t doIdx = 0; doIdx < dynamicOffsetCount; doIdx++) { + _dynamicOffsets.push_back(pDynamicOffsets[doIdx]); + } +} + +void MVKCmdBindDescriptorSets::encode(MVKCommandEncoder* cmdEncoder) { + _pipelineLayout->bindDescriptorSets(cmdEncoder, _descriptorSets, _firstSet, _dynamicOffsets); +} + +MVKCmdBindDescriptorSets::MVKCmdBindDescriptorSets(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdPushConstants + +void MVKCmdPushConstants::setContent(VkPipelineLayout layout, + VkShaderStageFlags stageFlags, + uint32_t offset, + uint32_t size, + const void* pValues) { + _pipelineLayout = (MVKPipelineLayout*)layout; + _stageFlags = stageFlags; + _offset = offset; + + size_t pcBuffSize = mvkAlignByteOffset(size, getDevice()->_pMetalFeatures->mtlBufferAlignment); + mvkEnsureSize(_pushConstants, pcBuffSize); + copy_n((char*)pValues, size, _pushConstants.begin()); +} + +void MVKCmdPushConstants::encode(MVKCommandEncoder* cmdEncoder) { + if (mvkAreFlagsEnabled(_stageFlags, VK_SHADER_STAGE_VERTEX_BIT)) { + cmdEncoder->getPushConstants(VK_SHADER_STAGE_VERTEX_BIT)->setPushConstants(_offset, _pushConstants); + } + if (mvkAreFlagsEnabled(_stageFlags, VK_SHADER_STAGE_FRAGMENT_BIT)) { + cmdEncoder->getPushConstants(VK_SHADER_STAGE_FRAGMENT_BIT)->setPushConstants(_offset, _pushConstants); + } + if (mvkAreFlagsEnabled(_stageFlags, VK_SHADER_STAGE_COMPUTE_BIT)) { + cmdEncoder->getPushConstants(VK_SHADER_STAGE_COMPUTE_BIT)->setPushConstants(_offset, _pushConstants); + } +} + +MVKCmdPushConstants::MVKCmdPushConstants(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark Command creation functions + +void mvkCmdPipelineBarrier(VkCommandBuffer commandBuffer, + VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkDependencyFlags dependencyFlags, + uint32_t memoryBarrierCount, + const VkMemoryBarrier* pMemoryBarriers, + uint32_t bufferMemoryBarrierCount, + const VkBufferMemoryBarrier* pBufferMemoryBarriers, + uint32_t imageMemoryBarrierCount, + const VkImageMemoryBarrier* pImageMemoryBarriers) { + + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdPipelineBarrier* cmd = cmdBuff->_commandPool->_cmdPipelineBarrierPool.acquireObject(); + cmd->setContent(srcStageMask, dstStageMask, dependencyFlags, + memoryBarrierCount, pMemoryBarriers, + bufferMemoryBarrierCount, pBufferMemoryBarriers, + imageMemoryBarrierCount, pImageMemoryBarriers); + cmdBuff->addCommand(cmd); +} + +void mvkCmdBindPipeline(VkCommandBuffer cmdBuffer, + VkPipelineBindPoint pipelineBindPoint, + VkPipeline pipeline) { + + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)cmdBuffer; + MVKCmdBindPipeline* cmd = cmdBuff->_commandPool->_cmdBindPipelinePool.acquireObject(); + cmd->setContent(pipelineBindPoint, pipeline); + cmdBuff->addCommand(cmd); +} + +void mvkCmdBindDescriptorSets(VkCommandBuffer cmdBuffer, + VkPipelineBindPoint pipelineBindPoint, + VkPipelineLayout layout, + uint32_t firstSet, + uint32_t setCount, + const VkDescriptorSet* pDescriptorSets, + uint32_t dynamicOffsetCount, + const uint32_t* pDynamicOffsets) { + + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)cmdBuffer; + MVKCmdBindDescriptorSets* cmd = cmdBuff->_commandPool->_cmdBindDescriptorSetsPool.acquireObject(); + cmd->setContent(pipelineBindPoint, layout, firstSet, setCount, pDescriptorSets, dynamicOffsetCount, pDynamicOffsets); + cmdBuff->addCommand(cmd); +} + +void mvkCmdPushConstants(VkCommandBuffer cmdBuffer, + VkPipelineLayout layout, + VkShaderStageFlags stageFlags, + uint32_t offset, + uint32_t size, + const void* pValues) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)cmdBuffer; + MVKCmdPushConstants* cmd = cmdBuff->_commandPool->_cmdPushConstantsPool.acquireObject(); + cmd->setContent(layout, stageFlags, offset, size, pValues); + cmdBuff->addCommand(cmd); +} + diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdQueries.h b/MoltenVK/MoltenVK/Commands/MVKCmdQueries.h new file mode 100644 index 00000000..9064e693 --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCmdQueries.h @@ -0,0 +1,178 @@ +/* + * MVKCmdQueries.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKCommand.h" + +class MVKQueryPool; + + +#pragma mark - +#pragma mark MVKCmdQuery + +/** Abstract Vulkan command to manage queries. */ +class MVKCmdQuery : public MVKCommand { + +public: + void setContent(VkQueryPool queryPool, uint32_t query); + + MVKCmdQuery(MVKCommandTypePool* pool); + +protected: + MVKQueryPool* _queryPool; + uint32_t _query; +}; + + +#pragma mark - +#pragma mark MVKCmdBeginQuery + +/** Vulkan command to begin a query. */ +class MVKCmdBeginQuery : public MVKCmdQuery { + +public: + void setContent(VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags); + + void added(MVKCommandBuffer* cmdBuffer) override; + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdBeginQuery(MVKCommandTypePool* pool); + +protected: + VkQueryControlFlags _flags; +}; + + +#pragma mark - +#pragma mark MVKCmdEndQuery + +/** Vulkan command to end a query. */ +class MVKCmdEndQuery : public MVKCmdQuery { + +public: + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdEndQuery(MVKCommandTypePool* pool); +}; + + +#pragma mark - +#pragma mark MVKCmdWriteTimestamp + +/** Vulkan command to write a timestamp. */ +class MVKCmdWriteTimestamp : public MVKCmdQuery { + +public: + void setContent(VkPipelineStageFlagBits pipelineStage, + VkQueryPool queryPool, + uint32_t query); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdWriteTimestamp(MVKCommandTypePool* pool); + +protected: + VkPipelineStageFlagBits _pipelineStage; +}; + + +#pragma mark - +#pragma mark MVKCmdResetQueryPool + +/** Vulkan command to reset the results in a query pool. */ +class MVKCmdResetQueryPool : public MVKCmdQuery { + +public: + void setContent(VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdResetQueryPool(MVKCommandTypePool* pool); + +protected: + uint32_t _queryCount; +}; + + +#pragma mark - +#pragma mark MVKCmdCopyQueryPoolResults + +/** Vulkan command to reset the results in a query pool. */ +class MVKCmdCopyQueryPoolResults : public MVKCmdQuery { + +public: + void setContent(VkQueryPool queryPool, + uint32_t firstQuery, + uint32_t queryCount, + VkBuffer destBuffer, + VkDeviceSize destOffset, + VkDeviceSize destStride, + VkQueryResultFlags flags); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdCopyQueryPoolResults(MVKCommandTypePool* pool); + +protected: + uint32_t _queryCount; + MVKBuffer* _destBuffer; + VkDeviceSize _destOffset; + VkDeviceSize _destStride; + VkQueryResultFlags _flags; +}; + + +#pragma mark - +#pragma mark Command creation functions + +/** Adds a begin query command to the specified command buffer. */ +void mvkCmdBeginQuery(VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t query, + VkQueryControlFlags flags); + +/** Adds an end query command to the specified command buffer. */ +void mvkCmdEndQuery(VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t query); + +/** Adds a write timestamp command to the specified command buffer. */ +void mvkCmdWriteTimestamp(VkCommandBuffer commandBuffer, + VkPipelineStageFlagBits pipelineStage, + VkQueryPool queryPool, + uint32_t query); + +/** Adds a reset query pool command to the specified command buffer. */ +void mvkCmdResetQueryPool(VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t firstQuery, + uint32_t queryCount); + +/** Adds a copy query pool results command to the specified command buffer. */ +void mvkCmdCopyQueryPoolResults(VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t firstQuery, + uint32_t queryCount, + VkBuffer destBuffer, + VkDeviceSize destOffset, + VkDeviceSize destStride, + VkQueryResultFlags flags); + + diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdQueries.mm b/MoltenVK/MoltenVK/Commands/MVKCmdQueries.mm new file mode 100644 index 00000000..20fa7bdb --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCmdQueries.mm @@ -0,0 +1,185 @@ +/* + * MVKCmdQueries.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKCmdQueries.h" +#include "MVKCommandBuffer.h" +#include "MVKCommandPool.h" +#include "MVKQueryPool.h" + + +#pragma mark - +#pragma mark MVKCmdQuery + +void MVKCmdQuery::setContent(VkQueryPool queryPool, uint32_t query) { + _queryPool = (MVKQueryPool*)queryPool; + _query = query; +} + +MVKCmdQuery::MVKCmdQuery(MVKCommandTypePool* pool) : MVKCommand::MVKCommand(pool) {} + + +#pragma mark - +#pragma mark MVKCmdBeginQuery + +void MVKCmdBeginQuery::added(MVKCommandBuffer* cmdBuffer) { + _queryPool->beginQueryAddedTo(_query, cmdBuffer); +}; + +void MVKCmdBeginQuery::setContent(VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags) { + MVKCmdQuery::setContent(queryPool, query); + _flags = flags; +} + +void MVKCmdBeginQuery::encode(MVKCommandEncoder* cmdEncoder) { + _queryPool->beginQuery(_query, _flags, cmdEncoder); +} + +MVKCmdBeginQuery::MVKCmdBeginQuery(MVKCommandTypePool* pool) + : MVKCmdQuery::MVKCmdQuery((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdEndQuery + +void MVKCmdEndQuery::encode(MVKCommandEncoder* cmdEncoder) { + _queryPool->endQuery(_query, cmdEncoder); +} + +MVKCmdEndQuery::MVKCmdEndQuery(MVKCommandTypePool* pool) + : MVKCmdQuery::MVKCmdQuery((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdWriteTimestamp + +void MVKCmdWriteTimestamp::setContent(VkPipelineStageFlagBits pipelineStage, + VkQueryPool queryPool, + uint32_t query) { + MVKCmdQuery::setContent(queryPool, query); + _pipelineStage = pipelineStage; +} + +void MVKCmdWriteTimestamp::encode(MVKCommandEncoder* cmdEncoder) { + cmdEncoder->markTimestamp(_queryPool, _query); +} + +MVKCmdWriteTimestamp::MVKCmdWriteTimestamp(MVKCommandTypePool* pool) + : MVKCmdQuery::MVKCmdQuery((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdResetQueryPool + +void MVKCmdResetQueryPool::setContent(VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) { + MVKCmdQuery::setContent(queryPool, firstQuery); + _queryCount = queryCount; +} + +void MVKCmdResetQueryPool::encode(MVKCommandEncoder* cmdEncoder) { + _queryPool->resetResults(_query, _queryCount, cmdEncoder); +} + +MVKCmdResetQueryPool::MVKCmdResetQueryPool(MVKCommandTypePool* pool) + : MVKCmdQuery::MVKCmdQuery((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdCopyQueryPoolResults + +void MVKCmdCopyQueryPoolResults::setContent(VkQueryPool queryPool, + uint32_t firstQuery, + uint32_t queryCount, + VkBuffer destBuffer, + VkDeviceSize destOffset, + VkDeviceSize destStride, + VkQueryResultFlags flags) { + MVKCmdQuery::setContent(queryPool, firstQuery); + _queryCount = queryCount; + _destBuffer = (MVKBuffer*) destBuffer; + _destOffset = destOffset; + _destStride = destStride; + _flags = flags; +} + +void MVKCmdCopyQueryPoolResults::encode(MVKCommandEncoder* cmdEncoder) { + [cmdEncoder->_mtlCmdBuffer addCompletedHandler: ^(id mtlCmdBuff) { + _queryPool->copyQueryPoolResults(_query, _queryCount, _destBuffer, _destOffset, _destStride, _flags); + }]; + cmdEncoder->flush(); +} + +MVKCmdCopyQueryPoolResults::MVKCmdCopyQueryPoolResults(MVKCommandTypePool* pool) + : MVKCmdQuery::MVKCmdQuery((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark Command creation functions + +void mvkCmdBeginQuery(VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t query, + VkQueryControlFlags flags) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdBeginQuery* cmd = cmdBuff->_commandPool->_cmdBeginQueryPool.acquireObject(); + cmd->setContent(queryPool, query, flags); + cmdBuff->addCommand(cmd); +} + +void mvkCmdEndQuery(VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t query) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdEndQuery* cmd = cmdBuff->_commandPool->_cmdEndQueryPool.acquireObject(); + cmd->setContent(queryPool, query); + cmdBuff->addCommand(cmd); +} + +void mvkCmdWriteTimestamp(VkCommandBuffer commandBuffer, + VkPipelineStageFlagBits pipelineStage, + VkQueryPool queryPool, + uint32_t query) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdWriteTimestamp* cmd = cmdBuff->_commandPool->_cmdWriteTimestampPool.acquireObject(); + cmd->setContent(pipelineStage, queryPool, query); + cmdBuff->addCommand(cmd); +} + +void mvkCmdResetQueryPool(VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t firstQuery, + uint32_t queryCount) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdResetQueryPool* cmd = cmdBuff->_commandPool->_cmdResetQueryPoolPool.acquireObject(); + cmd->setContent(queryPool, firstQuery, queryCount); + cmdBuff->addCommand(cmd); +} + +void mvkCmdCopyQueryPoolResults(VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t firstQuery, + uint32_t queryCount, + VkBuffer destBuffer, + VkDeviceSize destOffset, + VkDeviceSize destStride, + VkQueryResultFlags flags) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdCopyQueryPoolResults* cmd = cmdBuff->_commandPool->_cmdCopyQueryPoolResultsPool.acquireObject(); + cmd->setContent(queryPool, firstQuery, queryCount, destBuffer, destOffset, destStride, flags); + cmdBuff->addCommand(cmd); +} + diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h new file mode 100644 index 00000000..1b8d4601 --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h @@ -0,0 +1,340 @@ +/* + * MVKCmdRenderPass.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKCommand.h" +#include + +#import + +class MVKCommandBuffer; +class MVKRenderPass; +class MVKFramebuffer; + + +#pragma mark - +#pragma mark MVKCmdBeginRenderPass + +/** Vulkan command to begin a render pass. */ +class MVKCmdBeginRenderPass : public MVKCommand { + +public: + void setContent(const VkRenderPassBeginInfo* pRenderPassBegin, + VkSubpassContents contents); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdBeginRenderPass(MVKCommandTypePool* pool); + +private: + VkRenderPassBeginInfo _info; + VkSubpassContents _contents; + MVKRenderPass* _renderPass; + MVKFramebuffer* _framebuffer; + std::vector _clearValues; +}; + + +#pragma mark - +#pragma mark MVKCmdNextSubpass + +/** Vulkan command to begin a render pass. */ +class MVKCmdNextSubpass : public MVKCommand { + +public: + void setContent(VkSubpassContents contents); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdNextSubpass(MVKCommandTypePool* pool); + +private: + VkSubpassContents _contents; +}; + + +#pragma mark - +#pragma mark MVKCmdEndRenderPass + +/** Vulkan command to end the current render pass. */ +class MVKCmdEndRenderPass : public MVKCommand { + +public: + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdEndRenderPass(MVKCommandTypePool* pool); +}; + + +#pragma mark - +#pragma mark MVKCmdExecuteCommands + +/** Vulkan command to end the current render pass. */ +class MVKCmdExecuteCommands : public MVKCommand { + +public: + void setContent(uint32_t commandBuffersCount, const VkCommandBuffer* pCmdBuffers); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdExecuteCommands(MVKCommandTypePool* pool); + +private: + std::vector _secondaryCommandBuffers; +}; + +#pragma mark - +#pragma mark MVKCmdSetViewport + +/** Vulkan command to set the viewports. */ +class MVKCmdSetViewport : public MVKCommand { + +public: + void setContent(uint32_t firstViewport, uint32_t viewportCount, const VkViewport* pViewports); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdSetViewport(MVKCommandTypePool* pool); + +private: + uint32_t _firstViewport; + std::vector _mtlViewports; +}; + + +#pragma mark - +#pragma mark MVKCmdSetScissor + +/** Vulkan command to set the scissor rectangles. */ +class MVKCmdSetScissor : public MVKCommand { + +public: + void setContent(uint32_t firstScissor, uint32_t scissorCount, const VkRect2D* pScissors); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdSetScissor(MVKCommandTypePool* pool); + +private: + uint32_t _firstScissor; + std::vector _mtlScissors; +}; + + +#pragma mark - +#pragma mark MVKCmdSetLineWidth + +/** Vulkan command to set the line width. */ +class MVKCmdSetLineWidth : public MVKCommand { + +public: + void setContent(float lineWidth); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdSetLineWidth(MVKCommandTypePool* pool); + +private: + float _lineWidth; +}; + + +#pragma mark - +#pragma mark MVKCmdSetDepthBias + +/** Vulkan command to set the depth bias. */ +class MVKCmdSetDepthBias : public MVKCommand { + +public: + void setContent(float depthBiasConstantFactor, + float depthBiasSlopeFactor, + float depthBiasClamp); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdSetDepthBias(MVKCommandTypePool* pool); + +private: + float _depthBiasConstantFactor; + float _depthBiasClamp; + float _depthBiasSlopeFactor; +}; + + +#pragma mark - +#pragma mark MVKCmdSetBlendConstants + +/** Vulkan command to set the blend constants. */ +class MVKCmdSetBlendConstants : public MVKCommand { + +public: + void setContent(const float blendConst[4]); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdSetBlendConstants(MVKCommandTypePool* pool); + +private: + float _red; + float _green; + float _blue; + float _alpha; +}; + + +#pragma mark - +#pragma mark MVKCmdSetDepthBounds + +/** Vulkan command to set depth bounds. */ +class MVKCmdSetDepthBounds : public MVKCommand { + +public: + void setContent(float minDepthBounds, float maxDepthBounds); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdSetDepthBounds(MVKCommandTypePool* pool); + +private: + float _minDepthBounds; + float _maxDepthBounds; +}; + + +#pragma mark - +#pragma mark MVKCmdSetStencilCompareMask + +/** Vulkan command to set the stencil compare mask. */ +class MVKCmdSetStencilCompareMask : public MVKCommand { + +public: + void setContent(VkStencilFaceFlags faceMask, uint32_t stencilCompareMask); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdSetStencilCompareMask(MVKCommandTypePool* pool); + +private: + VkStencilFaceFlags _faceMask; + uint32_t _stencilCompareMask; +}; + + +#pragma mark - +#pragma mark MVKCmdSetStencilWriteMask + +/** Vulkan command to set the stencil write mask. */ +class MVKCmdSetStencilWriteMask : public MVKCommand { + +public: + void setContent(VkStencilFaceFlags faceMask, uint32_t stencilWriteMask); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdSetStencilWriteMask(MVKCommandTypePool* pool); + +private: + VkStencilFaceFlags _faceMask; + uint32_t _stencilWriteMask; +}; + + +#pragma mark - +#pragma mark MVKCmdSetStencilReference + +/** Vulkan command to set the stencil reference value. */ +class MVKCmdSetStencilReference : public MVKCommand { + +public: + void setContent(VkStencilFaceFlags faceMask, uint32_t stencilReference); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdSetStencilReference(MVKCommandTypePool* pool); + +private: + VkStencilFaceFlags _faceMask; + uint32_t _stencilReference; +}; + + +#pragma mark - +#pragma mark Command creation functions + +/** Adds a begin render pass command to the specified command buffer. */ +void mvkCmdBeginRenderPass(VkCommandBuffer commandBuffer, + const VkRenderPassBeginInfo* pRenderPassBegin, + VkSubpassContents contents); + +/** Adds a next render pass command to the specified command buffer. */ +void mvkCmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContents contents); + +/** Adds an end render pass command to the specified command buffer. */ +void mvkCmdEndRenderPass(VkCommandBuffer commandBuffer); + +/** Adds an execute commands command to the specified command buffer. */ +void mvkCmdExecuteCommands(VkCommandBuffer commandBuffer, + uint32_t cmdBuffersCount, + const VkCommandBuffer* pCmdBuffers); + +/** Adds a set viewport command to the specified command buffer. */ +void mvkCmdSetViewport(VkCommandBuffer commandBuffer, + uint32_t firstViewport, + uint32_t viewportCount, + const VkViewport* pViewports); + +/** Adds a set scissor command to the specified command buffer. */ +void mvkCmdSetScissor(VkCommandBuffer commandBuffer, + uint32_t firstScissor, + uint32_t scissorCount, + const VkRect2D* pScissors); + +/** Adds a set line width command to the specified command buffer. */ +void mvkCmdSetLineWidth(VkCommandBuffer commandBuffer, float lineWidth); + +/** Adds a set depth bias command to the specified command buffer. */ +void mvkCmdSetDepthBias(VkCommandBuffer commandBuffer, + float depthBiasConstantFactor, + float depthBiasClamp, + float depthBiasSlopeFactor); + +/** Adds a set blend constants command to the specified command buffer. */ +void mvkCmdSetBlendConstants(VkCommandBuffer commandBuffer, + const float blendConst[4]); + +/** Adds a set depth bounds command to the specified command buffer. */ +void mvkCmdSetDepthBounds(VkCommandBuffer commandBuffer, + float minDepthBounds, + float maxDepthBounds); + +/** Adds a set stencil compare mask command to the specified command buffer. */ +void mvkCmdSetStencilCompareMask(VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + uint32_t stencilCompareMask); + +/** Adds a set stencil write mask command to the specified command buffer. */ +void mvkCmdSetStencilWriteMask(VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + uint32_t stencilWriteMask); + +/** Adds a set stencil reference value command to the specified command buffer. */ +void mvkCmdSetStencilReference(VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + uint32_t stencilReference); + diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm new file mode 100644 index 00000000..188f5f29 --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm @@ -0,0 +1,391 @@ +/* + * MVKCmdRenderPass.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKCmdRenderPass.h" +#include "MVKCommandBuffer.h" +#include "MVKCommandPool.h" +#include "MVKRenderPass.h" +#include "MVKPipeline.h" +#include "MVKFoundation.h" +#include "mvk_datatypes.h" + + +#pragma mark - +#pragma mark MVKCmdBeginRenderPass + +void MVKCmdBeginRenderPass::setContent(const VkRenderPassBeginInfo* pRenderPassBegin, + VkSubpassContents contents) { + _info = *pRenderPassBegin; + _contents = contents; + _renderPass = (MVKRenderPass*)_info.renderPass; + _framebuffer = (MVKFramebuffer*)_info.framebuffer; + + // Add clear values + _clearValues.clear(); // Clear for reuse + _clearValues.reserve(_info.clearValueCount); + for (uint32_t i = 0; i < _info.clearValueCount; i++) { + _clearValues.push_back(_info.pClearValues[i]); + } +} + +void MVKCmdBeginRenderPass::encode(MVKCommandEncoder* cmdEncoder) { +// MVKLogDebug("Encoding vkCmdBeginRenderPass(). Elapsed time: %.6f ms.", mvkGetElapsedMilliseconds()); + cmdEncoder->beginRenderpass(_contents, _renderPass, _framebuffer, _info.renderArea, &_clearValues); +} + +MVKCmdBeginRenderPass::MVKCmdBeginRenderPass(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdNextSubpass + +void MVKCmdNextSubpass::setContent(VkSubpassContents contents) { + _contents = contents; +} + +void MVKCmdNextSubpass::encode(MVKCommandEncoder* cmdEncoder) { + cmdEncoder->beginNextSubpass(_contents); +} + +MVKCmdNextSubpass::MVKCmdNextSubpass(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdEndRenderPass + +void MVKCmdEndRenderPass::encode(MVKCommandEncoder* cmdEncoder) { +// MVKLogDebug("Encoding vkCmdEndRenderPass(). Elapsed time: %.6f ms.", mvkGetElapsedMilliseconds()); + cmdEncoder->endMetalRenderEncoding(); +} + +MVKCmdEndRenderPass::MVKCmdEndRenderPass(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdExecuteCommands + +void MVKCmdExecuteCommands::setContent(uint32_t commandBuffersCount, + const VkCommandBuffer* pCmdBuffers) { + // Add clear values + _secondaryCommandBuffers.clear(); // Clear for reuse + _secondaryCommandBuffers.reserve(commandBuffersCount); + for (uint32_t i = 0; i < commandBuffersCount; i++) { + _secondaryCommandBuffers.push_back((MVKCommandBuffer*)pCmdBuffers[i]); + } +} + +void MVKCmdExecuteCommands::encode(MVKCommandEncoder* cmdEncoder) { + for (auto& cb : _secondaryCommandBuffers) { cmdEncoder->encodeSecondary(cb); } +} + +MVKCmdExecuteCommands::MVKCmdExecuteCommands(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdSetViewport + +void MVKCmdSetViewport::setContent(uint32_t firstViewport, uint32_t viewportCount, const VkViewport* pViewports) { + _firstViewport = firstViewport; + _mtlViewports.clear(); // Clear for reuse + _mtlViewports.reserve(viewportCount); + for (uint32_t i = 0; i < viewportCount; i++) { + _mtlViewports.push_back(mvkMTLViewportFromVkViewport(pViewports[i])); + } +} + +void MVKCmdSetViewport::encode(MVKCommandEncoder* cmdEncoder) { + cmdEncoder->_viewportState.setViewports(_mtlViewports, _firstViewport); +} + +MVKCmdSetViewport::MVKCmdSetViewport(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdSetScissor + +void MVKCmdSetScissor::setContent(uint32_t firstScissor, uint32_t scissorCount, const VkRect2D* pScissors) { + _firstScissor = firstScissor; + _mtlScissors.clear(); // Clear for reuse + _mtlScissors.reserve(scissorCount); + for (uint32_t i = 0; i < scissorCount; i++) { + _mtlScissors.push_back(mvkMTLScissorRectFromVkRect2D(pScissors[i])); + } +} + +void MVKCmdSetScissor::encode(MVKCommandEncoder* cmdEncoder) { + cmdEncoder->_scissorState.setScissors(_mtlScissors, _firstScissor); +} + +MVKCmdSetScissor::MVKCmdSetScissor(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdSetLineWidth + +void MVKCmdSetLineWidth::setContent(float lineWidth) { + _lineWidth = lineWidth; + + // Validate + clearConfigurationResult(); + if (_lineWidth != 1.0 || getDevice()->_pFeatures->wideLines) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdSetLineWidth(): The current device does not support wide lines.")); + } +} + +void MVKCmdSetLineWidth::encode(MVKCommandEncoder* cmdEncoder) {} + +MVKCmdSetLineWidth::MVKCmdSetLineWidth(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdSetDepthBias + +void MVKCmdSetDepthBias::setContent(float depthBiasConstantFactor, + float depthBiasSlopeFactor, + float depthBiasClamp) { + _depthBiasConstantFactor = depthBiasConstantFactor; + _depthBiasSlopeFactor = depthBiasSlopeFactor; + _depthBiasClamp = depthBiasClamp; +} + +void MVKCmdSetDepthBias::encode(MVKCommandEncoder* cmdEncoder) { + cmdEncoder->_depthBiasState.setDepthBias(_depthBiasConstantFactor, + _depthBiasSlopeFactor, + _depthBiasClamp); +} + +MVKCmdSetDepthBias::MVKCmdSetDepthBias(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdSetBlendConstants + +void MVKCmdSetBlendConstants::setContent(const float blendConst[4]) { + _red = blendConst[0]; + _green = blendConst[1]; + _blue = blendConst[2]; + _alpha = blendConst[3]; +} + +void MVKCmdSetBlendConstants::encode(MVKCommandEncoder* cmdEncoder) { + cmdEncoder->_blendColorState.setBlendColor(_red, _green, _blue, _alpha, true); +} + +MVKCmdSetBlendConstants::MVKCmdSetBlendConstants(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdSetDepthBounds + +void MVKCmdSetDepthBounds::setContent(float minDepthBounds, float maxDepthBounds) { + _minDepthBounds = minDepthBounds; + _maxDepthBounds = maxDepthBounds; + + // Validate + clearConfigurationResult(); + if (getDevice()->_pFeatures->depthBounds) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdSetDepthBounds(): The current device does not support setting depth bounds.")); + } +} + +void MVKCmdSetDepthBounds::encode(MVKCommandEncoder* cmdEncoder) {} + +MVKCmdSetDepthBounds::MVKCmdSetDepthBounds(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdSetStencilCompareMask + +void MVKCmdSetStencilCompareMask::setContent(VkStencilFaceFlags faceMask, + uint32_t stencilCompareMask) { + _faceMask = faceMask; + _stencilCompareMask = stencilCompareMask; +} + +void MVKCmdSetStencilCompareMask::encode(MVKCommandEncoder* cmdEncoder) { + cmdEncoder->_depthStencilState.setStencilCompareMask(_faceMask, _stencilCompareMask); +} + +MVKCmdSetStencilCompareMask::MVKCmdSetStencilCompareMask(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdSetStencilWriteMask + +void MVKCmdSetStencilWriteMask::setContent(VkStencilFaceFlags faceMask, + uint32_t stencilWriteMask) { + _faceMask = faceMask; + _stencilWriteMask = stencilWriteMask; +} + +void MVKCmdSetStencilWriteMask::encode(MVKCommandEncoder* cmdEncoder) { + cmdEncoder->_depthStencilState.setStencilWriteMask(_faceMask, _stencilWriteMask); +} + +MVKCmdSetStencilWriteMask::MVKCmdSetStencilWriteMask(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdSetStencilReference + +void MVKCmdSetStencilReference::setContent(VkStencilFaceFlags faceMask, + uint32_t stencilReference) { + _faceMask = faceMask; + _stencilReference = stencilReference; +} + +void MVKCmdSetStencilReference::encode(MVKCommandEncoder* cmdEncoder) { + cmdEncoder->_stencilReferenceValueState.setReferenceValues(_faceMask, _stencilReference); +} + +MVKCmdSetStencilReference::MVKCmdSetStencilReference(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark Command creation functions + +void mvkCmdBeginRenderPass(VkCommandBuffer commandBuffer, + const VkRenderPassBeginInfo* pRenderPassBegin, + VkSubpassContents contents) { + + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdBeginRenderPass* cmd = cmdBuff->_commandPool->_cmdBeginRenderPassPool.acquireObject(); + cmd->setContent(pRenderPassBegin, contents); + cmdBuff->addCommand(cmd); +} + +void mvkCmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContents contents) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdNextSubpass* cmd = cmdBuff->_commandPool->_cmdNextSubpassPool.acquireObject(); + cmd->setContent(contents); + cmdBuff->addCommand(cmd); +} + +void mvkCmdEndRenderPass(VkCommandBuffer commandBuffer) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdEndRenderPass* cmd = cmdBuff->_commandPool->_cmdEndRenderPassPool.acquireObject(); + cmdBuff->addCommand(cmd); +} + +void mvkCmdExecuteCommands(VkCommandBuffer commandBuffer, + uint32_t commandBuffersCount, + const VkCommandBuffer* pCmdBuffers) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdExecuteCommands* cmd = cmdBuff->_commandPool->_cmdExecuteCommandsPool.acquireObject(); + cmd->setContent(commandBuffersCount, pCmdBuffers); + cmdBuff->addCommand(cmd); +} + +void mvkCmdSetViewport(VkCommandBuffer commandBuffer, + uint32_t firstViewport, + uint32_t viewportCount, + const VkViewport* pViewports) { + if (viewportCount == 0 || firstViewport > 0) { return; } // Nothing to set + + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdSetViewport* cmd = cmdBuff->_commandPool->_cmdSetViewportPool.acquireObject(); + cmd->setContent(firstViewport, viewportCount, pViewports); + cmdBuff->addCommand(cmd); +} + +void mvkCmdSetScissor(VkCommandBuffer commandBuffer, + uint32_t firstScissor, + uint32_t scissorCount, + const VkRect2D* pScissors) { + if (scissorCount == 0) { return; } // Nothing to set + + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdSetScissor* cmd = cmdBuff->_commandPool->_cmdSetScissorPool.acquireObject(); + cmd->setContent(firstScissor, scissorCount, pScissors); + cmdBuff->addCommand(cmd); +} + +void mvkCmdSetLineWidth(VkCommandBuffer commandBuffer, float lineWidth) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdSetLineWidth* cmd = cmdBuff->_commandPool->_cmdSetLineWidthPool.acquireObject(); + cmd->setContent(lineWidth); + cmdBuff->addCommand(cmd); +} + +void mvkCmdSetDepthBias(VkCommandBuffer commandBuffer, + float depthBiasConstantFactor, + float depthBiasClamp, + float depthBiasSlopeFactor) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdSetDepthBias* cmd = cmdBuff->_commandPool->_cmdSetDepthBiasPool.acquireObject(); + cmd->setContent(depthBiasConstantFactor, depthBiasSlopeFactor, depthBiasClamp); + cmdBuff->addCommand(cmd); +} + +void mvkCmdSetBlendConstants(VkCommandBuffer commandBuffer, + const float blendConst[4]) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdSetBlendConstants* cmd = cmdBuff->_commandPool->_cmdSetBlendConstantsPool.acquireObject(); + cmd->setContent(blendConst); + cmdBuff->addCommand(cmd); +} + +void mvkCmdSetDepthBounds(VkCommandBuffer commandBuffer, + float minDepthBounds, + float maxDepthBounds) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdSetDepthBounds* cmd = cmdBuff->_commandPool->_cmdSetDepthBoundsPool.acquireObject(); + cmd->setContent(minDepthBounds, maxDepthBounds); + cmdBuff->addCommand(cmd); +} + +void mvkCmdSetStencilCompareMask(VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + uint32_t stencilCompareMask) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdSetStencilCompareMask* cmd = cmdBuff->_commandPool->_cmdSetStencilCompareMaskPool.acquireObject(); + cmd->setContent(faceMask, stencilCompareMask); + cmdBuff->addCommand(cmd); +} + +void mvkCmdSetStencilWriteMask(VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + uint32_t stencilWriteMask) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdSetStencilWriteMask* cmd = cmdBuff->_commandPool->_cmdSetStencilWriteMaskPool.acquireObject(); + cmd->setContent(faceMask, stencilWriteMask); + cmdBuff->addCommand(cmd); +} + +void mvkCmdSetStencilReference(VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + uint32_t stencilReference) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdSetStencilReference* cmd = cmdBuff->_commandPool->_cmdSetStencilReferencePool.acquireObject(); + cmd->setContent(faceMask, stencilReference); + cmdBuff->addCommand(cmd); +} + diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h new file mode 100644 index 00000000..1ea3a997 --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h @@ -0,0 +1,424 @@ +/* + * MVKCmdTransfer.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKCommand.h" +#include "MVKMTLBufferAllocation.h" +#include "MVKCommandResourceFactory.h" +#include "MVKFoundation.h" +#include + +#import + +class MVKCommandBuffer; +class MVKImage; +class MVKBuffer; + + +#pragma mark - +#pragma mark MVKCmdCopyImage + +/** Describes the Metal texture copying parameters. */ +typedef struct { + NSUInteger srcLevel; + NSUInteger srcSlice; + MTLOrigin srcOrigin; + MTLSize srcSize; + NSUInteger dstLevel; + NSUInteger dstSlice; + MTLOrigin dstOrigin; +} MVKMetalCopyTextureRegion; + +/** Vulkan command to copy image regions. */ +class MVKCmdCopyImage : public MVKCommand { + +public: + void setContent(VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageCopy* pRegions, + MVKCommandUse commandUse = kMVKCommandUseCopyImage); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdCopyImage(MVKCommandTypePool* pool); + +protected: + void addMetalCopyRegions(const VkImageCopy* pRegion); + + MVKImage* _srcImage; + VkImageLayout _srcLayout; + MVKImage* _dstImage; + VkImageLayout _dstLayout; + std::vector _mtlTexCopyRegions; + MVKCommandUse _commandUse = kMVKCommandUseNone; +}; + + +#pragma mark - +#pragma mark MVKCmdBlitImage + +/** Number of vertices in a BLIT rectangle. */ +#define kMVKBlitVertexCount 4 + +/** Describes Metal texture rendering parameters. */ +typedef struct { + NSUInteger srcLevel; + NSUInteger srcSlice; + NSUInteger dstLevel; + NSUInteger dstSlice; + MVKVertexPosTex vertices[kMVKBlitVertexCount]; +} MVKMetalBlitTextureRender; + +/** Vulkan command to BLIT image regions. */ +class MVKCmdBlitImage : public MVKCmdCopyImage { + +public: + void setContent(VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageBlit* pRegions, + VkFilter filter, + MVKCommandUse commandUse = kMVKCommandUseBlitImage); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdBlitImage(MVKCommandTypePool* pool); + + ~MVKCmdBlitImage() override; + +protected: + bool canCopy(const VkImageBlit* pRegion); + void addMetalCopyRegions(const VkImageBlit* pRegion); + void addMetalBlitRenders(const VkImageBlit* pRegion); + void populateVertices(MVKVertexPosTex* vertices, const VkImageBlit* pRegion); + void initMTLRenderPassDescriptor(); + + MTLRenderPassDescriptor* _mtlRenderPassDescriptor; + MTLSamplerMinMagFilter _mtlFilter; + MTLPixelFormat _mtlPixFmt; + std::vector _mtlTexBlitRenders; + bool _isDepthFormat; +}; + + +#pragma mark - +#pragma mark MVKCmdResolveImage + +/** Describes Metal texture resolve parameters. */ +typedef struct { + NSUInteger level; + NSUInteger slice; +} MVKMetalResolveSlice; + +/** Vulkan command to resolve image regions. */ +class MVKCmdResolveImage : public MVKCommand { + +public: + void setContent(VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageResolve* pRegions); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdResolveImage(MVKCommandTypePool* pool); + + ~MVKCmdResolveImage() override; + +protected: + void addExpansionRegion(const VkImageResolve& resolveRegion); + void addCopyRegion(const VkImageResolve& resolveRegion); + void addResolveSlices(const VkImageResolve& resolveRegion); + void initMTLRenderPassDescriptor(); + + MVKImage* _srcImage; + VkImageLayout _srcLayout; + MVKImage* _dstImage; + VkImageLayout _dstLayout; + std::vector _expansionRegions; + std::vector _copyRegions; + MVKImageDescriptorData _transferImageData; + MTLRenderPassDescriptor* _mtlRenderPassDescriptor; + std::vector _mtlResolveSlices; +}; + + +#pragma mark - +#pragma mark MVKCmdCopyBuffer + +/** Vulkan command to copy buffer regions. */ +class MVKCmdCopyBuffer : public MVKCommand { + +public: + void setContent(VkBuffer srcBuffer, + VkBuffer destBuffer, + uint32_t regionCount, + const VkBufferCopy* pRegions); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdCopyBuffer(MVKCommandTypePool* pool); + +protected: + + MVKBuffer* _srcBuffer; + MVKBuffer* _dstBuffer; + std::vector _mtlBuffCopyRegions; +}; + + +#pragma mark - +#pragma mark MVKCmdBufferImageCopy + +/** Command to copy either from a buffer to an image, or from an image to a buffer. */ +class MVKCmdBufferImageCopy : public MVKCommand { + +public: + void setContent(VkBuffer buffer, + VkImage image, + VkImageLayout imageLayout, + uint32_t regionCount, + const VkBufferImageCopy* pRegions, + bool toImage); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdBufferImageCopy(MVKCommandTypePool* pool); + +protected: + MVKBuffer* _buffer; + MVKImage* _image; + VkImageLayout _imageLayout; + std::vector _mtlBuffImgCopyRegions; + bool _toImage = false; +}; + + +#pragma mark - +#pragma mark MVKCmdClearAttachments + +/** Vulkan command to clear attachment regions. */ +class MVKCmdClearAttachments : public MVKCommand { + +public: + void setContent(uint32_t attachmentCount, + const VkClearAttachment* pAttachments, + uint32_t rectCount, + const VkClearRect* pRects); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdClearAttachments(MVKCommandTypePool* pool); + + ~MVKCmdClearAttachments() override; + +protected: + void populateVertices(float attWidth, float attHeight); + void populateVertices(VkClearRect& clearRect, float attWidth, float attHeight); + + std::vector _clearRects; + std::vector _vertices; + simd::float4 _clearColors[kMVKAttachmentFormatCount]; + VkClearValue _vkClearValues[kMVKAttachmentFormatCount]; + MVKRPSKeyClearAtt _rpsKey; + uint32_t _mtlStencilValue; + bool _isClearingDepth; + bool _isClearingStencil; +}; + + +#pragma mark - +#pragma mark MVKCmdClearImage + +/** Vulkan command to clear an image. */ +class MVKCmdClearImage : public MVKCommand { + +public: + void setContent(VkImage image, + VkImageLayout imageLayout, + const VkClearValue& clearValue, + uint32_t rangeCount, + const VkImageSubresourceRange* pRanges, + bool isDepthStencilClear); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdClearImage(MVKCommandTypePool* pool); + + ~MVKCmdClearImage(); + +protected: + uint32_t populateMetalCopyRegions(const VkImageBlit* pRegion, uint32_t cpyRgnIdx); + uint32_t populateMetalBlitRenders(const VkImageBlit* pRegion, uint32_t rendRgnIdx); + void populateVertices(MVKVertexPosTex* vertices, const VkImageBlit* pRegion); + + MVKImage* _image; + VkImageLayout _imgLayout; + std::vector _subresourceRanges; + simd::float4 _clearColors[kMVKAttachmentFormatCount]; + MTLRenderPassDescriptor* _mtlRenderPassDescriptor; + MVKRPSKeyClearAtt _rpsKey; + uint32_t _mtlStencilValue; + bool _isDepthStencilClear; +}; + + +#pragma mark - +#pragma mark MVKCmdFillBuffer + +/** Vulkan command to fill a buffer. */ +class MVKCmdFillBuffer : public MVKCommand { + +public: + void setContent(VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdFillBuffer(MVKCommandTypePool* pool); + +protected: + MVKBuffer* _dstBuffer; + VkDeviceSize _dstOffset; + VkDeviceSize _size; + uint32_t _dataValue; +}; + + +#pragma mark - +#pragma mark MVKCmdUpdateBuffer + +/** Vulkan command to update the contents of a buffer. */ +class MVKCmdUpdateBuffer : public MVKCommand { + +public: + void setContent(VkBuffer dstBuffer, + VkDeviceSize dstOffset, + VkDeviceSize dataSize, + const void* pData, + bool useDataCache); + + void encode(MVKCommandEncoder* cmdEncoder) override; + + MVKCmdUpdateBuffer(MVKCommandTypePool* pool); + +protected: + MVKBuffer* _dstBuffer; + VkDeviceSize _dstOffset; + VkDeviceSize _dataSize; + std::vector _srcDataCache; +}; + + +#pragma mark - +#pragma mark Command creation functions + +/** Adds a copy image command to the specified command buffer. */ +void mvkCmdCopyImage(VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageCopy* pRegions); + +/** Adds a BLIT image command to the specified command buffer. */ +void mvkCmdBlitImage(VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageBlit* pRegions, + VkFilter filter); + +/** Adds a resolve image command to the specified command buffer. */ +void mvkCmdResolveImage(VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageResolve* pRegions); + +/** Adds a copy buffer command to the specified command buffer. */ +void mvkCmdCopyBuffer(VkCommandBuffer commandBuffer, + VkBuffer srcBuffer, + VkBuffer dstBuffer, + uint32_t regionCount, + const VkBufferCopy* pRegions); + +/** Adds a copy buffer to image command to the specified command buffer. */ +void mvkCmdCopyBufferToImage(VkCommandBuffer commandBuffer, + VkBuffer srcBuffer, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkBufferImageCopy* pRegions); + +/** Adds a copy buffer to image command to the specified command buffer. */ +void mvkCmdCopyImageToBuffer(VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkBuffer dstBuffer, + uint32_t regionCount, + const VkBufferImageCopy* pRegions); + +/** Adds a clear attachments command to the specified command buffer. */ +void mvkCmdClearAttachments(VkCommandBuffer commandBuffer, + uint32_t attachmentCount, + const VkClearAttachment* pAttachments, + uint32_t rectCount, + const VkClearRect* pRects); + +/** Adds a clear color image command to the specified command buffer. */ +void mvkCmdClearImage(VkCommandBuffer commandBuffer, + VkImage image, + VkImageLayout imageLayout, + const VkClearColorValue* pColor, + uint32_t rangeCount, + const VkImageSubresourceRange* pRanges); + +/** Adds a clear depth stencil image command to the specified command buffer. */ +void mvkCmdClearDepthStencilImage(VkCommandBuffer commandBuffer, + VkImage image, + VkImageLayout imageLayout, + const VkClearDepthStencilValue* pDepthStencil, + uint32_t rangeCount, + const VkImageSubresourceRange* pRanges); + +/** Adds a fill buffer command to the specified command buffer. */ +void mvkCmdFillBuffer(VkCommandBuffer commandBuffer, + VkBuffer dstBuffer, + VkDeviceSize dstOffset, + VkDeviceSize size, + uint32_t data); + +/** Adds a buffer update command to the specified command buffer. */ +void mvkCmdUpdateBuffer(VkCommandBuffer commandBuffer, + VkBuffer dstBuffer, + VkDeviceSize dstOffset, + VkDeviceSize dataSize, + const void* pData); diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm new file mode 100644 index 00000000..f1165d82 --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm @@ -0,0 +1,1182 @@ +/* + * MVKCmdTransfer.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKCmdTransfer.h" +#include "MVKCommandBuffer.h" +#include "MVKCommandPool.h" +#include "MVKCommandEncodingPool.h" +#include "MVKImage.h" +#include "MVKBuffer.h" +#include "MVKFramebuffer.h" +#include "MVKRenderPass.h" +#include "mvk_datatypes.h" + + +#pragma mark - +#pragma mark MVKCmdCopyImage + +void MVKCmdCopyImage::setContent(VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageCopy* pRegions, + MVKCommandUse commandUse) { + _srcImage = (MVKImage*)srcImage; + _srcLayout = srcImageLayout; + _dstImage = (MVKImage*)dstImage; + _dstLayout = dstImageLayout; + _commandUse = commandUse; + + // Deterine the total number of texture layers being affected + uint32_t layerCnt = 0; + for (uint32_t i = 0; i < regionCount; i++) { + layerCnt += pRegions[i].srcSubresource.layerCount; + } + + // Add image regions + _mtlTexCopyRegions.clear(); // Clear for reuse + _mtlTexCopyRegions.reserve(layerCnt); + for (uint32_t i = 0; i < regionCount; i++) { + addMetalCopyRegions(&pRegions[i]); + } + + // Validate + clearConfigurationResult(); + if (_srcImage->getMTLPixelFormat() != _dstImage->getMTLPixelFormat()) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdCopyImage(): The source and destination images must have the same format.")); + } +} + +// Adds a Metal copy region structure for each layer in the specified copy region. +void MVKCmdCopyImage::addMetalCopyRegions(const VkImageCopy* pRegion) { + + MVKMetalCopyTextureRegion mtlImgRgn; + mtlImgRgn.srcOrigin = mvkMTLOriginFromVkOffset3D(pRegion->srcOffset); + mtlImgRgn.dstOrigin = mvkMTLOriginFromVkOffset3D(pRegion->dstOffset); + mtlImgRgn.srcSize = mvkMTLSizeFromVkExtent3D(pRegion->extent); + mtlImgRgn.srcLevel = pRegion->srcSubresource.mipLevel; + mtlImgRgn.dstLevel = pRegion->dstSubresource.mipLevel; + + uint32_t srcBaseLayer = pRegion->srcSubresource.baseArrayLayer; + uint32_t dstBaseLayer = pRegion->dstSubresource.baseArrayLayer; + uint32_t layCnt = pRegion->srcSubresource.layerCount; + + for (uint32_t layIdx = 0; layIdx < layCnt; layIdx++) { + mtlImgRgn.srcSlice = srcBaseLayer + layIdx; + mtlImgRgn.dstSlice = dstBaseLayer + layIdx; + _mtlTexCopyRegions.push_back(mtlImgRgn); + } +} + +void MVKCmdCopyImage::encode(MVKCommandEncoder* cmdEncoder) { + id srcMTLTex = _srcImage->getMTLTexture(); + id dstMTLTex = _dstImage->getMTLTexture(); + if ( !srcMTLTex || !dstMTLTex ) { return; } + + id mtlBlitEnc = cmdEncoder->getMTLBlitEncoder(_commandUse); + + for (auto& cpyRgn : _mtlTexCopyRegions) { + [mtlBlitEnc copyFromTexture: srcMTLTex + sourceSlice: cpyRgn.srcSlice + sourceLevel: cpyRgn.srcLevel + sourceOrigin: cpyRgn.srcOrigin + sourceSize: cpyRgn.srcSize + toTexture: dstMTLTex + destinationSlice: cpyRgn.dstSlice + destinationLevel: cpyRgn.dstLevel + destinationOrigin: cpyRgn.dstOrigin]; + } +} + +MVKCmdCopyImage::MVKCmdCopyImage(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdBlitImage + +void MVKCmdBlitImage::setContent(VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageBlit* pRegions, + VkFilter filter, + MVKCommandUse commandUse) { + _srcImage = (MVKImage*)srcImage; + _srcLayout = srcImageLayout; + _dstImage = (MVKImage*)dstImage; + _dstLayout = dstImageLayout; + + _mtlPixFmt = _dstImage->getMTLPixelFormat(); + _isDepthFormat = mvkMTLPixelFormatIsDepthFormat(_mtlPixFmt); + _mtlFilter = mvkMTLSamplerMinMagFilterFromVkFilter(filter); + + _commandUse = commandUse; + + // Determine which regions can be copied and which must be rendered to the destination texture + bool canCopyRegion[regionCount]; + uint32_t copyRegionCount = 0; + uint32_t renderRegionCount = 0; + for (uint32_t i = 0; i < regionCount; i++) { + const VkImageBlit* pRegion = &pRegions[i]; + uint32_t layCnt = pRegion->srcSubresource.layerCount; + if ( canCopy(pRegion) ) { + canCopyRegion[i] = true; + copyRegionCount += layCnt; + } else { + canCopyRegion[i] = false; + renderRegionCount += layCnt; + } + } + + // Add copy and BLIT regions accordingly + _mtlTexCopyRegions.clear(); // Clear for reuse + _mtlTexCopyRegions.reserve(copyRegionCount); + _mtlTexBlitRenders.clear(); // Clear for reuse + _mtlTexBlitRenders.reserve(renderRegionCount); + + for (uint32_t i = 0; i < regionCount; i++) { + const VkImageBlit* pRegion = &pRegions[i]; + if (canCopyRegion[i]) { + addMetalCopyRegions(pRegion); + } else { + addMetalBlitRenders(pRegion); + } + } + + // Validate + clearConfigurationResult(); + if (_srcImage->getMTLPixelFormat() != _mtlPixFmt) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdBlitImage(): The source and destination images must have the same format.")); + } + if ( !_mtlTexBlitRenders.empty() && mvkMTLPixelFormatIsStencilFormat(_mtlPixFmt)) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdBlitImage(): Stencil image formats cannot be scaled or inverted.")); + } +} + +bool MVKCmdBlitImage::canCopy(const VkImageBlit* pRegion) { + VkOffset3D srcSize = mvkVkOffset3DDifference(pRegion->srcOffsets[1], pRegion->srcOffsets[0]); + VkOffset3D dstSize = mvkVkOffset3DDifference(pRegion->dstOffsets[1], pRegion->dstOffsets[0]); + + // The source and destination sizes must be equal and not be negative in any direction + return (mvkVkOffset3DsAreEqual(srcSize, dstSize) && + (srcSize.x >= 0) && (srcSize.y >= 0) && (srcSize.z >= 0)); +} + +// Adds a Metal copy region structure for each layer in the specified BLIT region. +void MVKCmdBlitImage::addMetalCopyRegions(const VkImageBlit* pRegion) { + + const VkOffset3D* pSo0 = &pRegion->srcOffsets[0]; + const VkOffset3D* pSo1 = &pRegion->srcOffsets[1]; + + MVKMetalCopyTextureRegion mtlImgRgn; + mtlImgRgn.srcOrigin = mvkMTLOriginFromVkOffset3D(*pSo0); + mtlImgRgn.dstOrigin = mvkMTLOriginFromVkOffset3D(pRegion->dstOffsets[0]); + mtlImgRgn.srcSize = MTLSizeMake((pSo1->x - pSo0->x), (pSo1->y - pSo0->y), (pSo1->z - pSo0->z)); + mtlImgRgn.srcLevel = pRegion->srcSubresource.mipLevel; + mtlImgRgn.dstLevel = pRegion->dstSubresource.mipLevel; + + uint32_t srcBaseLayer = pRegion->srcSubresource.baseArrayLayer; + uint32_t dstBaseLayer = pRegion->dstSubresource.baseArrayLayer; + uint32_t layCnt = pRegion->srcSubresource.layerCount; + + for (uint32_t layIdx = 0; layIdx < layCnt; layIdx++) { + mtlImgRgn.srcSlice = srcBaseLayer + layIdx; + mtlImgRgn.dstSlice = dstBaseLayer + layIdx; + _mtlTexCopyRegions.push_back(mtlImgRgn); + } +} + +// Adds a Metal BLIT render region structure for each layer in the specified BLIT region. +void MVKCmdBlitImage::addMetalBlitRenders(const VkImageBlit* pRegion) { + + MVKMetalBlitTextureRender mtlBlitRndr; + mtlBlitRndr.srcLevel = pRegion->srcSubresource.mipLevel; + mtlBlitRndr.dstLevel = pRegion->dstSubresource.mipLevel; + populateVertices(mtlBlitRndr.vertices, pRegion); + + uint32_t srcBaseLayer = pRegion->srcSubresource.baseArrayLayer; + uint32_t dstBaseLayer = pRegion->dstSubresource.baseArrayLayer; + uint32_t layCnt = pRegion->srcSubresource.layerCount; + + for (uint32_t layIdx = 0; layIdx < layCnt; layIdx++) { + mtlBlitRndr.srcSlice = srcBaseLayer + layIdx; + mtlBlitRndr.dstSlice = dstBaseLayer + layIdx; + _mtlTexBlitRenders.push_back(mtlBlitRndr); + } +} + +// Populates the vertices in the specified array from the specified region. +void MVKCmdBlitImage::populateVertices(MVKVertexPosTex* vertices, const VkImageBlit* pRegion) { + const VkOffset3D* pSo0 = &pRegion->srcOffsets[0]; + const VkOffset3D* pSo1 = &pRegion->srcOffsets[1]; + const VkOffset3D* pDo0 = &pRegion->dstOffsets[0]; + const VkOffset3D* pDo1 = &pRegion->dstOffsets[1]; + + // Get the extents of the source and destination textures. + VkExtent3D srcExtent = _srcImage->getExtent3D(pRegion->srcSubresource.mipLevel); + VkExtent3D dstExtent = _srcImage->getExtent3D(pRegion->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. + CGPoint srcBL = CGPointMake((CGFloat)(pSo0->x) / (CGFloat)srcExtent.width, + (CGFloat)(pSo0->y) / (CGFloat)srcExtent.height); + CGPoint srcTR = CGPointMake((CGFloat)(pSo1->x) / (CGFloat)srcExtent.width, + (CGFloat)(pSo1->y) / (CGFloat)srcExtent.height); + CGPoint dstBL = CGPointMake((CGFloat)(pDo0->x) / (CGFloat)dstExtent.width, + (CGFloat)(pDo0->y) / (CGFloat)dstExtent.height); + CGPoint dstTR = CGPointMake((CGFloat)(pDo1->x) / (CGFloat)dstExtent.width, + (CGFloat)(pDo1->y) / (CGFloat)dstExtent.height); + + // The destination region is used for vertex positions, + // which are bounded by (-1.0 < p < 1.0) in clip-space. + // Map texture coordinates (0.0 < p < 1.0) to vertex coordinates (-1.0 < p < 1.0). + dstBL = CGPointMake((dstBL.x * 2.0) - 1.0, (dstBL.y * 2.0) - 1.0); + dstTR = CGPointMake((dstTR.x * 2.0) - 1.0, (dstTR.y * 2.0) - 1.0); + + MVKVertexPosTex* pVtx; + + // Bottom left vertex + pVtx = &vertices[0]; + pVtx->position.x = dstBL.x; + pVtx->position.y = dstBL.y; + pVtx->texCoord.x = srcBL.x; + pVtx->texCoord.y = (1.0 - srcBL.y); + + // Bottom right vertex + pVtx = &vertices[1]; + pVtx->position.x = dstTR.x; + pVtx->position.y = dstBL.y; + pVtx->texCoord.x = srcTR.x; + pVtx->texCoord.y = (1.0 - srcBL.y); + + // Top left vertex + pVtx = &vertices[2]; + pVtx->position.x = dstBL.x; + pVtx->position.y = dstTR.y; + pVtx->texCoord.x = srcBL.x; + pVtx->texCoord.y = (1.0 - srcTR.y); + + // Top right vertex + pVtx = &vertices[3]; + pVtx->position.x = dstTR.x; + pVtx->position.y = dstTR.y; + pVtx->texCoord.x = srcTR.x; + pVtx->texCoord.y = (1.0 - srcTR.y); +} + +void MVKCmdBlitImage::encode(MVKCommandEncoder* cmdEncoder) { + + // Perform those BLITs that can be covered by simple texture copying. + if ( !_mtlTexCopyRegions.empty() ) { + MVKCmdCopyImage::encode(cmdEncoder); + } + + // Perform those BLITs that require rendering to destination texture. + if ( !_mtlTexBlitRenders.empty() ) { + + cmdEncoder->endCurrentMetalEncoding(); + + id srcMTLTex = _srcImage->getMTLTexture(); + id dstMTLTex = _dstImage->getMTLTexture(); + if ( !srcMTLTex || !dstMTLTex ) { return; } + + MTLRenderPassColorAttachmentDescriptor* mtlColorAttDesc = _mtlRenderPassDescriptor.colorAttachments[0]; + mtlColorAttDesc.texture = _isDepthFormat ? nil : dstMTLTex; + + uint32_t vtxBuffIdx = getDevice()->getMetalBufferIndexForVertexAttributeBinding(kMVKVertexContentBufferIndex); + + MVKCommandEncodingPool* cmdEncPool = cmdEncoder->getCommandEncodingPool(); + + for (auto& bltRend : _mtlTexBlitRenders) { + + // Update the render pass descriptor for the texture level and slice, and create a render encoder. + mtlColorAttDesc.level = bltRend.dstLevel; + mtlColorAttDesc.slice = bltRend.dstSlice; + id mtlRendEnc = [cmdEncoder->_mtlCmdBuffer renderCommandEncoderWithDescriptor: _mtlRenderPassDescriptor]; + mtlRendEnc.label = mvkMTLRenderCommandEncoderLabel(_commandUse); + + [mtlRendEnc pushDebugGroup: @"vkCmdBlitImage"]; + [mtlRendEnc setRenderPipelineState: cmdEncPool->getCmdBlitImageMTLRenderPipelineState(_mtlPixFmt)]; + cmdEncoder->setVertexBytes(mtlRendEnc, bltRend.vertices, sizeof(bltRend.vertices), vtxBuffIdx); + if (_isDepthFormat) { + [mtlRendEnc setDepthStencilState: cmdEncPool->getMTLDepthStencilState(_isDepthFormat, false)]; + [mtlRendEnc setVertexTexture: srcMTLTex atIndex: 0]; + [mtlRendEnc setVertexSamplerState: cmdEncPool->getCmdBlitImageMTLSamplerState(_mtlFilter) atIndex: 0]; + } else { + [mtlRendEnc setFragmentTexture: srcMTLTex atIndex: 0]; + [mtlRendEnc setFragmentSamplerState: cmdEncPool->getCmdBlitImageMTLSamplerState(_mtlFilter) atIndex: 0]; + } + [mtlRendEnc drawPrimitives: MTLPrimitiveTypeTriangleStrip vertexStart: 0 vertexCount: kMVKBlitVertexCount]; + [mtlRendEnc popDebugGroup]; + [mtlRendEnc endEncoding]; + } + } +} + + +#pragma mark Construction + +MVKCmdBlitImage::MVKCmdBlitImage(MVKCommandTypePool* pool) + : MVKCmdCopyImage::MVKCmdCopyImage((MVKCommandTypePool*)pool) { + + initMTLRenderPassDescriptor(); +} + +// Create and configure the render pass descriptor +void MVKCmdBlitImage::initMTLRenderPassDescriptor() { + _mtlRenderPassDescriptor = [[MTLRenderPassDescriptor renderPassDescriptor] retain]; // retained + MTLRenderPassColorAttachmentDescriptor* mtlColorAttDesc = _mtlRenderPassDescriptor.colorAttachments[0]; + mtlColorAttDesc.loadAction = MTLLoadActionLoad; + mtlColorAttDesc.storeAction = MTLStoreActionStore; +} + +MVKCmdBlitImage::~MVKCmdBlitImage() { + [_mtlRenderPassDescriptor release]; +} + + +#pragma mark - +#pragma mark MVKCmdResolveImage + +void MVKCmdResolveImage::setContent(VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageResolve* pRegions) { + _srcImage = (MVKImage*)srcImage; + _srcLayout = srcImageLayout; + _dstImage = (MVKImage*)dstImage; + _dstLayout = dstImageLayout; + + // Deterine the total number of texture layers being affected + uint32_t layerCnt = 0; + for (uint32_t i = 0; i < regionCount; i++) { + layerCnt += pRegions[i].dstSubresource.layerCount; + } + + // Resize the region arrays accordingly + _expansionRegions.clear(); // Clear for reuse + _expansionRegions.reserve(regionCount); + _copyRegions.clear(); // Clear for reuse + _copyRegions.reserve(regionCount); + _mtlResolveSlices.clear(); // Clear for reuse + _mtlResolveSlices.reserve(layerCnt); + + // Add image regions + for (uint32_t i = 0; i < regionCount; i++) { + const VkImageResolve& rslvRgn = pRegions[i]; + addExpansionRegion(rslvRgn); + addCopyRegion(rslvRgn); + addResolveSlices(rslvRgn); + } + + _srcImage->getTransferDescriptorData(_transferImageData); +} + +/** + * Adds a VkImageBlit region, constructed from the resolve region, to the internal collection + * of expansion regions, unless the entire content of the destination texture of this command + * is to be resolved, an expansion region will not be added. + * + * The purpose of an expansion regions is to render the existing content of the destination + * image of this command to the temporary transfer multisample image, so that regions of that + * temporary transfer image can then be overwritten with content from the source image of this + * command, prior to resolving it back to the destination image of this command. + * + * As such, the source of this expansion stage is the destination image of this command, + * and the destination of this expansion stage is a temp image that has the same shape + * as the source image of this command. + */ +void MVKCmdResolveImage::addExpansionRegion(const VkImageResolve& resolveRegion) { + uint32_t mipLvl = resolveRegion.dstSubresource.mipLevel; + VkExtent3D srcImgExt = _srcImage->getExtent3D(mipLvl); + VkExtent3D dstImgExt = _dstImage->getExtent3D(mipLvl); + + // No need to add an expansion region if the entire content of + // the source image is being resolved to the destination image. + if (mvkVkExtent3DsAreEqual(srcImgExt, resolveRegion.extent)) { return; } + + // The source of this temporary content move is the full extent of the DESTINATION + // image of the resolve command, and the destination of this temporary content move + // is the full extent of the SOURCE image of the resolve command. + VkImageBlit expRgn = { + .srcSubresource = resolveRegion.dstSubresource, + .srcOffsets[0] = { 0, 0, 0 }, + .srcOffsets[1] = { int32_t(dstImgExt.width), int32_t(dstImgExt.height), int32_t(dstImgExt.depth) }, + .dstSubresource = resolveRegion.dstSubresource, + .dstOffsets[0] = { 0, 0, 0 }, + .dstOffsets[1] = { int32_t(srcImgExt.width), int32_t(srcImgExt.height), int32_t(srcImgExt.depth) }, + }; + _expansionRegions.push_back(expRgn); +} + +/** + * Adds a VkImageCopy region, constructed from the resolve region, + * to the internal collection of copy regions. + * + * The purpose of a copy region is to copy regions from the source image of this command to + * the temporary image, prior to the temporary image being resolved back to the destination + * image of this command. + * + * As such, the source of this copy stage is the source image of this command, and the + * destination of this copy stage is the temporary transfer image that has the same shape + * as the source image of this command. + */ +void MVKCmdResolveImage::addCopyRegion(const VkImageResolve& resolveRegion) { + VkImageCopy cpyRgn = { + .srcSubresource = resolveRegion.srcSubresource, + .srcOffset = resolveRegion.srcOffset, + .dstSubresource = resolveRegion.srcSubresource, + .dstOffset = resolveRegion.srcOffset, + .extent = resolveRegion.extent, + }; + _copyRegions.push_back(cpyRgn); +} + +/** Adds a resolve slice struct for each destination layer in the resolve region. */ +void MVKCmdResolveImage::addResolveSlices(const VkImageResolve& resolveRegion) { + MVKMetalResolveSlice rslvSlice; + rslvSlice.level = resolveRegion.dstSubresource.mipLevel; + + uint32_t baseLayer = resolveRegion.dstSubresource.baseArrayLayer; + uint32_t layCnt = resolveRegion.dstSubresource.layerCount; + for (uint32_t layIdx = 0; layIdx < layCnt; layIdx++) { + rslvSlice.slice = baseLayer + layIdx; + _mtlResolveSlices.push_back(rslvSlice); + } +} + +void MVKCmdResolveImage::encode(MVKCommandEncoder* cmdEncoder) { + MVKImage* xfrImage = cmdEncoder->getCommandEncodingPool()->getTransferMVKImage(_transferImageData); + + id xfrMTLTex = xfrImage->getMTLTexture(); + id dstMTLTex = _dstImage->getMTLTexture(); + if ( !xfrMTLTex || !dstMTLTex ) { return; } + + // Expand the current content of the destination image to the temporary transfer image. + // Create and execute a temporary BLIT image command. + // To be threadsafe...do NOT acquire and return the command from the pool. + uint32_t expRgnCnt = uint32_t(_expansionRegions.size()); + if (expRgnCnt > 0) { + MVKCmdBlitImage expandCmd(&getCommandPool()->_cmdBlitImagePool); + expandCmd.setContent((VkImage)_dstImage, _dstLayout, (VkImage)xfrImage, _dstLayout, + uint32_t(_expansionRegions.size()), _expansionRegions.data(), + VK_FILTER_LINEAR, kMVKCommandUseResolveExpandImage); + expandCmd.encode(cmdEncoder); + } + + // Copy the resolve regions of the source image to the temporary transfer image. + // Create and execute a temporary copy image command. + // To be threadsafe...do NOT acquire and return the command from the pool. + uint32_t cpyRgnCnt = uint32_t(_copyRegions.size()); + if (cpyRgnCnt > 0) { + MVKCmdCopyImage copyCmd(&getCommandPool()->_cmdCopyImagePool); + copyCmd.setContent((VkImage)_srcImage, _srcLayout, (VkImage)xfrImage, _dstLayout, + uint32_t(_copyRegions.size()), _copyRegions.data(), kMVKCommandUseResolveCopyImage); + copyCmd.encode(cmdEncoder); + } + + cmdEncoder->endCurrentMetalEncoding(); + + MTLRenderPassColorAttachmentDescriptor* mtlColorAttDesc = _mtlRenderPassDescriptor.colorAttachments[0]; + mtlColorAttDesc.texture = xfrMTLTex; + mtlColorAttDesc.resolveTexture = dstMTLTex; + + for (auto& rslvSlice : _mtlResolveSlices) { + + // Update the render pass descriptor for the texture level and slice, and create a render encoder. + mtlColorAttDesc.level = rslvSlice.level; + mtlColorAttDesc.slice = rslvSlice.slice; + mtlColorAttDesc.resolveLevel = rslvSlice.level; + mtlColorAttDesc.resolveSlice = rslvSlice.slice; + id mtlRendEnc = [cmdEncoder->_mtlCmdBuffer renderCommandEncoderWithDescriptor: _mtlRenderPassDescriptor]; + mtlRendEnc.label = mvkMTLRenderCommandEncoderLabel(kMVKCommandUseResolveImage); + + [mtlRendEnc pushDebugGroup: @"vkCmdResolveImage"]; + [mtlRendEnc popDebugGroup]; + [mtlRendEnc endEncoding]; + } +} + +MVKCmdResolveImage::MVKCmdResolveImage(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) { + + initMTLRenderPassDescriptor(); +} + +// Create and configure the render pass descriptor +void MVKCmdResolveImage::initMTLRenderPassDescriptor() { + _mtlRenderPassDescriptor = [[MTLRenderPassDescriptor renderPassDescriptor] retain]; // retained + MTLRenderPassColorAttachmentDescriptor* mtlColorAttDesc = _mtlRenderPassDescriptor.colorAttachments[0]; + mtlColorAttDesc.loadAction = MTLLoadActionLoad; + mtlColorAttDesc.storeAction = MTLStoreActionMultisampleResolve; +} + +MVKCmdResolveImage::~MVKCmdResolveImage() { + [_mtlRenderPassDescriptor release]; +} + + +#pragma mark - +#pragma mark MVKCmdCopyBuffer + +void MVKCmdCopyBuffer::setContent(VkBuffer srcBuffer, + VkBuffer destBuffer, + uint32_t regionCount, + const VkBufferCopy* pRegions) { + _srcBuffer = (MVKBuffer*)srcBuffer; + _dstBuffer = (MVKBuffer*)destBuffer; + + // Add buffer regions + _mtlBuffCopyRegions.clear(); // Clear for reuse + _mtlBuffCopyRegions.reserve(regionCount); + for (uint32_t i = 0; i < regionCount; i++) { + _mtlBuffCopyRegions.push_back(pRegions[i]); + } +} + +void MVKCmdCopyBuffer::encode(MVKCommandEncoder* cmdEncoder) { + + id mtlBlitEnc = cmdEncoder->getMTLBlitEncoder(kMVKCommandUseCopyBuffer); + + id srcMTLBuff = _srcBuffer->getMTLBuffer(); + NSUInteger srcMTLBuffOffset = _srcBuffer->getMTLBufferOffset(); + + id dstMTLBuff = _dstBuffer->getMTLBuffer(); + NSUInteger dstMTLBuffOffset = _dstBuffer->getMTLBufferOffset(); + + for (auto& cpyRgn : _mtlBuffCopyRegions) { + [mtlBlitEnc copyFromBuffer: srcMTLBuff + sourceOffset: (srcMTLBuffOffset + cpyRgn.srcOffset) + toBuffer: dstMTLBuff + destinationOffset: (dstMTLBuffOffset + cpyRgn.dstOffset) + size: cpyRgn.size]; + } +} + +MVKCmdCopyBuffer::MVKCmdCopyBuffer(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdBufferImageCopy + +void MVKCmdBufferImageCopy::setContent(VkBuffer buffer, + VkImage image, + VkImageLayout imageLayout, + uint32_t regionCount, + const VkBufferImageCopy* pRegions, + bool toImage) { + _buffer = (MVKBuffer*)buffer; + _image = (MVKImage*)image; + _imageLayout = imageLayout; + _toImage = toImage; + + // Add buffer regions + _mtlBuffImgCopyRegions.clear(); // Clear for reuse + _mtlBuffImgCopyRegions.reserve(regionCount); + for (uint32_t i = 0; i < regionCount; i++) { + _mtlBuffImgCopyRegions.push_back(pRegions[i]); + } + + // Validate + clearConfigurationResult(); + if ( !_image->hasExpectedTexelSize() ) { + const char* cmdName = _toImage ? "vkCmdCopyBufferToImage" : "vkCmdCopyImageToBuffer"; + setConfigurationResult(mvkNotifyErrorWithText(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, mvkMTLPixelFormatName(_image->getMTLPixelFormat()), mvkVkFormatName(_image->getVkFormat()))); + } +} + +void MVKCmdBufferImageCopy::encode(MVKCommandEncoder* cmdEncoder) { + id mtlBuffer = _buffer->getMTLBuffer(); + id mtlTexture = _image->getMTLTexture(); + if ( !mtlBuffer || !mtlTexture ) { return; } + + MTLPixelFormat mtlPixFmt = mtlTexture.pixelFormat; + MVKCommandUse cmdUse = _toImage ? kMVKCommandUseCopyBufferToImage : kMVKCommandUseCopyImageToBuffer; + id mtlBlitEnc = cmdEncoder->getMTLBlitEncoder(cmdUse); + + for (auto& cpyRgn : _mtlBuffImgCopyRegions) { + + MTLOrigin mtlTxtOrigin = mvkMTLOriginFromVkOffset3D(cpyRgn.imageOffset); + MTLSize mtlTxtSize = mvkMTLSizeFromVkExtent3D(cpyRgn.imageExtent); + + uint32_t buffImgWd = cpyRgn.bufferRowLength; + if (buffImgWd == 0) { buffImgWd = cpyRgn.imageExtent.width; } + + uint32_t buffImgHt = cpyRgn.bufferImageHeight; + if (buffImgHt == 0) { buffImgHt = cpyRgn.imageExtent.height; } + + NSUInteger bytesPerRow = mvkMTLPixelFormatBytesPerRow(mtlPixFmt, buffImgWd); + NSUInteger bytesPerImg = mvkMTLPixelFormatBytesPerLayer(mtlPixFmt, bytesPerRow, buffImgHt); + + // If the format combines BOTH depth and stencil, determine whether one or both + // components are to be copied, and adjust the byte counts and copy options accordingly. + MTLBlitOption blitOptions = MTLBlitOptionNone; + if (mvkMTLPixelFormatIsDepthFormat(mtlPixFmt) && mvkMTLPixelFormatIsStencilFormat(mtlPixFmt)) { + VkImageAspectFlags imgFlags = cpyRgn.imageSubresource.aspectMask; + bool wantDepth = mvkAreFlagsEnabled(imgFlags, VK_IMAGE_ASPECT_DEPTH_BIT); + bool wantStencil = mvkAreFlagsEnabled(imgFlags, VK_IMAGE_ASPECT_STENCIL_BIT); + + // The stencil component is always 1 byte per pixel. + if (wantDepth && !wantStencil) { + bytesPerRow -= buffImgWd; + bytesPerImg -= buffImgWd * buffImgHt; + blitOptions |= MTLBlitOptionDepthFromDepthStencil; + } else if (wantStencil && !wantDepth) { + bytesPerRow = buffImgWd; + bytesPerImg = buffImgWd * buffImgHt; + blitOptions |= MTLBlitOptionStencilFromDepthStencil; + } + } + + for (uint32_t lyrIdx = 0; lyrIdx < cpyRgn.imageSubresource.layerCount; lyrIdx++) { + if (_toImage) { + [mtlBlitEnc copyFromBuffer: mtlBuffer + sourceOffset: (cpyRgn.bufferOffset + (bytesPerImg * lyrIdx)) + sourceBytesPerRow: bytesPerRow + sourceBytesPerImage: bytesPerImg + sourceSize: mtlTxtSize + toTexture: mtlTexture + destinationSlice: (cpyRgn.imageSubresource.baseArrayLayer + lyrIdx) + destinationLevel: cpyRgn.imageSubresource.mipLevel + destinationOrigin: mtlTxtOrigin + options: blitOptions]; + } else { + [mtlBlitEnc copyFromTexture: mtlTexture + sourceSlice: (cpyRgn.imageSubresource.baseArrayLayer + lyrIdx) + sourceLevel: cpyRgn.imageSubresource.mipLevel + sourceOrigin: mtlTxtOrigin + sourceSize: mtlTxtSize + toBuffer: mtlBuffer + destinationOffset: (cpyRgn.bufferOffset + (bytesPerImg * lyrIdx)) + destinationBytesPerRow: bytesPerRow + destinationBytesPerImage: bytesPerImg + options: blitOptions]; + } + } + } +} + +MVKCmdBufferImageCopy::MVKCmdBufferImageCopy(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdClearAttachments + +void MVKCmdClearAttachments::setContent(uint32_t attachmentCount, + const VkClearAttachment* pAttachments, + uint32_t rectCount, + const VkClearRect* pRects) { + _rpsKey.reset(); + _mtlStencilValue = 0; + _isClearingDepth = false; + _isClearingStencil = false; + float mtlDepthVal = 0.0; + + // For each attachment to be cleared, mark it so in the render pipeline state + // attachment key, and populate the clear color value into a uniform array. + // Also set the depth and stencil clear value to the last clear attachment that specifies them. + for (uint32_t i = 0; i < attachmentCount; i++) { + auto& clrAtt = pAttachments[i]; + + if (mvkIsAnyFlagEnabled(clrAtt.aspectMask, VK_IMAGE_ASPECT_COLOR_BIT)) { + uint32_t caIdx = clrAtt.colorAttachment; // Might be VK_ATTACHMENT_UNUSED + if (caIdx != VK_ATTACHMENT_UNUSED) { + _rpsKey.enable(caIdx); + _vkClearValues[caIdx] = clrAtt.clearValue; + } + } + + if (mvkIsAnyFlagEnabled(clrAtt.aspectMask, VK_IMAGE_ASPECT_DEPTH_BIT)) { + _isClearingDepth = true; + _rpsKey.enable(kMVKAttachmentFormatDepthStencilIndex); + mtlDepthVal = mvkMTLClearDepthFromVkClearValue(clrAtt.clearValue); + } + + if (mvkIsAnyFlagEnabled(clrAtt.aspectMask, VK_IMAGE_ASPECT_STENCIL_BIT)) { + _isClearingStencil = true; + _rpsKey.enable(kMVKAttachmentFormatDepthStencilIndex); + _mtlStencilValue = mvkMTLClearStencilFromVkClearValue(clrAtt.clearValue); + } + } + + // The depth value (including vertex position Z value) is held in the last index. + _clearColors[kMVKAttachmentFormatDepthStencilIndex] = { mtlDepthVal, mtlDepthVal, mtlDepthVal, mtlDepthVal }; + + _clearRects.clear(); + _clearRects.reserve(rectCount); + for (uint32_t i = 0; i < rectCount; i++) { + _clearRects.push_back(pRects[i]); + } + + _vertices.reserve(rectCount * 6); +} + +/** Populates the vertices for all clear rectangles within an attachment of the specified size. */ +void MVKCmdClearAttachments::populateVertices(float attWidth, float attHeight) { + _vertices.clear(); + for (auto& rect : _clearRects) { populateVertices(rect, attWidth, attHeight); } +} + +/** Populates the vertices from the specified rectangle within an attachment of the specified size. */ +void MVKCmdClearAttachments::populateVertices(VkClearRect& clearRect, float attWidth, float attHeight) { + + // Determine the positions of the four edges of the + // clear rectangle as a fraction of the attachment size. + float leftPos = (float)(clearRect.rect.offset.x) / attWidth; + float rightPos = (float)(clearRect.rect.extent.width) / attWidth + leftPos; + float bottomPos = (float)(clearRect.rect.offset.y) / attHeight; + float topPos = (float)(clearRect.rect.extent.height) / attHeight + bottomPos; + + // Now transform to clip-space coordinates, + // which are bounded by (-1.0 < p < 1.0) in clip-space. + leftPos = (leftPos * 2.0) - 1.0; + rightPos = (rightPos * 2.0) - 1.0; + bottomPos = (bottomPos * 2.0) - 1.0; + topPos = (topPos * 2.0) - 1.0; + + simd::float2 vtx; + + // Top left vertex - First triangle + vtx.y = topPos; + vtx.x = leftPos; + _vertices.push_back(vtx); + + // Bottom left vertex + vtx.y = bottomPos; + vtx.x = leftPos; + _vertices.push_back(vtx); + + // Bottom right vertex + vtx.y = bottomPos; + vtx.x = rightPos; + _vertices.push_back(vtx); + + // Bottom right vertex - Second triangle + _vertices.push_back(vtx); + + // Top right vertex + vtx.y = topPos; + vtx.x = rightPos; + _vertices.push_back(vtx); + + // Top left vertex + vtx.y = topPos; + vtx.x = leftPos; + _vertices.push_back(vtx); +} + +void MVKCmdClearAttachments::encode(MVKCommandEncoder* cmdEncoder) { + + MVKCommandPool* cmdPool = getCommandPool(); + MVKRenderSubpass* subpass = cmdEncoder->getSubpass(); + VkExtent2D fbExtent = cmdEncoder->_framebuffer->getExtent2D(); + populateVertices(fbExtent.width, fbExtent.height); + uint32_t vtxCnt = (uint32_t)_vertices.size(); + + uint32_t vtxBuffIdx = getDevice()->getMetalBufferIndexForVertexAttributeBinding(kMVKVertexContentBufferIndex); + + // Populate the render pipeline state attachment key with + // the format of each color attachment used by the subpass + uint32_t caCnt = subpass->getColorAttachmentCount(); + for (uint32_t caIdx = 0; caIdx < caCnt; caIdx++) { + VkFormat vkAttFmt = subpass->getColorAttachmentFormat(caIdx); + if (_rpsKey.isEnabled(caIdx)) { + _rpsKey.attachmentMTLPixelFormats[caIdx] = cmdPool->mtlPixelFormatFromVkFormat(vkAttFmt); + MTLClearColor mtlCC = mvkMTLClearColorFromVkClearValue(_vkClearValues[caIdx], vkAttFmt); + _clearColors[caIdx] = { (float)mtlCC.red, (float)mtlCC.green, (float)mtlCC.blue, (float)mtlCC.alpha}; + } + } + + VkFormat vkAttFmt = subpass->getDepthStencilFormat(); + _rpsKey.attachmentMTLPixelFormats[kMVKAttachmentFormatDepthStencilIndex] = cmdPool->mtlPixelFormatFromVkFormat(vkAttFmt); + + // Render the clear colors to the attachments + id mtlRendEnc = cmdEncoder->_mtlRenderEncoder; + [mtlRendEnc pushDebugGroup: @"vkCmdClearAttachments"]; + [mtlRendEnc setRenderPipelineState: cmdEncoder->getCommandEncodingPool()->getCmdClearMTLRenderPipelineState(_rpsKey)]; + [mtlRendEnc setDepthStencilState: cmdEncoder->getCommandEncodingPool()->getMTLDepthStencilState(_isClearingDepth, _isClearingStencil)]; + [mtlRendEnc setStencilReferenceValue: _mtlStencilValue]; + + cmdEncoder->setVertexBytes(mtlRendEnc, _clearColors, sizeof(_clearColors), 0); + cmdEncoder->setFragmentBytes(mtlRendEnc, _clearColors, sizeof(_clearColors), 0); + cmdEncoder->setVertexBytes(mtlRendEnc, _vertices.data(), vtxCnt * sizeof(simd::float2), vtxBuffIdx); + [mtlRendEnc drawPrimitives: MTLPrimitiveTypeTriangle vertexStart: 0 vertexCount: vtxCnt]; + [mtlRendEnc popDebugGroup]; +} + + +#pragma mark Construction + +MVKCmdClearAttachments::MVKCmdClearAttachments(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + +MVKCmdClearAttachments::~MVKCmdClearAttachments() {} + + +#pragma mark - +#pragma mark MVKCmdClearImage + +void MVKCmdClearImage::setContent(VkImage image, + VkImageLayout imageLayout, + const VkClearValue& clearValue, + uint32_t rangeCount, + const VkImageSubresourceRange* pRanges, + bool isDepthStencilClear) { + _image = (MVKImage*)image; + _imgLayout = imageLayout; + _isDepthStencilClear = isDepthStencilClear; + + _rpsKey.reset(); + _mtlStencilValue = 0; + + if (_isDepthStencilClear) { + _rpsKey.enable(kMVKAttachmentFormatDepthStencilIndex); + float mtlDepthVal = mvkMTLClearDepthFromVkClearValue(clearValue); + _clearColors[kMVKAttachmentFormatDepthStencilIndex] = { mtlDepthVal, mtlDepthVal, mtlDepthVal, mtlDepthVal }; + _mtlStencilValue = mvkMTLClearStencilFromVkClearValue(clearValue); + } else { + _rpsKey.enable(0); + _rpsKey.attachmentMTLPixelFormats[0] = _image->getMTLPixelFormat(); + MTLClearColor mtlCC = mvkMTLClearColorFromVkClearValue(clearValue, _image->getVkFormat()); + _clearColors[0] = { (float)mtlCC.red, (float)mtlCC.green, (float)mtlCC.blue, (float)mtlCC.alpha}; + } + + // Add subresource ranges + _subresourceRanges.clear(); + _subresourceRanges.reserve(rangeCount); + for (uint32_t i = 0; i < rangeCount; i++) { + _subresourceRanges.push_back(pRanges[i]); + } +} + +void MVKCmdClearImage::encode(MVKCommandEncoder* cmdEncoder) { + + id imgMTLTex = _image->getMTLTexture(); + if ( !imgMTLTex ) { return; } + + cmdEncoder->endCurrentMetalEncoding(); + + MTLRenderPassColorAttachmentDescriptor* mtlColorAttDesc = _mtlRenderPassDescriptor.colorAttachments[0]; + mtlColorAttDesc.texture = imgMTLTex; + + static const simd::float2 vertices[] = { + { -1.0, -1.0 }, // Bottom-left + { 1.0, -1.0 }, // Bottom-right + { -1.0, 1.0 }, // Top-left + { 1.0, 1.0 }, // Top-right + }; + + uint32_t vtxBuffIdx = getDevice()->getMetalBufferIndexForVertexAttributeBinding(kMVKVertexContentBufferIndex); + + NSString* mtlRendEncName; + NSString* mtlDebugGroupName; + if (_isDepthStencilClear) { + mtlDebugGroupName = @"vkCmdClearDepthStencilImage"; + mtlRendEncName = mvkMTLRenderCommandEncoderLabel(kMVKCommandUseClearDepthStencilImage); + } else { + mtlDebugGroupName = @"vkCmdClearColorImage"; + mtlRendEncName = mvkMTLRenderCommandEncoderLabel(kMVKCommandUseClearColorImage); + } + + MVKCommandEncodingPool* cmdEncPool = cmdEncoder->getCommandEncodingPool(); + + size_t srCnt = _subresourceRanges.size(); + for (uint32_t srIdx = 0; srIdx < srCnt; srIdx++) { + auto& srRange = _subresourceRanges[srIdx]; + + bool isClearingDepth = _isDepthStencilClear && mvkIsAnyFlagEnabled(srRange.aspectMask, VK_IMAGE_ASPECT_DEPTH_BIT); + bool isClearingStencil = _isDepthStencilClear && mvkIsAnyFlagEnabled(srRange.aspectMask, VK_IMAGE_ASPECT_STENCIL_BIT); + + // Extract the mipmap levels that are to be updated + uint32_t mipLvlStart = srRange.baseMipLevel; + uint32_t mipLvlCnt = srRange.levelCount; + uint32_t mipLvlEnd = (mipLvlCnt == VK_REMAINING_MIP_LEVELS + ? _image->getMipLevelCount() + : (mipLvlStart + mipLvlCnt)); + + // Extract the cube or array layers (slices) that are to be updated + uint32_t layerStart = srRange.baseArrayLayer; + uint32_t layerCnt = srRange.layerCount; + uint32_t layerEnd = (layerCnt == VK_REMAINING_ARRAY_LAYERS + ? _image->getLayerCount() + : (layerStart + layerCnt)); + + // Iterate across mipmap levels and layers, and render to clear each + for (uint32_t mipLvl = mipLvlStart; mipLvl < mipLvlEnd; mipLvl++) { + mtlColorAttDesc.level = mipLvl; + for (uint32_t layer = layerStart; layer < layerEnd; layer++) { + mtlColorAttDesc.slice = layer; + + id mtlRendEnc = [cmdEncoder->_mtlCmdBuffer renderCommandEncoderWithDescriptor: _mtlRenderPassDescriptor]; + mtlRendEnc.label = mtlRendEncName; + + [mtlRendEnc pushDebugGroup: mtlDebugGroupName]; + [mtlRendEnc setRenderPipelineState: cmdEncPool->getCmdClearMTLRenderPipelineState(_rpsKey)]; + [mtlRendEnc setDepthStencilState: cmdEncPool->getMTLDepthStencilState(isClearingDepth, isClearingStencil)]; + [mtlRendEnc setStencilReferenceValue: _mtlStencilValue]; + + cmdEncoder->setVertexBytes(mtlRendEnc, _clearColors, sizeof(_clearColors), 0); + cmdEncoder->setFragmentBytes(mtlRendEnc, _clearColors, sizeof(_clearColors), 0); + cmdEncoder->setVertexBytes(mtlRendEnc, vertices, sizeof(vertices), vtxBuffIdx); + [mtlRendEnc drawPrimitives: MTLPrimitiveTypeTriangleStrip vertexStart: 0 vertexCount: kMVKBlitVertexCount]; + [mtlRendEnc popDebugGroup]; + [mtlRendEnc endEncoding]; + } + } + } +} + + +#pragma mark Construction + +MVKCmdClearImage::MVKCmdClearImage(MVKCommandTypePool* pool) +: MVKCommand::MVKCommand((MVKCommandTypePool*)pool) { + + // Create and configure the render pass descriptor + _mtlRenderPassDescriptor = [[MTLRenderPassDescriptor renderPassDescriptor] retain]; // retained + MTLRenderPassColorAttachmentDescriptor* mtlColorAttDesc = _mtlRenderPassDescriptor.colorAttachments[0]; + mtlColorAttDesc.loadAction = MTLLoadActionLoad; + mtlColorAttDesc.storeAction = MTLStoreActionStore; +} + +MVKCmdClearImage::~MVKCmdClearImage() { + [_mtlRenderPassDescriptor release]; +} + + +#pragma mark - +#pragma mark MVKCmdFillBuffer + +void MVKCmdFillBuffer::setContent(VkBuffer dstBuffer, + VkDeviceSize dstOffset, + VkDeviceSize size, + uint32_t data) { + _dstBuffer = (MVKBuffer*)dstBuffer; + _dstOffset = dstOffset; + _size = size; + _dataValue = data; +} + +void MVKCmdFillBuffer::encode(MVKCommandEncoder* cmdEncoder) { + + id mtlBlitEnc = cmdEncoder->getMTLBlitEncoder(kMVKCommandUseFillBuffer); + + id dstMTLBuff = _dstBuffer->getMTLBuffer(); + NSUInteger dstMTLBuffOffset = _dstBuffer->getMTLBufferOffset() + _dstOffset; + VkDeviceSize byteCnt = (_size == VK_WHOLE_SIZE) ? (_dstBuffer->getByteCount() - _dstOffset) : _size; + + // Metal only supports filling with a single byte value, so each byte in the + // buffer will be filled with the lower 8 bits of the Vulkan 32-bit data value. + [mtlBlitEnc fillBuffer: dstMTLBuff + range: NSMakeRange(dstMTLBuffOffset, byteCnt) + value: (uint8_t)_dataValue]; +} + +MVKCmdFillBuffer::MVKCmdFillBuffer(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark MVKCmdUpdateBuffer + +void MVKCmdUpdateBuffer::setContent(VkBuffer dstBuffer, + VkDeviceSize dstOffset, + VkDeviceSize dataSize, + const void* pData, + bool useDataCache) { + _dstBuffer = (MVKBuffer*)dstBuffer; + _dstOffset = dstOffset; + _dataSize = dataSize; + + _srcDataCache.reserve(_dataSize); + memcpy(_srcDataCache.data(), pData, _dataSize); +} + +void MVKCmdUpdateBuffer::encode(MVKCommandEncoder* cmdEncoder) { + + id mtlBlitEnc = cmdEncoder->getMTLBlitEncoder(kMVKCommandUseUpdateBuffer); + + id dstMTLBuff = _dstBuffer->getMTLBuffer(); + NSUInteger dstMTLBuffOffset = _dstBuffer->getMTLBufferOffset() + _dstOffset; + + // Copy data to the source MTLBuffer + MVKMTLBufferAllocation* srcMTLBufferAlloc = (MVKMTLBufferAllocation*)cmdEncoder->getCommandEncodingPool()->acquireMTLBufferAllocation(_dataSize); + memcpy(srcMTLBufferAlloc->getContents(), _srcDataCache.data(), _dataSize); + + [mtlBlitEnc copyFromBuffer: srcMTLBufferAlloc->_mtlBuffer + sourceOffset: srcMTLBufferAlloc->_offset + toBuffer: dstMTLBuff + destinationOffset: dstMTLBuffOffset + size: _dataSize]; + + // Return the MTLBuffer allocation to the pool once the command buffer is done with it + [cmdEncoder->_mtlCmdBuffer addCompletedHandler: ^(id mcb) { + srcMTLBufferAlloc->returnToPool(); + }]; +} + +MVKCmdUpdateBuffer::MVKCmdUpdateBuffer(MVKCommandTypePool* pool) + : MVKCommand::MVKCommand((MVKCommandTypePool*)pool) {} + + +#pragma mark - +#pragma mark Command creation functions + +void mvkCmdCopyImage(VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageCopy* pRegions) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdCopyImage* cmd = cmdBuff->_commandPool->_cmdCopyImagePool.acquireObject(); + cmd->setContent(srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions); + cmdBuff->addCommand(cmd); +} + +void mvkCmdBlitImage(VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageBlit* pRegions, + VkFilter filter) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdBlitImage* cmd = cmdBuff->_commandPool->_cmdBlitImagePool.acquireObject(); + cmd->setContent(srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions, filter); + cmdBuff->addCommand(cmd); +} + +void mvkCmdResolveImage(VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageResolve* pRegions) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdResolveImage* cmd = cmdBuff->_commandPool->_cmdResolveImagePool.acquireObject(); + cmd->setContent(srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions); + cmdBuff->addCommand(cmd); +} + +void mvkCmdCopyBuffer(VkCommandBuffer commandBuffer, + VkBuffer srcBuffer, + VkBuffer dstBuffer, + uint32_t regionCount, + const VkBufferCopy* pRegions) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdCopyBuffer* cmd = cmdBuff->_commandPool->_cmdCopyBufferPool.acquireObject(); + cmd->setContent(srcBuffer, dstBuffer, regionCount, pRegions); + cmdBuff->addCommand(cmd); +} + +void mvkCmdCopyBufferToImage(VkCommandBuffer commandBuffer, + VkBuffer srcBuffer, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkBufferImageCopy* pRegions) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdBufferImageCopy* cmd = cmdBuff->_commandPool->_cmdBufferImageCopyPool.acquireObject(); + cmd->setContent(srcBuffer, dstImage, dstImageLayout, regionCount, pRegions, true); + cmdBuff->addCommand(cmd); +} + +void mvkCmdCopyImageToBuffer(VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkBuffer dstBuffer, + uint32_t regionCount, + const VkBufferImageCopy* pRegions) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdBufferImageCopy* cmd = cmdBuff->_commandPool->_cmdBufferImageCopyPool.acquireObject(); + cmd->setContent(dstBuffer, srcImage, srcImageLayout, regionCount, pRegions, false); + cmdBuff->addCommand(cmd); +} + +void mvkCmdClearAttachments(VkCommandBuffer commandBuffer, + uint32_t attachmentCount, + const VkClearAttachment* pAttachments, + uint32_t rectCount, + const VkClearRect* pRects) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdClearAttachments* cmd = cmdBuff->_commandPool->_cmdClearAttachmentsPool.acquireObject(); + cmd->setContent(attachmentCount, pAttachments, rectCount, pRects); + cmdBuff->addCommand(cmd); +} + +void mvkCmdClearImage(VkCommandBuffer commandBuffer, + VkImage image, + VkImageLayout imageLayout, + const VkClearColorValue* pColor, + uint32_t rangeCount, + const VkImageSubresourceRange* pRanges) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdClearImage* cmd = cmdBuff->_commandPool->_cmdClearImagePool.acquireObject(); + VkClearValue clrVal; + clrVal.color = *pColor; + cmd->setContent(image, imageLayout, clrVal, rangeCount, pRanges, false); + cmdBuff->addCommand(cmd); +} + +void mvkCmdClearDepthStencilImage(VkCommandBuffer commandBuffer, + VkImage image, + VkImageLayout imageLayout, + const VkClearDepthStencilValue* pDepthStencil, + uint32_t rangeCount, + const VkImageSubresourceRange* pRanges) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdClearImage* cmd = cmdBuff->_commandPool->_cmdClearImagePool.acquireObject(); + VkClearValue clrVal; + clrVal.depthStencil = *pDepthStencil; + cmd->setContent(image, imageLayout, clrVal, rangeCount, pRanges, true); + cmdBuff->addCommand(cmd); +} + +void mvkCmdFillBuffer(VkCommandBuffer commandBuffer, + VkBuffer dstBuffer, + VkDeviceSize dstOffset, + VkDeviceSize size, + uint32_t data) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdFillBuffer* cmd = cmdBuff->_commandPool->_cmdFillBufferPool.acquireObject(); + cmd->setContent(dstBuffer, dstOffset, size, data); + cmdBuff->addCommand(cmd); +} + +void mvkCmdUpdateBuffer(VkCommandBuffer commandBuffer, + VkBuffer dstBuffer, + VkDeviceSize dstOffset, + VkDeviceSize dataSize, + const void* pData) { + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + MVKCmdUpdateBuffer* cmd = cmdBuff->_commandPool->_cmdUpdateBufferPool.acquireObject(); + cmd->setContent(dstBuffer, dstOffset, dataSize, pData, cmdBuff->getIsReusable()); + cmdBuff->addCommand(cmd); +} + diff --git a/MoltenVK/MoltenVK/Commands/MVKCommand.h b/MoltenVK/MoltenVK/Commands/MVKCommand.h new file mode 100644 index 00000000..920f03d9 --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCommand.h @@ -0,0 +1,110 @@ +/* + * MVKCommand.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + + +#include "MVKObjectPool.h" +#include "MVKDevice.h" + +class MVKCommandBuffer; +class MVKCommandEncoder; +class MVKCommandPool; +template class MVKCommandTypePool; + + +#pragma mark - +#pragma mark MVKCommand + +/** Abstract class that represents a Vulkan command. */ +class MVKCommand : public MVKConfigurableObject { + +public: + + /** Called when this command is added to a command buffer. */ + virtual void added(MVKCommandBuffer* cmdBuffer) {}; + + /** Indicates that this command has a valid configuration and can be encoded. */ + inline bool canEncode() { return _configurationResult == VK_SUCCESS; } + + /** Encodes this command on the specified command encoder. */ + virtual void encode(MVKCommandEncoder* cmdEncoder) = 0; + + /** + * Returns this object back to the pool that created it. This will reset the value of _next member. + * + * This method is not thread-safe. Vulkan Command Pools are externally synchronized. + * For a particular MVKCommandTypePool instance, all calls to pool->aquireObject(), + * and returnToPool() (or pool->returnObject()), MUST be called from the same thread. + * + * It is possible to instantiate command instances directly, without retrieving them from + * a command pool via acquireObject(). This can be done when a transient sub-command can be + * used to perform some of the work during the execution of another command. In that case, + * this method should not be called. It is sufficient to just destroy the command instance. + */ + void returnToPool(); + + /** Constructs this instance with the specified pool as its origin. */ + MVKCommand(MVKCommandTypePool* pool) : _pool(pool) {} + + /** Returns the command pool that is managing the resources used by this command. */ + MVKCommandPool* getCommandPool(); + + /** Returns the device for which this command was created. */ + MVKDevice* getDevice(); + + /** Returns the underlying Metal device. */ + id getMTLDevice(); + + /** + * Instances of this class can participate in a linked list or pool. When so participating, + * this is a reference to the next command in the linked list. This value should only be + * managed and set by the linked list. + */ + MVKCommand* _next = nullptr; + +protected: + MVKCommandTypePool* _pool; +}; + + +#pragma mark - +#pragma mark MVKCommandTypePool + +/** A pool of MVKCommand instances of a particular type. */ +template +class MVKCommandTypePool : public MVKObjectPool { + +public: + + /** Some commands require access to the command pool to access resources. */ + MVKCommandPool* getCommandPool() { return _commandPool; } + + /** Returns a new command instance. */ + T* newObject() override { return new T(this); } + + /** + * Configures this instance to either use pooling, or not, depending on the + * value of isPooling, which defaults to true if not indicated explicitly. + */ + MVKCommandTypePool(MVKCommandPool* cmdPool, bool isPooling = true) : MVKObjectPool(isPooling), _commandPool(cmdPool) {} + +protected: + MVKCommandPool* _commandPool; +}; + diff --git a/MoltenVK/MoltenVK/Commands/MVKCommand.mm b/MoltenVK/MoltenVK/Commands/MVKCommand.mm new file mode 100644 index 00000000..f1e26fcc --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCommand.mm @@ -0,0 +1,37 @@ +/* + * MVKCommand.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKCommand.h" +#include "MVKCommandPool.h" + + +#pragma mark - +#pragma mark MVKCommand + +// TODO: Manage command resources in Command Pool +// Opt 1: Leave arrays & rezs allocated in command, per current practice +// Opt 2: Allocate arrays & rezs from pools in Command pool, and return in returnToPool + +void MVKCommand::returnToPool() { _pool->returnObject(this); } + +MVKCommandPool* MVKCommand::getCommandPool() { return _pool->getCommandPool(); } + +MVKDevice* MVKCommand::getDevice() { return getCommandPool()->getDevice(); } + +id MVKCommand::getMTLDevice() { return getCommandPool()->getMTLDevice(); } + diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h new file mode 100644 index 00000000..9ca21407 --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h @@ -0,0 +1,503 @@ +/* + * MVKCommandBuffer.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKDevice.h" +#include "MVKCommand.h" +#include "MVKCommandEncoderState.h" +#include "MVKCmdPipeline.h" +#include +#include +#include + +class MVKCommandPool; +class MVKQueue; +class MVKQueueCommandBufferSubmission; +class MVKCommandEncoder; +class MVKCommandEncodingPool; +class MVKRenderPass; +class MVKFramebuffer; +class MVKRenderSubpass; +class MVKQueryPool; +class MVKPipeline; +class MVKGraphicsPipeline; +class MVKComputePipeline; + +typedef uint64_t MVKMTLCommandBufferID; + +/** The position of a specific MVKCommandBuffer within a batch as part of a queue submission. */ +typedef struct { + uint32_t index; + uint32_t count; + MVKCommandUse use; +} MVKCommandBufferBatchPosition; + + +#pragma mark - +#pragma mark MVKCommandBuffer + +/** Represents a Vulkan command pool. */ +class MVKCommandBuffer : public MVKBaseDeviceObject { + +public: + + /** Prepares this instance to receive commands. */ + VkResult begin(const VkCommandBufferBeginInfo* pBeginInfo); + + /** Resets this instance to allow it to receive new commands. */ + VkResult reset(VkCommandBufferResetFlags flags); + + /** Closes this buffer from receiving commands and prepares for submission to a queue. */ + VkResult end(); + + /** Adds the specified execution command at the end of this command buffer. */ + void addCommand(MVKCommand* command); + + /** Returns the number of commands currently in this command buffer. */ + inline uint32_t getCommandCount() { return _commandCount; } + + /** + * Encode commands from this command buffer onto the Metal command buffer, as part of + * the execution of a batch of command buffers, where the position of this command buffer + * within that batch is specified by the batchPosition parameter. + * + * This call is thread-safe and can be called simultaneously from more than one thread. + */ + void execute(MVKQueueCommandBufferSubmission* cmdBuffSubmit, + const MVKCommandBufferBatchPosition& batchPosition); + + /*** If no error has occured yet, records the specified result. */ + inline void recordResult(VkResult vkResult) { if (_recordingResult == VK_SUCCESS) { _recordingResult = vkResult; } } + + /** Returns the first abnormal VkResult that occured during command recording. */ + inline VkResult getRecordingResult() { return _recordingResult; } + + /** Returns whether this command buffer can be submitted to a queue more than once. */ + inline bool getIsReusable() { return _isReusable; } + + /** Called when a MTLCommandBuffer has started for this instance. */ + inline void mtlCommandBufferHasStarted(id mtlCmdBuff) { + _activeMTLCommandBufferCount++; + } + + /** Called when a MTLCommandBuffer has completed. */ + inline void mtlCommandBufferHasCompleted(id mtlCmdBuff) { + _activeMTLCommandBufferCount--; + } + + /** The command pool that is the source of commands for this buffer. */ + MVKCommandPool* _commandPool; + + /** + * Metal requires that a visibility buffer is established when a render pass is created, + * but Vulkan permits it to be set during a render pass. When the first occlusion query + * command is added, it sets this value so that it can be applied when the first renderpass + * is begun. The execution of subsequent occlusion query commmands may change the visibility + * buffer during command execution, and begin a new Metal renderpass. + */ + id _initialVisibilityResultMTLBuffer; + + /** + * Instances of this class can participate in a linked list or pool. When so participating, + * this is a reference to the next command in the linked list. This value should only be + * managed and set by the linked list. + */ + MVKCommandBuffer* _next; + + +#pragma mark Construction + + MVKCommandBuffer(MVKDevice* device, const VkCommandBufferAllocateInfo* pAllocateInfo); + + ~MVKCommandBuffer() override; + +protected: + friend class MVKCommandEncoder; + + bool canExecute(); + + VkCommandBufferLevel _level; + MVKCommand* _head; + MVKCommand* _tail; + uint32_t _commandCount; + std::atomic _activeMTLCommandBufferCount; + VkResult _recordingResult; + VkCommandBufferInheritanceInfo _secondaryInheritanceInfo; + bool _isSecondary; + bool _doesContinueRenderPass; + bool _canAcceptCommands; + bool _isReusable; + bool _supportsConcurrentExecution; + bool _wasExecuted; +}; + + +#pragma mark - +#pragma mark MVKCommandEncoder + +// The following commands can be issued both inside and outside a renderpass and their state must +// span multiple MTLRenderCommandEncoders, to allow state to be set before a renderpass, and to +// allow more than one MTLRenderCommandEncoder to be used for a single Vulkan renderpass or subpass. +// +// + vkCmdBindPipeline() : _graphicsPipelineState & _computePipelineState +// + vkCmdBindDescriptorSets() : _graphicsResourcesState & _computeResourcesState +// + vkCmdBindVertexBuffers() : _graphicsResourcesState +// + vkCmdBindIndexBuffer() : _graphicsResourcesState +// + vkCmdPushConstants() : _vertexPushConstants & _fragmentPushConstants & _computePushConstants +// + vkCmdSetViewport() : _viewportState +// + vkCmdSetDepthBias() : _depthBiasState +// + vkCmdSetScissor() : _scissorState +// + vkCmdSetStencilCompareMask() : _depthStencilState +// + vkCmdSetStencilWriteMask() : _depthStencilState +// + vkCmdSetStencilReference() : _stencilReferenceValueState +// + vkCmdSetBlendConstants() : _blendColorState +// + vkCmdBeginQuery() : _occlusionQueryState +// + vkCmdEndQuery() : _occlusionQueryState +// + vkCmdPipelineBarrier() : handled via textureBarrier and MTLBlitCommandEncoder +// + vkCmdWriteTimestamp() : doesn't affect MTLCommandEncoders +// + vkCmdExecuteCommands() : state managed by embedded commands +// - vkCmdSetLineWidth() - unsupported by Metal +// - vkCmdSetDepthBounds() - unsupported by Metal +// - vkCmdWaitEvents() - unsupported by Metal + +// The above list of Vulkan commands covers the following corresponding MTLRenderCommandEncoder state: +// + setBlendColorRed : _blendColorState +// + setCullMode : _graphicsPipelineState +// + setDepthBias : _depthBiasState +// + setDepthClipMode : _graphicsPipelineState +// + setDepthStencilState : _depthStencilState +// + setFrontFacingWinding : _graphicsPipelineState +// + setRenderPipelineState : _graphicsPipelineState +// + setScissorRect : _scissorState +// + setStencilFrontReferenceValue : _stencilReferenceValueState +// + setStencilReferenceValue (unused) : _stencilReferenceValueState +// + setTriangleFillMode : _graphicsPipelineState +// + setViewport : _viewportState +// + setVisibilityResultMode : _occlusionQueryState +// + setVertexBuffer : _graphicsResourcesState & _vertexPushConstants +// + setVertexBuffers (unused) : _graphicsResourcesState +// + setVertexBytes : _vertexPushConstants +// + setVertexBufferOffset (unused) : _graphicsResourcesState +// + setVertexTexture : _graphicsResourcesState +// + setVertexTextures (unused) : _graphicsResourcesState +// + setVertexSamplerState : _graphicsResourcesState +// + setVertexSamplerStates : (unused) : _graphicsResourcesState +// + setFragmentBuffer : _graphicsResourcesState & _fragmentPushConstants +// + setFragmentBuffers (unused) : _graphicsResourcesState +// + setFragmentBytes : _fragmentPushConstants +// + setFragmentBufferOffset (unused) : _graphicsResourcesState +// + setFragmentTexture : _graphicsResourcesState +// + setFragmentTextures (unused) : _graphicsResourcesState +// + setFragmentSamplerState : _graphicsResourcesState +// + setFragmentSamplerStates : (unused) : _graphicsResourcesState + +// The above list of Vulkan commands covers the following corresponding MTLComputeCommandEncoder state: +// + setComputePipelineState : _computePipelineState +// + setBuffer : _computeResourcesState & _computePushConstants +// + setBuffers (unused) : _computeResourcesState +// + setBytes : _computePushConstants +// + setBufferOffset (unused) : _computeResourcesState +// + setTexture : _computeResourcesState +// + setTextures (unused) : _computeResourcesState +// + setSamplerState : _computeResourcesState +// + setSamplerStates : (unused) : _computeResourcesState + + +/*** Holds a collection of active queries for each query pool. */ +typedef std::unordered_map> MVKActivatedQueries; + +/** + * MVKCommandEncoder uses a visitor design pattern iterate the commands in a MVKCommandBuffer, + * tracking and caching dynamic encoding state, and encoding the commands onto Metal MTLCommandBuffers. + * + * Much of the dynamic cached encoding state has public access and is accessed directly + * from the commands in the command buffer. + */ +class MVKCommandEncoder : public MVKBaseDeviceObject { + +public: + + /** Encode commands from the command buffer onto the Metal command buffer. */ + void encode(MVKQueueCommandBufferSubmission* cmdBuffSubmit); + + /** Encode commands from the specified secondary command buffer onto the Metal command buffer. */ + void encodeSecondary(MVKCommandBuffer* secondaryCmdBuffer); + + /** Begins a render pass and establishes initial draw state. */ + void beginRenderpass(VkSubpassContents subpassContents, + MVKRenderPass* renderPass, + MVKFramebuffer* framebuffer, + VkRect2D& renderArea, + std::vector* clearValues); + + /** Begins the next render subpass. */ + void beginNextSubpass(VkSubpassContents renderpassContents); + + /** Returns the render subpass that is currently active. */ + MVKRenderSubpass* getSubpass(); + + /** Binds a pipeline to a bind point. */ + void bindPipeline(VkPipelineBindPoint pipelineBindPoint, MVKPipeline* pipeline); + + /** + * If a pipeline is currently bound, returns whether the current pipeline permits dynamic + * setting of the specified state. If no pipeline is currently bound, returns true. + */ + bool supportsDynamicState(VkDynamicState state); + + /** Called by each graphics draw command to establish any outstanding state just prior to performing the draw. */ + void finalizeDrawState(); + + /** Called by each compute dispatch command to establish any outstanding state just prior to performing the dispatch. */ + void finalizeDispatchState(); + + /** + * Ends all encoding operations on the current Metal command encoder. + * + * This must be called once all encoding is complete, and prior + * to each switch between render, compute, and BLIT encoding. + */ + void endCurrentMetalEncoding(); + + /** Ends encoding operations on the current Metal command encoder if it is a rendering encoder. */ + void endMetalRenderEncoding(); + + /** + * Commits any commands already encoded onto the command buffer, to ensure they are + * completed as quickly as possible, without waiting for future commands to be encoded. + * + * If a render pass is currently active, command flushing will occur at the end of the render pass. + */ + void flush(); + + /** + * The current Metal compute encoder. + * + * If the current encoder is not a compute encoder, this function ends current before + * beginning compute encoding. + */ + id getMTLComputeEncoder(); + + /** + * The current Metal BLIT encoder for the specified use, + * which determines the label assigned to the returned encoder. + * + * If the current encoder is not a BLIT encoder, this function ends + * the current encoder before beginning BLIT encoding. + */ + id getMTLBlitEncoder(MVKCommandUse cmdBlitEncUse); + + /** Returns the push constants associated with the specified shader stage. */ + MVKPushConstantsCommandEncoderState* getPushConstants(VkShaderStageFlagBits shaderStage); + + /** Copy bytes into the Metal encoder at a Metal vertex buffer index. */ + void setVertexBytes(id mtlEncoder, const void* bytes, NSUInteger length, uint32_t mtlBuffIndex); + + /** Copy bytes into the Metal encoder at a Metal fragment buffer index. */ + void setFragmentBytes(id mtlEncoder, const void* bytes, NSUInteger length, uint32_t mtlBuffIndex); + + /** Copy bytes into the Metal encoder at a Metal compute buffer index. */ + void setComputeBytes(id mtlEncoder, const void* bytes, NSUInteger length, uint32_t mtlBuffIndex); + + /** Returns the command encoding pool. */ + MVKCommandEncodingPool* getCommandEncodingPool(); + +#pragma mark Queries + + /** Begins an occulusion query. */ + void beginOcclusionQuery(MVKOcclusionQueryPool* pQueryPool, uint32_t query, VkQueryControlFlags flags); + + /** Ends the current occulusion query. */ + void endOcclusionQuery(MVKOcclusionQueryPool* pQueryPool, uint32_t query); + + /** Marks a timestamp for the specified query. */ + void markTimestamp(MVKQueryPool* pQueryPool, uint32_t query); + + +#pragma mark Dynamic encoding state accessed directly + + /** A reference to the Metal features supported by the device. */ + const MVKPhysicalDeviceMetalFeatures* _pDeviceMetalFeatures; + + /** A reference to the Vulkan features supported by the device. */ + const VkPhysicalDeviceFeatures* _pDeviceFeatures; + + /** Pointer to the properties of the device. */ + const VkPhysicalDeviceProperties* _pDeviceProperties; + + /** Pointer to the memory properties of the device. */ + const VkPhysicalDeviceMemoryProperties* _pDeviceMemoryProperties; + + /** The command buffer whose commands are being encoded. */ + MVKCommandBuffer* _cmdBuffer; + + /** The framebuffer to which rendering is currently directed. */ + MVKFramebuffer* _framebuffer; + + /** The current Metal command buffer. */ + id _mtlCmdBuffer; + + /** The current Metal render encoder. */ + id _mtlRenderEncoder; + + /** Tracks the current graphics pipeline bound to the encoder. */ + MVKPipelineCommandEncoderState _graphicsPipelineState; + + /** Tracks the current compute pipeline bound to the encoder. */ + MVKPipelineCommandEncoderState _computePipelineState; + + /** Tracks the current viewport state of the encoder. */ + MVKViewportCommandEncoderState _viewportState; + + /** Tracks the current scissor state of the encoder. */ + MVKScissorCommandEncoderState _scissorState; + + /** Tracks the current depth bias state of the encoder. */ + MVKDepthBiasCommandEncoderState _depthBiasState; + + /** Tracks the current blend color state of the encoder. */ + MVKBlendColorCommandEncoderState _blendColorState; + + /** Tracks the current depth stencil state of the encoder. */ + MVKDepthStencilCommandEncoderState _depthStencilState; + + /** Tracks the current stencil reference value state of the encoder. */ + MVKStencilReferenceValueCommandEncoderState _stencilReferenceValueState; + + /** Tracks the current graphics resources state of the encoder. */ + MVKGraphicsResourcesCommandEncoderState _graphicsResourcesState; + + /** Tracks the current compute resources state of the encoder. */ + MVKComputeResourcesCommandEncoderState _computeResourcesState; + + /** The type of primitive that will be rendered. */ + MTLPrimitiveType _mtlPrimitiveType; + + /** The size of the threadgroup for the compute shader. */ + MTLSize _mtlThreadgroupSize; + + +#pragma mark Construction + + MVKCommandEncoder(MVKCommandBuffer* cmdBuffer, + const MVKCommandBufferBatchPosition& batchPosition); + +protected: + void startMTLCommandBuffer(); + void commitMTLCommandBuffer(); + void addActivatedQuery(MVKQueryPool* pQueryPool, uint32_t query); + void finishQueries(); + void setSubpass(VkSubpassContents subpassContents, uint32_t subpassIndex); + void beginMetalRenderPass(); + void clearRenderArea(); + const MVKMTLBufferAllocation* copyToTempMTLBufferAllocation(const void* bytes, NSUInteger length); + NSString* getMTLCommandBufferName(); + NSString* getMTLRenderCommandEncoderName(); + + MVKQueue* _queue; + VkSubpassContents _subpassContents; + MVKRenderPass* _renderPass; + uint32_t _renderSubpassIndex; + VkRect2D _renderArea; + MVKActivatedQueries* _pActivatedQueries; + std::vector _clearValues; + id _mtlComputeEncoder; + id _mtlBlitEncoder; + MVKCommandUse _mtlBlitEncoderUse; + MVKPushConstantsCommandEncoderState _vertexPushConstants; + MVKPushConstantsCommandEncoderState _fragmentPushConstants; + MVKPushConstantsCommandEncoderState _computePushConstants; + MVKOcclusionQueryCommandEncoderState _occlusionQueryState; + MVKCommandBufferBatchPosition _batchPosition; + uint32_t _flushCount = 0; + bool _isRenderingEntireAttachment; + bool _isAwaitingFlush; +}; + + +#pragma mark - +#pragma mark MVKMTLCommandBufferCountdown + +/** + * Abstract class that can be initialized with the number of active MTLCommandBuffers and the + * ID of the MTLCommandBuffer after those tracked by this countdown, counts down as each earlier + * active MTLCommandBuffer completes, and takes action when the countdown reaches zero. + * + * Subclasses must override the finish() member function to perform the action + * that is to be taken upon completion of the countdown. + * + * This class is not thread-safe. When using this class with multiple threads, + * you must ensure that operations that change the count value are guarded. + */ +class MVKMTLCommandBufferCountdown : public MVKBaseObject { + +public: + + /** + * Sets the number of active MTLCommandBuffers and the ID of the next MTLCommandBuffer + * after those tracked by this countdown. This countdown is interested in MTLCommandBuffers + * whose ID's are less than the specified ID. + * + * If the count is zero, the finish() member function is called. + * + * Returns whether the count is zero. If this function returns true, it is possible + * that this intance has completed and has been destroyed. No further references should be + * made to this instance. + */ + bool setActiveMTLCommandBufferCount(uint32_t count, MVKMTLCommandBufferID mtlCmdBuffID); + + /** + * Called when the MTLCommandBuffer with the specified ID has completed. If the specified + * ID is less than the ID registered via the setActiveMTLCommandBufferCount() function, + * the count of active MTLCommandBuffers is decremented. If the count is zero, the finish() + * member function is called. + * + * Returns whether the count is now at zero. If this function returns true, it is possible + * that this intance has completed and has been destroyed. No further references should be + * made to this instance. + */ + bool mtlCommandBufferHasCompleted(MVKMTLCommandBufferID mtlCmdBuffID); + + /** Returns the current count value. */ + uint32_t getCount(); + +protected: + + /** Performs the action to take when the count has reached zero. */ + virtual void finish() = 0; + + bool checkFinished(); + + uint32_t _activeMTLCommandBufferCount; + MVKMTLCommandBufferID _maxMTLCmdBuffID; +}; + + +#pragma mark - +#pragma mark Support functions + +/** Returns a name, suitable for use as a MTLCommandBuffer label, based on the MVKCommandUse. */ +NSString* mvkMTLCommandBufferLabel(MVKCommandUse cmdBuffUse); + +/** Returns a name, suitable for use as a MTLRenderCommandEncoder label, based on the MVKCommandUse. */ +NSString* mvkMTLRenderCommandEncoderLabel(MVKCommandUse cmdBlitEncUse); + +/** Returns a name, suitable for use as a MTLBlitCommandEncoder label, based on the MVKCommandUse. */ +NSString* mvkMTLBlitCommandEncoderLabel(MVKCommandUse cmdBlitEncUse); + diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm new file mode 100644 index 00000000..ec012697 --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm @@ -0,0 +1,598 @@ +/* + * MVKCommandBuffer.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKCommandBuffer.h" +#include "MVKCommandPool.h" +#include "MVKQueue.h" +#include "MVKPipeline.h" +#include "MVKRenderPass.h" +#include "MVKFramebuffer.h" +#include "MVKQueryPool.h" +#include "MVKFoundation.h" + +using namespace std; + + +#pragma mark - +#pragma mark MVKCommandBuffer + +VkResult MVKCommandBuffer::begin(const VkCommandBufferBeginInfo* pBeginInfo) { + _recordingResult = VK_SUCCESS; + _canAcceptCommands = true; + reset(0); + + VkCommandBufferUsageFlags usage = pBeginInfo->flags; + _isReusable = !mvkAreFlagsEnabled(usage, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); + _supportsConcurrentExecution = mvkAreFlagsEnabled(usage, VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT); + + // If this is a secondary command buffer, and contains inheritance info, set the inheritance info and determine + // whether it contains render pass continuation info. Otherwise, clear the inheritance info, and ignore it. + const VkCommandBufferInheritanceInfo* pInheritInfo = (_isSecondary ? pBeginInfo->pInheritanceInfo : NULL); + bool hasInheritInfo = mvkSetOrClear(&_secondaryInheritanceInfo, pInheritInfo); + _doesContinueRenderPass = mvkAreFlagsEnabled(usage, VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT) && hasInheritInfo; + + _wasExecuted = false; + return _recordingResult; +} + +VkResult MVKCommandBuffer::reset(VkCommandBufferResetFlags flags) { + MVKCommand* cmd = _head; + while (cmd) { + MVKCommand* nextCmd = cmd->_next; // Establish next before returning current to pool. + cmd->returnToPool(); + cmd = nextCmd; + } + + _head = nullptr; + _tail = nullptr; + _commandCount = 0; + _initialVisibilityResultMTLBuffer = nil; // not retained + + if (mvkAreFlagsEnabled(flags, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT)) { + // TODO: what are we releasing or returning here? + } + + return VK_SUCCESS; +} + +VkResult MVKCommandBuffer::end() { + _canAcceptCommands = false; + return _recordingResult; +} + +void MVKCommandBuffer::addCommand(MVKCommand* command) { + if ( !_canAcceptCommands ) { + recordResult(mvkNotifyErrorWithText(VK_NOT_READY, "Command buffer cannot accept commands before vkBeginCommandBuffer() is called.")); + return; + } + + if (_tail) { _tail->_next = command; } + command->_next = VK_NULL_HANDLE; + _tail = command; + if ( !_head ) { _head = command; } + _commandCount++; + + command->added(this); + + recordResult(command->getConfigurationResult()); +} + +void MVKCommandBuffer::execute(MVKQueueCommandBufferSubmission* cmdBuffSubmit, + const MVKCommandBufferBatchPosition& batchPosition) { + if ( !canExecute() ) { return; } + +// MVKLogDebug("MVKCommandBuffer %p starting execution.", this); + MVKCommandEncoder encoder(this, batchPosition); + encoder.encode(cmdBuffSubmit); +// MVKLogDebug("MVKCommandBuffer %p ending execution.", this); +} + +bool MVKCommandBuffer::canExecute() { + if (_isSecondary) { + recordResult(mvkNotifyErrorWithText(VK_NOT_READY, "Secondary command buffers may not be submitted directly to a queue.")); + return false; + } + if ( !_isReusable && _wasExecuted ) { + recordResult(mvkNotifyErrorWithText(VK_NOT_READY, "Command buffer does not support execution more that once.")); + return false; + } + if ( !_supportsConcurrentExecution && _activeMTLCommandBufferCount > 0) { + recordResult(mvkNotifyErrorWithText(VK_NOT_READY, "Command buffer does not support concurrent execution.")); + return false; + } + + _wasExecuted = true; + return true; +} + + +#pragma mark Construction + +MVKCommandBuffer::MVKCommandBuffer(MVKDevice* device, + const VkCommandBufferAllocateInfo* pAllocateInfo) : MVKBaseDeviceObject(device) { + + _commandPool = (MVKCommandPool*)pAllocateInfo->commandPool; + _commandPool->addCommandBuffer(this); + _level = pAllocateInfo->level; + _isSecondary = (_level == VK_COMMAND_BUFFER_LEVEL_SECONDARY); + _doesContinueRenderPass = false; + _canAcceptCommands = false; + _isReusable = false; + _supportsConcurrentExecution = false; + _wasExecuted = false; + _activeMTLCommandBufferCount = 0; + _recordingResult = VK_NOT_READY; + _head = VK_NULL_HANDLE; + _tail = VK_NULL_HANDLE; + _commandCount = 0; + _initialVisibilityResultMTLBuffer = nil; +} + +MVKCommandBuffer::~MVKCommandBuffer() { + reset(0); + _commandPool->removeCommandBuffer(this); +} + +// TODO: +// - Opt 1: pool command buffers including non-command state +// - Opt 2: pool command buffer content (eg- pool constants) + + +#pragma mark - +#pragma mark MVKCommandEncoder + +void MVKCommandEncoder::encode(MVKQueueCommandBufferSubmission* cmdBuffSubmit) { +// MVKLogDebug("\tEncoder %s starting.", getMTLCommandBufferName().UTF8String); + _queue = cmdBuffSubmit->_queue; + _subpassContents = VK_SUBPASS_CONTENTS_INLINE; + _renderSubpassIndex = 0; + _isAwaitingFlush = false; + + startMTLCommandBuffer(); + +// MVKLogDebug("\tEncoder %p encoding commands.", this); + + MVKCommand* cmd = _cmdBuffer->_head; + while (cmd) { + if (cmd->canEncode()) { cmd->encode(this); } + cmd = cmd->_next; + } + + commitMTLCommandBuffer(); + +// MVKLogDebug("\tEncoder %s done.", getMTLCommandBufferName().UTF8String); +} + +void MVKCommandEncoder::encodeSecondary(MVKCommandBuffer* secondaryCmdBuffer) { + MVKCommand* cmd = secondaryCmdBuffer->_head; + while (cmd) { + cmd->encode(this); + cmd = cmd->_next; + } +} + +// Creates and starts the Metal command buffer. +void MVKCommandEncoder::startMTLCommandBuffer() { +// MVKLogDebug("\tEncoder %p retrieving MTLCommandBuffer.", this); + _mtlCmdBuffer = _queue->getNextMTLCommandBuffer(getMTLCommandBufferName(), _cmdBuffer); + [_mtlCmdBuffer enqueue]; +} + +// Returns a name for use as a MTLCommandBuffer label +NSString* MVKCommandEncoder::getMTLCommandBufferName() { + if (_flushCount == 0) { + return [NSString stringWithFormat: @"%@ %u of %u with %u Vulkan commands", + mvkMTLCommandBufferLabel(_batchPosition.use), + _batchPosition.index, _batchPosition.count, _cmdBuffer->_commandCount]; + } else { + return [NSString stringWithFormat: @"%@ %u of %u with %u Vulkan commands after %u flushes", + mvkMTLCommandBufferLabel(_batchPosition.use), _batchPosition.index, + _batchPosition.count, _cmdBuffer->_commandCount, _flushCount]; + } +} + +// Commits the command buffer. +void MVKCommandEncoder::commitMTLCommandBuffer() { + endCurrentMetalEncoding(); + finishQueries(); + [_mtlCmdBuffer commit]; + _mtlCmdBuffer = nil; // not retained +} + +void MVKCommandEncoder::beginRenderpass(VkSubpassContents subpassContents, + MVKRenderPass* renderPass, + MVKFramebuffer* framebuffer, + VkRect2D& renderArea, + vector* clearValues) { + _renderPass = renderPass; + _framebuffer = framebuffer; + _renderArea = renderArea; + _isRenderingEntireAttachment = (mvkVkOffset2DsAreEqual(_renderArea.offset, {0,0}) && + mvkVkExtent2DsAreEqual(_renderArea.extent, _framebuffer->getExtent2D())); + _clearValues.assign(clearValues->begin(), clearValues->end()); + setSubpass(subpassContents, 0); +} + +void MVKCommandEncoder::beginNextSubpass(VkSubpassContents contents) { + setSubpass(contents, _renderSubpassIndex + 1); +} + +/** Sets the current render subpass to the subpass with the specified index. */ +void MVKCommandEncoder::setSubpass(VkSubpassContents subpassContents, uint32_t subpassIndex) { + _subpassContents = subpassContents; + _renderSubpassIndex = subpassIndex; + + beginMetalRenderPass(); +// MVKLogDebug("Render subpass begin MTLRenderCommandEncoder."); +} + +// Called after the _mtlRenderEncoder is established. +// Marks cached render state as dirty so it will be set into the _mtlRenderEncoder. +void MVKCommandEncoder::beginMetalRenderPass() { + + endCurrentMetalEncoding(); + + MTLRenderPassDescriptor* mtlRPDesc = [MTLRenderPassDescriptor renderPassDescriptor]; + getSubpass()->populateMTLRenderPassDescriptor(mtlRPDesc, _framebuffer, _clearValues, _isRenderingEntireAttachment); + mtlRPDesc.visibilityResultBuffer = _occlusionQueryState.getVisibilityResultMTLBuffer(); + + _mtlRenderEncoder = [_mtlCmdBuffer renderCommandEncoderWithDescriptor: mtlRPDesc]; // not retained + _mtlRenderEncoder.label = getMTLRenderCommandEncoderName(); + + if ( !_isRenderingEntireAttachment ) { clearRenderArea(); } + + _graphicsPipelineState.beginMetalRenderPass(); + _graphicsResourcesState.beginMetalRenderPass(); + _viewportState.beginMetalRenderPass(); + _scissorState.beginMetalRenderPass(); + _depthBiasState.beginMetalRenderPass(); + _blendColorState.beginMetalRenderPass(); + _vertexPushConstants.beginMetalRenderPass(); + _fragmentPushConstants.beginMetalRenderPass(); + _depthStencilState.beginMetalRenderPass(); + _stencilReferenceValueState.beginMetalRenderPass(); + _occlusionQueryState.beginMetalRenderPass(); +} + +MVKRenderSubpass* MVKCommandEncoder::getSubpass() { return _renderPass->getSubpass(_renderSubpassIndex); } + +// Returns a name for use as a MTLRenderCommandEncoder label +NSString* MVKCommandEncoder::getMTLRenderCommandEncoderName() { + MVKCommandUse cmdUse = (_renderSubpassIndex == 0) ? kMVKCommandUseBeginRenderPass : kMVKCommandUseNextSubpass; + return mvkMTLRenderCommandEncoderLabel(cmdUse); +} + +void MVKCommandEncoder::bindPipeline(VkPipelineBindPoint pipelineBindPoint, MVKPipeline* pipeline) { + switch (pipelineBindPoint) { + case VK_PIPELINE_BIND_POINT_GRAPHICS: + _graphicsPipelineState.setPipeline(pipeline); + break; + + case VK_PIPELINE_BIND_POINT_COMPUTE: + _computePipelineState.setPipeline(pipeline); + break; + + default: + break; + } +} + +bool MVKCommandEncoder::supportsDynamicState(VkDynamicState state) { + MVKGraphicsPipeline* gpl = (MVKGraphicsPipeline*)_graphicsPipelineState.getPipeline(); + return !gpl || gpl->supportsDynamicState(state); +} + +void MVKCommandEncoder::finalizeDrawState() { + _graphicsPipelineState.encode(); // Must do first..it sets others + _graphicsResourcesState.encode(); + _viewportState.encode(); + _scissorState.encode(); + _depthBiasState.encode(); + _blendColorState.encode(); + _vertexPushConstants.encode(); + _fragmentPushConstants.encode(); + _depthStencilState.encode(); + _stencilReferenceValueState.encode(); + _occlusionQueryState.encode(); +} + +// Clears the render area of the framebuffer attachments. +void MVKCommandEncoder::clearRenderArea() { + + vector clearAtts; + getSubpass()->populateClearAttachments(clearAtts, _clearValues); + + uint32_t clearAttCnt = (uint32_t)clearAtts.size(); + + if (clearAttCnt == 0) { return; } + + VkClearRect clearRect; + clearRect.rect = _renderArea; + clearRect.baseArrayLayer = 0; + clearRect.layerCount = 1; + + // Create and execute a temporary clear attachments command. + // To be threadsafe...do NOT acquire and return the command from the pool. + MVKCmdClearAttachments cmd(&_cmdBuffer->_commandPool->_cmdClearAttachmentsPool); + cmd.setContent(clearAttCnt, clearAtts.data(), 1, &clearRect); + cmd.encode(this); +} + +void MVKCommandEncoder::finalizeDispatchState() { + _computePipelineState.encode(); + _computeResourcesState.encode(); +} + +void MVKCommandEncoder::endMetalRenderEncoding() { +// MVKLogDebugIf(_mtlRenderEncoder, "Render subpass end MTLRenderCommandEncoder."); + [_mtlRenderEncoder endEncoding]; + _mtlRenderEncoder = nil; // not retained + + if (_isAwaitingFlush) { flush(); } // if awaiting a flush, do so now +} + +void MVKCommandEncoder::endCurrentMetalEncoding() { + endMetalRenderEncoding(); + + [_mtlComputeEncoder endEncoding]; + _mtlComputeEncoder = nil; // not retained + + [_mtlBlitEncoder endEncoding]; + _mtlBlitEncoder = nil; // not retained + _mtlBlitEncoderUse = kMVKCommandUseNone; +} + +void MVKCommandEncoder::flush() { + if (_mtlRenderEncoder) { + // If currently in a render pass, wait until it's done before flushing, + _isAwaitingFlush = true; + } else { + // Otherwise, flush immediately by committing the current MTLCommandBuffer and starting a new one. + _isAwaitingFlush = false; + _flushCount++; + commitMTLCommandBuffer(); + startMTLCommandBuffer(); + } +} + +id MVKCommandEncoder::getMTLComputeEncoder() { + if ( !_mtlComputeEncoder ) { + endCurrentMetalEncoding(); + _mtlComputeEncoder = [_mtlCmdBuffer computeCommandEncoder]; // not retained + } + return _mtlComputeEncoder; +} + +id MVKCommandEncoder::getMTLBlitEncoder(MVKCommandUse cmdBlitEncUse) { + if ( !_mtlBlitEncoder ) { + endCurrentMetalEncoding(); + _mtlBlitEncoder = [_mtlCmdBuffer blitCommandEncoder]; // not retained + } + if (_mtlBlitEncoderUse != cmdBlitEncUse) { + _mtlBlitEncoderUse = cmdBlitEncUse; + _mtlBlitEncoder.label = mvkMTLBlitCommandEncoderLabel(cmdBlitEncUse); + } + return _mtlBlitEncoder; +} +MVKPushConstantsCommandEncoderState* MVKCommandEncoder::getPushConstants(VkShaderStageFlagBits shaderStage) { + switch (shaderStage) { + case VK_SHADER_STAGE_VERTEX_BIT: return &_vertexPushConstants; + case VK_SHADER_STAGE_FRAGMENT_BIT: return &_fragmentPushConstants; + case VK_SHADER_STAGE_COMPUTE_BIT: return &_computePushConstants; + default: + MVKAssert(false, "Invalid shader stage: %lu", shaderStage); + return VK_NULL_HANDLE; + } +} + +void MVKCommandEncoder::setVertexBytes(id mtlEncoder, + const void* bytes, + NSUInteger length, + uint32_t mtlBuffIndex) { + if (_pDeviceMetalFeatures->dynamicMTLBuffers) { + [mtlEncoder setVertexBytes: bytes length: length atIndex: mtlBuffIndex]; + } else { + const MVKMTLBufferAllocation* mtlBuffAlloc = copyToTempMTLBufferAllocation(bytes, length); + [mtlEncoder setVertexBuffer: mtlBuffAlloc->_mtlBuffer offset: mtlBuffAlloc->_offset atIndex: mtlBuffIndex]; + } +} + +void MVKCommandEncoder::setFragmentBytes(id mtlEncoder, + const void* bytes, + NSUInteger length, + uint32_t mtlBuffIndex) { + if (_pDeviceMetalFeatures->dynamicMTLBuffers) { + [mtlEncoder setFragmentBytes: bytes length: length atIndex: mtlBuffIndex]; + } else { + const MVKMTLBufferAllocation* mtlBuffAlloc = copyToTempMTLBufferAllocation(bytes, length); + [mtlEncoder setFragmentBuffer: mtlBuffAlloc->_mtlBuffer offset: mtlBuffAlloc->_offset atIndex: mtlBuffIndex]; + } +} + +void MVKCommandEncoder::setComputeBytes(id mtlEncoder, + const void* bytes, + NSUInteger length, + uint32_t mtlBuffIndex) { + if (_pDeviceMetalFeatures->dynamicMTLBuffers) { + [mtlEncoder setBytes: bytes length: length atIndex: mtlBuffIndex]; + } else { + const MVKMTLBufferAllocation* mtlBuffAlloc = copyToTempMTLBufferAllocation(bytes, length); + [mtlEncoder setBuffer: mtlBuffAlloc->_mtlBuffer offset: mtlBuffAlloc->_offset atIndex: mtlBuffIndex]; + } +} + +MVKCommandEncodingPool* MVKCommandEncoder::getCommandEncodingPool() { return _queue->getCommandEncodingPool(); } + +// Copies the specified bytes into a temporary allocation within a pooled MTLBuffer, and returns the MTLBuffer allocation. +const MVKMTLBufferAllocation* MVKCommandEncoder::copyToTempMTLBufferAllocation(const void* bytes, NSUInteger length) { + const MVKMTLBufferAllocation* mtlBuffAlloc = getCommandEncodingPool()->acquireMTLBufferAllocation(length); + void* pBuffData = mtlBuffAlloc->getContents(); + memcpy(pBuffData, bytes, length); + + // Return the MTLBuffer allocation to the pool once the command buffer is done with it + [_mtlCmdBuffer addCompletedHandler: ^(id mcb) { + ((MVKMTLBufferAllocation*)mtlBuffAlloc)->returnToPool(); + }]; + + return mtlBuffAlloc; +} + + +#pragma mark Queries + +void MVKCommandEncoder::beginOcclusionQuery(MVKOcclusionQueryPool* pQueryPool, uint32_t query, VkQueryControlFlags flags) { + _occlusionQueryState.beginOcclusionQuery(pQueryPool, query, flags); + addActivatedQuery(pQueryPool, query); +} + +void MVKCommandEncoder::endOcclusionQuery(MVKOcclusionQueryPool* pQueryPool, uint32_t query) { + _occlusionQueryState.endOcclusionQuery(pQueryPool, query); + flush(); +} + +void MVKCommandEncoder::markTimestamp(MVKQueryPool* pQueryPool, uint32_t query) { + addActivatedQuery(pQueryPool, query); + flush(); +} + +// Marks the specified query as activated +void MVKCommandEncoder::addActivatedQuery(MVKQueryPool* pQueryPool, uint32_t query) { + if ( !_pActivatedQueries ) { _pActivatedQueries = new MVKActivatedQueries(); } + (*_pActivatedQueries)[pQueryPool].push_back(query); +} + +// Register a command buffer completion handler that finishes each activated query. +// Ownership of the collection of activated queries is passed to the handler. +void MVKCommandEncoder::finishQueries() { + if ( !_pActivatedQueries ) { return; } + + MVKActivatedQueries* pAQs = _pActivatedQueries; + [_mtlCmdBuffer addCompletedHandler: ^(id mtlCmdBuff) { + for (auto& qryPair : *pAQs) { + qryPair.first->finishQueries(qryPair.second); + } + delete pAQs; + }]; + _pActivatedQueries = nullptr; +} + + +#pragma mark Construction + +MVKCommandEncoder::MVKCommandEncoder(MVKCommandBuffer* cmdBuffer, + const MVKCommandBufferBatchPosition& batchPosition) : MVKBaseDeviceObject(cmdBuffer->getDevice()), + _cmdBuffer(cmdBuffer), + _batchPosition(batchPosition), + _graphicsPipelineState(this), + _computePipelineState(this), + _viewportState(this), + _scissorState(this), + _depthBiasState(this), + _blendColorState(this), + _vertexPushConstants(this, VK_SHADER_STAGE_VERTEX_BIT), + _fragmentPushConstants(this, VK_SHADER_STAGE_FRAGMENT_BIT), + _computePushConstants(this, VK_SHADER_STAGE_COMPUTE_BIT), + _depthStencilState(this), + _stencilReferenceValueState(this), + _graphicsResourcesState(this), + _computeResourcesState(this), + _occlusionQueryState(this) { + + _pDeviceFeatures = _device->_pFeatures; + _pDeviceMetalFeatures = _device->_pMetalFeatures; + _pDeviceProperties = _device->_pProperties; + _pDeviceMemoryProperties = _device->_pMemoryProperties; + _pActivatedQueries = nullptr; + _mtlCmdBuffer = nil; + _mtlRenderEncoder = nil; + _mtlComputeEncoder = nil; + _mtlBlitEncoder = nil; + _mtlBlitEncoderUse = kMVKCommandUseNone; +} + + +#pragma mark - +#pragma mark MVKMTLCommandBufferCountdown + +bool MVKMTLCommandBufferCountdown::setActiveMTLCommandBufferCount(uint32_t count, + MVKMTLCommandBufferID mtlCmdBuffID) { + _activeMTLCommandBufferCount = count; + _maxMTLCmdBuffID = mtlCmdBuffID; + + return checkFinished(); +} + +bool MVKMTLCommandBufferCountdown::mtlCommandBufferHasCompleted(MVKMTLCommandBufferID mtlCmdBuffID) { + if ( (_activeMTLCommandBufferCount > 0) && (mtlCmdBuffID < _maxMTLCmdBuffID) ) { + _activeMTLCommandBufferCount--; + } + return checkFinished(); +} + +// If the count of active MTLCommandBuffers is zero, calls the finish() member function. +// Returns whether the count is now at zero. +bool MVKMTLCommandBufferCountdown::checkFinished() { + bool isDone = (_activeMTLCommandBufferCount == 0); + if (isDone) { finish(); } + return isDone; +} + +uint32_t MVKMTLCommandBufferCountdown::getCount() { return _activeMTLCommandBufferCount; } + + +#pragma mark - +#pragma mark Support functions + +NSString* mvkMTLCommandBufferLabel(MVKCommandUse cmdBuffUse) { + switch (cmdBuffUse) { + case kMVKCommandUseQueueSubmit: return @"vkQueueSubmit CommandBuffer"; + case kMVKCommandUseQueuePresent: return @"vkQueuePresentKHR CommandBuffer"; + case kMVKCommandUseQueueWaitIdle: return @"vkQueueWaitIdle CommandBuffer"; + case kMVKCommandUseDeviceWaitIdle: return @"vkDeviceWaitIdle CommandBuffer"; + default: return @"Unknown Use CommandBuffer"; + } +} + +NSString* mvkMTLRenderCommandEncoderLabel(MVKCommandUse cmdRendEncUse) { + switch (cmdRendEncUse) { + case kMVKCommandUseBeginRenderPass: return @"vkCmdBeginRenderPass RenderEncoder"; + case kMVKCommandUseNextSubpass: return @"vkCmdNextSubpass RenderEncoder"; + case kMVKCommandUseBlitImage: return @"vkCmdBlitImage RenderEncoder"; + case kMVKCommandUseResolveImage: return @"vkCmdResolveImage (resolve stage) RenderEncoder"; + case kMVKCommandUseResolveExpandImage: return @"vkCmdResolveImage (expand stage) RenderEncoder"; + case kMVKCommandUseClearColorImage: return @"vkCmdClearColorImage RenderEncoder"; + case kMVKCommandUseClearDepthStencilImage: return @"vkCmdClearDepthStencilImage RenderEncoder"; + default: return @"Unknown Use RenderEncoder"; + } +} + +NSString* mvkMTLBlitCommandEncoderLabel(MVKCommandUse cmdBlitEncUse) { + switch (cmdBlitEncUse) { + case kMVKCommandUsePipelineBarrier: return @"vkCmdPipelineBarrier BlitEncoder"; + case kMVKCommandUseCopyImage: return @"vkCmdCopyImage BlitEncoder"; + case kMVKCommandUseResolveCopyImage: return @"vkCmdResolveImage (copy stage) RenderEncoder"; + case kMVKCommandUseCopyBuffer: return @"vkCmdCopyBuffer BlitEncoder"; + case kMVKCommandUseCopyBufferToImage: return @"vkCmdCopyBufferToImage BlitEncoder"; + case kMVKCommandUseCopyImageToBuffer: return @"vkCmdCopyImageToBuffer BlitEncoder"; + case kMVKCommandUseFillBuffer: return @"vkCmdFillBuffer BlitEncoder"; + case kMVKCommandUseUpdateBuffer: return @"vkCmdUpdateBuffer BlitEncoder"; + case kMVKCommandUseResetQueryPool: return @"vkCmdResetQueryPool BlitEncoder"; + default: return @"Unknown Use BlitEncoder"; + } +} + diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h new file mode 100644 index 00000000..4f1ea211 --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h @@ -0,0 +1,515 @@ +/* + * MVKCommandEncoderState.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKCommandPool.h" +#include + +class MVKCommandEncoder; +class MVKOcclusionQueryPool; + + +#pragma mark - +#pragma mark MVKCommandEncoderState + +/** + * Abstract class that holds encoder state established by Vulkan commands. + * + * Some Vulkan commands can be issued both inside or outside a render pass, and the state + * encoded by the command needs to be retained by the encoder for use by following render + * passes. In addition, some Vulkan commands can be issued multiple times to accumulate + * encoded content that should be submitted in one shot to the Metal encoder. + */ +class MVKCommandEncoderState : public MVKBaseObject { + +public: + + /** + * Marks the content of this instance as dirty, relative to the + * current or next Metal render pass, and in need of submission to Metal. + */ + virtual void markDirty() { + _isDirty = true; + _isModified = true; + } + + /** + * Called automatically when a Metal render pass begins. If the contents have been + * modified from the default values, this instance is marked as dirty, so the contents + * will be encoded to Metal, otherwise it is marked as clean, so the contents will NOT + * be encoded. Default state can be left unencoded on a new Metal encoder. + */ + void beginMetalRenderPass() { if (_isModified) { markDirty(); } } + + /** + * If the content of this instance is dirty, marks this instance as no longer dirty + * and calls the encodeImpl() function to encode the content onto the Metal encoder. + * Subclasses must override the encodeImpl() function to do the actual work. + */ + void encode() { + if ( !_isDirty ) { return; } + + _isDirty = false; + encodeImpl(); + } + + /** + * Marks this instance as dirty and calls resetImpl() function to reset this instance + * back to initial state. Subclasses must override the resetImpl() function. + */ + void reset() { + _isDirty = true; + _isModified = false; + + resetImpl(); + } + + /** Constructs this instance for the specified command encoder. */ + MVKCommandEncoderState(MVKCommandEncoder* cmdEncoder) : _cmdEncoder(cmdEncoder) {} + +protected: + virtual void encodeImpl() = 0; + virtual void resetImpl() = 0; + + MVKCommandEncoder* _cmdEncoder; + bool _isDirty = false; + bool _isModified = false; +}; + + +#pragma mark - +#pragma mark MVKPipelineCommandEncoderState + +/** Holds encoder state established by pipeline commands. */ +class MVKPipelineCommandEncoderState : public MVKCommandEncoderState { + +public: + + /** Sets the pipeline during pipeline binding. */ + void setPipeline(MVKPipeline* pipeline); + + /** Returns the currently bound pipeline. */ + MVKPipeline* getPipeline(); + + /** Constructs this instance for the specified command encoder. */ + MVKPipelineCommandEncoderState(MVKCommandEncoder* cmdEncoder) + : MVKCommandEncoderState(cmdEncoder) {} + +protected: + void encodeImpl() override; + void resetImpl() override; + + MVKPipeline* _pipeline = nullptr; +}; + + +#pragma mark - +#pragma mark MVKViewportCommandEncoderState + +/** Holds encoder state established by viewport commands. */ +class MVKViewportCommandEncoderState : public MVKCommandEncoderState { + +public: + + /** Sets all of the viewports during pipeline binding. */ + void setViewports(std::vector mtlViewports); + + /** Sets one or more of the viewports, starting at the first index. */ + void setViewports(std::vector mtlViewports, uint32_t firstViewport); + + /** Constructs this instance for the specified command encoder. */ + MVKViewportCommandEncoderState(MVKCommandEncoder* cmdEncoder) + : MVKCommandEncoderState(cmdEncoder) {} + +protected: + void encodeImpl() override; + void resetImpl() override; + + MTLViewport _mtlViewport = { 0, 0, 0, 0, 0, 0 }; +}; + + +#pragma mark - +#pragma mark MVKScissorCommandEncoderState + +/** Holds encoder state established by viewport commands. */ +class MVKScissorCommandEncoderState : public MVKCommandEncoderState { + +public: + + /** Sets all of the scissors during pipeline binding. */ + void setScissors(std::vector mtlScissors); + + /** Sets one or more of the scissors, starting at the first index. */ + void setScissors(std::vector mtlScissors, uint32_t firstScissor); + + /** Constructs this instance for the specified command encoder. */ + MVKScissorCommandEncoderState(MVKCommandEncoder* cmdEncoder) + : MVKCommandEncoderState(cmdEncoder) {} + +protected: + void encodeImpl() override; + void resetImpl() override; + + MTLScissorRect _mtlScissor = { 0, 0, 0, 0 }; +}; + + +#pragma mark - +#pragma mark MVKPushConstantsCommandEncoderState + +/** Holds encoder state established by push constant commands for a single shader stage. */ +class MVKPushConstantsCommandEncoderState : public MVKCommandEncoderState { + +public: + + /** Sets the specified push constants. */ + void setPushConstants(uint32_t offset, std::vector& pushConstants); + + /** Sets the index of the Metal buffer used to hold the push constants. */ + void setMTLBufferIndex(uint32_t mtlBufferIndex); + + /** Constructs this instance for the specified command encoder. */ + MVKPushConstantsCommandEncoderState(MVKCommandEncoder* cmdEncoder, + VkShaderStageFlagBits shaderStage) + : MVKCommandEncoderState(cmdEncoder), _shaderStage(shaderStage) {} + +protected: + void encodeImpl() override; + void resetImpl() override; + + std::vector _pushConstants; + VkShaderStageFlagBits _shaderStage; + uint32_t _mtlBufferIndex = 0; +}; + + +#pragma mark - +#pragma mark MVKDepthStencilCommandEncoderState + +/** Holds encoder state established by depth stencil commands. */ +class MVKDepthStencilCommandEncoderState : public MVKCommandEncoderState { + +public: + + /** Sets the depth stencil state during pipeline binding. */ + void setDepthStencilState(VkPipelineDepthStencilStateCreateInfo& vkDepthStencilInfo); + + /** + * Sets the stencil compare mask value of the indicated faces + * to the specified value, from explicit dynamic command. + */ + void setStencilCompareMask(VkStencilFaceFlags faceMask, uint32_t stencilCompareMask); + + /** + * Sets the stencil write mask value of the indicated faces + * to the specified value, from explicit dynamic command. + */ + void setStencilWriteMask(VkStencilFaceFlags faceMask, uint32_t stencilWriteMask); + + /** Constructs this instance for the specified command encoder. */ + MVKDepthStencilCommandEncoderState(MVKCommandEncoder* cmdEncoder) + : MVKCommandEncoderState(cmdEncoder) {} + +protected: + void encodeImpl() override; + void resetImpl() override; + void setStencilState(MVKMTLStencilDescriptorData& stencilInfo, + VkStencilOpState& vkStencil, + bool enabled); + + MVKMTLDepthStencilDescriptorData _depthStencilData; +}; + + +#pragma mark - +#pragma mark MVKStencilReferenceValueCommandEncoderState + +/** Holds encoder state established by stencil reference values commands. */ +class MVKStencilReferenceValueCommandEncoderState : public MVKCommandEncoderState { + +public: + + /** Sets the stencil references during pipeline binding. */ + void setReferenceValues(VkPipelineDepthStencilStateCreateInfo& vkDepthStencilInfo); + + /** Sets the stencil state from explicit dynamic command. */ + void setReferenceValues(VkStencilFaceFlags faceMask, uint32_t stencilReference); + + /** Constructs this instance for the specified command encoder. */ + MVKStencilReferenceValueCommandEncoderState(MVKCommandEncoder* cmdEncoder) + : MVKCommandEncoderState(cmdEncoder) {} + +protected: + void encodeImpl() override; + void resetImpl() override; + + uint32_t _frontFaceValue = 0; + uint32_t _backFaceValue = 0; +}; + + +#pragma mark - +#pragma mark MVKDepthBiasCommandEncoderState + +/** Holds encoder state established by depth bias commands. */ +class MVKDepthBiasCommandEncoderState : public MVKCommandEncoderState { + +public: + + /** Sets the depth bias during pipeline binding. */ + void setDepthBias(VkPipelineRasterizationStateCreateInfo vkRasterInfo); + + /** Sets the depth bias dynamically. */ + void setDepthBias(float depthBiasConstantFactor, + float depthBiasSlopeFactor, + float depthBiasClamp); + + /** Constructs this instance for the specified command encoder. */ + MVKDepthBiasCommandEncoderState(MVKCommandEncoder* cmdEncoder) + : MVKCommandEncoderState(cmdEncoder) {} + +protected: + void encodeImpl() override; + void resetImpl() override; + + float _depthBiasConstantFactor = 0; + float _depthBiasClamp = 0; + float _depthBiasSlopeFactor = 0; + bool _isEnabled = false; +}; + + +#pragma mark - +#pragma mark MVKBlendColorCommandEncoderState + +/** Holds encoder state established by blend color commands. */ +class MVKBlendColorCommandEncoderState : public MVKCommandEncoderState { + +public: + + /** Sets the blend color, either as part of pipeline binding, or dynamically. */ + void setBlendColor(float red, float green, + float blue, float alpha, + bool isDynamic); + + /** Constructs this instance for the specified command encoder. */ + MVKBlendColorCommandEncoderState(MVKCommandEncoder* cmdEncoder) + : MVKCommandEncoderState(cmdEncoder) {} + +protected: + void encodeImpl() override; + void resetImpl() override; + + float _red = 0; + float _green = 0; + float _blue = 0; + float _alpha = 0; +}; + + +#pragma mark - +#pragma mark MVKResourcesCommandEncoderState + +/** Abstract resource state class for supporting encoder resources. */ +class MVKResourcesCommandEncoderState : public MVKCommandEncoderState { + +public: + + /** Constructs this instance for the specified command encoder. */ + MVKResourcesCommandEncoderState(MVKCommandEncoder* cmdEncoder) : MVKCommandEncoderState(cmdEncoder) {} + +protected: + + // Template function that marks both the vector and all binding elements in the vector as dirty. + template + void markDirty(std::vector& bindings, bool& bindingsDirtyFlag) { + for (auto& b : bindings) { b.isDirty = true; } + bindingsDirtyFlag = true; + } + + // Template function that updates an existing binding or adds a new binding to a vector + // of bindings, and marks the binding, the vector, and this instance as dirty + template + void bind(const T& b, std::vector& bindings, bool& bindingsDirtyFlag) { + + if ( !b.mtlResource ) { return; } + + T db = b; // Copy that can be marked dirty + MVKCommandEncoderState::markDirty(); + bindingsDirtyFlag = true; + db.isDirty = true; + + for (auto iter = bindings.begin(), end = bindings.end(); iter != end; iter++) { + if( iter->index == db.index ) { + *iter = db; + return; + } + } + bindings.push_back(db); + } + + // Template function that executes a lambda expression on each dirty element of + // a vector of bindings, and marks the bindings and the vector as no longer dirty. + template + void encodeBinding(std::vector& bindings, + bool& bindingsDirtyFlag, + std::function mtlOperation) { + if (bindingsDirtyFlag) { + bindingsDirtyFlag = false; + for (auto& b : bindings) { + if (b.isDirty) { + mtlOperation(_cmdEncoder, b); + b.isDirty = false; + } + } + } + } +}; + + +#pragma mark - +#pragma mark MVKGraphicsResourcesCommandEncoderState + +/** Holds graphics encoder resource state established by bind vertex buffer and descriptor set commands. */ +class MVKGraphicsResourcesCommandEncoderState : public MVKResourcesCommandEncoderState { + +public: + + /** Binds the specified vertex buffer. */ + void bindVertexBuffer(const MVKMTLBufferBinding& binding); + + /** Binds the specified fragment buffer. */ + void bindFragmentBuffer(const MVKMTLBufferBinding& binding); + + /** Binds the specified vertex texture. */ + void bindVertexTexture(const MVKMTLTextureBinding& binding); + + /** Binds the specified fragment texture. */ + void bindFragmentTexture(const MVKMTLTextureBinding& binding); + + /** Binds the specified vertex sampler state. */ + void bindVertexSamplerState(const MVKMTLSamplerStateBinding& binding); + + /** Binds the specified fragment sampler state. */ + void bindFragmentSamplerState(const MVKMTLSamplerStateBinding& binding); + + /** The type of index that will be used to render primitives. Exposed directly. */ + MVKIndexMTLBufferBinding _mtlIndexBufferBinding; + + /** Binds the specified index buffer. */ + void bindIndexBuffer(const MVKIndexMTLBufferBinding& binding) { + _mtlIndexBufferBinding = binding; // No need to track dirty state + } + + +#pragma mark Construction + + /** Constructs this instance for the specified command encoder. */ + MVKGraphicsResourcesCommandEncoderState(MVKCommandEncoder* cmdEncoder) : MVKResourcesCommandEncoderState(cmdEncoder) {} + +protected: + void encodeImpl() override; + void resetImpl() override; + void markDirty() override; + + std::vector _vertexBufferBindings; + std::vector _fragmentBufferBindings; + std::vector _vertexTextureBindings; + std::vector _fragmentTextureBindings; + std::vector _vertexSamplerStateBindings; + std::vector _fragmentSamplerStateBindings; + + bool _areVertexBufferBindingsDirty = false; + bool _areFragmentBufferBindingsDirty = false; + bool _areVertexTextureBindingsDirty = false; + bool _areFragmentTextureBindingsDirty = false; + bool _areVertexSamplerStateBindingsDirty = false; + bool _areFragmentSamplerStateBindingsDirty = false; +}; + + +#pragma mark - +#pragma mark MVKComputeResourcesCommandEncoderState + +/** Holds compute encoder resource state established by bind vertex buffer and descriptor set commands. */ +class MVKComputeResourcesCommandEncoderState : public MVKResourcesCommandEncoderState { + +public: + + /** Binds the specified buffer. */ + void bindBuffer(const MVKMTLBufferBinding& binding); + + /** Binds the specified texture. */ + void bindTexture(const MVKMTLTextureBinding& binding); + + /** Binds the specified sampler state. */ + void bindSamplerState(const MVKMTLSamplerStateBinding& binding); + + +#pragma mark Construction + + /** Constructs this instance for the specified command encoder. */ + MVKComputeResourcesCommandEncoderState(MVKCommandEncoder* cmdEncoder) : MVKResourcesCommandEncoderState(cmdEncoder) {} + +protected: + void encodeImpl() override; + void resetImpl() override; + void markDirty() override; + + std::vector _bufferBindings; + std::vector _textureBindings; + std::vector _samplerStateBindings; + + bool _areBufferBindingsDirty = false; + bool _areTextureBindingsDirty = false; + bool _areSamplerStateBindingsDirty = false; +}; + + +#pragma mark - +#pragma mark MVKOcclusionQueryCommandEncoderState + +/** Holds encoder state established by occlusion query commands. */ +class MVKOcclusionQueryCommandEncoderState : public MVKCommandEncoderState { + +public: + + /** Begins an occlusion query. */ + void beginOcclusionQuery(MVKOcclusionQueryPool* pQueryPool, uint32_t query, VkQueryControlFlags flags); + + /** Ends an occlusion query. */ + void endOcclusionQuery(MVKOcclusionQueryPool* pQueryPool, uint32_t query); + + /** Returns the MTLBuffer used to hold occlusion query results. */ + id getVisibilityResultMTLBuffer(); + + /** Constructs this instance for the specified command encoder. */ + MVKOcclusionQueryCommandEncoderState(MVKCommandEncoder* cmdEncoder); + +protected: + void encodeImpl() override; + void resetImpl() override; + + id _visibilityResultMTLBuffer = nil; + MTLVisibilityResultMode _mtlVisibilityResultMode = MTLVisibilityResultModeDisabled; + NSUInteger _mtlVisibilityResultOffset = 0; +}; + + diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm new file mode 100644 index 00000000..e8633108 --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm @@ -0,0 +1,567 @@ +/* + * MVKCommandEncoderState.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKCommandEncoderState.h" +#include "MVKCommandEncodingPool.h" +#include "MVKCommandBuffer.h" +#include "MVKPipeline.h" +#include "MVKQueryPool.h" +#include "MVKLogging.h" +#include "mvk_datatypes.h" + +using namespace std; + + +#pragma mark - +#pragma mark MVKPipelineCommandEncoderState + +void MVKPipelineCommandEncoderState::setPipeline(MVKPipeline* pipeline) { + _pipeline = pipeline; + markDirty(); +} + +MVKPipeline* MVKPipelineCommandEncoderState::getPipeline() { return _pipeline; } + +void MVKPipelineCommandEncoderState::encodeImpl() { + if (_pipeline) { _pipeline->encode(_cmdEncoder); } +} + +void MVKPipelineCommandEncoderState::resetImpl() { + _pipeline = nullptr; +} + + +#pragma mark - +#pragma mark MVKViewportCommandEncoderState + +void MVKViewportCommandEncoderState::setViewports(vector mtlViewports) { + + // If ref values are to be set dynamically, don't set them here. + if (_cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_VIEWPORT) || mtlViewports.size() < 1) { return; } + + _mtlViewport = mtlViewports[0]; + + markDirty(); +} + +void MVKViewportCommandEncoderState::setViewports(vector mtlViewports, + uint32_t firstViewport) { + + if ( !(_cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_VIEWPORT) && + (firstViewport < mtlViewports.size()) && + (firstViewport == 0)) ) { return; } + + _mtlViewport = mtlViewports[firstViewport]; + + markDirty(); +} + +void MVKViewportCommandEncoderState::encodeImpl() { + [_cmdEncoder->_mtlRenderEncoder setViewport: _mtlViewport]; +} + +void MVKViewportCommandEncoderState::resetImpl() { + _mtlViewport = { 0, 0, 0, 0, 0, 0 }; +} + + +#pragma mark - +#pragma mark MVKScissorCommandEncoderState + +void MVKScissorCommandEncoderState::setScissors(vector mtlScissors) { + + // If ref values are to be set dynamically, don't set them here. + if (_cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_SCISSOR) || mtlScissors.size() < 1) { return; } + + _mtlScissor = mtlScissors[0]; + + markDirty(); +} + +void MVKScissorCommandEncoderState::setScissors(vector mtlScissors, + uint32_t firstScissor) { + + if ( !(_cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_SCISSOR) && + (firstScissor < mtlScissors.size()) && + (firstScissor == 0)) ) { return; } + + _mtlScissor = mtlScissors[firstScissor]; + + markDirty(); +} + +void MVKScissorCommandEncoderState::encodeImpl() { + [_cmdEncoder->_mtlRenderEncoder setScissorRect: _mtlScissor]; +} + +void MVKScissorCommandEncoderState::resetImpl() { + _mtlScissor = { 0, 0, 0, 0 }; +} + + +#pragma mark - +#pragma mark MVKPushConstantsCommandEncoderState + +void MVKPushConstantsCommandEncoderState:: setPushConstants(uint32_t offset, vector& pushConstants) { + uint32_t pcCnt = (uint32_t)pushConstants.size(); + mvkEnsureSize(_pushConstants, offset + pcCnt); + copy(pushConstants.begin(), pushConstants.end(), _pushConstants.begin() + offset); + if (pcCnt > 0) { markDirty(); } +} + +void MVKPushConstantsCommandEncoderState::setMTLBufferIndex(uint32_t mtlBufferIndex) { + if (mtlBufferIndex != _mtlBufferIndex) { + _mtlBufferIndex = mtlBufferIndex; + markDirty(); + } +} + +void MVKPushConstantsCommandEncoderState::encodeImpl() { + if (_pushConstants.empty() ) { return; } + + switch (_shaderStage) { + case VK_SHADER_STAGE_VERTEX_BIT: + _cmdEncoder->setVertexBytes(_cmdEncoder->_mtlRenderEncoder, + _pushConstants.data(), + _pushConstants.size(), + _mtlBufferIndex); + break; + case VK_SHADER_STAGE_FRAGMENT_BIT: + _cmdEncoder->setFragmentBytes(_cmdEncoder->_mtlRenderEncoder, + _pushConstants.data(), + _pushConstants.size(), + _mtlBufferIndex); + break; + case VK_SHADER_STAGE_COMPUTE_BIT: + _cmdEncoder->setComputeBytes(_cmdEncoder->getMTLComputeEncoder(), + _pushConstants.data(), + _pushConstants.size(), + _mtlBufferIndex); + break; + default: + MVKAssert(false, "Unsupported shader stage: %d", _shaderStage); + break; + } +} + +void MVKPushConstantsCommandEncoderState::resetImpl() { + _pushConstants.clear(); +} + + +#pragma mark - +#pragma mark MVKDepthStencilCommandEncoderState + +void MVKDepthStencilCommandEncoderState:: setDepthStencilState(VkPipelineDepthStencilStateCreateInfo& vkDepthStencilInfo) { + + if (vkDepthStencilInfo.depthTestEnable) { + _depthStencilData.depthCompareFunction = mvkMTLCompareFunctionFromVkCompareOp(vkDepthStencilInfo.depthCompareOp); + _depthStencilData.depthWriteEnabled = vkDepthStencilInfo.depthWriteEnable; + } else { + _depthStencilData.depthCompareFunction = kMVKMTLDepthStencilDescriptorDataDefault.depthCompareFunction; + _depthStencilData.depthWriteEnabled = kMVKMTLDepthStencilDescriptorDataDefault.depthWriteEnabled; + } + + setStencilState(_depthStencilData.frontFaceStencilData, vkDepthStencilInfo.front, vkDepthStencilInfo.stencilTestEnable); + setStencilState(_depthStencilData.backFaceStencilData, vkDepthStencilInfo.back, vkDepthStencilInfo.stencilTestEnable); + + _depthStencilData.clearHash(); // Hash will change + + markDirty(); +} + +void MVKDepthStencilCommandEncoderState::setStencilState(MVKMTLStencilDescriptorData& stencilInfo, + VkStencilOpState& vkStencil, + bool enabled) { + if ( !enabled ) { + stencilInfo = kMVKMTLStencilDescriptorDataDefault; + return; + } + + stencilInfo.enabled = true; + stencilInfo.stencilCompareFunction = mvkMTLCompareFunctionFromVkCompareOp(vkStencil.compareOp); + stencilInfo.stencilFailureOperation = mvkMTLStencilOperationFromVkStencilOp(vkStencil.failOp); + stencilInfo.depthFailureOperation = mvkMTLStencilOperationFromVkStencilOp(vkStencil.depthFailOp); + stencilInfo.depthStencilPassOperation = mvkMTLStencilOperationFromVkStencilOp(vkStencil.passOp); + + bool useCompareMask = !_cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK); + if (useCompareMask) { stencilInfo.readMask = vkStencil.compareMask; } + + bool useWriteMask = !_cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK); + if (useWriteMask) { stencilInfo.writeMask = vkStencil.writeMask; } +} + +void MVKDepthStencilCommandEncoderState::setStencilCompareMask(VkStencilFaceFlags faceMask, + uint32_t stencilCompareMask) { + + // If we can't set the state, or nothing is being set, just leave + if ( !(_cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK) && + mvkIsAnyFlagEnabled(faceMask, VK_STENCIL_FRONT_AND_BACK)) ) { return; } + + if (mvkAreFlagsEnabled(faceMask, VK_STENCIL_FACE_FRONT_BIT)) { + _depthStencilData.frontFaceStencilData.readMask = stencilCompareMask; + } + if (mvkAreFlagsEnabled(faceMask, VK_STENCIL_FACE_BACK_BIT)) { + _depthStencilData.backFaceStencilData.readMask = stencilCompareMask; + } + + markDirty(); +} + +void MVKDepthStencilCommandEncoderState::setStencilWriteMask(VkStencilFaceFlags faceMask, + uint32_t stencilWriteMask) { + + // If we can't set the state, or nothing is being set, just leave + if ( !(_cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK) && + mvkIsAnyFlagEnabled(faceMask, VK_STENCIL_FRONT_AND_BACK)) ) { return; } + + if (mvkAreFlagsEnabled(faceMask, VK_STENCIL_FACE_FRONT_BIT)) { + _depthStencilData.frontFaceStencilData.writeMask = stencilWriteMask; + } + if (mvkAreFlagsEnabled(faceMask, VK_STENCIL_FACE_BACK_BIT)) { + _depthStencilData.backFaceStencilData.writeMask = stencilWriteMask; + } + + markDirty(); +} + +void MVKDepthStencilCommandEncoderState::encodeImpl() { + id mtlDSS = _cmdEncoder->getCommandEncodingPool()->getMTLDepthStencilState(_depthStencilData); + [_cmdEncoder->_mtlRenderEncoder setDepthStencilState: mtlDSS]; +} + +void MVKDepthStencilCommandEncoderState::resetImpl() { + _depthStencilData = kMVKMTLDepthStencilDescriptorDataDefault; +} + + +#pragma mark - +#pragma mark MVKStencilReferenceValueCommandEncoderState + +void MVKStencilReferenceValueCommandEncoderState:: setReferenceValues(VkPipelineDepthStencilStateCreateInfo& vkDepthStencilInfo) { + + // If ref values are to be set dynamically, don't set them here. + if (_cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_STENCIL_REFERENCE)) { return; } + + _frontFaceValue = vkDepthStencilInfo.front.reference; + _backFaceValue = vkDepthStencilInfo.back.reference; + markDirty(); +} + +void MVKStencilReferenceValueCommandEncoderState::setReferenceValues(VkStencilFaceFlags faceMask, + uint32_t stencilReference) { + + // If we can't set the state, or nothing is being set, just leave + if ( !(_cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_STENCIL_REFERENCE) && + mvkIsAnyFlagEnabled(faceMask, VK_STENCIL_FRONT_AND_BACK)) ) { return; } + + if (mvkAreFlagsEnabled(faceMask, VK_STENCIL_FACE_FRONT_BIT)) { + _frontFaceValue = stencilReference; + } + if (mvkAreFlagsEnabled(faceMask, VK_STENCIL_FACE_BACK_BIT)) { + _backFaceValue = stencilReference; + } + + markDirty(); +} + +void MVKStencilReferenceValueCommandEncoderState::encodeImpl() { + [_cmdEncoder->_mtlRenderEncoder setStencilFrontReferenceValue: _frontFaceValue + backReferenceValue: _backFaceValue]; +} + +void MVKStencilReferenceValueCommandEncoderState::resetImpl() { + _frontFaceValue = 0; + _backFaceValue = 0; +} + + +#pragma mark - +#pragma mark MVKDepthBiasCommandEncoderState + +void MVKDepthBiasCommandEncoderState::setDepthBias(VkPipelineRasterizationStateCreateInfo vkRasterInfo) { + + _isEnabled = vkRasterInfo.depthBiasEnable; + + // If ref values are to be set dynamically, don't set them here. + if (_cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_DEPTH_BIAS)) { return; } + + _depthBiasConstantFactor = vkRasterInfo.depthBiasConstantFactor; + _depthBiasSlopeFactor = vkRasterInfo.depthBiasSlopeFactor; + _depthBiasClamp = vkRasterInfo.depthBiasClamp; + + markDirty(); +} + +void MVKDepthBiasCommandEncoderState::setDepthBias(float depthBiasConstantFactor, + float depthBiasSlopeFactor, + float depthBiasClamp) { + + if ( !_cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_DEPTH_BIAS) ) { return; } + + _depthBiasConstantFactor = depthBiasConstantFactor; + _depthBiasSlopeFactor = depthBiasSlopeFactor; + _depthBiasClamp = depthBiasClamp; + + markDirty(); +} + +void MVKDepthBiasCommandEncoderState::encodeImpl() { + if (_isEnabled) { + [_cmdEncoder->_mtlRenderEncoder setDepthBias: _depthBiasConstantFactor + slopeScale: _depthBiasSlopeFactor + clamp: _depthBiasClamp]; + } else { + [_cmdEncoder->_mtlRenderEncoder setDepthBias: 0 slopeScale: 0 clamp: 0]; + } +} + +void MVKDepthBiasCommandEncoderState::resetImpl() { + _depthBiasConstantFactor = 0; + _depthBiasClamp = 0; + _depthBiasSlopeFactor = 0; + _isEnabled = false; +} + + +#pragma mark - +#pragma mark MVKBlendColorCommandEncoderState + +void MVKBlendColorCommandEncoderState::setBlendColor(float red, float green, + float blue, float alpha, + bool isDynamic) { + + // Abort if dynamic allowed but call is not dynamic, or vice-versa + if ( !(_cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_BLEND_CONSTANTS) == isDynamic) ) { return; } + + _red = red; + _green = green; + _blue = blue; + _alpha = alpha; + + markDirty(); +} + +void MVKBlendColorCommandEncoderState::encodeImpl() { + [_cmdEncoder->_mtlRenderEncoder setBlendColorRed: _red green: _green blue: _blue alpha: _alpha]; +} + +void MVKBlendColorCommandEncoderState::resetImpl() { + _red = 0; + _green = 0; + _blue = 0; + _alpha = 0; +} + + +#pragma mark - +#pragma mark MVKGraphicsResourcesCommandEncoderState + +void MVKGraphicsResourcesCommandEncoderState::bindVertexBuffer(const MVKMTLBufferBinding& binding) { + bind(binding, _vertexBufferBindings, _areVertexBufferBindingsDirty); +} + +void MVKGraphicsResourcesCommandEncoderState::bindFragmentBuffer(const MVKMTLBufferBinding& binding) { + bind(binding, _fragmentBufferBindings, _areFragmentBufferBindingsDirty); +} + +void MVKGraphicsResourcesCommandEncoderState::bindVertexTexture(const MVKMTLTextureBinding& binding) { + bind(binding, _vertexTextureBindings, _areVertexTextureBindingsDirty); +} + +void MVKGraphicsResourcesCommandEncoderState::bindFragmentTexture(const MVKMTLTextureBinding& binding) { + bind(binding, _fragmentTextureBindings, _areFragmentTextureBindingsDirty); +} + +void MVKGraphicsResourcesCommandEncoderState::bindVertexSamplerState(const MVKMTLSamplerStateBinding& binding) { + bind(binding, _vertexSamplerStateBindings, _areVertexSamplerStateBindingsDirty); +} + +void MVKGraphicsResourcesCommandEncoderState::bindFragmentSamplerState(const MVKMTLSamplerStateBinding& binding) { + bind(binding, _fragmentSamplerStateBindings, _areFragmentSamplerStateBindingsDirty); +} + +// Mark everything as dirty +void MVKGraphicsResourcesCommandEncoderState::markDirty() { + MVKCommandEncoderState::markDirty(); + MVKResourcesCommandEncoderState::markDirty(_vertexBufferBindings, _areVertexBufferBindingsDirty); + MVKResourcesCommandEncoderState::markDirty(_fragmentBufferBindings, _areFragmentBufferBindingsDirty); + MVKResourcesCommandEncoderState::markDirty(_vertexTextureBindings, _areVertexTextureBindingsDirty); + MVKResourcesCommandEncoderState::markDirty(_fragmentTextureBindings, _areFragmentTextureBindingsDirty); + MVKResourcesCommandEncoderState::markDirty(_vertexSamplerStateBindings, _areVertexSamplerStateBindingsDirty); + MVKResourcesCommandEncoderState::markDirty(_fragmentSamplerStateBindings, _areFragmentSamplerStateBindingsDirty); +} + +void MVKGraphicsResourcesCommandEncoderState::encodeImpl() { + + encodeBinding(_vertexBufferBindings, _areVertexBufferBindingsDirty, + [](MVKCommandEncoder* cmdEncoder, MVKMTLBufferBinding& b)->void { + [cmdEncoder->_mtlRenderEncoder setVertexBuffer: b.mtlBuffer + offset: b.offset + atIndex: b.index]; + }); + + encodeBinding(_fragmentBufferBindings, _areFragmentBufferBindingsDirty, + [](MVKCommandEncoder* cmdEncoder, MVKMTLBufferBinding& b)->void { + [cmdEncoder->_mtlRenderEncoder setFragmentBuffer: b.mtlBuffer + offset: b.offset + atIndex: b.index]; + }); + + encodeBinding(_vertexTextureBindings, _areVertexTextureBindingsDirty, + [](MVKCommandEncoder* cmdEncoder, MVKMTLTextureBinding& b)->void { + [cmdEncoder->_mtlRenderEncoder setVertexTexture: b.mtlTexture + atIndex: b.index]; + }); + + encodeBinding(_fragmentTextureBindings, _areFragmentTextureBindingsDirty, + [](MVKCommandEncoder* cmdEncoder, MVKMTLTextureBinding& b)->void { + [cmdEncoder->_mtlRenderEncoder setFragmentTexture: b.mtlTexture + atIndex: b.index]; + }); + + encodeBinding(_vertexSamplerStateBindings, _areVertexSamplerStateBindingsDirty, + [](MVKCommandEncoder* cmdEncoder, MVKMTLSamplerStateBinding& b)->void { + [cmdEncoder->_mtlRenderEncoder setVertexSamplerState: b.mtlSamplerState + atIndex: b.index]; + }); + + encodeBinding(_fragmentSamplerStateBindings, _areFragmentSamplerStateBindingsDirty, + [](MVKCommandEncoder* cmdEncoder, MVKMTLSamplerStateBinding& b)->void { + [cmdEncoder->_mtlRenderEncoder setFragmentSamplerState: b.mtlSamplerState + atIndex: b.index]; + }); +} + +void MVKGraphicsResourcesCommandEncoderState::resetImpl() { + _vertexBufferBindings.clear(); + _fragmentBufferBindings.clear(); + _vertexTextureBindings.clear(); + _fragmentTextureBindings.clear(); + _vertexSamplerStateBindings.clear(); + _fragmentSamplerStateBindings.clear(); + + _areVertexBufferBindingsDirty = false; + _areFragmentBufferBindingsDirty = false; + _areVertexTextureBindingsDirty = false; + _areFragmentTextureBindingsDirty = false; + _areVertexSamplerStateBindingsDirty = false; + _areFragmentSamplerStateBindingsDirty = false; +} + + +#pragma mark - +#pragma mark MVKComputeResourcesCommandEncoderState + +void MVKComputeResourcesCommandEncoderState::bindBuffer(const MVKMTLBufferBinding& binding) { + bind(binding, _bufferBindings, _areBufferBindingsDirty); +} + +void MVKComputeResourcesCommandEncoderState::bindTexture(const MVKMTLTextureBinding& binding) { + bind(binding, _textureBindings, _areTextureBindingsDirty); +} + +void MVKComputeResourcesCommandEncoderState::bindSamplerState(const MVKMTLSamplerStateBinding& binding) { + bind(binding, _samplerStateBindings, _areSamplerStateBindingsDirty); +} + +// Mark everything as dirty +void MVKComputeResourcesCommandEncoderState::markDirty() { + MVKCommandEncoderState::markDirty(); + MVKResourcesCommandEncoderState::markDirty(_bufferBindings, _areBufferBindingsDirty); + MVKResourcesCommandEncoderState::markDirty(_textureBindings, _areTextureBindingsDirty); + MVKResourcesCommandEncoderState::markDirty(_samplerStateBindings, _areSamplerStateBindingsDirty); +} + +void MVKComputeResourcesCommandEncoderState::encodeImpl() { + + encodeBinding(_bufferBindings, _areBufferBindingsDirty, + [](MVKCommandEncoder* cmdEncoder, MVKMTLBufferBinding& b)->void { + [cmdEncoder->getMTLComputeEncoder() setBuffer: b.mtlBuffer + offset: b.offset + atIndex: b.index]; + }); + + encodeBinding(_textureBindings, _areTextureBindingsDirty, + [](MVKCommandEncoder* cmdEncoder, MVKMTLTextureBinding& b)->void { + [cmdEncoder->getMTLComputeEncoder() setTexture: b.mtlTexture + atIndex: b.index]; + }); + + encodeBinding(_samplerStateBindings, _areSamplerStateBindingsDirty, + [](MVKCommandEncoder* cmdEncoder, MVKMTLSamplerStateBinding& b)->void { + [cmdEncoder->getMTLComputeEncoder() setSamplerState: b.mtlSamplerState + atIndex: b.index]; + }); +} + +void MVKComputeResourcesCommandEncoderState::resetImpl() { + _bufferBindings.clear(); + _textureBindings.clear(); + _samplerStateBindings.clear(); + + _areBufferBindingsDirty = false; + _areTextureBindingsDirty = false; + _areSamplerStateBindingsDirty = false; +} + + +#pragma mark - +#pragma mark MVKOcclusionQueryCommandEncoderState + +void MVKOcclusionQueryCommandEncoderState::beginOcclusionQuery(MVKOcclusionQueryPool* pQueryPool, uint32_t query, VkQueryControlFlags flags) { + + NSUInteger offset = pQueryPool->getVisibilityResultOffset(query); + NSUInteger maxOffset = _cmdEncoder->_pDeviceMetalFeatures->maxQueryBufferSize - kMVKQuerySlotSizeInBytes; + + bool shouldCount = _cmdEncoder->_pDeviceFeatures->occlusionQueryPrecise && mvkAreFlagsEnabled(flags, VK_QUERY_CONTROL_PRECISE_BIT); + _mtlVisibilityResultMode = shouldCount ? MTLVisibilityResultModeCounting : MTLVisibilityResultModeBoolean; + _mtlVisibilityResultOffset = min(offset, maxOffset); + + _visibilityResultMTLBuffer = pQueryPool->getVisibilityResultMTLBuffer(); // not retained + + markDirty(); +} + +void MVKOcclusionQueryCommandEncoderState::endOcclusionQuery(MVKOcclusionQueryPool* pQueryPool, uint32_t query) { + _mtlVisibilityResultMode = MTLVisibilityResultModeDisabled; + _mtlVisibilityResultOffset = 0; + + markDirty(); +} + +// If the MTLBuffer has not yet been set, see if the command buffer is configured with it +id MVKOcclusionQueryCommandEncoderState::getVisibilityResultMTLBuffer() { return _visibilityResultMTLBuffer; } + +void MVKOcclusionQueryCommandEncoderState::encodeImpl() { + [_cmdEncoder->_mtlRenderEncoder setVisibilityResultMode: _mtlVisibilityResultMode + offset: _mtlVisibilityResultOffset]; +} + +void MVKOcclusionQueryCommandEncoderState::resetImpl() { + _visibilityResultMTLBuffer = _cmdEncoder->_cmdBuffer->_initialVisibilityResultMTLBuffer; + _mtlVisibilityResultMode = MTLVisibilityResultModeDisabled; + _mtlVisibilityResultOffset = 0; +} + +MVKOcclusionQueryCommandEncoderState::MVKOcclusionQueryCommandEncoderState(MVKCommandEncoder* cmdEncoder) + : MVKCommandEncoderState(cmdEncoder) { + resetImpl(); +} + + diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncodingPool.h b/MoltenVK/MoltenVK/Commands/MVKCommandEncodingPool.h new file mode 100644 index 00000000..e75f38b3 --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncodingPool.h @@ -0,0 +1,121 @@ +/* + * MVKCommandEncodingPool.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKDevice.h" +#include "MVKCommandResourceFactory.h" +#include "MVKMTLBufferAllocation.h" +#include + +#import + + +#pragma mark - +#pragma mark MVKCommandEncodingPool + +/** + * Represents a pool containing trainsient resources that commands can use during encoding + * onto a queue. This is distinct from a command pool, which contains resources that can be + * assigned to commands when their content is established. + * + * Access to the content within this pool is not thread-safe. All access to the content + * of this pool should be done during the MVKCommand::encode() member functions. + */ +class MVKCommandEncodingPool : public MVKBaseDeviceObject { + +public: + +#pragma mark Command resources + + /** + * Returns a MTLRenderPipelineState dedicated to rendering to a texture + * in the specified pixel format to support certain Vulkan BLIT commands. + */ + id getCmdBlitImageMTLRenderPipelineState(MTLPixelFormat mtlPixFmt); + + /** + * Returns a MTLSamplerState dedicated to rendering to a texture using the + * specified min/mag filter value to support certain Vulkan BLIT commands. + */ + id getCmdBlitImageMTLSamplerState(MTLSamplerMinMagFilter mtlFilter); + + /** + * Returns a MTLDepthStencilState dedicated to rendering to several attachments + * to support clearing regions of those attachments. + */ + id getMTLDepthStencilState(bool useDepth, bool useStencil); + + /** + * Acquires and returns an allocation of the specified length from within a MTLBuffer. + * + * The returned allocation will have a size that is the next + * power-of-two value that is at least as big as the requested size. + * + * To return the returned allocation back to the pool to be reused, + * call the returnToPool() function on the returned allocation. + */ + const MVKMTLBufferAllocation* acquireMTLBufferAllocation(NSUInteger length); + + /** + * Returns a MTLRenderPipelineState dedicated to rendering to several attachments + * to support clearing regions of those attachments. + */ + id getCmdClearMTLRenderPipelineState(MVKRPSKeyClearAtt& attKey); + + /** Returns a MTLDepthStencilState configured from the specified data. */ + id getMTLDepthStencilState(MVKMTLDepthStencilDescriptorData& dsData); + + /** + * Returns an MVKImage configured from the specified MTLTexture configuration, + * with content held in Private storage. The object returned can be used as a + * temporary image during image transfers. + * + * The same image instance will be returned for two calls to this funciton with + * the same image descriptor data. This implies that the same image instance could + * be used by two transfers within the same encoder or queue. This is acceptable + * becuase the content only needss to be valid during the transfer, and it can be + * reused by subsequent transfers in the same encoding run. + */ + MVKImage* getTransferMVKImage(MVKImageDescriptorData& imgData); + + +#pragma mark Construction + + MVKCommandEncodingPool(MVKDevice* device); + + ~MVKCommandEncodingPool() override; + +private: + void initTextureDeviceMemory(); + void destroyMetalResources(); + + std::unordered_map> _cmdBlitImageMTLRenderPipelineStates; + std::unordered_map> _cmdClearMTLRenderPipelineStates; + std::unordered_map> _mtlDepthStencilStates; + std::unordered_map _transferImages; + MVKDeviceMemory* _transferImageMemory; + MVKMTLBufferAllocator _mtlBufferAllocator; + id _cmdBlitImageLinearMTLSamplerState; + id _cmdBlitImageNearestMTLSamplerState; + id _cmdClearDepthOnlyDepthStencilState; + id _cmdClearStencilOnlyDepthStencilState; + id _cmdClearDepthAndStencilDepthStencilState; + id _cmdClearDefaultDepthStencilState; +}; + diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncodingPool.mm b/MoltenVK/MoltenVK/Commands/MVKCommandEncodingPool.mm new file mode 100644 index 00000000..ccdf316a --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncodingPool.mm @@ -0,0 +1,178 @@ +/* + * MVKCommandEncodingPool.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKCommandEncodingPool.h" +#include "MVKImage.h" + +using namespace std; + +#pragma mark - +#pragma mark MVKCommandEncodingPool + +id MVKCommandEncodingPool::getCmdClearMTLRenderPipelineState(MVKRPSKeyClearAtt& attKey) { + id rps = _cmdClearMTLRenderPipelineStates[attKey]; + if ( !rps ) { + rps = _device->getCommandResourceFactory()->newCmdClearMTLRenderPipelineState(attKey); // retained + _cmdClearMTLRenderPipelineStates[attKey] = rps; + } + return rps; +} + +id MVKCommandEncodingPool::getCmdBlitImageMTLRenderPipelineState(MTLPixelFormat mtlPixFmt) { + id rps = _cmdBlitImageMTLRenderPipelineStates[mtlPixFmt]; + if ( !rps ) { + rps = _device->getCommandResourceFactory()->newCmdBlitImageMTLRenderPipelineState(mtlPixFmt); // retained + _cmdBlitImageMTLRenderPipelineStates[mtlPixFmt] = rps; + } + return rps; +} + +id MVKCommandEncodingPool::getCmdBlitImageMTLSamplerState(MTLSamplerMinMagFilter mtlFilter) { + switch (mtlFilter) { + case MTLSamplerMinMagFilterNearest: + if ( !_cmdBlitImageNearestMTLSamplerState ) { + _cmdBlitImageNearestMTLSamplerState = _device->getCommandResourceFactory()->newCmdBlitImageMTLSamplerState(mtlFilter); // retained + } + return _cmdBlitImageNearestMTLSamplerState; + + case MTLSamplerMinMagFilterLinear: + if ( !_cmdBlitImageLinearMTLSamplerState ) { + _cmdBlitImageLinearMTLSamplerState = _device->getCommandResourceFactory()->newCmdBlitImageMTLSamplerState(mtlFilter); // retained + } + return _cmdBlitImageLinearMTLSamplerState; + } +} + +id MVKCommandEncodingPool::getMTLDepthStencilState(bool useDepth, bool useStencil) { + + if (useDepth && useStencil) { + if ( !_cmdClearDepthAndStencilDepthStencilState ) { + _cmdClearDepthAndStencilDepthStencilState = _device->getCommandResourceFactory()->newMTLDepthStencilState(useDepth, useStencil); // retained + } + return _cmdClearDepthAndStencilDepthStencilState; + } + + if (useDepth) { + if ( !_cmdClearDepthOnlyDepthStencilState ) { + _cmdClearDepthOnlyDepthStencilState = _device->getCommandResourceFactory()->newMTLDepthStencilState(useDepth, useStencil); // retained + } + return _cmdClearDepthOnlyDepthStencilState; + } + + if (useStencil) { + if ( !_cmdClearStencilOnlyDepthStencilState ) { + _cmdClearStencilOnlyDepthStencilState = _device->getCommandResourceFactory()->newMTLDepthStencilState(useDepth, useStencil); // retained + } + return _cmdClearStencilOnlyDepthStencilState; + } + + if ( !_cmdClearDefaultDepthStencilState ) { + _cmdClearDefaultDepthStencilState = _device->getCommandResourceFactory()->newMTLDepthStencilState(useDepth, useStencil); // retained + } + return _cmdClearDefaultDepthStencilState; +} + +const MVKMTLBufferAllocation* MVKCommandEncodingPool::acquireMTLBufferAllocation(NSUInteger length) { + return _mtlBufferAllocator.acquireMTLBufferRegion(length); +} + + +id MVKCommandEncodingPool::getMTLDepthStencilState(MVKMTLDepthStencilDescriptorData& dsData) { + id dss = _mtlDepthStencilStates[dsData]; + if ( !dss ) { + dss = _device->getCommandResourceFactory()->newMTLDepthStencilState(dsData); // retained + _mtlDepthStencilStates[dsData] = dss; + } + return dss; +} + +MVKImage* MVKCommandEncodingPool::getTransferMVKImage(MVKImageDescriptorData& imgData) { + MVKImage* mvkImg = _transferImages[imgData]; + if ( !mvkImg ) { + mvkImg = _device->getCommandResourceFactory()->newMVKImage(imgData); + mvkImg->bindDeviceMemory(_transferImageMemory, 0); + _transferImages[imgData] = mvkImg; + } + return mvkImg; +} + + +#pragma mark Construction + +MVKCommandEncodingPool::MVKCommandEncodingPool(MVKDevice* device) : MVKBaseDeviceObject(device), + _mtlBufferAllocator(device, device->_pMetalFeatures->maxMTLBufferSize) { + + _cmdBlitImageLinearMTLSamplerState = nil; + _cmdBlitImageNearestMTLSamplerState = nil; + _cmdClearDepthAndStencilDepthStencilState = nil; + _cmdClearDepthOnlyDepthStencilState = nil; + _cmdClearStencilOnlyDepthStencilState = nil; + _cmdClearDefaultDepthStencilState = nil; + + initTextureDeviceMemory(); +} + +// Initializes the empty device memory used to back temporary VkImages. +void MVKCommandEncodingPool::initTextureDeviceMemory() { + VkMemoryAllocateInfo allocInfo = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = NULL, + .allocationSize = 0, + .memoryTypeIndex = _device->getVulkanMemoryTypeIndex(MTLStorageModePrivate), + }; + _transferImageMemory = _device->allocateMemory(&allocInfo, nullptr); +} + +MVKCommandEncodingPool::~MVKCommandEncodingPool() { + if (_transferImageMemory) { delete _transferImageMemory; } + destroyMetalResources(); +} + +/** Ensure all cached Metal components are released. */ +void MVKCommandEncodingPool::destroyMetalResources() { + for (auto& pair : _cmdBlitImageMTLRenderPipelineStates) { [pair.second release]; } + _cmdBlitImageMTLRenderPipelineStates.clear(); + + for (auto& pair : _cmdClearMTLRenderPipelineStates) { [pair.second release]; } + _cmdClearMTLRenderPipelineStates.clear(); + + for (auto& pair : _mtlDepthStencilStates) { [pair.second release]; } + _mtlDepthStencilStates.clear(); + + for (auto& pair : _transferImages) { delete pair.second; } + _transferImages.clear(); + + [_cmdBlitImageLinearMTLSamplerState release]; + _cmdBlitImageLinearMTLSamplerState = nil; + + [_cmdBlitImageNearestMTLSamplerState release]; + _cmdBlitImageNearestMTLSamplerState = nil; + + [_cmdClearDepthAndStencilDepthStencilState release]; + _cmdClearDepthAndStencilDepthStencilState = nil; + + [_cmdClearDepthOnlyDepthStencilState release]; + _cmdClearDepthOnlyDepthStencilState = nil; + + [_cmdClearStencilOnlyDepthStencilState release]; + _cmdClearStencilOnlyDepthStencilState = nil; + + [_cmdClearDefaultDepthStencilState release]; + _cmdClearDefaultDepthStencilState = nil; +} + diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandPipelineStateFactoryShaderSource.h b/MoltenVK/MoltenVK/Commands/MVKCommandPipelineStateFactoryShaderSource.h new file mode 100644 index 00000000..d34f85c2 --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCommandPipelineStateFactoryShaderSource.h @@ -0,0 +1,198 @@ +/* + * MVKCommandPipelineStateFactoryShaderSource.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** This file contains static source code for the MoltenVK command shaders. */ + +static const char* _MVKStaticCmdShaderSource = " \n\ +#include \n\ +using namespace metal; \n\ + \n\ +typedef struct { \n\ + float2 a_position [[attribute(0)]]; \n\ +} AttributesPos; \n\ + \n\ +typedef struct { \n\ + float4 gl_Position [[position]]; \n\ +} VaryingsPos; \n\ + \n\ +typedef struct { \n\ + float2 a_position [[attribute(0)]]; \n\ + float2 a_texCoord [[attribute(1)]]; \n\ +} AttributesPosTex; \n\ + \n\ +typedef struct { \n\ + float4 gl_Position [[position]]; \n\ + float2 v_texCoord; \n\ +} VaryingsPosTex; \n\ + \n\ +vertex VaryingsPosTex vtxCmdBlitImage(AttributesPosTex attributes [[stage_in]]) { \n\ + VaryingsPosTex varyings; \n\ + varyings.gl_Position = float4(attributes.a_position, 0.0, 1.0); \n\ + varyings.v_texCoord = attributes.a_texCoord; \n\ + return varyings; \n\ +} \n\ + \n\ +vertex VaryingsPos vtxCmdBlitImageD(AttributesPosTex attributes [[stage_in]], \n\ + depth2d texture [[texture(0)]], \n\ + sampler sampler [[ sampler(0) ]]) { \n\ + float depth = texture.sample(sampler, attributes.a_texCoord); \n\ + VaryingsPos varyings; \n\ + varyings.gl_Position = float4(attributes.a_position, depth, 1.0); \n\ + return varyings; \n\ +} \n\ + \n\ +fragment float4 fragCmdBlitImageF(VaryingsPosTex varyings [[stage_in]], \n\ + texture2d texture [[texture(0)]], \n\ + sampler sampler [[ sampler(0) ]]) { \n\ + return texture.sample(sampler, varyings.v_texCoord); \n\ +}; \n\ + \n\ +fragment int4 fragCmdBlitImageI(VaryingsPosTex varyings [[stage_in]], \n\ + texture2d texture [[texture(0)]], \n\ + sampler sampler [[ sampler(0) ]]) { \n\ + return texture.sample(sampler, varyings.v_texCoord); \n\ +}; \n\ + \n\ +fragment uint4 fragCmdBlitImageU(VaryingsPosTex varyings [[stage_in]], \n\ + texture2d texture [[texture(0)]], \n\ + sampler sampler [[ sampler(0) ]]) { \n\ + return texture.sample(sampler, varyings.v_texCoord); \n\ +}; \n\ + \n\ +fragment float4 fragCmdBlitImageDF(VaryingsPosTex varyings [[stage_in]], \n\ + depth2d texture [[texture(0)]], \n\ + sampler sampler [[ sampler(0) ]]) { \n\ + return texture.sample(sampler, varyings.v_texCoord); \n\ +}; \n\ + \n\ +fragment int4 fragCmdBlitImageDI(VaryingsPosTex varyings [[stage_in]], \n\ + depth2d texture [[texture(0)]], \n\ + sampler sampler [[ sampler(0) ]]) { \n\ + return int4(texture.sample(sampler, varyings.v_texCoord)); \n\ +}; \n\ + \n\ +fragment uint4 fragCmdBlitImageDU(VaryingsPosTex varyings [[stage_in]], \n\ + depth2d texture [[texture(0)]], \n\ + sampler sampler [[ sampler(0) ]]) { \n\ + return uint4(texture.sample(sampler, varyings.v_texCoord)); \n\ +}; \n\ + \n\ +typedef struct { \n\ + float4 colors[9]; \n\ +} ClearColorsIn; \n\ + \n\ +typedef struct { \n\ + float4 color0 [[color(0)]]; \n\ + float4 color1 [[color(1)]]; \n\ + float4 color2 [[color(2)]]; \n\ + float4 color3 [[color(3)]]; \n\ + float4 color4 [[color(4)]]; \n\ + float4 color5 [[color(5)]]; \n\ + float4 color6 [[color(6)]]; \n\ + float4 color7 [[color(7)]]; \n\ +} ClearColorsOutF; \n\ + \n\ +typedef struct { \n\ + int4 color0 [[color(0)]]; \n\ + int4 color1 [[color(1)]]; \n\ + int4 color2 [[color(2)]]; \n\ + int4 color3 [[color(3)]]; \n\ + int4 color4 [[color(4)]]; \n\ + int4 color5 [[color(5)]]; \n\ + int4 color6 [[color(6)]]; \n\ + int4 color7 [[color(7)]]; \n\ +} ClearColorsOutI; \n\ + \n\ +typedef struct { \n\ + uint4 color0 [[color(0)]]; \n\ + uint4 color1 [[color(1)]]; \n\ + uint4 color2 [[color(2)]]; \n\ + uint4 color3 [[color(3)]]; \n\ + uint4 color4 [[color(4)]]; \n\ + uint4 color5 [[color(5)]]; \n\ + uint4 color6 [[color(6)]]; \n\ + uint4 color7 [[color(7)]]; \n\ +} ClearColorsOutU; \n\ + \n\ +vertex VaryingsPos vtxCmdClearAttachments(AttributesPos attributes [[stage_in]], \n\ + constant ClearColorsIn& ccIn [[buffer(0)]]) { \n\ + VaryingsPos varyings; \n\ + varyings.gl_Position = float4(attributes.a_position.x, -attributes.a_position.y, ccIn.colors[8].r, 1.0); \n\ + return varyings; \n\ +} \n\ + \n\ +fragment ClearColorsOutF fragCmdClearAttachmentsF(VaryingsPos varyings [[stage_in]], \n\ + constant ClearColorsIn& ccIn [[buffer(0)]]) { \n\ + ClearColorsOutF ccOut; \n\ + ccOut.color0 = ccIn.colors[0]; \n\ + ccOut.color1 = ccIn.colors[1]; \n\ + ccOut.color2 = ccIn.colors[2]; \n\ + ccOut.color3 = ccIn.colors[3]; \n\ + ccOut.color4 = ccIn.colors[4]; \n\ + ccOut.color5 = ccIn.colors[5]; \n\ + ccOut.color6 = ccIn.colors[6]; \n\ + ccOut.color7 = ccIn.colors[7]; \n\ + return ccOut; \n\ +}; \n\ + \n\ +fragment float4 fragCmdClearAttachments0F(VaryingsPos varyings [[stage_in]], \n\ + constant ClearColorsIn& ccIn [[buffer(0)]]) { \n\ + return ccIn.colors[0]; \n\ +}; \n\ + \n\ +fragment ClearColorsOutI fragCmdClearAttachmentsI(VaryingsPos varyings [[stage_in]], \n\ + constant ClearColorsIn& ccIn [[buffer(0)]]) { \n\ + ClearColorsOutI ccOut; \n\ + ccOut.color0 = int4(ccIn.colors[0]); \n\ + ccOut.color1 = int4(ccIn.colors[1]); \n\ + ccOut.color2 = int4(ccIn.colors[2]); \n\ + ccOut.color3 = int4(ccIn.colors[3]); \n\ + ccOut.color4 = int4(ccIn.colors[4]); \n\ + ccOut.color5 = int4(ccIn.colors[5]); \n\ + ccOut.color6 = int4(ccIn.colors[6]); \n\ + ccOut.color7 = int4(ccIn.colors[7]); \n\ + return ccOut; \n\ +}; \n\ + \n\ +fragment int4 fragCmdClearAttachments0I(VaryingsPos varyings [[stage_in]], \n\ + constant ClearColorsIn& ccIn [[buffer(0)]]) { \n\ + return int4(ccIn.colors[0]); \n\ +}; \n\ + \n\ +fragment ClearColorsOutU fragCmdClearAttachmentsU(VaryingsPos varyings [[stage_in]], \n\ + constant ClearColorsIn& ccIn [[buffer(0)]]) { \n\ + ClearColorsOutU ccOut; \n\ + ccOut.color0 = uint4(ccIn.colors[0]); \n\ + ccOut.color1 = uint4(ccIn.colors[1]); \n\ + ccOut.color2 = uint4(ccIn.colors[2]); \n\ + ccOut.color3 = uint4(ccIn.colors[3]); \n\ + ccOut.color4 = uint4(ccIn.colors[4]); \n\ + ccOut.color5 = uint4(ccIn.colors[5]); \n\ + ccOut.color6 = uint4(ccIn.colors[6]); \n\ + ccOut.color7 = uint4(ccIn.colors[7]); \n\ + return ccOut; \n\ +}; \n\ + \n\ +fragment uint4 fragCmdClearAttachments0U(VaryingsPos varyings [[stage_in]], \n\ + constant ClearColorsIn& ccIn [[buffer(0)]]) { \n\ + return uint4(ccIn.colors[0]); \n\ +}; \n\ +"; + + diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandPool.h b/MoltenVK/MoltenVK/Commands/MVKCommandPool.h new file mode 100644 index 00000000..aba4fb0f --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCommandPool.h @@ -0,0 +1,166 @@ +/* + * MVKCommandPool.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKDevice.h" +#include "MVKCommandResourceFactory.h" +#include "MVKCommand.h" +#include "MVKCmdPipeline.h" +#include "MVKCmdRenderPass.h" +#include "MVKCmdDispatch.h" +#include "MVKCmdDraw.h" +#include "MVKCmdTransfer.h" +#include "MVKCmdQueries.h" +#include "MVKMTLBufferAllocation.h" +#include +#include +#include +#include + +#import + + +#pragma mark - +#pragma mark MVKCommandPool + +/** + * Represents a Vulkan command pool. + * + * Access to a command pool in Vulkan is externally synchronized. + * As such, unless indicated otherwise, access to the content within this command pool + * is generally NOT thread-safe. + * + * Except where noted otherwise on specific member functions, all access to the content + * of this pool should be done during the setContent() function of each MVKCommand, and NOT + * during the execution of the command via the MVKCommand::encode() member function. + */ +class MVKCommandPool : public MVKBaseDeviceObject { + +public: + +#pragma mark Command type pools + + MVKCommandTypePool _cmdPipelineBarrierPool; + + MVKCommandTypePool _cmdBindPipelinePool; + + MVKCommandTypePool _cmdBeginRenderPassPool; + + MVKCommandTypePool _cmdNextSubpassPool; + + MVKCommandTypePool _cmdEndRenderPassPool; + + MVKCommandTypePool _cmdExecuteCommandsPool; + + MVKCommandTypePool _cmdBindDescriptorSetsPool; + + MVKCommandTypePool _cmdSetViewportPool; + + MVKCommandTypePool _cmdSetScissorPool; + + MVKCommandTypePool _cmdSetLineWidthPool; + + MVKCommandTypePool _cmdSetDepthBiasPool; + + MVKCommandTypePool _cmdSetBlendConstantsPool; + + MVKCommandTypePool _cmdSetDepthBoundsPool; + + MVKCommandTypePool _cmdSetStencilCompareMaskPool; + + MVKCommandTypePool _cmdSetStencilWriteMaskPool; + + MVKCommandTypePool _cmdSetStencilReferencePool; + + MVKCommandTypePool _cmdBindVertexBuffersPool; + + MVKCommandTypePool _cmdBindIndexBufferPool; + + MVKCommandTypePool _cmdDrawPool; + + MVKCommandTypePool _cmdDrawIndexedPool; + + MVKCommandTypePool _cmdDrawIndirectPool; + + MVKCommandTypePool _cmdDrawIndexedIndirectPool; + + MVKCommandTypePool _cmdCopyImagePool; + + MVKCommandTypePool _cmdBlitImagePool; + + MVKCommandTypePool _cmdResolveImagePool; + + MVKCommandTypePool _cmdFillBufferPool; + + MVKCommandTypePool _cmdUpdateBufferPool; + + MVKCommandTypePool _cmdCopyBufferPool; + + MVKCommandTypePool _cmdBufferImageCopyPool; + + MVKCommandTypePool _cmdClearAttachmentsPool; + + MVKCommandTypePool _cmdClearImagePool; + + MVKCommandTypePool _cmdBeginQueryPool; + + MVKCommandTypePool _cmdEndQueryPool; + + MVKCommandTypePool _cmdWriteTimestampPool; + + MVKCommandTypePool _cmdResetQueryPoolPool; + + MVKCommandTypePool _cmdCopyQueryPoolResultsPool; + + MVKCommandTypePool _cmdPushConstantsPool; + + MVKCommandTypePool _cmdDispatchPool; + + MVKCommandTypePool _cmdDispatchIndirectPool; + + +#pragma mark Command resources + + /** Allocates command buffers from this pool. */ + VkResult allocateCommandBuffers(const VkCommandBufferAllocateInfo* pAllocateInfo, + VkCommandBuffer* pCmdBuffer); + + /** Frees the specified command buffers from this pool. */ + void freeCommandBuffers(uint32_t commandBufferCount, + const VkCommandBuffer* pCommandBuffers); + + +#pragma mark Construction + + /** Resets the command pool. */ + VkResult reset( VkCommandPoolResetFlags flags); + + MVKCommandPool(MVKDevice* device, const VkCommandPoolCreateInfo* pCreateInfo); + + ~MVKCommandPool() override; + +private: + friend class MVKCommandBuffer; + + void addCommandBuffer(MVKCommandBuffer* cmdBuffer); + void removeCommandBuffer(MVKCommandBuffer* cmdBuffer); + + std::unordered_set _commandBuffers; +}; + diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm b/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm new file mode 100644 index 00000000..217bfa89 --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm @@ -0,0 +1,123 @@ +/* + * MVKCommandPool.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKCommandPool.h" +#include "MVKCommandBuffer.h" +#include "MVKImage.h" +#include "MVKDeviceMemory.h" +#include "MVKFoundation.h" +#include "mvk_datatypes.h" +#include "MVKLogging.h" + +using namespace std; + +#pragma mark - +#pragma mark MVKCommandPool + + +VkResult MVKCommandPool::reset(VkCommandPoolResetFlags flags) { + + // Reset all of the command buffers + for (auto& cb : _commandBuffers) { + cb->reset(VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); + } + + return VK_SUCCESS; +} + + +#pragma mark Command Buffers + +VkResult MVKCommandPool::allocateCommandBuffers(const VkCommandBufferAllocateInfo* pAllocateInfo, + VkCommandBuffer* pCmdBuffer) { + VkResult rslt = VK_SUCCESS; + uint32_t cbCnt = pAllocateInfo->commandBufferCount; + for (uint32_t cbIdx = 0; cbIdx < cbCnt; cbIdx++) { + MVKCommandBuffer* mvkCmdBuff = new MVKCommandBuffer(_device, pAllocateInfo); + pCmdBuffer[cbIdx] = (VkCommandBuffer)mvkCmdBuff; + if (rslt == VK_SUCCESS) { rslt = mvkCmdBuff->getConfigurationResult(); } + } + return rslt; +} + +void MVKCommandPool::freeCommandBuffers(uint32_t commandBufferCount, + const VkCommandBuffer* pCommandBuffers) { + for (uint32_t cbIdx = 0; cbIdx < commandBufferCount; cbIdx++) { + MVKCommandBuffer* mvkCmdBuff = (MVKCommandBuffer*)pCommandBuffers[cbIdx]; + delete mvkCmdBuff; + } +} + +void MVKCommandPool::addCommandBuffer(MVKCommandBuffer* cmdBuffer) { + _commandBuffers.insert(cmdBuffer); +} + +void MVKCommandPool::removeCommandBuffer(MVKCommandBuffer* cmdBuffer) { + _commandBuffers.erase(cmdBuffer); +} + + +#pragma mark Construction + +MVKCommandPool::MVKCommandPool(MVKDevice* device, + const VkCommandPoolCreateInfo* pCreateInfo) : MVKBaseDeviceObject(device), + _cmdPipelineBarrierPool(this, true), + _cmdBindPipelinePool(this, true), + _cmdBeginRenderPassPool(this, true), + _cmdNextSubpassPool(this, true), + _cmdExecuteCommandsPool(this, true), + _cmdEndRenderPassPool(this, true), + _cmdBindDescriptorSetsPool(this, true), + _cmdSetViewportPool(this, true), + _cmdSetScissorPool(this, true), + _cmdSetLineWidthPool(this, true), + _cmdSetDepthBiasPool(this, true), + _cmdSetBlendConstantsPool(this, true), + _cmdSetDepthBoundsPool(this, true), + _cmdSetStencilCompareMaskPool(this, true), + _cmdSetStencilWriteMaskPool(this, true), + _cmdSetStencilReferencePool(this, true), + _cmdBindVertexBuffersPool(this, true), + _cmdBindIndexBufferPool(this, true), + _cmdDrawPool(this, true), + _cmdDrawIndexedPool(this, true), + _cmdDrawIndirectPool(this, true), + _cmdDrawIndexedIndirectPool(this, true), + _cmdCopyImagePool(this, true), + _cmdBlitImagePool(this, true), + _cmdResolveImagePool(this, true), + _cmdFillBufferPool(this, true), + _cmdUpdateBufferPool(this, true), + _cmdCopyBufferPool(this, true), + _cmdBufferImageCopyPool(this, true), + _cmdClearAttachmentsPool(this, true), + _cmdClearImagePool(this, true), + _cmdBeginQueryPool(this, true), + _cmdEndQueryPool(this, true), + _cmdWriteTimestampPool(this, true), + _cmdResetQueryPoolPool(this, true), + _cmdCopyQueryPoolResultsPool(this, true), + _cmdPushConstantsPool(this, true), + _cmdDispatchPool(this, true), + _cmdDispatchIndirectPool(this, true) +{} + +// TODO: Destroying a command pool implicitly destroys all command buffers and commands created from it. + +MVKCommandPool::~MVKCommandPool() {} + diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h new file mode 100644 index 00000000..0a351c4b --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h @@ -0,0 +1,292 @@ +/* + * MVKCommandResourceFactory.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKDevice.h" +#include "MVKFoundation.h" +#include + +#import + + +#pragma mark - +#pragma mark MVKRPSKeyClearAtt + +#define kMVKAttachmentFormatCount 9 +#define kMVKAttachmentFormatDepthStencilIndex (kMVKAttachmentFormatCount - 1) + +/** + * Key to use for looking up cached MTLRenderPipelineState instances. + * Holds the formats for each color attachment plus one depth/stencil attachment. + * + * This structure can be used as a key in a std::map and std::unordered_map. + */ +typedef struct MVKRPSKeyClearAtt_t { + uint16_t attachmentMTLPixelFormats[kMVKAttachmentFormatCount]; + uint32_t enabledFlags; + const static uint32_t bitFlag = 1; + + void enable(uint32_t attIdx) { mvkEnableFlag(enabledFlags, bitFlag << attIdx); } + + bool isEnabled(uint32_t attIdx) { return mvkIsAnyFlagEnabled(enabledFlags, bitFlag << attIdx); } + + bool isEnabledOnly(uint32_t attIdx) { + // Ignore depth stencil bit + uint32_t colorFlags = enabledFlags; + mvkDisableFlag(colorFlags, bitFlag << kMVKAttachmentFormatDepthStencilIndex); + return mvkAreOnlyFlagsEnabled(colorFlags, bitFlag << attIdx); + } + + bool operator==(const MVKRPSKeyClearAtt_t& rhs) const { + return ((enabledFlags == rhs.enabledFlags) && + (memcmp(attachmentMTLPixelFormats, rhs.attachmentMTLPixelFormats, sizeof(attachmentMTLPixelFormats)) == 0)); + } + + void reset() { memset(this, 0, sizeof(*this)); } + + MVKRPSKeyClearAtt_t() { reset(); } +} MVKRPSKeyClearAtt; + +/** + * Hash structure implementation for MVKRPSKeyClearAtt in std namespace, + * so MVKRPSKeyClearAtt can be used as a key in a std::map and std::unordered_map. + */ +namespace std { + template <> + struct hash { + std::size_t operator()(const MVKRPSKeyClearAtt& k) const { + std::size_t hash = mvkHash(&k.enabledFlags, 1); + return mvkHash(k.attachmentMTLPixelFormats, kMVKAttachmentFormatCount, hash); + } + }; +} + + +#pragma mark - +#pragma mark MVKMTLDepthStencilDescriptorData + +/** + * A structure to hold configuration data for creating an MTLStencilDescriptor instance. + * + * The order of elements is designed to "fail-fast", with the more commonly changing elements + * situated near the beginning of the structure so that a memory comparison will detect any + * change as early as possible. + */ +typedef struct MVKMTLStencilDescriptorData_t { + bool enabled; /**< Indicates whether stencil testing for this face is enabled. */ + uint8_t stencilCompareFunction; /**< The stencil compare function (interpreted as MTLCompareFunction). */ + uint8_t stencilFailureOperation; /**< The operation to take when the stencil test fails (interpreted as MTLStencilOperation). */ + uint8_t depthFailureOperation; /**< The operation to take when the stencil test passes, but the depth test fails (interpreted as MTLStencilOperation). */ + uint8_t depthStencilPassOperation; /**< The operation to take when both the stencil and depth tests pass (interpreted as MTLStencilOperation). */ + uint32_t readMask; /**< The bit-mask to apply when comparing the stencil buffer value to the reference value. */ + uint32_t writeMask; /**< The bit-mask to apply when writing values to the stencil buffer. */ + + MVKMTLStencilDescriptorData_t() { + + // Start with all zeros to ensure memory comparisons will work, + // even if the structure contains alignment gaps. + memset(this, 0, sizeof(*this)); + + enabled = false, + stencilCompareFunction = MTLCompareFunctionAlways; + stencilFailureOperation = MTLStencilOperationKeep; + depthFailureOperation = MTLStencilOperationKeep; + depthStencilPassOperation = MTLStencilOperationKeep; + readMask = static_cast(~0); + writeMask = static_cast(~0); + } + +} MVKMTLStencilDescriptorData; + +/** An instance populated with default values, for use in resetting other instances to default state. */ +const MVKMTLStencilDescriptorData kMVKMTLStencilDescriptorDataDefault; + +/** + * A structure to hold configuration data for creating an MTLDepthStencilDescriptor instance. + * Instances of this structure can be used as a map key. + * + * The order of elements is designed to "fail-fast", with the more commonly changing elements + * situated near the beginning of the structure so that a memory comparison will detect any + * change as early as possible. + */ +typedef struct MVKMTLDepthStencilDescriptorData_t { + std::size_t _hash; + uint8_t depthCompareFunction; /**< The depth compare function (interpreted as MTLCompareFunction). */ + bool depthWriteEnabled; /**< Indicates whether depth writing is enabled. */ + MVKMTLStencilDescriptorData frontFaceStencilData; + MVKMTLStencilDescriptorData backFaceStencilData; + + std::size_t hash() { + if ( !_hash ) { _hash = mvkHash((uint64_t*)this, sizeof(*this) / sizeof(uint64_t)); } + return _hash; + } + void clearHash() { _hash = 0; } + + bool operator==(const MVKMTLDepthStencilDescriptorData_t& rhs) const { + MVKMTLDepthStencilDescriptorData_t* pLHS = (MVKMTLDepthStencilDescriptorData_t*)this; + MVKMTLDepthStencilDescriptorData_t* pRHS = (MVKMTLDepthStencilDescriptorData_t*)&rhs; + + if (pLHS == pRHS) { return true; } + if (!pLHS) { return false; } + if (!pRHS) { return false; } + if (pLHS->hash() != pRHS->hash()) { return false; } + + return (memcmp(pLHS, pRHS, sizeof(*this)) == 0); + } + + MVKMTLDepthStencilDescriptorData_t() { + + // Start with all zeros to ensure memory comparisons will work, + // even if the structure contains alignment gaps. + memset(this, 0, sizeof(*this)); + + _hash = 0; + depthCompareFunction = MTLCompareFunctionAlways; + depthWriteEnabled = false; + + frontFaceStencilData = kMVKMTLStencilDescriptorDataDefault; + backFaceStencilData = kMVKMTLStencilDescriptorDataDefault; + } + +} __attribute__((aligned(sizeof(uint64_t)))) MVKMTLDepthStencilDescriptorData; + +/** An instance populated with default values, for use in resetting other instances to default state. */ +const MVKMTLDepthStencilDescriptorData kMVKMTLDepthStencilDescriptorDataDefault; + +namespace std { + template <> + struct hash { + std::size_t operator()(const MVKMTLDepthStencilDescriptorData_t& k) const { + MVKMTLDepthStencilDescriptorData_t* pK = (MVKMTLDepthStencilDescriptorData_t*)&k; + return pK->hash(); + } + }; +} + + +#pragma mark - +#pragma mark MVKImageDescriptorData + +/** + * Key to use for looking up cached MVKImage instances, and to create a new MVKImage when needed. + * The contents of this structure is a subset of the contents of the VkImageCreateInfo structure. + * + * This structure can be used as a key in a std::map and std::unordered_map. + */ +typedef struct MVKImageDescriptorData_t { + VkImageType imageType; + VkFormat format; + VkExtent3D extent; + uint32_t mipLevels; + uint32_t arrayLayers; + VkSampleCountFlagBits samples; + VkImageUsageFlags usage; + + bool operator==(const MVKImageDescriptorData_t& rhs) const { + return (memcmp(this, &rhs, sizeof(*this)) == 0); + } + + MVKImageDescriptorData_t() { memset(this, 0, sizeof(*this)); } + +} __attribute__((aligned(sizeof(uint64_t)))) MVKImageDescriptorData; + +/** + * Hash structure implementation for MVKImageDescriptorData in std namespace, so + * MVKImageDescriptorData can be used as a key in a std::map and std::unordered_map. + */ +namespace std { + template <> + struct hash { + std::size_t operator()(const MVKImageDescriptorData& k) const { + return mvkHash((uint64_t*)this, sizeof(*this) / sizeof(uint64_t)); + } + }; +} + + +#pragma mark - +#pragma mark MVKCommandResourceFactory + +/** + * This factory class consolidates the manufacturing of various pipeline components + * for commands whose functionality is realized through render or compute pipelines. + */ +class MVKCommandResourceFactory : public MVKBaseDeviceObject { + +public: + +#pragma mark Command resources + + /** + * Returns a new MTLRenderPipelineState dedicated to rendering to a texture + * in the specified pixel format to support certain Vulkan BLIT commands. + */ + id newCmdBlitImageMTLRenderPipelineState(MTLPixelFormat mtlPixFmt); + + /** + * Returns a new MTLSamplerState dedicated to rendering to a texture using the + * specified min/mag filter value to support certain Vulkan BLIT commands. + */ + id newCmdBlitImageMTLSamplerState(MTLSamplerMinMagFilter mtlFilter); + + /** + * Returns a new MTLRenderPipelineState dedicated to rendering to several + * attachments to support clearing regions of those attachments. + */ + id newCmdClearMTLRenderPipelineState(MVKRPSKeyClearAtt& attKey); + + /** + * Returns a new MTLDepthStencilState dedicated to rendering to several + * attachments to support clearing regions of those attachments. + */ + id newMTLDepthStencilState(bool useDepth, bool useStencil); + + /** + * Returns a new MTLDepthStencilState configured from the specified data. + * Returns nil if the specified data indicates depth and stencil testing is disabled. + */ + id newMTLDepthStencilState(MVKMTLDepthStencilDescriptorData& dsData); + + /** Returns an autoreleased MTLStencilDescriptor constructed from the stencil data. */ + MTLStencilDescriptor* mtlMTLStencilDescriptor(MVKMTLStencilDescriptorData& sData); + + /** + * Returns a new MVKImage configured from the specified MTLTexture configuration, + * with content held in Private storage. The object returned can be used as a + * temporary image during image transfers. + */ + MVKImage* newMVKImage(MVKImageDescriptorData& imgData); + + +#pragma mark Construction + + MVKCommandResourceFactory(MVKDevice* device); + + ~MVKCommandResourceFactory() override; + +protected: + void initMTLLibrary(); + std::string getFragFunctionSuffix(MTLPixelFormat mtlPixFmt); + std::string getFragFunctionSuffix(MVKRPSKeyClearAtt& attKey); + id getFunctionNamed(const char* funcName); + id newMTLRenderPipelineState(MTLRenderPipelineDescriptor* plDesc); + + id _mtlLibrary; +}; + diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm new file mode 100644 index 00000000..01dce2aa --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm @@ -0,0 +1,266 @@ +/* + * MVKCommandResourceFactory.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKCommandResourceFactory.h" +#include "MVKCommandPipelineStateFactoryShaderSource.h" +#include "MVKFoundation.h" +#include "mvk_datatypes.h" +#include "MVKLogging.h" + +using namespace std; + + +#pragma mark - +#pragma mark MVKCommandResourceFactory + +id MVKCommandResourceFactory::newCmdBlitImageMTLRenderPipelineState(MTLPixelFormat mtlPixFmt) { + bool isDepthFormat = mvkMTLPixelFormatIsDepthFormat(mtlPixFmt); + string fragFuncSfx = getFragFunctionSuffix(mtlPixFmt); + + MTLRenderPipelineDescriptor* plDesc = [[[MTLRenderPipelineDescriptor alloc] init] autorelease]; + plDesc.label = [NSString stringWithFormat: @"CmdBlitImage%s-%lu", fragFuncSfx.data(), (unsigned long)mtlPixFmt]; + + plDesc.vertexFunction = getFunctionNamed(isDepthFormat ? "vtxCmdBlitImageD" : "vtxCmdBlitImage"); + plDesc.fragmentFunction = getFunctionNamed((string("fragCmdBlitImage") + fragFuncSfx).data()); + + if ( isDepthFormat ) { + plDesc.depthAttachmentPixelFormat = mtlPixFmt; + } else { + MTLRenderPipelineColorAttachmentDescriptor* colorDesc = plDesc.colorAttachments[0]; + colorDesc.pixelFormat = mtlPixFmt; + } + + MTLVertexDescriptor* vtxDesc = plDesc.vertexDescriptor; + + // Vertex attribute descriptors + MTLVertexAttributeDescriptorArray* vaDescArray = vtxDesc.attributes; + MTLVertexAttributeDescriptor* vaDesc; + NSUInteger vtxBuffIdx = _device->getMetalBufferIndexForVertexAttributeBinding(kMVKVertexContentBufferIndex); + NSUInteger vtxStride = 0; + + // Vertex location + vaDesc = vaDescArray[0]; + vaDesc.format = MTLVertexFormatFloat2; + vaDesc.bufferIndex = vtxBuffIdx; + vaDesc.offset = vtxStride; + vtxStride += sizeof(simd::float2); + + // Vertex texture coords + vaDesc = vaDescArray[1]; + vaDesc.format = MTLVertexFormatFloat2; + vaDesc.bufferIndex = vtxBuffIdx; + vaDesc.offset = vtxStride; + vtxStride += sizeof(simd::float2); + + // Vertex attribute buffer. + MTLVertexBufferLayoutDescriptorArray* vbDescArray = vtxDesc.layouts; + MTLVertexBufferLayoutDescriptor* vbDesc = vbDescArray[vtxBuffIdx]; + vbDesc.stepFunction = MTLVertexStepFunctionPerVertex; + vbDesc.stepRate = 1; + vbDesc.stride = vtxStride; + + return newMTLRenderPipelineState(plDesc); +} + +id MVKCommandResourceFactory::newCmdBlitImageMTLSamplerState(MTLSamplerMinMagFilter mtlFilter) { + + MTLSamplerDescriptor* sDesc = [[[MTLSamplerDescriptor alloc] init] autorelease]; + sDesc.rAddressMode = MTLSamplerAddressModeClampToZero; + sDesc.sAddressMode = MTLSamplerAddressModeClampToZero; + sDesc.tAddressMode = MTLSamplerAddressModeClampToZero; + sDesc.mipFilter = MTLSamplerMipFilterNotMipmapped; + sDesc.normalizedCoordinates = YES; + sDesc.minFilter = mtlFilter; + sDesc.magFilter = mtlFilter; + return [getMTLDevice() newSamplerStateWithDescriptor: sDesc]; +} + +id MVKCommandResourceFactory::newCmdClearMTLRenderPipelineState(MVKRPSKeyClearAtt& attKey) { + string fragFuncSfx = getFragFunctionSuffix(attKey); + + MTLRenderPipelineDescriptor* plDesc = [[[MTLRenderPipelineDescriptor alloc] init] autorelease]; + plDesc.label = [NSString stringWithFormat: @"CmdClearAttachments%s", fragFuncSfx.data()]; + plDesc.vertexFunction = getFunctionNamed("vtxCmdClearAttachments"); + plDesc.fragmentFunction = getFunctionNamed((string("fragCmdClearAttachments") + fragFuncSfx).data()); + + for (uint32_t caIdx = 0; caIdx < kMVKAttachmentFormatDepthStencilIndex; caIdx++) { + MTLRenderPipelineColorAttachmentDescriptor* colorDesc = plDesc.colorAttachments[caIdx]; + colorDesc.pixelFormat = (MTLPixelFormat)attKey.attachmentMTLPixelFormats[caIdx]; + colorDesc.writeMask = attKey.isEnabled(caIdx) ? MTLColorWriteMaskAll : MTLColorWriteMaskNone; + } + MTLPixelFormat mtlDSFormat = (MTLPixelFormat)attKey.attachmentMTLPixelFormats[kMVKAttachmentFormatDepthStencilIndex]; + if (mvkMTLPixelFormatIsDepthFormat(mtlDSFormat)) { plDesc.depthAttachmentPixelFormat = mtlDSFormat; } + if (mvkMTLPixelFormatIsStencilFormat(mtlDSFormat)) { plDesc.stencilAttachmentPixelFormat = mtlDSFormat; } + + MTLVertexDescriptor* vtxDesc = plDesc.vertexDescriptor; + + // Vertex attribute descriptors + MTLVertexAttributeDescriptorArray* vaDescArray = vtxDesc.attributes; + MTLVertexAttributeDescriptor* vaDesc; + NSUInteger vtxBuffIdx = _device->getMetalBufferIndexForVertexAttributeBinding(kMVKVertexContentBufferIndex); + NSUInteger vtxStride = 0; + + // Vertex location + vaDesc = vaDescArray[0]; + vaDesc.format = MTLVertexFormatFloat2; + vaDesc.bufferIndex = vtxBuffIdx; + vaDesc.offset = vtxStride; + vtxStride += sizeof(simd::float2); + + // Vertex attribute buffer. + MTLVertexBufferLayoutDescriptorArray* vbDescArray = vtxDesc.layouts; + MTLVertexBufferLayoutDescriptor* vbDesc = vbDescArray[vtxBuffIdx]; + vbDesc.stepFunction = MTLVertexStepFunctionPerVertex; + vbDesc.stepRate = 1; + vbDesc.stride = vtxStride; + + return newMTLRenderPipelineState(plDesc); +} + +id MVKCommandResourceFactory::newMTLDepthStencilState(bool useDepth, bool useStencil) { + + MTLDepthStencilDescriptor* dsDesc = [[[MTLDepthStencilDescriptor alloc] init] autorelease]; + dsDesc.depthCompareFunction = MTLCompareFunctionAlways; + dsDesc.depthWriteEnabled = useDepth; + + if (useStencil) { + MTLStencilDescriptor* sDesc = [[[MTLStencilDescriptor alloc] init] autorelease]; + sDesc.stencilCompareFunction = MTLCompareFunctionAlways; + sDesc.stencilFailureOperation = MTLStencilOperationReplace; + sDesc.depthFailureOperation = MTLStencilOperationReplace; + sDesc.depthStencilPassOperation = MTLStencilOperationReplace; + + dsDesc.frontFaceStencil = sDesc; + dsDesc.backFaceStencil = sDesc; + } else { + dsDesc.frontFaceStencil = nil; + dsDesc.backFaceStencil = nil; + } + + return [getMTLDevice() newDepthStencilStateWithDescriptor: dsDesc]; +} + +id MVKCommandResourceFactory::newMTLDepthStencilState(MVKMTLDepthStencilDescriptorData& dsData) { + MTLDepthStencilDescriptor* dsDesc = [[[MTLDepthStencilDescriptor alloc] init] autorelease]; + dsDesc.depthCompareFunction = (MTLCompareFunction)dsData.depthCompareFunction; + dsDesc.depthWriteEnabled = dsData.depthWriteEnabled; + dsDesc.frontFaceStencil = mtlMTLStencilDescriptor(dsData.frontFaceStencilData); + dsDesc.backFaceStencil = mtlMTLStencilDescriptor(dsData.backFaceStencilData); + + return [getMTLDevice() newDepthStencilStateWithDescriptor: dsDesc]; +} + +MTLStencilDescriptor* MVKCommandResourceFactory::mtlMTLStencilDescriptor(MVKMTLStencilDescriptorData& sData) { + if ( !sData.enabled ) { return nil; } + + MTLStencilDescriptor* sDesc = [[[MTLStencilDescriptor alloc] init] autorelease]; + sDesc.stencilCompareFunction = (MTLCompareFunction)sData.stencilCompareFunction; + sDesc.stencilFailureOperation = (MTLStencilOperation)sData.stencilFailureOperation; + sDesc.depthFailureOperation = (MTLStencilOperation)sData.depthFailureOperation; + sDesc.depthStencilPassOperation = (MTLStencilOperation)sData.depthStencilPassOperation; + sDesc.readMask = sData.readMask; + sDesc.writeMask = sData.writeMask; + return sDesc; +} + +MVKImage* MVKCommandResourceFactory::newMVKImage(MVKImageDescriptorData& imgData) { + const VkImageCreateInfo createInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .imageType = imgData.imageType, + .format = imgData.format, + .extent = imgData.extent, + .mipLevels = imgData.mipLevels, + .arrayLayers = imgData.arrayLayers, + .samples = imgData.samples, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = imgData.usage, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + .initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED + }; + return _device->createImage(&createInfo, nullptr); +} + +string MVKCommandResourceFactory::getFragFunctionSuffix(MTLPixelFormat mtlPixFmt) { + string suffix; + switch (mvkFormatTypeFromMTLPixelFormat(mtlPixFmt)) { + case kMVKFormatDepthStencil: + suffix += "DS"; + case kMVKFormatColorUInt: + suffix += "U"; + case kMVKFormatColorInt: + suffix += "I"; + default: + suffix += "F"; + break; + } + + return suffix; +} + +string MVKCommandResourceFactory::getFragFunctionSuffix(MVKRPSKeyClearAtt& attKey) { + string suffix; + if (attKey.isEnabledOnly(0)) { suffix += "0"; } + suffix += getFragFunctionSuffix((MTLPixelFormat)attKey.attachmentMTLPixelFormats[0]); + return suffix; +} + +id MVKCommandResourceFactory::getFunctionNamed(const char* funcName) { + NSTimeInterval startTime = _device->getPerformanceTimestamp(); + id mtlFunc = [[_mtlLibrary newFunctionWithName: @(funcName)] autorelease]; + _device->addShaderCompilationEventPerformance(_device->_shaderCompilationPerformance.functionRetrieval, startTime); + return mtlFunc; +} + +id MVKCommandResourceFactory::newMTLRenderPipelineState(MTLRenderPipelineDescriptor* plDesc) { + NSTimeInterval startTime = _device->getPerformanceTimestamp(); + NSError* err = nil; + id rps = [getMTLDevice() newRenderPipelineStateWithDescriptor: plDesc error: &err]; // retained + MVKAssert( !err, "Could not create %s pipeline state: %s (code %li) %s", plDesc.label.UTF8String, err.localizedDescription.UTF8String, (long)err.code, err.localizedFailureReason.UTF8String); + _device->addShaderCompilationEventPerformance(_device->_shaderCompilationPerformance.pipelineCompile, startTime); + return rps; +} + +#pragma mark Construction + +MVKCommandResourceFactory::MVKCommandResourceFactory(MVKDevice* device) : MVKBaseDeviceObject(device) { + initMTLLibrary(); +} + +/** Initializes the Metal shaders used for command activity. */ +void MVKCommandResourceFactory::initMTLLibrary() { + NSTimeInterval startTime = _device->getPerformanceTimestamp(); + @autoreleasepool { + MTLCompileOptions* shdrOpts = [[MTLCompileOptions new] autorelease]; + NSError* err = nil; + _mtlLibrary = [getMTLDevice() newLibraryWithSource: @(_MVKStaticCmdShaderSource) + options: shdrOpts + error: &err]; // retained + MVKAssert( !err, "Could not compile command shaders %s (code %li) %s", err.localizedDescription.UTF8String, (long)err.code, err.localizedFailureReason.UTF8String); + } + _device->addShaderCompilationEventPerformance(_device->_shaderCompilationPerformance.mslCompile, startTime); +} + +MVKCommandResourceFactory::~MVKCommandResourceFactory() { + [_mtlLibrary release]; + _mtlLibrary = nil; +} + diff --git a/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.h b/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.h new file mode 100644 index 00000000..4c1c216c --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.h @@ -0,0 +1,143 @@ +/* + * MVKMTLBufferAllocation.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + + +#include "MVKFoundation.h" +#include "MVKObjectPool.h" +#include "MVKDevice.h" +#include + +class MVKMTLBufferAllocationPool; + + +#pragma mark - +#pragma mark MVKMTLBufferAllocation + +/** Defines a contiguous region of bytes within a MTLBuffer. */ +class MVKMTLBufferAllocation : public MVKBaseObject { + +public: + id _mtlBuffer; + NSUInteger _offset; + NSUInteger _length; + + /** + * Returns a pointer to the begining of this allocation memory, taking into + * consideration this allocation's offset into the underlying MTLBuffer. + */ + inline void* getContents() const { return (void*)((uintptr_t)_mtlBuffer.contents + _offset); } + + /** Returns this object back to the pool that created it. This will reset the value of _next member. */ + void returnToPool(); + + /** Constructs this instance with the specified pool as its origin. */ + MVKMTLBufferAllocation(MVKMTLBufferAllocationPool* pool, + id mtlBuffer, + NSUInteger offset, + NSUInteger length) : _pool(pool), _mtlBuffer(mtlBuffer), _offset(offset), _length(length) {} + + /** + * Instances of this class can participate in a linked list or pool. When so participating, + * this is a reference to the next command in the linked list. This value should only be + * managed and set by the linked list. + */ + MVKMTLBufferAllocation* _next = nullptr; + +protected: + MVKMTLBufferAllocationPool* _pool; + +}; + + +#pragma mark - +#pragma mark MVKMTLBufferAllocationPool + +/** + * A pool of MVKMTLBufferAllocation instances of a single size. All MVKMTLBufferAllocation + * instances will have the same size, as defined when this pool is created. + * + * To return a MVKMTLBufferAllocation retrieved from this pool, back to this pool, + * call the returnToPool() function on the MVKMTLBufferAllocation instance. + */ +class MVKMTLBufferAllocationPool : public MVKObjectPool { + +public: + + /** Returns a new MVKMTLBufferAllocation instance. */ + MVKMTLBufferAllocation* newObject() override; + + /** Configures this instance to dispense MVKMTLBufferAllocation instances of the specified size. */ + MVKMTLBufferAllocationPool(MVKDevice* device, NSUInteger allocationLength); + + ~MVKMTLBufferAllocationPool() override; + +protected: + uint32_t calcMTLBufferAllocationCount(); + void addMTLBuffer(); + + NSUInteger _nextOffset; + NSUInteger _allocationLength; + NSUInteger _mtlBufferLength; + std::vector> _mtlBuffers; + MVKDevice* _device; +}; + + +#pragma mark - +#pragma mark MVKMTLBufferAllocator + +/** + * A pool of MVKMTLBufferAllocation instances of any size. When requesting a MVKMTLBufferAllocation + * from this pool, the caller can request a specific size. The MVKMTLBufferAllocation instance + * returned from such a call will have a size that is the next power-of-two value that is + * at least as big as the requested size. + * + * To return a MVKMTLBufferAllocation retrieved from this pool, back to this pool, + * call the returnToPool() function on the MVKMTLBufferAllocation instance. + */ +class MVKMTLBufferAllocator : public MVKBaseDeviceObject { + +public: + + /** + * Returns a MVKMTLBufferAllocation instance with a size that is the next + * power-of-two value that is at least as big as the requested size. + * + * To return the MVKMTLBufferAllocation back to the pool, call + * the returnToPool() function on the returned instance. + */ + const MVKMTLBufferAllocation* acquireMTLBufferRegion(NSUInteger length); + + /** + * Configures this instance to dispense MVKMTLBufferAllocation up to the specified + * maximum size. Because MVKMTLBufferRegions are created with a power-of-two size, + * the largest size of a MVKMTLBufferAllocation dispensed by this instance will be the + * next power-of-two value that is at least as big as the specified maximum size. + */ + MVKMTLBufferAllocator(MVKDevice* device, NSUInteger maxRegionLength); + + ~MVKMTLBufferAllocator() override; + +protected: + std::vector _regionPools; + NSUInteger _maxAllocationLength; + +}; + diff --git a/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.mm b/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.mm new file mode 100644 index 00000000..e8d09b4a --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.mm @@ -0,0 +1,104 @@ +/* + * MVKMTLBufferAllocation.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKMTLBufferAllocation.h" +#include "MVKLogging.h" + + +#pragma mark - +#pragma mark MVKMTLBufferAllocation + +void MVKMTLBufferAllocation::returnToPool() { _pool->returnObjectSafely(this); } + + +#pragma mark - +#pragma mark MVKMTLBufferAllocationPool + +MVKMTLBufferAllocation* MVKMTLBufferAllocationPool::newObject() { + // If we're at the end of the current MTLBuffer, add a new one. + if (_nextOffset >= _mtlBufferLength) { addMTLBuffer(); } + + // Extract and return the next allocation from the current buffer, + // which is always the last one in the array, and advance the offset + // of future allocation to beyond this allocation. + NSUInteger offset = _nextOffset; + _nextOffset += _allocationLength; + return new MVKMTLBufferAllocation(this, _mtlBuffers.back(), offset, _allocationLength); +} + +// Adds a new MTLBuffer to the buffer pool and resets the next offset to the start of it +void MVKMTLBufferAllocationPool::addMTLBuffer() { + MTLResourceOptions mbOpts = MTLResourceStorageModeShared | MTLResourceCPUCacheModeDefaultCache; + _mtlBuffers.push_back([_device->getMTLDevice() newBufferWithLength: _mtlBufferLength options: mbOpts]); + _nextOffset = 0; +} + + +MVKMTLBufferAllocationPool::MVKMTLBufferAllocationPool(MVKDevice* device, NSUInteger allocationLength) + : MVKObjectPool(true) { + _device = device; + _allocationLength = allocationLength; + _mtlBufferLength = _allocationLength * calcMTLBufferAllocationCount(); + _nextOffset = _mtlBufferLength; // Force a MTLBuffer to be added on first access +} + +// Returns the number of regions to allocate per MTLBuffer, as determined from the allocation size. +uint32_t MVKMTLBufferAllocationPool::calcMTLBufferAllocationCount() { + if (_allocationLength <= 256 ) { return 256; } + if (_allocationLength <= (1 * KIBI) ) { return 128; } + if (_allocationLength <= (4 * KIBI) ) { return 64; } + if (_allocationLength <= (256 * KIBI) ) { return (512 * KIBI) / _allocationLength; } + + return 1; +} + +MVKMTLBufferAllocationPool::~MVKMTLBufferAllocationPool() { + mvkReleaseContainerContents(_mtlBuffers); +} + + +#pragma mark - +#pragma mark MVKMTLBufferAllocator + +const MVKMTLBufferAllocation* MVKMTLBufferAllocator::acquireMTLBufferRegion(NSUInteger length) { + MVKAssert(length <= _maxAllocationLength, "This MVKMTLBufferAllocator has been configured to dispense MVKMTLBufferRegions no larger than %llu bytes.", _maxAllocationLength); + + // Convert max length to the next power-of-two exponent to use as a lookup + uint32_t p2Exp = mvkPowerOfTwoExponent(length); + return _regionPools[p2Exp]->acquireObjectSafely(); +} + +MVKMTLBufferAllocator::MVKMTLBufferAllocator(MVKDevice* device, NSUInteger maxAllocationLength) : MVKBaseDeviceObject(device) { + _maxAllocationLength = maxAllocationLength; + + // Convert max length to the next power-of-two exponent + uint32_t maxP2Exp = mvkPowerOfTwoExponent(_maxAllocationLength); + + // Populate the array of region pools to cover the maximum region size + _regionPools.reserve(maxP2Exp + 1); + NSUInteger allocLen = 1; + for (uint32_t p2Exp = 0; p2Exp <= maxP2Exp; p2Exp++) { + _regionPools.push_back(new MVKMTLBufferAllocationPool(device, allocLen)); + allocLen <<= 1; + } +} + +MVKMTLBufferAllocator::~MVKMTLBufferAllocator() { + mvkDestroyContainerContents(_regionPools); +} + diff --git a/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h b/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h new file mode 100644 index 00000000..f044a359 --- /dev/null +++ b/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h @@ -0,0 +1,51 @@ +/* + * MVKMTLResourceBindings.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#import + +/** Describes a MTLTexture resource binding. */ +typedef struct { + union { id mtlTexture = nil; id mtlResource; }; // aliases + uint32_t index = 0; + bool isDirty = true; +} MVKMTLTextureBinding; + +/** Describes a MTLSamplerState resource binding. */ +typedef struct { + union { id mtlSamplerState = nil; id mtlResource; }; // aliases + uint32_t index = 0; + bool isDirty = true; +} MVKMTLSamplerStateBinding; + +/** Describes a MTLBuffer resource binding. */ +typedef struct { + union { id mtlBuffer = nil; id mtlResource; }; // aliases + NSUInteger offset = 0; + uint32_t index = 0; + bool isDirty = true; +} MVKMTLBufferBinding; + +/** Describes a MTLBuffer resource binding as used for an index buffer. */ +typedef struct { + union { id mtlBuffer = nil; id mtlResource; }; // aliases + NSUInteger offset = 0; + MTLIndexType mtlIndexType; + bool isDirty = true; +} MVKIndexMTLBufferBinding; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h new file mode 100644 index 00000000..8edde975 --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h @@ -0,0 +1,125 @@ +/* + * MVKBuffer.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKResource.h" +#include "MVKCommandBuffer.h" + +class MVKCommandEncoder; + + +#pragma mark MVKBuffer + +/** Represents a Vulkan buffer. */ +class MVKBuffer : public MVKResource { + +public: + +#pragma mark Resource memory + + /** Returns the memory requirements of this resource by populating the specified structure. */ + VkResult getMemoryRequirements(VkMemoryRequirements* pMemoryRequirements) override; + + /** Applies the specified global memory barrier. */ + void applyMemoryBarrier(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkMemoryBarrier* pMemoryBarrier, + MVKCommandEncoder* cmdEncoder, + MVKCommandUse cmdUse) override; + + /** Applies the specified buffer memory barrier. */ + void applyBufferMemoryBarrier(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkBufferMemoryBarrier* pBufferMemoryBarrier, + MVKCommandEncoder* cmdEncoder, + MVKCommandUse cmdUse); + + +#pragma mark Metal + + /** Returns the Metal buffer underlying this memory allocation. */ + id getMTLBuffer(); + + /** Returns the offset at which the contents of this instance starts within the underlying Metal buffer. */ + NSUInteger getMTLBufferOffset(); + + +#pragma mark Construction + + MVKBuffer(MVKDevice* device, const VkBufferCreateInfo* pCreateInfo); + + ~MVKBuffer() override; + +protected: + using MVKResource::needsHostReadSync; + + void* map(VkDeviceSize offset, VkDeviceSize size) override; + VkResult flushToDevice(VkDeviceSize offset, VkDeviceSize size) override; + VkResult pullFromDevice(VkDeviceSize offset, VkDeviceSize size) override; + VkResult copyMTLBufferContent(VkDeviceSize offset, VkDeviceSize size, bool intoMTLBuffer); + NSRange mtlBufferRange(VkDeviceSize offset, VkDeviceSize size); + bool needsHostReadSync(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkBufferMemoryBarrier* pBufferMemoryBarrier); + + id _mtlBuffer; +}; + + +#pragma mark MVKBufferView + +/** Represents a Vulkan buffer view. */ +class MVKBufferView : public MVKBaseDeviceObject { + +public: + + +#pragma mark Resource memory + + /** Returns the number of bytes used by this buffer view. */ + inline VkDeviceSize getByteCount() { return _byteCount; }; + + +#pragma mark Metal + + /** Returns the Metal buffer underlying this memory allocation. */ + inline id getMTLBuffer() { return _buffer->getMTLBuffer(); } + + /** Returns the offset at which the contents of this instance starts within the underlying Metal buffer. */ + inline NSUInteger getMTLBufferOffset() { return _mtlBufferOffset; } + + /** Returns a Metal texture that overlays this buffer view. */ + id getMTLTexture(); + + +#pragma mark Construction + + MVKBufferView(MVKDevice* device, const VkBufferViewCreateInfo* pCreateInfo); + + ~MVKBufferView() override; + +protected: + MVKBuffer* _buffer; + NSUInteger _mtlBufferOffset; + MTLPixelFormat _mtlPixelFormat; + id _mtlTexture; + VkDeviceSize _byteCount; + VkExtent2D _textureSize; +}; + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm new file mode 100644 index 00000000..5503cabe --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm @@ -0,0 +1,241 @@ +/* + * MVKBuffer.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKBuffer.h" +#include "MVKCommandBuffer.h" +#include "MVKFoundation.h" +#include "mvk_datatypes.h" + +using namespace std; + + +#pragma mark - +#pragma mark MVKBuffer + +#pragma mark Resource memory + +VkResult MVKBuffer::getMemoryRequirements(VkMemoryRequirements* pMemoryRequirements) { + pMemoryRequirements->size = getByteCount(); + pMemoryRequirements->alignment = getByteAlignment(); + pMemoryRequirements->memoryTypeBits = _device->getPhysicalDevice()->getAllMemoryTypes(); + return VK_SUCCESS; +} + +void MVKBuffer::applyMemoryBarrier(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkMemoryBarrier* pMemoryBarrier, + MVKCommandEncoder* cmdEncoder, + MVKCommandUse cmdUse) { +#if MVK_MACOS + if ( needsHostReadSync(srcStageMask, dstStageMask, pMemoryBarrier) ) { + [cmdEncoder->getMTLBlitEncoder(cmdUse) synchronizeResource: getMTLBuffer()]; + } +#endif +} + +void MVKBuffer::applyBufferMemoryBarrier(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkBufferMemoryBarrier* pBufferMemoryBarrier, + MVKCommandEncoder* cmdEncoder, + MVKCommandUse cmdUse) { +#if MVK_MACOS + if ( needsHostReadSync(srcStageMask, dstStageMask, pBufferMemoryBarrier) ) { + [cmdEncoder->getMTLBlitEncoder(cmdUse) synchronizeResource: getMTLBuffer()]; + } +#endif +} + +/** + * Returns whether the specified buffer memory barrier requires a sync between this + * buffer and host memory for the purpose of the host reading texture memory. + */ +bool MVKBuffer::needsHostReadSync(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkBufferMemoryBarrier* pBufferMemoryBarrier) { +#if MVK_IOS + return false; +#endif +#if MVK_MACOS + return (mvkIsAnyFlagEnabled(dstStageMask, (VK_PIPELINE_STAGE_HOST_BIT)) && + mvkIsAnyFlagEnabled(pBufferMemoryBarrier->dstAccessMask, (VK_ACCESS_HOST_READ_BIT)) && + _deviceMemory->isMemoryHostAccessible() && !_deviceMemory->isMemoryHostCoherent()); +#endif +} + +/** Called when the bound device memory is updated. Flushes any associated resource memory. */ +VkResult MVKBuffer::flushToDevice(VkDeviceSize offset, VkDeviceSize size) { + VkResult rslt = copyMTLBufferContent(offset, size, true); + +#if MVK_MACOS + if (_deviceMemory->getMTLStorageMode() == MTLStorageModeManaged) { + [getMTLBuffer() didModifyRange: mtlBufferRange(offset, size)]; + } +#endif + + return rslt; +} + +// Called when the bound device memory is invalidated. Pulls any associated resource memory from the device. +VkResult MVKBuffer::pullFromDevice(VkDeviceSize offset, VkDeviceSize size) { + VkResult rslt = copyMTLBufferContent(offset, size, false); + + // If we are pulling to populate a newly created device memory MTLBuffer, + // from a previously created local MTLBuffer, remove the local MTLBuffer now. + if (_mtlBuffer && _deviceMemory->getMTLBuffer()) { + [_mtlBuffer release]; + _mtlBuffer = nil; + } + + return rslt; +} + +void* MVKBuffer::map(VkDeviceSize offset, VkDeviceSize size) { +// MVKLogDebug("Comparing map to buffer %p with memory offset %d and size %d.", this, _deviceMemoryOffset, _byteCount); +// if (doesContain(offset, size)) { MVKLogDebug("Mapping %d bytes to single buffer %p.", size, this); } + + return (doesContain(offset, size) + ? (void*)((uintptr_t)getMTLBuffer().contents + mtlBufferRange(offset, size).location) + : VK_NULL_HANDLE); +} + +// Copies host content into or out of the MTLBuffer. +VkResult MVKBuffer::copyMTLBufferContent(VkDeviceSize offset, VkDeviceSize size, bool intoMTLBuffer) { + + // Only copy if there is separate host memory and this buffer overlaps the host memory range + void* pMemBase = _deviceMemory->getLogicalMappedMemory(); + if (pMemBase && doesOverlap(offset, size)) { + + NSRange copyRange = mtlBufferRange(offset, size); + VkDeviceSize memOffset = max(offset, _deviceMemoryOffset); + +// MVKLogDebug("Copying contents %s buffer %p at buffer offset %d memory offset %d and length %d.", (intoMTLBuffer ? "to" : "from"), this, copyRange.location, memOffset, copyRange.length); + + void* pMemBytes = (void*)((uintptr_t)pMemBase + memOffset); + void* pMTLBuffBytes = (void*)((uintptr_t)getMTLBuffer().contents + copyRange.location); + + // Copy in the direction indicated. + // Don't copy if the source and destination are the same address, which will + // occur if the underlying MTLBuffer comes from the device memory object. + if (pMemBytes != pMTLBuffBytes) { +// MVKLogDebug("Copying buffer contents."); + if (intoMTLBuffer) { + memcpy(pMTLBuffBytes, pMemBytes, copyRange.length); + } else { + memcpy(pMemBytes, pMTLBuffBytes, copyRange.length); + } + } + } + + return VK_SUCCESS; +} + + +#pragma mark Metal + +// If a local MTLBuffer already exists, use it. +// If the device memory has a MTLBuffer, use it. +// Otherwise, create a new MTLBuffer and use it from now on. +id MVKBuffer::getMTLBuffer() { + + if (_mtlBuffer) { return _mtlBuffer; } + + id devMemMTLBuff = _deviceMemory->getMTLBuffer(); + if (devMemMTLBuff) { return devMemMTLBuff; } + + NSUInteger mtlBuffLen = mvkAlignByteOffset(_byteCount, _byteAlignment); + _mtlBuffer = [getMTLDevice() newBufferWithLength: mtlBuffLen + options: _deviceMemory->getMTLResourceOptions()]; // retained +// MVKLogDebug("MVKBuffer %p creating local MTLBuffer of size %d.", this, _mtlBuffer.length); + return _mtlBuffer; +} + +NSUInteger MVKBuffer::getMTLBufferOffset() { return _mtlBuffer ? 0 : _deviceMemoryOffset; } + +// Returns an NSRange that maps the specified host memory range to the MTLBuffer. +NSRange MVKBuffer::mtlBufferRange(VkDeviceSize offset, VkDeviceSize size) { + NSUInteger localRangeLoc = min((offset > _deviceMemoryOffset) ? (offset - _deviceMemoryOffset) : 0, _byteCount); + NSUInteger localRangeLen = min(size, _byteCount - localRangeLoc); + return NSMakeRange(getMTLBufferOffset() + localRangeLoc, localRangeLen); +} + + +#pragma mark Construction + +MVKBuffer::MVKBuffer(MVKDevice* device, const VkBufferCreateInfo* pCreateInfo) : MVKResource(device) { + _byteAlignment = _device->_pMetalFeatures->mtlBufferAlignment; + _byteCount = pCreateInfo->size; + _mtlBuffer = nil; +} + +MVKBuffer::~MVKBuffer() { + [_mtlBuffer release]; + _mtlBuffer = nil; +} + + +#pragma mark - +#pragma mark MVKBufferView + + +#pragma mark Metal + +id MVKBufferView::getMTLTexture() { + if ( !_mtlTexture && _mtlPixelFormat && _device->_pMetalFeatures->texelBuffers) { + VkDeviceSize byteAlign = _device->_pProperties->limits.minTexelBufferOffsetAlignment; + NSUInteger mtlByteCnt = mvkAlignByteOffset(_byteCount, byteAlign); + MTLTextureDescriptor* mtlTexDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: _mtlPixelFormat + width: _textureSize.width + height: _textureSize.height + mipmapped: NO]; + _mtlTexture = [getMTLBuffer() newTextureWithDescriptor: mtlTexDesc + offset: _mtlBufferOffset + bytesPerRow: mtlByteCnt]; + } + return _mtlTexture; +} + + +#pragma mark Construction + +MVKBufferView::MVKBufferView(MVKDevice* device, const VkBufferViewCreateInfo* pCreateInfo) : MVKBaseDeviceObject(device) { + _buffer = (MVKBuffer*)pCreateInfo->buffer; + _mtlBufferOffset = _buffer->getMTLBufferOffset() + pCreateInfo->offset; + _mtlPixelFormat = mtlPixelFormatFromVkFormat(pCreateInfo->format); + _mtlTexture = nil; + VkExtent2D fmtBlockSize = mvkVkFormatBlockTexelSize(pCreateInfo->format); // Pixel size of format + size_t bytesPerBlock = mvkVkFormatBytesPerBlock(pCreateInfo->format); + + // Layout texture as a 1D array of texel blocks (which are texels for non-compressed textures) that covers the bytes + _byteCount = pCreateInfo->range; + if (_byteCount == VK_WHOLE_SIZE) { _byteCount = _buffer->getByteCount() - _mtlBufferOffset; } // Remaining bytes in buffer + size_t blockCount = _byteCount / bytesPerBlock; + _byteCount = blockCount * bytesPerBlock; // Round down + + _textureSize.width = (uint32_t)blockCount * fmtBlockSize.width; + _textureSize.height = fmtBlockSize.height; + + if ( !_device->_pMetalFeatures->texelBuffers ) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "Texel buffers are not supported on this device.")); + } +} + +MVKBufferView::~MVKBufferView() { + [_mtlTexture release]; + _mtlTexture = nil; +} + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h new file mode 100644 index 00000000..21ed0097 --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h @@ -0,0 +1,317 @@ +/* + * MVKDescriptorSet.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKDevice.h" +#include "MVKImage.h" +#include +#include +#include +#include + +using namespace mvk; + + +class MVKDescriptorPool; +class MVKDescriptorBinding; +class MVKDescriptorSet; +class MVKDescriptorSetLayout; +class MVKPipelineLayout; +class MVKCommandEncoder; + + +#pragma mark MVKShaderStageResourceBinding + +/** Indicates the Metal resource indexes used by a single shader stage in a descriptor binding. */ +typedef struct MVKShaderStageResourceBinding { + uint32_t bufferIndex = 0; + uint32_t textureIndex = 0; + uint32_t samplerIndex = 0; + + MVKShaderStageResourceBinding operator+ (const MVKShaderStageResourceBinding& rhs); + MVKShaderStageResourceBinding& operator+= (const MVKShaderStageResourceBinding& rhs); + +} MVKShaderStageResourceBinding; + + +#pragma mark MVKShaderResourceBinding + +/** Indicates the Metal resource indexes used by each shader stage in a descriptor binding. */ +typedef struct MVKShaderResourceBinding { + MVKShaderStageResourceBinding vertexStage; + MVKShaderStageResourceBinding fragmentStage; + MVKShaderStageResourceBinding computeStage; + + MVKShaderResourceBinding operator+ (const MVKShaderResourceBinding& rhs); + MVKShaderResourceBinding& operator+= (const MVKShaderResourceBinding& rhs); + +} MVKShaderResourceBinding; + + +#pragma mark - +#pragma mark MVKDescriptorSetLayoutBinding + +/** Represents a Vulkan descriptor set layout binding. */ +class MVKDescriptorSetLayoutBinding : public MVKConfigurableObject { + +public: + + /** Encodes this binding layout and the specified descriptor set binding on the specified command encoder. */ + void bind(MVKCommandEncoder* cmdEncoder, + MVKDescriptorBinding& descBinding, + MVKShaderResourceBinding& dslMTLRezIdxOffsets, + std::vector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex); + + /** Populates the specified shader converter context, at the specified descriptor set binding. */ + void populateShaderConverterContext(SPIRVToMSLConverterContext& context, + MVKShaderResourceBinding& dslMTLRezIdxOffsets, + uint32_t dslIndex); + + /** Constructs an instance. */ + MVKDescriptorSetLayoutBinding(MVKDescriptorSetLayout* layout, + const VkDescriptorSetLayoutBinding* pBinding); + +protected: + friend class MVKDescriptorBinding; + + VkResult initMetalResourceIndexOffsets(MVKShaderStageResourceBinding* pBindingIndexes, + MVKShaderStageResourceBinding* pDescSetCounts, + const VkDescriptorSetLayoutBinding* pBinding); + + VkDescriptorSetLayoutBinding _info; + std::vector _immutableSamplers; + MVKShaderResourceBinding _mtlResourceIndexOffsets; + bool _applyToVertexStage; + bool _applyToFragmentStage; + bool _applyToComputeStage; +}; + + +#pragma mark - +#pragma mark MVKDescriptorSetLayout + +/** Represents a Vulkan descriptor set layout. */ +class MVKDescriptorSetLayout : public MVKBaseDeviceObject { + +public: + + /** Encodes this descriptor set layout and the specified descriptor set on the specified command encoder. */ + void bindDescriptorSet(MVKCommandEncoder* cmdEncoder, + MVKDescriptorSet* descSet, + MVKShaderResourceBinding& dslMTLRezIdxOffsets, + std::vector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex); + + + /** Populates the specified shader converter context, at the specified DSL index. */ + void populateShaderConverterContext(SPIRVToMSLConverterContext& context, + MVKShaderResourceBinding& dslMTLRezIdxOffsets, + uint32_t dslIndex); + + /** Constructs an instance for the specified device. */ + MVKDescriptorSetLayout(MVKDevice* device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo); + +protected: + + friend class MVKDescriptorSetLayoutBinding; + friend class MVKPipelineLayout; + friend class MVKDescriptorSet; + + std::vector _bindings; + MVKShaderResourceBinding _mtlResourceCounts; +}; + + +#pragma mark - +#pragma mark MVKDescriptorBinding + +/** Represents a Vulkan descriptor binding. */ +class MVKDescriptorBinding : public MVKBaseObject { + +public: + + /** + * Updates the internal element bindings from the specified content. + * + * Depending on the descriptor type of the descriptor set, the binding content is + * extracted from one of the specified pImageInfo, pBufferInfo, or pTexelBufferView + * arrays, and the other arrays are ignored (and may be a null pointer). + * + * The srcStartIndex parameter indicates the index of the initial pDescriptor element + * at which to start reading, and the dstStartIndex parameter indicates the index of + * the initial internal element at which to start writing. + * + * The count parameter indicates how many internal elements should be updated, and + * may be larger than the number of descriptors that can be updated in this instance. + * If count is larger than the number of internal elements remaining after dstStartIndex, + * only the remaining elements will be updated, and the number of pDescriptors that were + * not read will be returned, so that the remaining unread pDescriptors can be read by + * another MVKDescriptorBinding instance within the same descriptor set. If all of the + * remaining pDescriptors are read by this intance, this function returns zero, indicating + * that there is nothing left to be read by another MVKDescriptorBinding instance. + */ + uint32_t writeBindings(uint32_t srcStartIndex, + uint32_t dstStartIndex, + uint32_t count, + const VkDescriptorImageInfo* pImageInfo, + const VkDescriptorBufferInfo* pBufferInfo, + const VkBufferView* pTexelBufferView); + + /** + * Updates the specified content arrays from the internal element bindings. + * + * Depending on the descriptor type of the descriptor set, the binding content is + * placed into one of the specified pImageInfo, pBufferInfo, or pTexelBufferView + * arrays, and the other arrays are ignored (and may be a null pointer). + * + * The srcStartIndex parameter indicates the index of the initial internal element + * at which to start reading, and the dstStartIndex parameter indicates the index of + * the initial pDescriptor element at which to start writing. + * + * The count parameter indicates how many internal elements should be read, and may + * be larger than the number of descriptors that can be read from this instance. + * If count is larger than the number of internal elements remaining after srcStartIndex, + * only the remaining elements will be read, and the number of pDescriptors that were not + * updated will be returned, so that the remaining pDescriptors can be updated by another + * MVKDescriptorBinding instance within the same descriptor set. If all of the remaining + * pDescriptors are updated by this intance, this function returns zero, indicating that + * there is nothing left to be updated by another MVKDescriptorBinding instance. + */ + uint32_t readBindings(uint32_t srcStartIndex, + uint32_t dstStartIndex, + uint32_t count, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView); + + /** Returns whether this instance represents the specified Vulkan binding point. */ + bool hasBinding(uint32_t binding); + + /** Constructs an instance. */ + MVKDescriptorBinding(MVKDescriptorSetLayoutBinding* pBindingLayout); + +protected: + friend class MVKDescriptorSetLayoutBinding; + + void initMTLSamplers(MVKDescriptorSetLayoutBinding* pBindingLayout); + + MVKDescriptorSetLayoutBinding* _pBindingLayout; + std::vector _imageBindings; + std::vector _bufferBindings; + std::vector _texelBufferBindings; + std::vector> _mtlBuffers; + std::vector _mtlBufferOffsets; + std::vector> _mtlTextures; + std::vector> _mtlSamplers; + bool _hasDynamicSamplers; +}; + + +#pragma mark - +#pragma mark MVKDescriptorSet + +/** Represents a Vulkan descriptor set. */ +class MVKDescriptorSet : public MVKBaseDeviceObject { + +public: + + /** Updates the resource bindings in this instance from the specified content. */ + template + void writeDescriptorSets(const DescriptorAction* pDescriptorAction, + const VkDescriptorImageInfo* pImageInfo, + const VkDescriptorBufferInfo* pBufferInfo, + const VkBufferView* pTexelBufferView); + + /** + * Reads the resource bindings defined in the specified content + * from this instance into the specified collection of bindings. + */ + void readDescriptorSets(const VkCopyDescriptorSet* pDescriptorCopies, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView); + + /** Constructs an instance for the specified device. */ + MVKDescriptorSet(MVKDevice* device, MVKDescriptorSetLayout* layout); + +protected: + friend class MVKDescriptorSetLayout; + + MVKDescriptorBinding* getBinding(uint32_t binding); + + std::vector _bindings; +}; + + +#pragma mark - +#pragma mark MVKDescriptorPool + +/** Represents a Vulkan descriptor pool. */ +class MVKDescriptorPool : public MVKBaseDeviceObject { + +public: + + /** Allocates the specified number of descriptor sets. */ + VkResult allocateDescriptorSets(uint32_t count, + const VkDescriptorSetLayout* pSetLayouts, + VkDescriptorSet* pDescriptorSets); + + /** Free's up the specified descriptor set. */ + VkResult freeDescriptorSets(uint32_t count, const VkDescriptorSet* pDescriptorSets); + + /** Destoys all currently allocated descriptor sets. */ + VkResult reset(VkDescriptorPoolResetFlags flags); + + /** Constructs an instance for the specified device. */ + MVKDescriptorPool(MVKDevice* device, const VkDescriptorPoolCreateInfo* pCreateInfo); + + /** Destructor. */ + ~MVKDescriptorPool() override; + +protected: + uint32_t _maxSets; + std::forward_list _allocatedSets; + size_t _allocatedSetCount; + std::mutex _lock; +}; + + +#pragma mark - +#pragma mark Support functions + +/** Updates the resource bindings in the descriptor sets inditified in the specified content. */ +void mvkUpdateDescriptorSets(uint32_t writeCount, + const VkWriteDescriptorSet* pDescriptorWrites, + uint32_t copyCount, + const VkCopyDescriptorSet* pDescriptorCopies); + +/** + * If the shader stage binding has a binding defined for the specified stage, populates + * the context at the descriptor set binding from the shader stage resource binding. + */ +void mvkPopulateShaderConverterContext(SPIRVToMSLConverterContext& context, + MVKShaderStageResourceBinding& ssRB, + spv::ExecutionModel stage, + uint32_t descriptorSetIndex, + uint32_t bindingIndex); + + + + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm new file mode 100644 index 00000000..021e40aa --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm @@ -0,0 +1,710 @@ +/* + * MVKDescriptorSetLayout.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKDescriptorSet.h" +#include "MVKCommandBuffer.h" +#include "MVKBuffer.h" +#include "MVKFoundation.h" +#include + +using namespace std; + + +#pragma mark MVKShaderStageResourceBinding + +MVK_PUBLIC_SYMBOL MVKShaderStageResourceBinding MVKShaderStageResourceBinding::operator+ (const MVKShaderStageResourceBinding& rhs) { + MVKShaderStageResourceBinding rslt; + rslt.bufferIndex = this->bufferIndex + rhs.bufferIndex; + rslt.textureIndex = this->textureIndex + rhs.textureIndex; + rslt.samplerIndex = this->samplerIndex + rhs.samplerIndex; + return rslt; +} + +MVK_PUBLIC_SYMBOL MVKShaderStageResourceBinding& MVKShaderStageResourceBinding::operator+= (const MVKShaderStageResourceBinding& rhs) { + this->bufferIndex += rhs.bufferIndex; + this->textureIndex += rhs.textureIndex; + this->samplerIndex += rhs.samplerIndex; + return *this; +} + + +#pragma mark MVKShaderResourceBinding + +MVK_PUBLIC_SYMBOL MVKShaderResourceBinding MVKShaderResourceBinding::operator+ (const MVKShaderResourceBinding& rhs) { + MVKShaderResourceBinding rslt; + rslt.vertexStage = this->vertexStage + rhs.vertexStage; + rslt.fragmentStage = this->fragmentStage + rhs.fragmentStage; + rslt.computeStage = this->computeStage + rhs.computeStage; + return rslt; +} + +MVK_PUBLIC_SYMBOL MVKShaderResourceBinding& MVKShaderResourceBinding::operator+= (const MVKShaderResourceBinding& rhs) { + this->vertexStage += rhs.vertexStage; + this->fragmentStage += rhs.fragmentStage; + this->computeStage += rhs.computeStage; + return *this; +} + + +#pragma mark - +#pragma mark MVKDescriptorSetLayoutBinding + +void MVKDescriptorSetLayoutBinding::bind(MVKCommandEncoder* cmdEncoder, + MVKDescriptorBinding& descBinding, + MVKShaderResourceBinding& dslMTLRezIdxOffsets, + vector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex) { + MVKMTLBufferBinding bb; + MVKMTLTextureBinding tb; + MVKMTLSamplerStateBinding sb; + NSUInteger bufferDynamicOffset = 0; + + // Establish the resource indices to use, by combining the offsets of the DSL and this DSL binding. + MVKShaderResourceBinding mtlIdxs = _mtlResourceIndexOffsets + dslMTLRezIdxOffsets; + + for (uint32_t rezIdx = 0; rezIdx < _info.descriptorCount; rezIdx++) { + switch (_info.descriptorType) { + + // After determining dynamic part of offset (zero otherwise), fall through to non-dynamic handling + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: + bufferDynamicOffset = dynamicOffsets[*pDynamicOffsetIndex]; + (*pDynamicOffsetIndex)++; // Move on to next dynamic offset (and feedback to caller) + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: { + bb.mtlBuffer = descBinding._mtlBuffers[rezIdx]; + bb.offset = descBinding._mtlBufferOffsets[rezIdx] + bufferDynamicOffset; + if (_applyToVertexStage) { + bb.index = mtlIdxs.vertexStage.bufferIndex + rezIdx; + cmdEncoder->_graphicsResourcesState.bindVertexBuffer(bb); + } + if (_applyToFragmentStage) { + bb.index = mtlIdxs.fragmentStage.bufferIndex + rezIdx; + cmdEncoder->_graphicsResourcesState.bindFragmentBuffer(bb); + } + if (_applyToComputeStage) { + bb.index = mtlIdxs.computeStage.bufferIndex + rezIdx; + cmdEncoder->_computeResourcesState.bindBuffer(bb); + } + break; + } + + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: + case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { + tb.mtlTexture = descBinding._mtlTextures[rezIdx]; + if (_applyToVertexStage) { + tb.index = mtlIdxs.vertexStage.textureIndex + rezIdx; + cmdEncoder->_graphicsResourcesState.bindVertexTexture(tb); + } + if (_applyToFragmentStage) { + tb.index = mtlIdxs.fragmentStage.textureIndex + rezIdx; + cmdEncoder->_graphicsResourcesState.bindFragmentTexture(tb); + } + if (_applyToComputeStage) { + tb.index = mtlIdxs.computeStage.textureIndex + rezIdx; + cmdEncoder->_computeResourcesState.bindTexture(tb); + } + break; + } + + case VK_DESCRIPTOR_TYPE_SAMPLER: { + sb.mtlSamplerState = descBinding._mtlSamplers[rezIdx]; + if (_applyToVertexStage) { + sb.index = mtlIdxs.vertexStage.samplerIndex + rezIdx; + cmdEncoder->_graphicsResourcesState.bindVertexSamplerState(sb); + } + if (_applyToFragmentStage) { + sb.index = mtlIdxs.fragmentStage.samplerIndex + rezIdx; + cmdEncoder->_graphicsResourcesState.bindFragmentSamplerState(sb); + } + if (_applyToComputeStage) { + sb.index = mtlIdxs.computeStage.samplerIndex + rezIdx; + cmdEncoder->_computeResourcesState.bindSamplerState(sb); + } + break; + } + + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { + tb.mtlTexture = descBinding._mtlTextures[rezIdx]; + sb.mtlSamplerState = descBinding._mtlSamplers[rezIdx]; + if (_applyToVertexStage) { + tb.index = mtlIdxs.vertexStage.textureIndex + rezIdx; + cmdEncoder->_graphicsResourcesState.bindVertexTexture(tb); + sb.index = mtlIdxs.vertexStage.samplerIndex + rezIdx; + cmdEncoder->_graphicsResourcesState.bindVertexSamplerState(sb); + } + if (_applyToFragmentStage) { + tb.index = mtlIdxs.fragmentStage.textureIndex + rezIdx; + cmdEncoder->_graphicsResourcesState.bindFragmentTexture(tb); + sb.index = mtlIdxs.fragmentStage.samplerIndex + rezIdx; + cmdEncoder->_graphicsResourcesState.bindFragmentSamplerState(sb); + } + if (_applyToComputeStage) { + tb.index = mtlIdxs.computeStage.textureIndex + rezIdx; + cmdEncoder->_computeResourcesState.bindTexture(tb); + sb.index = mtlIdxs.computeStage.samplerIndex + rezIdx; + cmdEncoder->_computeResourcesState.bindSamplerState(sb); + } + break; + } + + default: + break; + } + } +} + +void MVKDescriptorSetLayoutBinding::populateShaderConverterContext(SPIRVToMSLConverterContext& context, + MVKShaderResourceBinding& dslMTLRezIdxOffsets, + uint32_t dslIndex) { + + // Establish the resource indices to use, by combining the offsets of the DSL and this DSL binding. + MVKShaderResourceBinding mtlIdxs = _mtlResourceIndexOffsets + dslMTLRezIdxOffsets; + + if (_applyToVertexStage) { + mvkPopulateShaderConverterContext(context, + mtlIdxs.vertexStage, + spv::ExecutionModelVertex, + dslIndex, + _info.binding); + } + + if (_applyToFragmentStage) { + mvkPopulateShaderConverterContext(context, + mtlIdxs.fragmentStage, + spv::ExecutionModelFragment, + dslIndex, + _info.binding); + } + + if (_applyToComputeStage) { + mvkPopulateShaderConverterContext(context, + mtlIdxs.computeStage, + spv::ExecutionModelGLCompute, + dslIndex, + _info.binding); + } +} + +MVKDescriptorSetLayoutBinding::MVKDescriptorSetLayoutBinding(MVKDescriptorSetLayout* layout, + const VkDescriptorSetLayoutBinding* pBinding) : MVKConfigurableObject() { + + // MVKLogDebug("Creating MVKDescriptorSetLayoutBinding binding %d.", pBinding->binding); + + // Determine the shader stages used by this binding + _applyToVertexStage = mvkAreFlagsEnabled(pBinding->stageFlags, VK_SHADER_STAGE_VERTEX_BIT); + _applyToFragmentStage = mvkAreFlagsEnabled(pBinding->stageFlags, VK_SHADER_STAGE_FRAGMENT_BIT); + _applyToComputeStage = mvkAreFlagsEnabled(pBinding->stageFlags, VK_SHADER_STAGE_COMPUTE_BIT); + + // If this binding is used by the vertex shader, set the Metal resource index + if (_applyToVertexStage) { + setConfigurationResult(initMetalResourceIndexOffsets(&_mtlResourceIndexOffsets.vertexStage, + &layout->_mtlResourceCounts.vertexStage, pBinding)); + } + + // If this binding is used by the fragment shader, set the Metal resource index + if (_applyToFragmentStage) { + setConfigurationResult(initMetalResourceIndexOffsets(&_mtlResourceIndexOffsets.fragmentStage, + &layout->_mtlResourceCounts.fragmentStage, pBinding)); + } + + // If this binding is used by a compute shader, set the Metal resource index + if (_applyToComputeStage) { + setConfigurationResult(initMetalResourceIndexOffsets(&_mtlResourceIndexOffsets.computeStage, + &layout->_mtlResourceCounts.computeStage, pBinding)); + } + + // If immutable samplers are defined, copy them in + if ( pBinding->pImmutableSamplers && + (pBinding->descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || + pBinding->descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) ) { + _immutableSamplers.reserve(pBinding->descriptorCount); + for (uint32_t i = 0; i < pBinding->descriptorCount; i++) { + _immutableSamplers.push_back((MVKSampler*)pBinding->pImmutableSamplers[i]); + } + } + + _info = *pBinding; + _info.pImmutableSamplers = nullptr; // Remove dangling pointer +} + +/** + * Sets the appropriate Metal resource indexes within this binding from the + * specified descriptor set binding counts, and updates those counts accordingly. + */ +VkResult MVKDescriptorSetLayoutBinding::initMetalResourceIndexOffsets(MVKShaderStageResourceBinding* pBindingIndexes, + MVKShaderStageResourceBinding* pDescSetCounts, + const VkDescriptorSetLayoutBinding* pBinding) { + switch (pBinding->descriptorType) { + case VK_DESCRIPTOR_TYPE_SAMPLER: + pBindingIndexes->samplerIndex = pDescSetCounts->samplerIndex; + pDescSetCounts->samplerIndex += pBinding->descriptorCount; + break; + + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: + pBindingIndexes->textureIndex = pDescSetCounts->textureIndex; + pDescSetCounts->textureIndex += pBinding->descriptorCount; + pBindingIndexes->samplerIndex = pDescSetCounts->samplerIndex; + pDescSetCounts->samplerIndex += pBinding->descriptorCount; + break; + + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: + case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: + pBindingIndexes->textureIndex = pDescSetCounts->textureIndex; + pDescSetCounts->textureIndex += pBinding->descriptorCount; + break; + + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: + pBindingIndexes->bufferIndex = pDescSetCounts->bufferIndex; + pDescSetCounts->bufferIndex += pBinding->descriptorCount; + break; + + default: + break; + } + return VK_SUCCESS; +} + + +#pragma mark - +#pragma mark MVKDescriptorSetLayout + +void MVKDescriptorSetLayout::bindDescriptorSet(MVKCommandEncoder* cmdEncoder, + MVKDescriptorSet* descSet, + MVKShaderResourceBinding& dslMTLRezIdxOffsets, + vector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex) { + + uint32_t bindCnt = (uint32_t)_bindings.size(); + for (uint32_t bindIdx = 0; bindIdx < bindCnt; bindIdx++) { + _bindings[bindIdx].bind(cmdEncoder, descSet->_bindings[bindIdx], + dslMTLRezIdxOffsets, dynamicOffsets, + pDynamicOffsetIndex); + } +} + +void MVKDescriptorSetLayout::populateShaderConverterContext(SPIRVToMSLConverterContext& context, + MVKShaderResourceBinding& dslMTLRezIdxOffsets, + uint32_t dslIndex) { + uint32_t bindCnt = (uint32_t)_bindings.size(); + for (uint32_t bindIdx = 0; bindIdx < bindCnt; bindIdx++) { + _bindings[bindIdx].populateShaderConverterContext(context, dslMTLRezIdxOffsets, dslIndex); + } +} + +MVKDescriptorSetLayout::MVKDescriptorSetLayout(MVKDevice* device, + const VkDescriptorSetLayoutCreateInfo* pCreateInfo) : MVKBaseDeviceObject(device) { + // Create the descriptor bindings + _bindings.reserve(pCreateInfo->bindingCount); + for (uint32_t i = 0; i < pCreateInfo->bindingCount; i++) { + _bindings.emplace_back(this, &pCreateInfo->pBindings[i]); + setConfigurationResult(_bindings.back().getConfigurationResult()); + } +} + + +#pragma mark - +#pragma mark MVKDescriptorBinding + +uint32_t MVKDescriptorBinding::writeBindings(uint32_t srcStartIndex, + uint32_t dstStartIndex, + uint32_t count, + const VkDescriptorImageInfo* pImageInfo, + const VkDescriptorBufferInfo* pBufferInfo, + const VkBufferView* pTexelBufferView) { + + uint32_t dstCnt = MIN(count, _pBindingLayout->_info.descriptorCount - dstStartIndex); + + switch (_pBindingLayout->_info.descriptorType) { + case VK_DESCRIPTOR_TYPE_SAMPLER: + for (uint32_t i = 0; i < dstCnt; i++) { + uint32_t dstIdx = dstStartIndex + i; + const VkDescriptorImageInfo* pImgInfo = &pImageInfo[srcStartIndex + i]; + _imageBindings[dstIdx] = *pImgInfo; + if (_hasDynamicSamplers) { + _mtlSamplers[dstIdx] = ((MVKSampler*)pImgInfo->sampler)->getMTLSamplerState(); + } + } + break; + + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: + for (uint32_t i = 0; i < dstCnt; i++) { + uint32_t dstIdx = dstStartIndex + i; + const VkDescriptorImageInfo* pImgInfo = &pImageInfo[srcStartIndex + i]; + _imageBindings[dstIdx] = *pImgInfo; + _mtlTextures[dstIdx] = ((MVKImageView*)pImgInfo->imageView)->getMTLTexture(); + if (_hasDynamicSamplers) { + _mtlSamplers[dstIdx] = ((MVKSampler*)pImgInfo->sampler)->getMTLSamplerState(); + } + } + break; + + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: + for (uint32_t i = 0; i < dstCnt; i++) { + uint32_t dstIdx = dstStartIndex + i; + const VkDescriptorImageInfo* pImgInfo = &pImageInfo[srcStartIndex + i]; + _imageBindings[dstIdx] = *pImgInfo; + _mtlTextures[dstIdx] = ((MVKImageView*)pImgInfo->imageView)->getMTLTexture(); + } + break; + + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: + for (uint32_t i = 0; i < dstCnt; i++) { + uint32_t dstIdx = dstStartIndex + i; + const VkDescriptorBufferInfo* pBuffInfo = &pBufferInfo[srcStartIndex + i]; + _bufferBindings[dstIdx] = *pBuffInfo; + MVKBuffer* mtlBuff = (MVKBuffer*)pBuffInfo->buffer; + _mtlBuffers[dstIdx] = mtlBuff->getMTLBuffer(); + _mtlBufferOffsets[dstIdx] = mtlBuff->getMTLBufferOffset() + pBuffInfo->offset; + } + break; + + case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: + for (uint32_t i = 0; i < dstCnt; i++) { + uint32_t dstIdx = dstStartIndex + i; + const VkBufferView* pBuffView = &pTexelBufferView[srcStartIndex + i]; + _texelBufferBindings[dstIdx] = *pBuffView; + _mtlTextures[dstIdx] = ((MVKBufferView*)*pBuffView)->getMTLTexture(); + } + break; + default: + break; + } + + return count - dstCnt; +} + +uint32_t MVKDescriptorBinding::readBindings(uint32_t srcStartIndex, + uint32_t dstStartIndex, + uint32_t count, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView) { + + uint32_t srcCnt = MIN(count, _pBindingLayout->_info.descriptorCount - srcStartIndex); + + switch (_pBindingLayout->_info.descriptorType) { + case VK_DESCRIPTOR_TYPE_SAMPLER: + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: + for (uint32_t i = 0; i < srcCnt; i++) { + pImageInfo[dstStartIndex + i] = _imageBindings[srcStartIndex + i]; + } + break; + + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: + for (uint32_t i = 0; i < srcCnt; i++) { + pBufferInfo[dstStartIndex + i] = _bufferBindings[srcStartIndex + i]; + } + break; + + case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: + for (uint32_t i = 0; i < srcCnt; i++) { + pTexelBufferView[dstStartIndex + i] = _texelBufferBindings[srcStartIndex + i]; + } + break; + + default: + break; + } + + return count - srcCnt; +} + +bool MVKDescriptorBinding::hasBinding(uint32_t binding) { + return _pBindingLayout->_info.binding == binding; +} + +MVKDescriptorBinding::MVKDescriptorBinding(MVKDescriptorSetLayoutBinding* pBindingLayout) : MVKBaseObject() { + + uint32_t descCnt = pBindingLayout->_info.descriptorCount; + + // Create space for the binding and Metal resources and populate with NULL and zero values + switch (pBindingLayout->_info.descriptorType) { + case VK_DESCRIPTOR_TYPE_SAMPLER: + _imageBindings.resize(descCnt, VkDescriptorImageInfo()); + initMTLSamplers(pBindingLayout); + break; + + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: + _imageBindings.resize(descCnt, VkDescriptorImageInfo()); + _mtlTextures.resize(descCnt, VK_NULL_HANDLE); + initMTLSamplers(pBindingLayout); + break; + + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: + _imageBindings.resize(descCnt, VkDescriptorImageInfo()); + _mtlTextures.resize(descCnt, VK_NULL_HANDLE); + break; + + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: + _bufferBindings.resize(descCnt, VkDescriptorBufferInfo()); + _mtlBuffers.resize(descCnt, VK_NULL_HANDLE); + _mtlBufferOffsets.resize(descCnt, 0); + break; + + case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: + _texelBufferBindings.resize(descCnt, VK_NULL_HANDLE); + _mtlTextures.resize(descCnt, VK_NULL_HANDLE); + break; + + default: + break; + } + + // Okay to hold layout as a pointer. From the Vulkan spec... + // "VkDescriptorSetLayout objects may be accessed by commands that operate on descriptor + // sets allocated using that layout, and those descriptor sets must not be updated with + // vkUpdateDescriptorSets after the descriptor set layout has been destroyed. + _pBindingLayout = pBindingLayout; +} + +/** + * If the descriptor set layout binding contains immutable samplers, immediately populate + * the corresponding Metal sampler in this descriptor binding from it. Otherwise add a null + * placeholder that will be populated dynamically at a later time. + */ +void MVKDescriptorBinding::initMTLSamplers(MVKDescriptorSetLayoutBinding* pBindingLayout) { + uint32_t descCnt = pBindingLayout->_info.descriptorCount; + auto imtblSamps = pBindingLayout->_immutableSamplers; + _hasDynamicSamplers = imtblSamps.empty(); + + _mtlSamplers.reserve(descCnt); + for (uint32_t i = 0; i < descCnt; i++) { + _mtlSamplers.push_back(_hasDynamicSamplers ? VK_NULL_HANDLE : imtblSamps[i]->getMTLSamplerState()); + } +} + + +#pragma mark - +#pragma mark MVKDescriptorSet + +template +void MVKDescriptorSet::writeDescriptorSets(const DescriptorAction* pDescriptorAction, + const VkDescriptorImageInfo* pImageInfo, + const VkDescriptorBufferInfo* pBufferInfo, + const VkBufferView* pTexelBufferView) { + uint32_t dstStartIdx = pDescriptorAction->dstArrayElement; + uint32_t binding = pDescriptorAction->dstBinding; + uint32_t origCnt = pDescriptorAction->descriptorCount; + uint32_t remainCnt = origCnt; + +// MVKLogDebug("Writing descriptor sets with buffer info %p starting at binding point %d.", pBufferInfo, binding); + + MVKDescriptorBinding* mvkDescBind = getBinding(binding); + while (mvkDescBind && remainCnt > 0) { + +// MVKLogDebug("Writing MVKDescriptorBinding with binding point %d.", binding); + + uint32_t srcStartIdx = origCnt - remainCnt; + remainCnt = mvkDescBind->writeBindings(srcStartIdx, dstStartIdx, remainCnt, + pImageInfo, pBufferInfo, pTexelBufferView); + + binding++; // If not consumed, move to next consecutive binding point + mvkDescBind = getBinding(binding); + dstStartIdx = 0; // Subsequent bindings start reading at first element + } +} + +// Create concrete implementations of the two variations of the writeDescriptorSets() function. +template void MVKDescriptorSet::writeDescriptorSets(const VkWriteDescriptorSet* pDescriptorAction, + const VkDescriptorImageInfo* pImageInfo, + const VkDescriptorBufferInfo* pBufferInfo, + const VkBufferView* pTexelBufferView); +template void MVKDescriptorSet::writeDescriptorSets(const VkCopyDescriptorSet* pDescriptorAction, + const VkDescriptorImageInfo* pImageInfo, + const VkDescriptorBufferInfo* pBufferInfo, + const VkBufferView* pTexelBufferView); + +void MVKDescriptorSet::readDescriptorSets(const VkCopyDescriptorSet* pDescriptorCopy, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView) { + uint32_t srcStartIdx = pDescriptorCopy->srcArrayElement; + uint32_t binding = pDescriptorCopy->srcBinding; + uint32_t origCnt = pDescriptorCopy->descriptorCount; + uint32_t remainCnt = origCnt; + +// MVKLogDebug("Reading descriptor sets with buffer info %p starting at binding point %d.", pBufferInfo, binding); + + MVKDescriptorBinding* mvkDescBind = getBinding(binding); + while (mvkDescBind && remainCnt > 0) { + +// MVKLogDebug("Reading MVKDescriptorBinding with binding point %d.", binding); + + uint32_t dstStartIdx = origCnt - remainCnt; + remainCnt = mvkDescBind->readBindings(srcStartIdx, dstStartIdx, remainCnt, + pImageInfo, pBufferInfo, pTexelBufferView); + + binding++; // If not consumed, move to next consecutive binding point + mvkDescBind = getBinding(binding); + srcStartIdx = 0; // Subsequent bindings start reading at first element + } +} + +// Returns the binding instance that is assigned the specified +// binding number, or returns null if no such binding exists. +MVKDescriptorBinding* MVKDescriptorSet::getBinding(uint32_t binding) { + for (auto& mvkDB : _bindings) { if (mvkDB.hasBinding(binding)) { return &mvkDB; } } + return nullptr; +} + +MVKDescriptorSet::MVKDescriptorSet(MVKDevice* device, + MVKDescriptorSetLayout* layout) : MVKBaseDeviceObject(device) { + + // Create the binding slots, each referencing a corresponding binding layout + uint32_t bindCnt = (uint32_t)layout->_bindings.size(); + _bindings.reserve(bindCnt); + for (uint32_t i = 0; i < bindCnt; i++) { + _bindings.emplace_back(&layout->_bindings[i]); + } +} + + +#pragma mark - +#pragma mark MVKDescriptorPool + +VkResult MVKDescriptorPool::allocateDescriptorSets(uint32_t count, + const VkDescriptorSetLayout* pSetLayouts, + VkDescriptorSet* pDescriptorSets) { + lock_guard lock(_lock); + + if (_allocatedSetCount + count > _maxSets) { + return mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "The maximum number of descriptor sets that can be allocated by this descriptor pool is %d.", _maxSets); + } + + for (uint32_t dsIdx = 0; dsIdx < count; dsIdx++) { + MVKDescriptorSetLayout* mvkDSL = (MVKDescriptorSetLayout*)pSetLayouts[dsIdx]; + MVKDescriptorSet* mvkDescSet = new MVKDescriptorSet(_device, mvkDSL); + _allocatedSets.push_front(mvkDescSet); + pDescriptorSets[dsIdx] = (VkDescriptorSet)mvkDescSet; + _allocatedSetCount++; + } + return VK_SUCCESS; +} + +VkResult MVKDescriptorPool::freeDescriptorSets(uint32_t count, const VkDescriptorSet* pDescriptorSets) { + lock_guard lock(_lock); + + for (uint32_t dsIdx = 0; dsIdx < count; dsIdx++) { + MVKDescriptorSet* mvkDS = (MVKDescriptorSet*)pDescriptorSets[dsIdx]; + _allocatedSets.remove(mvkDS); + _allocatedSetCount--; + delete mvkDS; + } + return VK_SUCCESS; +} + +VkResult MVKDescriptorPool::reset(VkDescriptorPoolResetFlags flags) { + lock_guard lock(_lock); + + mvkDestroyContainerContents(_allocatedSets); + _allocatedSetCount = 0; + return VK_SUCCESS; +} + +MVKDescriptorPool::MVKDescriptorPool(MVKDevice* device, + const VkDescriptorPoolCreateInfo* pCreateInfo) : MVKBaseDeviceObject(device) { + _maxSets = pCreateInfo->maxSets; + _allocatedSetCount = 0; +} + +// TODO: Destroying a descriptor pool implicitly destroys all descriptor sets created from it. + +MVKDescriptorPool::~MVKDescriptorPool() { reset(0); } + + +#pragma mark - +#pragma mark Support functions + +/** Updates the resource bindings in the descriptor sets inditified in the specified content. */ +void mvkUpdateDescriptorSets(uint32_t writeCount, + const VkWriteDescriptorSet* pDescriptorWrites, + uint32_t copyCount, + const VkCopyDescriptorSet* pDescriptorCopies) { + + // Perform the write updates + for (uint32_t i = 0; i < writeCount; i++) { + const VkWriteDescriptorSet* pDescWrite = &pDescriptorWrites[i]; + MVKDescriptorSet* dstSet = (MVKDescriptorSet*)pDescWrite->dstSet; + dstSet->writeDescriptorSets(pDescWrite, + pDescWrite->pImageInfo, + pDescWrite->pBufferInfo, + pDescWrite->pTexelBufferView); + } + + // Perform the copy updates by reading bindings from one set and writing to other set. + for (uint32_t i = 0; i < copyCount; i++) { + const VkCopyDescriptorSet* pDescCopy = &pDescriptorCopies[i]; + + uint32_t descCnt = pDescCopy->descriptorCount; + VkDescriptorImageInfo imgInfos[descCnt]; + VkDescriptorBufferInfo buffInfos[descCnt]; + VkBufferView texelBuffInfos[descCnt]; + + MVKDescriptorSet* srcSet = (MVKDescriptorSet*)pDescCopy->srcSet; + srcSet->readDescriptorSets(pDescCopy, imgInfos, buffInfos, texelBuffInfos); + + MVKDescriptorSet* dstSet = (MVKDescriptorSet*)pDescCopy->dstSet; + dstSet->writeDescriptorSets(pDescCopy, imgInfos, buffInfos, texelBuffInfos); + } +} + +void mvkPopulateShaderConverterContext(SPIRVToMSLConverterContext& context, + MVKShaderStageResourceBinding& ssRB, + spv::ExecutionModel stage, + uint32_t descriptorSetIndex, + uint32_t bindingIndex) { + MSLResourceBinding ctxRB; + ctxRB.stage = stage; + ctxRB.descriptorSet = descriptorSetIndex; + ctxRB.binding = bindingIndex; + ctxRB.mslBuffer = ssRB.bufferIndex; + ctxRB.mslTexture = ssRB.textureIndex; + ctxRB.mslSampler = ssRB.samplerIndex; + context.resourceBindings.push_back(ctxRB); +} + + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h new file mode 100644 index 00000000..435028fa --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h @@ -0,0 +1,549 @@ +/* + * MVKDevice.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKFoundation.h" +#include "MVKBaseObject.h" +#include "MVKLayers.h" +#include "vk_mvk_moltenvk.h" +#include +#include +#include + +#import +#import + +class MVKInstance; +class MVKDevice; +class MVKQueue; +class MVKQueueFamily; +class MVKSurface; +class MVKSemaphoreImpl; +class MVKResource; +class MVKBuffer; +class MVKBufferView; +class MVKImage; +class MVKSwapchainImage; +class MVKImageView; +class MVKSwapchain; +class MVKDeviceMemory; +class MVKFence; +class MVKSemaphore; +class MVKQueryPool; +class MVKShaderModule; +class MVKPipelineCache; +class MVKPipelineLayout; +class MVKPipeline; +class MVKSampler; +class MVKDescriptorSetLayout; +class MVKDescriptorPool; +class MVKFramebuffer; +class MVKRenderPass; +class MVKCommandPool; +class MVKCommandEncoder; +class MVKCommandResourceFactory; + + +#define kMVKVertexContentBufferIndex 0 + +#define MVK_MAX_QUEUE_FAMILIES 1 +#define MVK_MIN_SWAPCHAIN_SURFACE_IMAGE_COUNT 2 +#define MVK_MAX_SWAPCHAIN_SURFACE_IMAGE_COUNT 2 // Metal supports 3 concurrent drawables, but if the + // swapchain is destroyed and rebuilt as part of resizing, + // one will be held by the current display image. + +#pragma mark - +#pragma mark MVKPhysicalDevice + +/** Represents a Vulkan physical GPU device. */ +class MVKPhysicalDevice : public MVKConfigurableObject { + +public: + + /** Populates the specified structure with the features of this device. */ + void getFeatures(VkPhysicalDeviceFeatures* features); + + /** Populates the specified structure with the Metal-specific features of this device. */ + void getMetalFeatures(MVKPhysicalDeviceMetalFeatures* mtlFeatures); + + /** Populates the specified structure with the properties of this device. */ + void getProperties(VkPhysicalDeviceProperties* properties); + + /** Populates the specified structure with the format properties of this device. */ + void getFormatProperties(VkFormat format, VkFormatProperties* pFormatProperties); + + /** + * Populates the specified structure with the image format properties + * supported for the specified image characteristics on this device. + */ + VkResult getImageFormatProperties(VkFormat format, + VkImageType type, + VkImageTiling tiling, + VkImageUsageFlags usage, + VkImageCreateFlags flags, + VkImageFormatProperties* pImageFormatProperties); + +#pragma mark Surfaces + + /** + * Queries whether this device supports presentation to the specified surface, + * using a queue of the specified queue family. + */ + VkResult getSurfaceSupport(uint32_t queueFamilyIndex, MVKSurface* surface, VkBool32* pSupported); + + /** Returns the capabilities of the specified surface. */ + VkResult getSurfaceCapabilities(MVKSurface* surface, VkSurfaceCapabilitiesKHR* pSurfaceCapabilities); + + /** + * Returns the pixel formats supported by the surface described by the specified + * surface description. + * + * If pSurfaceFormats is null, the value of pCount is updated with the number of + * pixel formats supported by the surface. + * + * If pSurfaceFormats is not null, then pCount formats are copied into the array. + * If the number of available formats is less than pCount, the value of pCount is + * updated to indicate the number of formats actually returned in the array. + * + * Returns VK_SUCCESS if successful. Returns VK_INCOMPLETE if the number of supported + * formats is larger than pCount. Returns other values if an error occurs. + */ + VkResult getSurfaceFormats(MVKSurface* surface, uint32_t* pCount, VkSurfaceFormatKHR* pSurfaceFormats); + + /** + * Returns the presentation modes supported by the surface described by the specified + * surface description. + * + * If pPresentModes is null, the value of pCount is updated with the number of + * presentation modes supported by the surface. + * + * If pPresentModes is not null, then pCount presentation modes are copied into the array. + * If the number of available modes is less than pCount, the value of pCount is updated + * to indicate the number of presentation modes actually returned in the array. + * + * Returns VK_SUCCESS if successful. Returns VK_INCOMPLETE if the number of supported + * presentation modes is larger than pCount. Returns other values if an error occurs. + */ + VkResult getSurfacePresentModes(MVKSurface* surface, uint32_t* pCount, VkPresentModeKHR* pPresentModes); + + +#pragma mark Queues + + /** Returns the number of queue families supported by this device. */ + inline uint32_t getQueueFamilyCount() { return _queueFamilyCount; } + + /** + * If properties is null, the value of pCount is updated with the number of + * queue families supported by this instance. + * + * If properties is not null, then pCount queue family properties are copied into the + * array. If the number of available queue families is less than pCount, the value of + * pCount is updated to indicate the number of queue families actually returned in the array. + * + * Returns VK_SUCCESS if successful. Returns VK_INCOMPLETE if the number of queue families + * available in this instance is larger than the specified pCount. Returns other values if + * an error occurs. + */ + VkResult getQueueFamilyProperties(uint32_t* pCount, VkQueueFamilyProperties* properties); + + /** Returns a pointer to the layer manager for this device. */ + inline MVKLayerManager* getLayerManager() { return MVKLayerManager::globalManager(); } + + +#pragma mark Memory models + + /** Returns a pointer to the memory characteristics of this device. */ + inline const VkPhysicalDeviceMemoryProperties* getPhysicalDeviceMemoryProperties() { return &_memoryProperties; } + + /** Populates the specified memory properties with the memory characteristics of this device. */ + VkResult getPhysicalDeviceMemoryProperties(VkPhysicalDeviceMemoryProperties* pMemoryProperties); + + /** + * Returns a bit mask of all memory type indices. + * Each bit [0..31] in the returned bit mask indicates a distinct memory type. + */ + inline uint32_t getAllMemoryTypes() { return _allMemoryTypes; } + + /** + * Returns a bit mask of all memory type indices that allow host visibility to the memory. + * Each bit [0..31] in the returned bit mask indicates a distinct memory type. + */ + inline uint32_t getHostVisibleMemoryTypes() { return _hostVisibleMemoryTypes; } + + /** + * Returns a bit mask of all memory type indices that do NOT allow host visibility to the memory. + * Each bit [0..31] in the returned bit mask indicates a distinct memory type. + */ + inline uint32_t getPrivateMemoryTypes() { return _privateMemoryTypes; } + + +#pragma mark Metal + + /** Returns the underlying Metal device. */ + inline id getMTLDevice() { return _mtlDevice; } + + +#pragma mark Construction + + /** Constructs an instance wrapping the specified Vulkan instance and Metal device. */ + MVKPhysicalDevice(MVKInstance* mvkInstance, id mtlDevice); + + /** Default destructor. */ + ~MVKPhysicalDevice() override; + +private: + friend class MVKDevice; + + MTLFeatureSet getMaximalMTLFeatureSet(); + void initMetalFeatures(); + void initFeatures(); + void initProperties(); + void logFeatureSets(); + void initMemoryProperties(); + void initQueueFamilies(); + + id _mtlDevice; + MVKInstance* _mvkInstance; + VkPhysicalDeviceFeatures _features; + MVKPhysicalDeviceMetalFeatures _metalFeatures; + VkPhysicalDeviceProperties _properties; + VkPhysicalDeviceMemoryProperties _memoryProperties; + VkQueueFamilyProperties _queueFamilyProperties[MVK_MAX_QUEUE_FAMILIES]; + uint32_t _allMemoryTypes; + uint32_t _hostVisibleMemoryTypes; + uint32_t _privateMemoryTypes; + uint32_t _queueFamilyCount; +}; + + +#pragma mark - +#pragma mark MVKDevice + +/** Represents a Vulkan logical GPU device, associated with a physical device. */ +class MVKDevice : public MVKConfigurableObject { + +public: + + /** Returns the physical device underlying this logical device. */ + inline MVKPhysicalDevice* getPhysicalDevice() { return _physicalDevice; } + + /** Returns the common resource factory for creating command resources. */ + inline MVKCommandResourceFactory* getCommandResourceFactory() { return _commandResourceFactory; } + + /** Returns the function pointer corresponding to the specified named entry point. */ + PFN_vkVoidFunction getProcAddr(const char* pName); + + /** Retrieves a queue at the specified index within the specified family. */ + VkResult getDeviceQueue(uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue); + + /** Block the current thread until all queues in this device are idle. */ + VkResult waitIdle(); + + +#pragma mark Object lifecycle + + MVKBuffer* createBuffer(const VkBufferCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + void destroyBuffer(MVKBuffer* mvkBuff, + const VkAllocationCallbacks* pAllocator); + + MVKBufferView* createBufferView(const VkBufferViewCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + void destroyBufferView(MVKBufferView* mvkBuffView, + const VkAllocationCallbacks* pAllocator); + + MVKImage* createImage(const VkImageCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + void destroyImage(MVKImage* mvkImg, + const VkAllocationCallbacks* pAllocator); + + MVKImageView* createImageView(const VkImageViewCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + void destroyImageView(MVKImageView* mvkImgView, const VkAllocationCallbacks* pAllocator); + + MVKSwapchain* createSwapchain(const VkSwapchainCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + void destroySwapchain(MVKSwapchain* mvkSwpChn, + const VkAllocationCallbacks* pAllocator); + + MVKSwapchainImage* createSwapchainImage(const VkImageCreateInfo* pCreateInfo, + MVKSwapchain* swapchain, + const VkAllocationCallbacks* pAllocator); + void destroySwapchainImage(MVKSwapchainImage* mvkImg, + const VkAllocationCallbacks* pAllocator); + + MVKFence* createFence(const VkFenceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + void destroyFence(MVKFence* mvkFence, + const VkAllocationCallbacks* pAllocator); + + MVKSemaphore* createSemaphore(const VkSemaphoreCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + void destroySemaphore(MVKSemaphore* mvkSem4, + const VkAllocationCallbacks* pAllocator); + + MVKQueryPool* createQueryPool(const VkQueryPoolCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + void destroyQueryPool(MVKQueryPool* mvkQP, + const VkAllocationCallbacks* pAllocator); + + MVKShaderModule* createShaderModule(const VkShaderModuleCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + void destroyShaderModule(MVKShaderModule* mvkShdrMod, + const VkAllocationCallbacks* pAllocator); + + MVKPipelineCache* createPipelineCache(const VkPipelineCacheCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + void destroyPipelineCache(MVKPipelineCache* mvkPLC, + const VkAllocationCallbacks* pAllocator); + + MVKPipelineLayout* createPipelineLayout(const VkPipelineLayoutCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + void destroyPipelineLayout(MVKPipelineLayout* mvkPLL, + const VkAllocationCallbacks* pAllocator); + + /** + * Template function that creates count number of pipelines of type PipelineType, + * using a collection of configuration information of type PipelineInfoType, + * and adds the new pipelines to the specified pipeline cache. + */ + template + VkResult createPipelines(VkPipelineCache pipelineCache, + uint32_t count, + const PipelineInfoType* pCreateInfos, + const VkAllocationCallbacks* pAllocator, + VkPipeline* pPipelines); + void destroyPipeline(MVKPipeline* mvkPPL, + const VkAllocationCallbacks* pAllocator); + + MVKSampler* createSampler(const VkSamplerCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + void destroySampler(MVKSampler* mvkSamp, + const VkAllocationCallbacks* pAllocator); + + MVKDescriptorSetLayout* createDescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + void destroyDescriptorSetLayout(MVKDescriptorSetLayout* mvkDSL, + const VkAllocationCallbacks* pAllocator); + + MVKDescriptorPool* createDescriptorPool(const VkDescriptorPoolCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + void destroyDescriptorPool(MVKDescriptorPool* mvkDP, + const VkAllocationCallbacks* pAllocator); + + MVKFramebuffer* createFramebuffer(const VkFramebufferCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + void destroyFramebuffer(MVKFramebuffer* mvkFB, + const VkAllocationCallbacks* pAllocator); + + MVKRenderPass* createRenderPass(const VkRenderPassCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + void destroyRenderPass(MVKRenderPass* mvkRP, + const VkAllocationCallbacks* pAllocator); + + MVKCommandPool* createCommandPool(const VkCommandPoolCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + void destroyCommandPool(MVKCommandPool* mvkCmdPool, + const VkAllocationCallbacks* pAllocator); + + MVKDeviceMemory* allocateMemory(const VkMemoryAllocateInfo* pAllocateInfo, + const VkAllocationCallbacks* pAllocator); + void freeMemory(MVKDeviceMemory* mvkDevMem, + const VkAllocationCallbacks* pAllocator); + + /** Applies the specified global memory barrier to all resource issued by this device. */ + void applyMemoryBarrier(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkMemoryBarrier* pMemoryBarrier, + MVKCommandEncoder* cmdEncoder, + MVKCommandUse cmdUse); + + /** + * If performance is being tracked, returns a marker indicating the current system time, + * otherwise returns zero. + * + * This marker is not guaranteed to be a meaningful value, but the difference between + * two calls to this function will indicate a time interval value measure in seconds. + */ + inline NSTimeInterval getPerformanceTimestamp() { + return _mvkConfig.performanceTracking ? [NSDate timeIntervalSinceReferenceDate] : 0.0; + } + + /** + * If performance is being tracked, adds a shader compilation event with a duration + * interval between the start and end times, to the given performance statistics. + * + * If endTime is zero, the current time is used. + */ + void addShaderCompilationEventPerformance(MVKShaderCompilationEventPerformance& shaderCompilationEvent, + NSTimeInterval startTime, NSTimeInterval endTime = 0.0); + + /** Populates the specified statistics structure from the current shader performance statistics. */ + void getShaderCompilationPerformanceStatistics(MVKShaderCompilationPerformance* pShaderCompPerf); + + +#pragma mark Metal + + /** Returns the underlying Metal device. */ + inline id getMTLDevice() { return _physicalDevice->getMTLDevice(); } + + /** Returns the Metal vertex buffer index to use for the specified vertex attribute binding number. */ + uint32_t getMetalBufferIndexForVertexAttributeBinding(uint32_t binding); + + /** + * Returns the Metal MTLPixelFormat corresponding to the specified Vulkan VkFormat, + * or returns MTLPixelFormatInvalid if no corresponding MTLPixelFormat exists. + * + * This function uses the MoltenVK API function mvkMTLPixelFormatFromVkFormat(), but + * not all MTLPixelFormats returned by that API function are supported by all GPU's. + * This function may substitute and return a MTLPixelFormat value that is different than + * the value returned by the mvkMTLPixelFormatFromVkFormat() function, but is compatible + * with the GPU underlying this instance. + * + * Not all macOS GPU's support the MTLPixelFormatDepth24Unorm_Stencil8 pixel format, and + * in that case, this function will return MTLPixelFormatDepth32Float_Stencil8 instead. + * + * All other pixel formats are returned unchanged. + */ + MTLPixelFormat mtlPixelFormatFromVkFormat(VkFormat vkFormat); + + /** + * Returns the MTLBuffer used to hold occlusion query results, + * when all query pools use the same MTLBuffer. + */ + id getGlobalVisibilityResultMTLBuffer(); + + /** + * Expands the visibility results buffer, used for occlusion queries, by replacing the + * existing buffer with a new MTLBuffer that is large enough to accommodate all occlusion + * queries created to date, including those defined in the specified query pool. + * Returns the previous query count, before the new queries were added, which can + * be used by the new query pool to locate its queries within the single large buffer. + */ + uint32_t expandVisibilityResultMTLBuffer(uint32_t queryCount); + + /** Returns the memory type index corresponding to the specified Metal memory storage mode. */ + uint32_t getVulkanMemoryTypeIndex(MTLStorageMode mtlStorageMode); + + +#pragma mark Properties directly accessible + + /** Pointer to the feature set of the underlying physical device. */ + const VkPhysicalDeviceFeatures* _pFeatures; + + /** Pointer to the Metal-specific features of the underlying physical device. */ + const MVKPhysicalDeviceMetalFeatures* _pMetalFeatures; + + /** Pointer to the properties of the underlying physical device. */ + const VkPhysicalDeviceProperties* _pProperties; + + /** Pointer to the memory properties of the underlying physical device. */ + const VkPhysicalDeviceMemoryProperties* _pMemoryProperties; + + /** The MoltenVK configuration settings for this device. */ + const MVKDeviceConfiguration _mvkConfig; + + /** The shader compilation performance statistics. */ + MVKShaderCompilationPerformance _shaderCompilationPerformance; + + +#pragma mark Construction + + /** Constructs an instance on the specified physical device. */ + MVKDevice(MVKPhysicalDevice* physicalDevice, const VkDeviceCreateInfo* pCreateInfo); + + ~MVKDevice() override; + +protected: + MVKResource* addResource(MVKResource* rez); + MVKResource* removeResource(MVKResource* rez); + void initPerformanceTracking(); + const char* getShaderCompilationEventName(MVKShaderCompilationEventPerformance& shaderCompilationEvent); + + MVKPhysicalDevice* _physicalDevice; + MVKCommandResourceFactory* _commandResourceFactory; + std::vector _queueFamilies; + std::vector _queues; + std::vector _resources; + std::mutex _rezLock; + std::mutex _shaderCompPerfLock; + id _globalVisibilityResultMTLBuffer; + uint32_t _globalVisibilityQueryCount; + std::mutex _vizLock; +}; + + +#pragma mark - +#pragma mark MVKBaseDeviceObject + +/** Represents an object that is spawned from a Vulkan device, and tracks that device. */ +class MVKBaseDeviceObject : public MVKConfigurableObject { + +public: + + /** Returns the device for which this object was created. */ + inline MVKDevice* getDevice() { return _device; } + + /** Returns the underlying Metal device. */ + inline id getMTLDevice() { return _device->getMTLDevice(); } + + /** + * Returns the Metal MTLPixelFormat corresponding to the specified Vulkan VkFormat, + * or returns MTLPixelFormatInvalid if no corresponding MTLPixelFormat exists. + * + * This function delegates to the MVKDevice::mtlPixelFormatFromVkFormat() function. + * See the notes for that function for more information about how MTLPixelFormats + * are managed for each platform device. + */ + inline MTLPixelFormat mtlPixelFormatFromVkFormat(VkFormat vkFormat) { + return _device->mtlPixelFormatFromVkFormat(vkFormat); + } + + /** Constructs an instance for the specified device. */ + MVKBaseDeviceObject(MVKDevice* device) : _device(device) {} + +protected: + MVKDevice* _device; +}; + + +#pragma mark - +#pragma mark Functions + +/** + * Returns a monotonic timestamp value for use in Vulkan timestamping. + * + * The returned value corresponds to the number of CPU "ticks" since the app was initialized. + * + * Calling this value twice, subtracting the first value from the second, and then multiplying + * the result by the VkPhysicalDeviceProperties.VkPhysicalDeviceLimits.timestampPeriod value + * will provide an indication of the number of nanoseconds between the two calls. + */ +uint64_t mvkGetTimestamp(); + +/** + * Returns the number of milliseconds since the app was initialized. + * + * This is a convenience function for tracking the time required to perform operations. + * Accuracy may be improved by using the mvkGetTimestamp() function and following the + * method provided in the notes for that function. + */ +double mvkGetElapsedMilliseconds(); + + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm new file mode 100644 index 00000000..5b069522 --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm @@ -0,0 +1,1449 @@ +/* + * MVKDevice.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKInstance.h" +#include "MVKDevice.h" +#include "MVKQueue.h" +#include "MVKSurface.h" +#include "MVKBuffer.h" +#include "MVKImage.h" +#include "MVKSwapchain.h" +#include "MVKQueryPool.h" +#include "MVKShaderModule.h" +#include "MVKPipeline.h" +#include "MVKFramebuffer.h" +#include "MVKRenderPass.h" +#include "MVKCommandPool.h" +#include "MVKFoundation.h" +#include "MVKEnvironment.h" +#include "MVKOSExtensions.h" +#include +#include "mvk_datatypes.h" +#include "vk_mvk_moltenvk.h" +#include + +using namespace std; + + +#if MVK_IOS +# include +# define MVKViewClass UIView +#endif +#if MVK_MACOS +# include +# define MVKViewClass NSView +#endif + +// To display the MoltenVK logo watermark by default, define the MVK_WATERMARK build setting. +#ifdef MVK_WATERMARK +# define MVK_DISPLAY_WATERMARK 1 +#else +# define MVK_DISPLAY_WATERMARK 0 +#endif + +static uint64_t _mvkTimestampBase; +static double _mvkTimestampPeriod; + + +#pragma mark - +#pragma mark MVKPhysicalDevice + +void MVKPhysicalDevice::getFeatures(VkPhysicalDeviceFeatures* features) { + if (features) { *features = _features; } +} + +void MVKPhysicalDevice::getMetalFeatures(MVKPhysicalDeviceMetalFeatures* mtlFeatures) { + if (mtlFeatures) { *mtlFeatures = _metalFeatures; } +} + +void MVKPhysicalDevice::getProperties(VkPhysicalDeviceProperties* properties) { + if (properties) { *properties = _properties; } +} + +#define MVK_FMT_NO_FEATS { 0, 0, 0 } + +void MVKPhysicalDevice::getFormatProperties(VkFormat format, + VkFormatProperties* pFormatProperties) { + if ( !pFormatProperties ) { return; } + + *pFormatProperties = mvkVkFormatProperties(format); + +#if MVK_MACOS + // Special-case certain formats that not all macOS GPU's support + MTLPixelFormat mtlPixFmt = mvkMTLPixelFormatFromVkFormat(format); + if (mtlPixFmt == MTLPixelFormatDepth24Unorm_Stencil8 && + !getMTLDevice().isDepth24Stencil8PixelFormatSupported) { + *pFormatProperties = MVK_FMT_NO_FEATS; + } +#endif +} + +VkResult MVKPhysicalDevice::getImageFormatProperties(VkFormat format, + VkImageType type, + VkImageTiling tiling, + VkImageUsageFlags usage, + VkImageCreateFlags flags, + VkImageFormatProperties* pImageFormatProperties) { + if (!pImageFormatProperties) { return VK_SUCCESS; } + + VkPhysicalDeviceLimits* pLimits = &_properties.limits; + VkExtent3D maxExt; + uint32_t maxLayers; + switch (type) { + case VK_IMAGE_TYPE_1D: + maxExt.width = pLimits->maxImageDimension1D; + maxExt.height = 1; + maxExt.depth = 1; + maxLayers = pLimits->maxImageArrayLayers; + break; + case VK_IMAGE_TYPE_2D: + maxExt.width = pLimits->maxImageDimension2D; + maxExt.height = pLimits->maxImageDimension2D; + maxExt.depth = 1; + maxLayers = pLimits->maxImageArrayLayers; + break; + case VK_IMAGE_TYPE_3D: + maxExt.width = pLimits->maxImageDimension3D; + maxExt.height = pLimits->maxImageDimension3D; + maxExt.depth = pLimits->maxImageDimension3D; + maxLayers = 1; + break; + default: + maxExt = { 1, 1, 1}; + maxLayers = 1; + break; + } + + pImageFormatProperties->maxExtent = maxExt; + pImageFormatProperties->maxMipLevels = mvkMipmapLevels3D(maxExt); + pImageFormatProperties->maxArrayLayers = maxLayers; + pImageFormatProperties->sampleCounts = _metalFeatures.supportedSampleCounts; + pImageFormatProperties->maxResourceSize = numeric_limits::max(); + + return VK_SUCCESS; +} + + +#pragma mark Surfaces + +VkResult MVKPhysicalDevice::getSurfaceSupport(uint32_t queueFamilyIndex, + MVKSurface* surface, + VkBool32* pSupported) { + // Check whether this is a headless device + bool isHeadless = false; +#if MVK_MACOS + isHeadless = getMTLDevice().isHeadless; +#endif + + // If this device is headless or the surface does not have a CAMetalLayer, it is not supported. + *pSupported = !(isHeadless || (surface->getCAMetalLayer() == nil)); + return *pSupported ? VK_SUCCESS : surface->getConfigurationResult(); +} + +VkResult MVKPhysicalDevice::getSurfaceCapabilities(MVKSurface* surface, + VkSurfaceCapabilitiesKHR* pSurfaceCapabilities) { + + // The layer underlying the surface view must be a CAMetalLayer. + CAMetalLayer* mtlLayer = surface->getCAMetalLayer(); + if ( !mtlLayer ) { return surface->getConfigurationResult(); } + + VkExtent2D surfExtnt = mvkVkExtent2DFromCGSize(mtlLayer.updatedDrawableSizeMVK); + + pSurfaceCapabilities->minImageCount = MVK_MIN_SWAPCHAIN_SURFACE_IMAGE_COUNT; + pSurfaceCapabilities->maxImageCount = MVK_MAX_SWAPCHAIN_SURFACE_IMAGE_COUNT; + pSurfaceCapabilities->currentExtent = surfExtnt; + pSurfaceCapabilities->minImageExtent = surfExtnt; + pSurfaceCapabilities->maxImageExtent = surfExtnt; + pSurfaceCapabilities->maxImageArrayLayers = 1; + pSurfaceCapabilities->supportedTransforms = (VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR); + pSurfaceCapabilities->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + pSurfaceCapabilities->supportedCompositeAlpha = (VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR | + VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR | + VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR); + pSurfaceCapabilities->supportedUsageFlags = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | + VK_IMAGE_USAGE_STORAGE_BIT | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT); + return VK_SUCCESS; +} + +VkResult MVKPhysicalDevice::getSurfaceFormats(MVKSurface* surface, + uint32_t* pCount, + VkSurfaceFormatKHR* pSurfaceFormats) { + + // The layer underlying the surface view must be a CAMetalLayer. + CAMetalLayer* mtlLayer = surface->getCAMetalLayer(); + if ( !mtlLayer ) { return surface->getConfigurationResult(); } + + const MTLPixelFormat mtlFormats[] = { + MTLPixelFormatBGRA8Unorm, + MTLPixelFormatBGRA8Unorm_sRGB, + MTLPixelFormatRGBA16Float, + }; + + const uint mtlFmtsCnt = sizeof(mtlFormats) / sizeof(MTLPixelFormat); + + // If properties aren't actually being requested yet, simply update the returned count + if ( !pSurfaceFormats ) { + *pCount = mtlFmtsCnt; + return VK_SUCCESS; + } + + // Determine how many results we'll return, and return that number + VkResult result = (*pCount <= mtlFmtsCnt) ? VK_SUCCESS : VK_INCOMPLETE; + *pCount = min(*pCount, mtlFmtsCnt); + + // Now populate the supplied array + for (uint fmtIdx = 0; fmtIdx < *pCount; fmtIdx++) { + pSurfaceFormats[fmtIdx].format = mvkVkFormatFromMTLPixelFormat(mtlFormats[fmtIdx]); + pSurfaceFormats[fmtIdx].colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + } + + return result; +} + +VkResult MVKPhysicalDevice::getSurfacePresentModes(MVKSurface* surface, + uint32_t* pCount, + VkPresentModeKHR* pPresentModes) { + + // The layer underlying the surface view must be a CAMetalLayer. + CAMetalLayer* mtlLayer = surface->getCAMetalLayer(); + if ( !mtlLayer ) { return surface->getConfigurationResult(); } + + // TODO: check which mode(s) are applicable to Metal + const VkPresentModeKHR presentModes[] = { +// VK_PRESENT_MODE_MAILBOX_KHR, + VK_PRESENT_MODE_FIFO_KHR, + }; + const uint presentModesCnt = sizeof(presentModes) / sizeof(VkPresentModeKHR); + + // If properties aren't actually being requested yet, simply update the returned count + if ( !pPresentModes ) { + *pCount = presentModesCnt; + return VK_SUCCESS; + } + + // Determine how many results we'll return, and return that number + VkResult result = (*pCount <= presentModesCnt) ? VK_SUCCESS : VK_INCOMPLETE; + *pCount = min(*pCount, presentModesCnt); + + // Now populate the supplied array + for (uint fmtIdx = 0; fmtIdx < *pCount; fmtIdx++) { + pPresentModes[fmtIdx] = presentModes[fmtIdx]; + } + + return result; +} + + +#pragma mark Queues + +VkResult MVKPhysicalDevice::getQueueFamilyProperties(uint32_t* pCount, + VkQueueFamilyProperties* queueProperties) { + + // If properties aren't actually being requested yet, simply update the returned count + if ( !queueProperties ) { + *pCount = getQueueFamilyCount(); + return VK_SUCCESS; + } + + // Determine how many families we'll return, and return that number + uint32_t qCnt = getQueueFamilyCount(); + VkResult result = (*pCount <= qCnt) ? VK_SUCCESS : VK_INCOMPLETE; + *pCount = min(*pCount, qCnt); + + // Now populate the queue families + for (uint32_t qIdx = 0; qIdx < *pCount; qIdx++) { + queueProperties[qIdx] = _queueFamilyProperties[qIdx]; + } + + return result; +} + + +#pragma mark Memory models + +/** Populates the specified memory properties with the memory characteristics of this device. */ +VkResult MVKPhysicalDevice::getPhysicalDeviceMemoryProperties(VkPhysicalDeviceMemoryProperties* pMemoryProperties) { + *pMemoryProperties = _memoryProperties; + return VK_SUCCESS; +} + + +#pragma mark Construction + +/** Returns the device type of the specified MTLDevice. */ +static VkPhysicalDeviceType getDeviceType(id mtlDevice) { +#if MVK_IOS + return VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; +#endif +#if MVK_MACOS + return (mtlDevice.isLowPower ? VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU : VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU); +#endif +} + +/** Initializes the Metal-specific physical device features of this instance. */ +void MVKPhysicalDevice::initMetalFeatures() { + memset(&_metalFeatures, 0, sizeof(_metalFeatures)); // Start with everything cleared + + _metalFeatures.maxPerStageBufferCount = 31; + _metalFeatures.maxMTLBufferSize = (256 * MEBI); + _metalFeatures.dynamicMTLBuffers = false; + + _metalFeatures.maxPerStageSamplerCount = 16; + _metalFeatures.maxQueryBufferSize = (64 * KIBI); + +#if MVK_IOS + _metalFeatures.mslVersion = SPIRVToMSLConverterOptions::makeMSLVersion(1); + _metalFeatures.maxPerStageTextureCount = 31; + _metalFeatures.mtlBufferAlignment = 64; + _metalFeatures.texelBuffers = true; + + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v1] ) { + _metalFeatures.indirectDrawing = true; + _metalFeatures.baseVertexInstanceDrawing = true; + _metalFeatures.mtlBufferAlignment = 16; // Min float4 alignment for typical vertex buffers. MTLBuffer may go down to 4 bytes for other data. + } + + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v2] ) { + _metalFeatures.mslVersion = SPIRVToMSLConverterOptions::makeMSLVersion(1, 1); + _metalFeatures.dynamicMTLBuffers = true; + } + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v3] ) { + _metalFeatures.mslVersion = SPIRVToMSLConverterOptions::makeMSLVersion(1, 2); + _metalFeatures.shaderSpecialization = true; + } + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v4] ) { + _metalFeatures.mslVersion = SPIRVToMSLConverterOptions::makeMSLVersion(2); + _metalFeatures.ioSurfaces = true; + _metalFeatures.depthClipMode = true; + } +#endif + +#if MVK_MACOS + _metalFeatures.mslVersion = SPIRVToMSLConverterOptions::makeMSLVersion(1, 1); + _metalFeatures.maxPerStageTextureCount = 128; + _metalFeatures.mtlBufferAlignment = 256; + _metalFeatures.indirectDrawing = true; + _metalFeatures.baseVertexInstanceDrawing = true; + _metalFeatures.ioSurfaces = true; + _metalFeatures.depthClipMode = true; + + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily1_v2] ) { + _metalFeatures.mslVersion = SPIRVToMSLConverterOptions::makeMSLVersion(1, 2); + _metalFeatures.dynamicMTLBuffers = true; + _metalFeatures.shaderSpecialization = true; + _metalFeatures.maxMTLBufferSize = (1 * GIBI); + } + + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily1_v3] ) { + _metalFeatures.mslVersion = SPIRVToMSLConverterOptions::makeMSLVersion(2); + _metalFeatures.texelBuffers = true; + } +#endif + + for (uint32_t sc = VK_SAMPLE_COUNT_1_BIT; sc <= VK_SAMPLE_COUNT_64_BIT; sc <<= 1) { + if ([_mtlDevice supportsTextureSampleCount: sc]) { + _metalFeatures.supportedSampleCounts |= sc; + } + } +} + +/** Initializes the physical device features of this instance. */ +void MVKPhysicalDevice::initFeatures() { + memset(&_features, 0, sizeof(_features)); // Start with everything cleared + + _features.independentBlend = true; + _features.depthBiasClamp = true; + _features.fillModeNonSolid = true; + _features.largePoints = true; + _features.alphaToOne = true; + _features.samplerAnisotropy = true; + _features.shaderImageGatherExtended = true; + _features.shaderStorageImageExtendedFormats = true; + _features.shaderClipDistance = true; + _features.shaderInt16 = true; + +#if MVK_IOS + _features.textureCompressionETC2 = true; + + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily2_v1] ) { + _features.textureCompressionASTC_LDR = true; + } + + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v1] ) { + _features.occlusionQueryPrecise = true; + } +#endif + +#if MVK_MACOS + _features.textureCompressionBC = true; + _features.occlusionQueryPrecise = true; + _features.imageCubeArray = true; + _features.depthClamp = true; + _features.vertexPipelineStoresAndAtomics = true; + _features.fragmentStoresAndAtomics = true; + + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily1_v2] ) { + _features.dualSrcBlend = true; + } + +#endif +} + + +#pragma mark VkPhysicalDeviceFeatures - List of features available on the device + +//typedef struct VkPhysicalDeviceFeatures { +// VkBool32 robustBufferAccess; +// VkBool32 fullDrawIndexUint32; +// VkBool32 imageCubeArray; // done +// VkBool32 independentBlend; // done +// VkBool32 geometryShader; +// VkBool32 tessellationShader; +// VkBool32 sampleRateShading; +// VkBool32 dualSrcBlend; // done +// VkBool32 logicOp; +// VkBool32 multiDrawIndirect; +// VkBool32 drawIndirectFirstInstance; +// VkBool32 depthClamp; // done +// VkBool32 depthBiasClamp; // done +// VkBool32 fillModeNonSolid; // done +// VkBool32 depthBounds; +// VkBool32 wideLines; +// VkBool32 largePoints; // done +// VkBool32 alphaToOne; // done +// VkBool32 multiViewport; +// VkBool32 samplerAnisotropy; // done +// VkBool32 textureCompressionETC2; // done +// VkBool32 textureCompressionASTC_LDR; // done +// VkBool32 textureCompressionBC; // done +// VkBool32 occlusionQueryPrecise; // done +// VkBool32 pipelineStatisticsQuery; +// VkBool32 vertexPipelineStoresAndAtomics; // done +// VkBool32 fragmentStoresAndAtomics; // done +// VkBool32 shaderTessellationAndGeometryPointSize; +// VkBool32 shaderImageGatherExtended; // done +// VkBool32 shaderStorageImageExtendedFormats; // done +// VkBool32 shaderStorageImageMultisample; +// VkBool32 shaderStorageImageReadWithoutFormat; +// VkBool32 shaderStorageImageWriteWithoutFormat; +// VkBool32 shaderUniformBufferArrayDynamicIndexing; +// VkBool32 shaderSampledImageArrayDynamicIndexing; +// VkBool32 shaderStorageBufferArrayDynamicIndexing; +// VkBool32 shaderStorageImageArrayDynamicIndexing; +// VkBool32 shaderClipDistance; // done +// VkBool32 shaderCullDistance; +// VkBool32 shaderFloat64; +// VkBool32 shaderInt64; +// VkBool32 shaderInt16; // done +// VkBool32 shaderResourceResidency; +// VkBool32 shaderResourceMinLod; +// VkBool32 sparseBinding; +// VkBool32 sparseResidencyBuffer; +// VkBool32 sparseResidencyImage2D; +// VkBool32 sparseResidencyImage3D; +// VkBool32 sparseResidency2Samples; +// VkBool32 sparseResidency4Samples; +// VkBool32 sparseResidency8Samples; +// VkBool32 sparseResidency16Samples; +// VkBool32 sparseResidencyAliased; +// VkBool32 variableMultisampleRate; +// VkBool32 inheritedQueries; +//} VkPhysicalDeviceFeatures; + +/** Initializes the physical device properties of this instance. */ +void MVKPhysicalDevice::initProperties() { + memset(&_properties, 0, sizeof(_properties)); // Start with everything cleared + + // TODO: determine correct values + _properties.apiVersion = MVK_VULKAN_API_VERSION; + _properties.driverVersion = MVK_VERSION; + _properties.vendorID = 0; + _properties.deviceID = 0; + _properties.deviceType = getDeviceType(_mtlDevice); + strlcpy(_properties.deviceName, _mtlDevice.name.UTF8String, VK_MAX_PHYSICAL_DEVICE_NAME_SIZE); + memset(_properties.pipelineCacheUUID, 0, VK_UUID_SIZE); + + // Limits +#if MVK_IOS + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily2_v1] ) { + _properties.limits.maxColorAttachments = 8; + } else { + _properties.limits.maxColorAttachments = 4; + } +#endif +#if MVK_MACOS + _properties.limits.maxColorAttachments = 8; +#endif + + _properties.limits.maxFragmentOutputAttachments = _properties.limits.maxColorAttachments; + _properties.limits.maxFragmentDualSrcAttachments = _properties.limits.maxFragmentOutputAttachments; + + _properties.limits.framebufferColorSampleCounts = _metalFeatures.supportedSampleCounts; + _properties.limits.framebufferDepthSampleCounts = _metalFeatures.supportedSampleCounts; + _properties.limits.framebufferStencilSampleCounts = _metalFeatures.supportedSampleCounts; + _properties.limits.framebufferNoAttachmentsSampleCounts = _metalFeatures.supportedSampleCounts; + _properties.limits.sampledImageColorSampleCounts = _metalFeatures.supportedSampleCounts; + _properties.limits.sampledImageIntegerSampleCounts = _metalFeatures.supportedSampleCounts; + _properties.limits.sampledImageDepthSampleCounts = _metalFeatures.supportedSampleCounts; + _properties.limits.sampledImageStencilSampleCounts = _metalFeatures.supportedSampleCounts; + _properties.limits.storageImageSampleCounts = _metalFeatures.supportedSampleCounts; + + uint32_t maxTextureDimension; +#if MVK_IOS + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v1] ) { + maxTextureDimension = (16 * KIBI); + } else if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v2] ) { + maxTextureDimension = (8 * KIBI); + } else { + maxTextureDimension = (4 * KIBI); + } +#endif +#if MVK_MACOS + maxTextureDimension = (16 * KIBI); +#endif + + _properties.limits.maxImageDimension1D = maxTextureDimension; + _properties.limits.maxImageDimension2D = maxTextureDimension; + _properties.limits.maxImageDimensionCube = maxTextureDimension; + _properties.limits.maxFramebufferWidth = maxTextureDimension; + _properties.limits.maxFramebufferHeight = maxTextureDimension; + _properties.limits.maxFramebufferLayers = 256; + + _properties.limits.maxViewportDimensions[0] = maxTextureDimension; + _properties.limits.maxViewportDimensions[1] = maxTextureDimension; + float maxVPDim = max(_properties.limits.maxViewportDimensions[0], _properties.limits.maxViewportDimensions[1]); + _properties.limits.viewportBoundsRange[0] = (-2.0 * maxVPDim); + _properties.limits.viewportBoundsRange[1] = (2.0 * maxVPDim) - 1; + + _properties.limits.maxImageDimension3D = (2 * KIBI); + _properties.limits.maxImageArrayLayers = (2 * KIBI); + _properties.limits.maxViewports = 1; + _properties.limits.maxSamplerAnisotropy = 16; + + _properties.limits.maxVertexInputAttributes = 31; + _properties.limits.maxVertexInputBindings = 31; + + _properties.limits.maxVertexInputAttributeOffset = (4 * KIBI); + _properties.limits.maxVertexInputBindingStride = _properties.limits.maxVertexInputAttributeOffset - 1; + + _properties.limits.maxPerStageDescriptorUniformBuffers = _metalFeatures.maxPerStageBufferCount; + _properties.limits.maxPerStageDescriptorStorageBuffers = _metalFeatures.maxPerStageBufferCount; + _properties.limits.maxPerStageDescriptorSampledImages = _metalFeatures.maxPerStageTextureCount; + _properties.limits.maxPerStageDescriptorStorageImages = _metalFeatures.maxPerStageTextureCount; + _properties.limits.maxPerStageDescriptorSamplers = _metalFeatures.maxPerStageSamplerCount; + _properties.limits.maxDescriptorSetInputAttachments = _metalFeatures.maxPerStageTextureCount; + + _properties.limits.maxPerStageResources = (_metalFeatures.maxPerStageBufferCount + _metalFeatures.maxPerStageTextureCount); + _properties.limits.maxFragmentCombinedOutputResources = _properties.limits.maxPerStageResources; + + _properties.limits.maxDescriptorSetSamplers = (_properties.limits.maxPerStageDescriptorSamplers * 2); + _properties.limits.maxDescriptorSetUniformBuffers = (_properties.limits.maxPerStageDescriptorUniformBuffers * 2); + _properties.limits.maxDescriptorSetUniformBuffersDynamic = (_properties.limits.maxPerStageDescriptorUniformBuffers * 2); + _properties.limits.maxDescriptorSetStorageBuffers = (_properties.limits.maxPerStageDescriptorStorageBuffers * 2); + _properties.limits.maxDescriptorSetStorageBuffersDynamic = (_properties.limits.maxPerStageDescriptorStorageBuffers * 2); + _properties.limits.maxDescriptorSetSampledImages = (_properties.limits.maxPerStageDescriptorSampledImages * 2); + _properties.limits.maxDescriptorSetStorageImages = (_properties.limits.maxPerStageDescriptorStorageImages * 2); + + _properties.limits.maxTexelBufferElements = (uint32_t)_metalFeatures.maxMTLBufferSize; + _properties.limits.maxUniformBufferRange = (uint32_t)_metalFeatures.maxMTLBufferSize; + _properties.limits.maxStorageBufferRange = (uint32_t)_metalFeatures.maxMTLBufferSize; + _properties.limits.maxPushConstantsSize = (4 * KIBI); + + _properties.limits.minMemoryMapAlignment = _metalFeatures.mtlBufferAlignment; + _properties.limits.minTexelBufferOffsetAlignment = _metalFeatures.mtlBufferAlignment; + _properties.limits.minUniformBufferOffsetAlignment = _metalFeatures.mtlBufferAlignment; + _properties.limits.minStorageBufferOffsetAlignment = _metalFeatures.mtlBufferAlignment; + _properties.limits.bufferImageGranularity = _metalFeatures.mtlBufferAlignment; + _properties.limits.nonCoherentAtomSize = _metalFeatures.mtlBufferAlignment; + +#if MVK_IOS + _properties.limits.maxFragmentInputComponents = 60; + + if ([_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v1]) { + _properties.limits.optimalBufferCopyOffsetAlignment = 16; + } else { + _properties.limits.optimalBufferCopyOffsetAlignment = 64; + } +#endif +#if MVK_MACOS + _properties.limits.maxFragmentInputComponents = 128; + _properties.limits.optimalBufferCopyOffsetAlignment = 256; +#endif + + + _properties.limits.maxVertexOutputComponents = _properties.limits.maxFragmentInputComponents; + + _properties.limits.optimalBufferCopyRowPitchAlignment = 1; + + _properties.limits.timestampComputeAndGraphics = VK_TRUE; + _properties.limits.timestampPeriod = _mvkTimestampPeriod; + + _properties.limits.pointSizeRange[0] = 1; + _properties.limits.pointSizeRange[1] = 511; + _properties.limits.pointSizeGranularity = 1; + _properties.limits.lineWidthRange[0] = 1; + _properties.limits.lineWidthRange[1] = 1; + _properties.limits.pointSizeGranularity = 1; + +#if MVK_IOS + if ([_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily4_v1]) { + _properties.limits.maxComputeSharedMemorySize = (32 * KIBI); + _properties.limits.maxComputeWorkGroupInvocations = (1 * KIBI); + } else if ([_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v1]) { + _properties.limits.maxComputeSharedMemorySize = (16 * KIBI); + _properties.limits.maxComputeWorkGroupInvocations = 512; + } else { + _properties.limits.maxComputeSharedMemorySize = ((16 * KIBI) - 32); + _properties.limits.maxComputeWorkGroupInvocations = 512; + } +#endif +#if MVK_MACOS + _properties.limits.maxComputeSharedMemorySize = (32 * KIBI); + _properties.limits.maxComputeWorkGroupInvocations = (1 * KIBI); +#endif + + _properties.limits.standardSampleLocations = VK_FALSE; + _properties.limits.strictLines = VK_FALSE; + + + // Features with no specific limits - default to unlimited int values + + _properties.limits.maxMemoryAllocationCount = numeric_limits::max(); + _properties.limits.maxSamplerAllocationCount = numeric_limits::max(); + _properties.limits.maxBoundDescriptorSets = numeric_limits::max(); + + _properties.limits.maxComputeWorkGroupCount[0] = numeric_limits::max(); + _properties.limits.maxComputeWorkGroupCount[1] = numeric_limits::max(); + _properties.limits.maxComputeWorkGroupCount[2] = numeric_limits::max(); + + _properties.limits.maxComputeWorkGroupSize[0] = numeric_limits::max(); + _properties.limits.maxComputeWorkGroupSize[1] = numeric_limits::max(); + _properties.limits.maxComputeWorkGroupSize[2] = numeric_limits::max(); + + _properties.limits.maxDrawIndexedIndexValue = numeric_limits::max(); + _properties.limits.maxDrawIndirectCount = numeric_limits::max(); + + _properties.limits.minTexelOffset = numeric_limits::min(); + _properties.limits.maxTexelOffset = numeric_limits::max(); + _properties.limits.minTexelGatherOffset = numeric_limits::min(); + _properties.limits.maxTexelGatherOffset = numeric_limits::max(); + + _properties.limits.maxClipDistances = numeric_limits::max(); + _properties.limits.maxCombinedClipAndCullDistances = numeric_limits::max(); + + + // Features with unknown limits - default to Vulkan required limits + + _properties.limits.subPixelPrecisionBits = 4; + _properties.limits.subTexelPrecisionBits = 4; + _properties.limits.mipmapPrecisionBits = 4; + _properties.limits.viewportSubPixelBits = 0; + + _properties.limits.maxSamplerLodBias = 2; + + _properties.limits.maxSampleMaskWords = 1; + + _properties.limits.discreteQueuePriorities = 2; + + + // Unsupported features - set to zeros generally + + _properties.limits.sparseAddressSpaceSize = 0; + + _properties.limits.maxTessellationGenerationLevel = 0; + _properties.limits.maxTessellationPatchSize = 0; + _properties.limits.maxTessellationControlPerVertexInputComponents = 0; + _properties.limits.maxTessellationControlPerVertexOutputComponents = 0; + _properties.limits.maxTessellationControlPerPatchOutputComponents = 0; + _properties.limits.maxTessellationControlTotalOutputComponents = 0; + _properties.limits.maxTessellationEvaluationInputComponents = 0; + _properties.limits.maxTessellationEvaluationOutputComponents = 0; + + _properties.limits.maxGeometryShaderInvocations = 0; + _properties.limits.maxGeometryInputComponents = 0; + _properties.limits.maxGeometryOutputComponents = 0; + _properties.limits.maxGeometryOutputVertices = 0; + _properties.limits.maxGeometryTotalOutputComponents = 0; + + _properties.limits.minInterpolationOffset = 0.0; + _properties.limits.maxInterpolationOffset = 0.0; + _properties.limits.subPixelInterpolationOffsetBits = 0; + + _properties.limits.maxCullDistances = 0; + +} + + +#pragma mark VkPhysicalDeviceLimits - List of feature limits available on the device + +//typedef struct VkPhysicalDeviceLimits { +// uint32_t maxImageDimension1D; // done +// uint32_t maxImageDimension2D; // done +// uint32_t maxImageDimension3D; // done +// uint32_t maxImageDimensionCube; // done +// uint32_t maxImageArrayLayers; // done +// uint32_t maxTexelBufferElements; // done +// uint32_t maxUniformBufferRange; // done +// uint32_t maxStorageBufferRange; // done +// uint32_t maxPushConstantsSize; // done +// uint32_t maxMemoryAllocationCount; // done +// uint32_t maxSamplerAllocationCount; // done +// VkDeviceSize bufferImageGranularity; // done +// VkDeviceSize sparseAddressSpaceSize; // done +// uint32_t maxBoundDescriptorSets; // done +// uint32_t maxPerStageDescriptorSamplers; // done +// uint32_t maxPerStageDescriptorUniformBuffers; // done +// uint32_t maxPerStageDescriptorStorageBuffers; // done +// uint32_t maxPerStageDescriptorSampledImages; // done +// uint32_t maxPerStageDescriptorStorageImages; // done +// uint32_t maxPerStageDescriptorInputAttachments; // done +// uint32_t maxPerStageResources; // done +// uint32_t maxDescriptorSetSamplers; // done +// uint32_t maxDescriptorSetUniformBuffers; // done +// uint32_t maxDescriptorSetUniformBuffersDynamic; // done +// uint32_t maxDescriptorSetStorageBuffers; // done +// uint32_t maxDescriptorSetStorageBuffersDynamic; // done +// uint32_t maxDescriptorSetSampledImages; // done +// uint32_t maxDescriptorSetStorageImages; // done +// uint32_t maxDescriptorSetInputAttachments; // done +// uint32_t maxVertexInputAttributes; // done +// uint32_t maxVertexInputBindings; // done +// uint32_t maxVertexInputAttributeOffset; // done +// uint32_t maxVertexInputBindingStride; // done +// uint32_t maxVertexOutputComponents; // done +// uint32_t maxTessellationGenerationLevel; // done +// uint32_t maxTessellationPatchSize; // done +// uint32_t maxTessellationControlPerVertexInputComponents; // done +// uint32_t maxTessellationControlPerVertexOutputComponents; // done +// uint32_t maxTessellationControlPerPatchOutputComponents; // done +// uint32_t maxTessellationControlTotalOutputComponents; // done +// uint32_t maxTessellationEvaluationInputComponents; // done +// uint32_t maxTessellationEvaluationOutputComponents; // done +// uint32_t maxGeometryShaderInvocations; // done +// uint32_t maxGeometryInputComponents; // done +// uint32_t maxGeometryOutputComponents; // done +// uint32_t maxGeometryOutputVertices; // done +// uint32_t maxGeometryTotalOutputComponents; // done +// uint32_t maxFragmentInputComponents; // done +// uint32_t maxFragmentOutputAttachments; // done +// uint32_t maxFragmentDualSrcAttachments; // done +// uint32_t maxFragmentCombinedOutputResources; // done +// uint32_t maxComputeSharedMemorySize; // done +// uint32_t maxComputeWorkGroupCount[3]; // done +// uint32_t maxComputeWorkGroupInvocations; // done +// uint32_t maxComputeWorkGroupSize[3]; // done +// uint32_t subPixelPrecisionBits; // done +// uint32_t subTexelPrecisionBits; // done +// uint32_t mipmapPrecisionBits; // done +// uint32_t maxDrawIndexedIndexValue; // done +// uint32_t maxDrawIndirectCount; // done +// float maxSamplerLodBias; // done +// float maxSamplerAnisotropy; // done +// uint32_t maxViewports; // done +// uint32_t maxViewportDimensions[2]; // done +// float viewportBoundsRange[2]; // done +// uint32_t viewportSubPixelBits; // done +// size_t minMemoryMapAlignment; // done +// VkDeviceSize minTexelBufferOffsetAlignment; // done +// VkDeviceSize minUniformBufferOffsetAlignment; // done +// VkDeviceSize minStorageBufferOffsetAlignment; // done +// int32_t minTexelOffset; // done +// uint32_t maxTexelOffset; // done +// int32_t minTexelGatherOffset; // done +// uint32_t maxTexelGatherOffset; // done +// float minInterpolationOffset; // done +// float maxInterpolationOffset; // done +// uint32_t subPixelInterpolationOffsetBits; // done +// uint32_t maxFramebufferWidth; // done +// uint32_t maxFramebufferHeight; // done +// uint32_t maxFramebufferLayers; // done +// VkSampleCountFlags framebufferColorSampleCounts; // done +// VkSampleCountFlags framebufferDepthSampleCounts; // done +// VkSampleCountFlags framebufferStencilSampleCounts; // done +// VkSampleCountFlags framebufferNoAttachmentsSampleCounts; // done +// uint32_t maxColorAttachments; // done +// VkSampleCountFlags sampledImageColorSampleCounts; // done +// VkSampleCountFlags sampledImageIntegerSampleCounts; // done +// VkSampleCountFlags sampledImageDepthSampleCounts; // done +// VkSampleCountFlags sampledImageStencilSampleCounts; // done +// VkSampleCountFlags storageImageSampleCounts; // done +// uint32_t maxSampleMaskWords; // done +// VkBool32 timestampComputeAndGraphics; // done +// float timestampPeriod; // done +// uint32_t maxClipDistances; // done +// uint32_t maxCullDistances; // done +// uint32_t maxCombinedClipAndCullDistances; // done +// uint32_t discreteQueuePriorities; // done +// float pointSizeRange[2]; // done +// float lineWidthRange[2]; // done +// float pointSizeGranularity; // done +// float lineWidthGranularity; // done +// VkBool32 strictLines; // done +// VkBool32 standardSampleLocations; // done +// VkDeviceSize optimalBufferCopyOffsetAlignment; // done +// VkDeviceSize optimalBufferCopyRowPitchAlignment; // done +// VkDeviceSize nonCoherentAtomSize; // done +//} VkPhysicalDeviceLimits; + +//typedef struct { +// VkBool32 residencyStandard2DBlockShape; +// VkBool32 residencyStandard2DMSBlockShape; +// VkBool32 residencyStandard3DBlockShape; +// VkBool32 residencyAlignedMipSize; +// VkBool32 residencyNonResident; +// VkBool32 residencyNonResidentStrict; +//} VkPhysicalDeviceSparseProperties; + +/** Initializes the memory properties of this instance. */ +void MVKPhysicalDevice::initMemoryProperties() { + + // Metal Shared: + // - applies to both buffers and textures + // - default mode for buffers on both iOS & macOS + // - default mode for textures on iOS + // - one copy of memory visible to both CPU & GPU + // - coherent at command buffer boundaries + // Metal Private: + // - applies to both buffers and textures + // - accessed only by GPU through render, compute, or BLIT operations + // - no access by CPU + // - always use for framebuffers and renderable textures + // Metal Managed: + // - applies to both buffers and textures + // - default mode for textures on macOS + // - two copies of each buffer or texture when discrete memory available + // - convenience of shared mode, performance of private mode + // - on unified systems behaves like shared memory and has only one copy of content + // - when writing, use: + // - buffer didModifyRange: + // - texture replaceRegion: + // - when reading, use: + // - encoder synchronizeResource: followed by + // - cmdbuff waitUntilCompleted (or completion handler) + // - buffer/texture getBytes: + + // TODO: determine correct values - particularly heap size + _memoryProperties.memoryHeapCount = 1; + _memoryProperties.memoryHeaps[0].flags = (VK_MEMORY_HEAP_DEVICE_LOCAL_BIT); + _memoryProperties.memoryHeaps[0].size = 0; + + _memoryProperties.memoryTypes[0].heapIndex = 0; + _memoryProperties.memoryTypes[0].propertyFlags = MVK_VK_MEMORY_TYPE_METAL_PRIVATE; // Private storage + _memoryProperties.memoryTypes[1].heapIndex = 0; + _memoryProperties.memoryTypes[1].propertyFlags = MVK_VK_MEMORY_TYPE_METAL_SHARED; // Shared storage + _memoryProperties.memoryTypes[2].heapIndex = 0; + _memoryProperties.memoryTypes[2].propertyFlags = MVK_VK_MEMORY_TYPE_METAL_MANAGED; // Managed storage + +#if MVK_MACOS + _memoryProperties.memoryTypeCount = 3; + _privateMemoryTypes = 0x1; // Private only + _hostVisibleMemoryTypes = 0x6; // Shared & managed + _allMemoryTypes = 0x7; // Private, shared, & managed +#endif +#if MVK_IOS + _memoryProperties.memoryTypeCount = 2; // Managed storage not available on iOS + _privateMemoryTypes = 0x1; // Private only + _hostVisibleMemoryTypes = 0x2; // Shared only + _allMemoryTypes = 0x3; // Private & shared +#endif +} + +void MVKPhysicalDevice::logFeatureSets() { + string fsMsg = "GPU device %s supports the following Metal Feature Sets:"; + +#if MVK_IOS + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily4_v1] ) { fsMsg += "\n\tiOS GPU Family 4 v1"; } + + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v3] ) { fsMsg += "\n\tiOS GPU Family 3 v3"; } + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v2] ) { fsMsg += "\n\tiOS GPU Family 3 v2"; } + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v1] ) { fsMsg += "\n\tiOS GPU Family 3 v1"; } + + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily2_v4] ) { fsMsg += "\n\tiOS GPU Family 2 v4"; } + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily2_v3] ) { fsMsg += "\n\tiOS GPU Family 2 v3"; } + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily2_v2] ) { fsMsg += "\n\tiOS GPU Family 2 v2"; } + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily2_v1] ) { fsMsg += "\n\tiOS GPU Family 2 v1"; } + + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v4] ) { fsMsg += "\n\tiOS GPU Family 1 v4"; } + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v3] ) { fsMsg += "\n\tiOS GPU Family 1 v3"; } + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v2] ) { fsMsg += "\n\tiOS GPU Family 1 v2"; } + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v1] ) { fsMsg += "\n\tiOS GPU Family 1 v1"; } +#endif + +#if MVK_MACOS + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily1_v3] ) { fsMsg += "\n\tOSX GPU Family 1 v3"; } + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily1_v2] ) { fsMsg += "\n\tOSX GPU Family 1 v2"; } + if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily1_v1] ) { fsMsg += "\n\tOSX GPU Family 1 v1"; } +#endif + + MVKLogInfo(fsMsg.c_str(), _properties.deviceName); +} + +/** Initializes the queue families supported by this instance. */ +void MVKPhysicalDevice::initQueueFamilies() { + + // TODO: determine correct values + _queueFamilyCount = 1; + _queueFamilyProperties[0].queueFlags = (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT); + _queueFamilyProperties[0].queueCount = 16; + _queueFamilyProperties[0].timestampValidBits = 64; + _queueFamilyProperties[0].minImageTransferGranularity = { 1, 1, 1}; +} + +MVKPhysicalDevice::MVKPhysicalDevice(MVKInstance* mvkInstance, id mtlDevice) { + _mvkInstance = mvkInstance; + _mtlDevice = [mtlDevice retain]; + + initMetalFeatures(); // Call first. + initFeatures(); // Call second. + initProperties(); // Call third. + initMemoryProperties(); + initQueueFamilies(); + logFeatureSets(); +} + +MVKPhysicalDevice::~MVKPhysicalDevice() { + [_mtlDevice release]; +} + + +#pragma mark - +#pragma mark MVKDevice + +PFN_vkVoidFunction MVKDevice::getProcAddr(const char* pName) { + return _physicalDevice->_mvkInstance->getProcAddr(pName); +} + +VkResult MVKDevice::getDeviceQueue(uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue) { + *pQueue = (VkQueue)_queueFamilies[queueFamilyIndex]->getQueue(queueIndex); + return VK_SUCCESS; +} + +VkResult MVKDevice::waitIdle() { + for (auto& q : _queues) { q->waitIdle(kMVKCommandUseDeviceWaitIdle); } + return VK_SUCCESS; +} + + +#pragma mark Object lifecycle + +uint32_t MVKDevice::getVulkanMemoryTypeIndex(MTLStorageMode mtlStorageMode) { + VkMemoryPropertyFlags vkMemFlags; + switch (mtlStorageMode) { + case MTLStorageModePrivate: + vkMemFlags = MVK_VK_MEMORY_TYPE_METAL_PRIVATE; + break; + case MTLStorageModeShared: + vkMemFlags = MVK_VK_MEMORY_TYPE_METAL_SHARED; + break; +#if MVK_MACOS + case MTLStorageModeManaged: + vkMemFlags = MVK_VK_MEMORY_TYPE_METAL_MANAGED; + break; +#endif + default: + vkMemFlags = MVK_VK_MEMORY_TYPE_METAL_SHARED; + break; + } + + for (uint32_t mtIdx = 0; mtIdx < _pMemoryProperties->memoryTypeCount; mtIdx++) { + if (_pMemoryProperties->memoryTypes[mtIdx].propertyFlags == vkMemFlags) { return mtIdx; } + } + MVKAssert(false, "Could not find memory type corresponding to VkMemoryPropertyFlags %d", vkMemFlags); + return 0; +} + +MVKBuffer* MVKDevice::createBuffer(const VkBufferCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + return (MVKBuffer*)addResource(new MVKBuffer(this, pCreateInfo)); +} + +void MVKDevice::destroyBuffer(MVKBuffer* mvkBuff, + const VkAllocationCallbacks* pAllocator) { + delete removeResource(mvkBuff); +} + +MVKBufferView* MVKDevice::createBufferView(const VkBufferViewCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + return new MVKBufferView(this, pCreateInfo); +} + +void MVKDevice::destroyBufferView(MVKBufferView* mvkBuffView, + const VkAllocationCallbacks* pAllocator) { + delete mvkBuffView; +} + +MVKImage* MVKDevice::createImage(const VkImageCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + return (MVKImage*)addResource(new MVKImage(this, pCreateInfo)); +} + +void MVKDevice::destroyImage(MVKImage* mvkImg, + const VkAllocationCallbacks* pAllocator) { + delete removeResource(mvkImg); +} + +MVKImageView* MVKDevice::createImageView(const VkImageViewCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + return new MVKImageView(this, pCreateInfo); +} + +void MVKDevice::destroyImageView(MVKImageView* mvkImgView, + const VkAllocationCallbacks* pAllocator) { + delete mvkImgView; +} + +MVKSwapchain* MVKDevice::createSwapchain(const VkSwapchainCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + return new MVKSwapchain(this, pCreateInfo); +} + +void MVKDevice::destroySwapchain(MVKSwapchain* mvkSwpChn, + const VkAllocationCallbacks* pAllocator) { + delete mvkSwpChn; +} + +MVKSwapchainImage* MVKDevice::createSwapchainImage(const VkImageCreateInfo* pCreateInfo, + MVKSwapchain* swapchain, + const VkAllocationCallbacks* pAllocator) { + return (MVKSwapchainImage*)addResource(new MVKSwapchainImage(this, pCreateInfo, swapchain)); +} + +void MVKDevice::destroySwapchainImage(MVKSwapchainImage* mvkImg, + const VkAllocationCallbacks* pAllocator) { + delete removeResource(mvkImg); +} + +MVKFence* MVKDevice::createFence(const VkFenceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + return new MVKFence(this, pCreateInfo); +} + +void MVKDevice::destroyFence(MVKFence* mvkFence, + const VkAllocationCallbacks* pAllocator) { + delete mvkFence; +} + +MVKSemaphore* MVKDevice::createSemaphore(const VkSemaphoreCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + return new MVKSemaphore(this, pCreateInfo); +} + +void MVKDevice::destroySemaphore(MVKSemaphore* mvkSem4, + const VkAllocationCallbacks* pAllocator) { + delete mvkSem4; +} + +MVKQueryPool* MVKDevice::createQueryPool(const VkQueryPoolCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + switch (pCreateInfo->queryType) { + case VK_QUERY_TYPE_OCCLUSION: + return new MVKOcclusionQueryPool(this, pCreateInfo); + case VK_QUERY_TYPE_TIMESTAMP: + return new MVKTimestampQueryPool(this, pCreateInfo); + default: + return new MVKUnsupportedQueryPool(this, pCreateInfo); + } +} + +void MVKDevice::destroyQueryPool(MVKQueryPool* mvkQP, + const VkAllocationCallbacks* pAllocator) { + delete mvkQP; +} + +MVKShaderModule* MVKDevice::createShaderModule(const VkShaderModuleCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + return new MVKShaderModule(this, pCreateInfo); +} + +void MVKDevice::destroyShaderModule(MVKShaderModule* mvkShdrMod, + const VkAllocationCallbacks* pAllocator) { + delete mvkShdrMod; +} + +MVKPipelineCache* MVKDevice::createPipelineCache(const VkPipelineCacheCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + return new MVKPipelineCache(this, pCreateInfo); +} + +void MVKDevice::destroyPipelineCache(MVKPipelineCache* mvkPLC, + const VkAllocationCallbacks* pAllocator) { + delete mvkPLC; +} + +MVKPipelineLayout* MVKDevice::createPipelineLayout(const VkPipelineLayoutCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + return new MVKPipelineLayout(this, pCreateInfo); +} + +void MVKDevice::destroyPipelineLayout(MVKPipelineLayout* mvkPLL, + const VkAllocationCallbacks* pAllocator) { + delete mvkPLL; +} + +template +VkResult MVKDevice::createPipelines(VkPipelineCache pipelineCache, + uint32_t count, + const PipelineInfoType* pCreateInfos, + const VkAllocationCallbacks* pAllocator, + VkPipeline* pPipelines) { + VkResult rslt = VK_SUCCESS; + MVKPipelineCache* mvkPLC = (MVKPipelineCache*)pipelineCache; + + for (uint32_t plIdx = 0; plIdx < count; plIdx++) { + const PipelineInfoType* pCreateInfo = &pCreateInfos[plIdx]; + + // See if this pipeline has a parent. This can come either directly + // via basePipelineHandle or indirectly via basePipelineIndex. + MVKPipeline* parentPL = VK_NULL_HANDLE; + if ( mvkAreFlagsEnabled(pCreateInfo->flags, VK_PIPELINE_CREATE_DERIVATIVE_BIT) ) { + VkPipeline vkParentPL = pCreateInfo->basePipelineHandle; + int32_t parentPLIdx = pCreateInfo->basePipelineIndex; + if ( !vkParentPL && (parentPLIdx >= 0)) { vkParentPL = pPipelines[parentPLIdx]; } + parentPL = vkParentPL ? (MVKPipeline*)vkParentPL : VK_NULL_HANDLE; + } + + // Create the pipeline and if creation was successful, insert the new pipeline + // in the return array and add it to the pipeline cache (if the cache was specified). + // If creation was unsuccessful, insert NULL into the return array, change the + // result code of this function, and destroy the broken pipeline. + MVKPipeline* mvkPL = new PipelineType(this, mvkPLC, parentPL, pCreateInfo); + VkResult plRslt = mvkPL->getConfigurationResult(); + if (plRslt == VK_SUCCESS) { + pPipelines[plIdx] = (VkPipeline)mvkPL; + } else { + rslt = plRslt; + pPipelines[plIdx] = VK_NULL_HANDLE; + delete mvkPL; + } + } + + return rslt; +} + +// Create concrete implementations of the two variations of the mvkCreatePipelines() function +// that we will be using. This is required since the template definition is location in this +// implementation file instead of in the header file. This is a realistic approach if the +// universe of possible template implementation variations is small and known in advance. +template VkResult MVKDevice::createPipelines(VkPipelineCache pipelineCache, + uint32_t count, + const VkGraphicsPipelineCreateInfo* pCreateInfos, + const VkAllocationCallbacks* pAllocator, + VkPipeline* pPipelines); + +template VkResult MVKDevice::createPipelines(VkPipelineCache pipelineCache, + uint32_t count, + const VkComputePipelineCreateInfo* pCreateInfos, + const VkAllocationCallbacks* pAllocator, + VkPipeline* pPipelines); + +void MVKDevice::destroyPipeline(MVKPipeline* mvkPL, + const VkAllocationCallbacks* pAllocator) { + delete mvkPL; +} + +MVKSampler* MVKDevice::createSampler(const VkSamplerCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + return new MVKSampler(this, pCreateInfo); +} + +void MVKDevice::destroySampler(MVKSampler* mvkSamp, + const VkAllocationCallbacks* pAllocator) { + delete mvkSamp; +} + +MVKDescriptorSetLayout* MVKDevice::createDescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + return new MVKDescriptorSetLayout(this, pCreateInfo); +} + +void MVKDevice::destroyDescriptorSetLayout(MVKDescriptorSetLayout* mvkDSL, + const VkAllocationCallbacks* pAllocator) { + delete mvkDSL; +} + +MVKDescriptorPool* MVKDevice::createDescriptorPool(const VkDescriptorPoolCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + return new MVKDescriptorPool(this, pCreateInfo); +} + +void MVKDevice::destroyDescriptorPool(MVKDescriptorPool* mvkDP, + const VkAllocationCallbacks* pAllocator) { + delete mvkDP; +} + +MVKFramebuffer* MVKDevice::createFramebuffer(const VkFramebufferCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + return new MVKFramebuffer(this, pCreateInfo); +} + +void MVKDevice::destroyFramebuffer(MVKFramebuffer* mvkFB, + const VkAllocationCallbacks* pAllocator) { + delete mvkFB; +} + +MVKRenderPass* MVKDevice::createRenderPass(const VkRenderPassCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + return new MVKRenderPass(this, pCreateInfo); +} + +void MVKDevice::destroyRenderPass(MVKRenderPass* mvkRP, + const VkAllocationCallbacks* pAllocator) { + delete mvkRP; +} + +MVKCommandPool* MVKDevice::createCommandPool(const VkCommandPoolCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + return new MVKCommandPool(this, pCreateInfo); +} + +void MVKDevice::destroyCommandPool(MVKCommandPool* mvkCmdPool, + const VkAllocationCallbacks* pAllocator) { + delete mvkCmdPool; +} + +MVKDeviceMemory* MVKDevice::allocateMemory(const VkMemoryAllocateInfo* pAllocateInfo, + const VkAllocationCallbacks* pAllocator) { + return new MVKDeviceMemory(this, pAllocateInfo, pAllocator); +} + +void MVKDevice::freeMemory(MVKDeviceMemory* mvkDevMem, + const VkAllocationCallbacks* pAllocator) { + delete mvkDevMem; +} + +/** Adds the specified resource for tracking, and returns the added resource. */ +MVKResource* MVKDevice::addResource(MVKResource* rez) { + lock_guard lock(_rezLock); + _resources.push_back(rez); + return rez; +} + +/** Removes the specified resource for tracking and returns the removed resource. */ +MVKResource* MVKDevice::removeResource(MVKResource* rez) { + lock_guard lock(_rezLock); + mvkRemoveFirstOccurance(_resources, rez); + return rez; +} + +void MVKDevice::applyMemoryBarrier(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkMemoryBarrier* pMemoryBarrier, + MVKCommandEncoder* cmdEncoder, + MVKCommandUse cmdUse) { + lock_guard lock(_rezLock); + for (auto& rez : _resources) { + rez->applyMemoryBarrier(srcStageMask, dstStageMask, pMemoryBarrier, cmdEncoder, cmdUse); + } +} + +#define asMS(V) ((V) * 1000.0) +void MVKDevice::addShaderCompilationEventPerformance(MVKShaderCompilationEventPerformance& shaderCompilationEvent, + NSTimeInterval startTime, NSTimeInterval endTime) { + + if ( !_mvkConfig.performanceTracking ) { return; } + + lock_guard lock(_shaderCompPerfLock); + + NSTimeInterval currInterval = (endTime ? endTime : getPerformanceTimestamp()) - startTime; + shaderCompilationEvent.minimumInterval = min(currInterval, shaderCompilationEvent.minimumInterval); + shaderCompilationEvent.maximumInterval = max(currInterval, shaderCompilationEvent.maximumInterval); + double totalInverval = (shaderCompilationEvent.averageInterval * shaderCompilationEvent.count++) + currInterval; + shaderCompilationEvent.averageInterval = totalInverval / shaderCompilationEvent.count; + + MVKLogInfo("%s performance curr: %.3f ms, avg: %.3f ms, min: %.3f ms, max: %.3f ms, count: %d", + getShaderCompilationEventName(shaderCompilationEvent), + asMS(currInterval), + asMS(shaderCompilationEvent.averageInterval), + asMS(shaderCompilationEvent.minimumInterval), + asMS(shaderCompilationEvent.maximumInterval), + shaderCompilationEvent.count); +} + +const char* MVKDevice::getShaderCompilationEventName(MVKShaderCompilationEventPerformance& shaderCompilationEvent) { + if (&shaderCompilationEvent == &_shaderCompilationPerformance.spirvToMSL) { return "Convert SPIR-V to MSL source code"; } + if (&shaderCompilationEvent == &_shaderCompilationPerformance.mslCompile) { return "Compile MSL source code into a MTLLibrary"; } + if (&shaderCompilationEvent == &_shaderCompilationPerformance.mslLoad) { return "Load pre-compiled MSL code into a MTLLibrary"; } + if (&shaderCompilationEvent == &_shaderCompilationPerformance.functionRetrieval) { return "Retrieve a MTLFunction from a MTLLibrary"; } + if (&shaderCompilationEvent == &_shaderCompilationPerformance.functionSpecialization) { return "Specialize a retrieved MTLFunction"; } + if (&shaderCompilationEvent == &_shaderCompilationPerformance.pipelineCompile) { return "Compile MTLFunctions into a pipeline"; } + return "Unknown shader compile event"; +} + +void MVKDevice::getShaderCompilationPerformanceStatistics(MVKShaderCompilationPerformance* pShaderCompPerf) { + lock_guard lock(_shaderCompPerfLock); + + if (pShaderCompPerf) { *pShaderCompPerf = _shaderCompilationPerformance; } +} + + +#pragma mark Metal + +uint32_t MVKDevice::getMetalBufferIndexForVertexAttributeBinding(uint32_t binding) { + return ((_pMetalFeatures->maxPerStageBufferCount - 1) - binding); +} + +MTLPixelFormat MVKDevice::mtlPixelFormatFromVkFormat(VkFormat vkFormat) { + MTLPixelFormat mtlPixFmt = mvkMTLPixelFormatFromVkFormat(vkFormat); +#if MVK_MACOS + if (mtlPixFmt == MTLPixelFormatDepth24Unorm_Stencil8 && + !getMTLDevice().isDepth24Stencil8PixelFormatSupported) { + return MTLPixelFormatDepth32Float_Stencil8; + } +#endif + return mtlPixFmt; +} + +id MVKDevice::getGlobalVisibilityResultMTLBuffer() { + lock_guard lock(_vizLock); + return _globalVisibilityResultMTLBuffer; +} + +uint32_t MVKDevice::expandVisibilityResultMTLBuffer(uint32_t queryCount) { + lock_guard lock(_vizLock); + + // Ensure we don't overflow the maximum number of queries + _globalVisibilityQueryCount += queryCount; + VkDeviceSize reqBuffLen = (VkDeviceSize)_globalVisibilityQueryCount * kMVKQuerySlotSizeInBytes; + VkDeviceSize maxBuffLen = _pMetalFeatures->maxQueryBufferSize; + VkDeviceSize newBuffLen = min(reqBuffLen, maxBuffLen); + _globalVisibilityQueryCount = uint32_t(newBuffLen / kMVKQuerySlotSizeInBytes); + + if (reqBuffLen > maxBuffLen) { + mvkNotifyErrorWithText(VK_ERROR_OUT_OF_DEVICE_MEMORY, "vkCreateQueryPool(): A maximum of %d total queries are available on this device in its current configuration. See the API notes for the MVKDeviceConfiguration.supportLargeQueryPools configuration parameter for more info.", _globalVisibilityQueryCount); + } + + NSUInteger mtlBuffLen = mvkAlignByteOffset(newBuffLen, _pMetalFeatures->mtlBufferAlignment); + MTLResourceOptions mtlBuffOpts = MTLResourceStorageModeShared | MTLResourceCPUCacheModeDefaultCache; + [_globalVisibilityResultMTLBuffer release]; + _globalVisibilityResultMTLBuffer = [getMTLDevice() newBufferWithLength: mtlBuffLen options: mtlBuffOpts]; // retained + + return _globalVisibilityQueryCount - queryCount; // Might be lower than requested if an overflow occurred +} + + +#pragma mark Construction + +MVKDevice::MVKDevice(MVKPhysicalDevice* physicalDevice, const VkDeviceCreateInfo* pCreateInfo) : _mvkConfig() { +// MVKLogDebug("Creating MVKDevice. Elapsed time: %.6f ms.", mvkGetElapsedMilliseconds()); + + _physicalDevice = physicalDevice; + _pFeatures = &_physicalDevice->_features; + _pMetalFeatures = &_physicalDevice->_metalFeatures; + _pProperties = &_physicalDevice->_properties; + _pMemoryProperties = &_physicalDevice->_memoryProperties; + + // Init const config. Use a pointer to bypass the const qualifier. + MVKDeviceConfiguration* pCfg = (MVKDeviceConfiguration*)&_mvkConfig; + pCfg->debugMode = MVK_DEBUG; + pCfg->supportLargeQueryPools = false; + pCfg->shaderConversionFlipVertexY = true; + pCfg->displayWatermark = MVK_DISPLAY_WATERMARK; + pCfg->performanceTracking = MVK_DEBUG; + pCfg->performanceLoggingFrameCount = MVK_DEBUG ? 300 : 0; + + _globalVisibilityResultMTLBuffer = nil; + _globalVisibilityQueryCount = 0; + + // Verify the requested extension names. Should be same as those requested from instance. + setConfigurationResult(_physicalDevice->_mvkInstance->verifyExtensions(pCreateInfo->enabledExtensionCount, + pCreateInfo->ppEnabledExtensionNames)); + + _commandResourceFactory = new MVKCommandResourceFactory(this); + + // Create the queues + uint32_t qfCnt = _physicalDevice->getQueueFamilyCount(); + VkQueueFamilyProperties qfProperties[qfCnt]; + _physicalDevice->getQueueFamilyProperties(&qfCnt, qfProperties); + _queueFamilies.assign(qfCnt, VK_NULL_HANDLE); + + // For each element in the queue record count, create a queue family with the requested number of queues. + uint32_t qrCnt = pCreateInfo->queueCreateInfoCount; + for (uint32_t qrIdx = 0; qrIdx < qrCnt; qrIdx++) { + const VkDeviceQueueCreateInfo* pQFInfo = &pCreateInfo->pQueueCreateInfos[qrIdx]; + uint32_t qfIdx = pQFInfo->queueFamilyIndex; + if (_queueFamilies[qfIdx] == VK_NULL_HANDLE) { + MVKQueueFamily* qFam = new MVKQueueFamily(this, pQFInfo, &qfProperties[qfIdx]); + _queueFamilies[qfIdx] = qFam; + + // Extract the queues from the queue family into a cache + uint32_t qCnt = qFam->getQueueCount(); + for (uint32_t qIdx = 0; qIdx < qCnt; qIdx++) { + _queues.push_back(qFam->getQueue(qIdx)); + } + } + } + + initPerformanceTracking(); +} + +void MVKDevice::initPerformanceTracking() { + MVKShaderCompilationEventPerformance initPerf; + initPerf.count = 0; + initPerf.averageInterval = 0.0; + initPerf.minimumInterval = numeric_limits::max(); + initPerf.maximumInterval = 0.0; + + _shaderCompilationPerformance.spirvToMSL = initPerf; + _shaderCompilationPerformance.mslCompile = initPerf; + _shaderCompilationPerformance.mslLoad = initPerf; + _shaderCompilationPerformance.functionRetrieval = initPerf; + _shaderCompilationPerformance.functionSpecialization = initPerf; + _shaderCompilationPerformance.pipelineCompile = initPerf; +} + +MVKDevice::~MVKDevice() { + mvkDestroyContainerContents(_queueFamilies); + [_globalVisibilityResultMTLBuffer release]; + delete _commandResourceFactory; +} + + +#pragma mark - +#pragma mark Functions + +/** Initializes the timestamping functionality. */ +static void initTimestamps() { + _mvkTimestampBase = mach_absolute_time(); + mach_timebase_info_data_t timebase; + mach_timebase_info(&timebase); + _mvkTimestampPeriod = (double)timebase.numer / (double)timebase.denom; +// MVKLogDebug("Initializing MoltenVK timestamping. Mach time: %llu. Time period: %d / %d = %.6f.", _mvkTimestampBase, timebase.numer, timebase.denom, _mvkTimestampPeriod); +} + +uint64_t mvkGetTimestamp() { return mach_absolute_time() - _mvkTimestampBase; } + +double mvkGetElapsedMilliseconds() { return (double)mvkGetTimestamp() * _mvkTimestampPeriod / 1e6; } + + +#pragma mark Library initialization + +/** + * Called automatically when the framework is loaded and initialized. + * + * Initialize various device content. + */ +static bool _mvkDevicesInitialized = false; +__attribute__((constructor)) static void MVKInitDataTypes() { + if (_mvkDevicesInitialized ) { return; } + initTimestamps(); + _mvkDevicesInitialized = true; +} + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h new file mode 100644 index 00000000..77062903 --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h @@ -0,0 +1,129 @@ +/* + * MVKDeviceMemory.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKDevice.h" +#include + +#import + +class MVKResource; + + +#pragma mark MVKDeviceMemory + +/** Represents a Vulkan device-space memory allocation. */ +class MVKDeviceMemory : public MVKBaseDeviceObject { + +public: + + /** Returns whether the memory is accessible from the host. */ + inline bool isMemoryHostAccessible() { return (_mtlStorageMode != MTLStorageModePrivate); } + + /** Returns whether the memory is automatically coherent between device and host. */ + inline bool isMemoryHostCoherent() { return (_mtlStorageMode == MTLStorageModeShared); } + + /** Returns the memory already committed by this instance. */ + inline VkDeviceSize getDeviceMemoryCommitment() { return _allocationSize; } + + /** + * Returns the host memory address that represents what would be the beginning of the + * mapped address space if the entire device memory represented by this object were to + * be mapped to host memory. + * + * This is the address to which the offset value in the vMapMemory() call references. + * It only has physical meaning if offset is zero, otherwise it is a logical address + * used to calculate resource offsets. + * + * This function must only be called between vkMapMemory() and vkUnmapMemory() calls. + */ + inline void* getLogicalMappedMemory() { return _pLogicalMappedMemory; } + + /** + * Maps the memory address at the specified offset from the start of this memory allocation, + * and returns the address in the specified data reference. + */ + VkResult map(VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData); + + /** Unmaps a previously mapped memory range. */ + void unmap(); + + /** Allocates mapped host memory, and returns a pointer to it. */ + void* allocateMappedMemory(VkDeviceSize offset, VkDeviceSize size); + + /** + * 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. + */ + VkResult flushToDevice(VkDeviceSize offset, VkDeviceSize size, bool evenIfCoherent = false); + + /** + * If this memory is host-visible, pulls the specified memory range from the device. + * Normally, pulling will only occur if the device memory is non-coherent, but pulling + * to coherent memory can be forced by setting evenIfCoherent to true. + */ + VkResult pullFromDevice(VkDeviceSize offset, VkDeviceSize size, bool evenIfCoherent = false); + + +#pragma mark Metal + + /** Returns the Metal buffer underlying this memory allocation. */ + inline id getMTLBuffer() { return _mtlBuffer; } + + /** Returns the Metal storage mode used by this memory allocation. */ + inline MTLStorageMode getMTLStorageMode() { return _mtlStorageMode; } + + /** Returns the Metal CPU cache mode used by this memory allocation. */ + inline MTLCPUCacheMode getMTLCPUCacheMode() { return _mtlCPUCacheMode; } + + /** Returns the Metal reource options used by this memory allocation. */ + inline MTLResourceOptions getMTLResourceOptions() { return _mtlResourceOptions; } + + +#pragma mark Construction + + /** Constructs an instance for the specified device. */ + MVKDeviceMemory(MVKDevice* device, + const VkMemoryAllocateInfo* pAllocateInfo, + const VkAllocationCallbacks* pAllocator); + + ~MVKDeviceMemory() override; + +protected: + friend MVKResource; + + VkDeviceSize adjustMemorySize(VkDeviceSize size, VkDeviceSize offset); + bool mapToSingleResource(VkDeviceSize offset, VkDeviceSize size); + void addResource(MVKResource* rez); + void removeResource(MVKResource* rez); + + std::vector _resources; + VkDeviceSize _allocationSize; + VkDeviceSize _mapOffset; + VkDeviceSize _mapSize; + id _mtlBuffer; + MTLResourceOptions _mtlResourceOptions; + MTLStorageMode _mtlStorageMode; + MTLCPUCacheMode _mtlCPUCacheMode; + void* _pMappedHostAllocation; + void* _pMappedMemory; + void* _pLogicalMappedMemory; +}; + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm new file mode 100644 index 00000000..05942dac --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm @@ -0,0 +1,192 @@ +/* + * MVKDeviceMemory.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKDeviceMemory.h" +#include "MVKImage.h" +#include "mvk_datatypes.h" +#include "MVKFoundation.h" +#include +#include + +#pragma mark MVKDeviceMemory + +// Metal does not support the concept of mappable device memory separate from individual +// resources. There are a number of potentially conflicting requirements defined by Vulkan +// that make it a challenge to map device memory to Metal resources. +// 1) Memory can be mapped and populated prior to any resources being bound. +// 2) Coherent memory can be mapped forever and simply overwritten without regard for +// requiring host generated update indications. +// 3) MTLTextures are never natively coherent. +// 4) MTLBuffers are restricted to smaller sizes (eg. 256MB - 1GB) than MTLTextures. +// +// To try to deal with all of this... +// 1) If the mapped range falls within a single resource, we map it directly. This allows +// us to maximize the size of the resources (images and buffers can be larger)...and +// coherent buffers can be mapped directly. +// 2) If we can't map to a single resource, and memory must be coherent, allocate a single +// coherent MTLBuffer for the entire memory range. If any attached resources already have +// content, the subsequent coherent pullFromDevice() will populate the larger MTLBuffer. +// 3) If we can't map to a single resource, and memory is not coherent, we can allocate the +// host portion as an aligned malloc, and the individual resources will copy to and from it. +// 4) There is no way around requiring coherent memory that is used for image to be updated +// by the host, or at least unmapped, so that we have a signal to update MTLTexture content. +VkResult MVKDeviceMemory::map(VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData) { + + if ( !isMemoryHostAccessible() ) { + return mvkNotifyErrorWithText(VK_ERROR_MEMORY_MAP_FAILED, "Private GPU-only memory cannot be mapped to host memory."); + } + + if (_pMappedMemory) { + return mvkNotifyErrorWithText(VK_ERROR_MEMORY_MAP_FAILED, "Memory is already mapped. Call vkUnmapMemory() first."); + } + + VkDeviceSize mapSize = adjustMemorySize(size, offset); +// MVKLogDebug("Mapping device memory %p with offset %d and size %d.", this, offset, mapSize); + if ( !mapToSingleResource(offset, mapSize) ) { + if (isMemoryHostCoherent()) { + if ( !_mtlBuffer ) { + NSUInteger mtlBuffLen = mvkAlignByteOffset(_allocationSize, _device->_pMetalFeatures->mtlBufferAlignment); + _mtlBuffer = [getMTLDevice() newBufferWithLength: mtlBuffLen options: _mtlResourceOptions]; // retained +// MVKLogDebug("Allocating host mapped memory %p with offset %d and size %d via underlying coherent MTLBuffer %p of size %d.", this, offset, mapSize, _mtlBuffer , _mtlBuffer.length); + } + _pLogicalMappedMemory = _mtlBuffer.contents; + _pMappedMemory = (void*)((uintptr_t)_pLogicalMappedMemory + offset); + } else { +// MVKLogDebug("Allocating host mapped memory %p with offset %d and size %d via host allocation.", this, offset, mapSize); + _pMappedMemory = allocateMappedMemory(offset, mapSize); + } + } + + *ppData = _pMappedMemory; + _mapOffset = offset; + _mapSize = mapSize; + + // Coherent memory does not require flushing by app, so we must flush now, to handle any texture updates. + if (isMemoryHostCoherent()) { pullFromDevice(offset, size, true); } + + return VK_SUCCESS; +} + +void MVKDeviceMemory::unmap() { +// MVKLogDebug("Unapping device memory %p.", this); + + if (!_pMappedMemory) { + mvkNotifyErrorWithText(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. + if (isMemoryHostCoherent()) { flushToDevice(_mapOffset, _mapSize, true); } + + free(_pMappedHostAllocation); + _pMappedHostAllocation = VK_NULL_HANDLE; + _pMappedMemory = VK_NULL_HANDLE; + _pLogicalMappedMemory = VK_NULL_HANDLE; + + _mapOffset = 0; + _mapSize = 0; +} + +// Attempts to map the memory defined by the offset and size to a single resource, and returns +// whether such a mapping was possible. If it was, the mapped region is stored in _pMappedMemory. +bool MVKDeviceMemory::mapToSingleResource(VkDeviceSize offset, VkDeviceSize size) { + for (auto& rez : _resources) { + _pMappedMemory = rez->map(offset, size); + if (_pMappedMemory) { return true; } + } + return false; +} + +void* MVKDeviceMemory::allocateMappedMemory(VkDeviceSize offset, VkDeviceSize size) { + + void* pMapAlloc = VK_NULL_HANDLE; + +// MVKLogDebug("Allocating %d bytes of device memory %p.", size, this); + + size_t mmAlign = _device->_pProperties->limits.minMemoryMapAlignment; + VkDeviceSize deltaOffset = offset % mmAlign; + int err = posix_memalign(&pMapAlloc, mmAlign, mvkAlignByteOffset(size + deltaOffset, mmAlign)); + if (err) { + mvkNotifyErrorWithText(VK_ERROR_MEMORY_MAP_FAILED, "Could not allocate host memory to map to GPU memory."); + return nullptr; + } + + _pMappedHostAllocation = pMapAlloc; + _pLogicalMappedMemory = (void*)((uintptr_t)pMapAlloc - offset); + + return (void*)((uintptr_t)pMapAlloc + deltaOffset); +} + +VkResult MVKDeviceMemory::flushToDevice(VkDeviceSize offset, VkDeviceSize size, bool evenIfCoherent) { + // Coherent memory is flushed on unmap(), so it is only flushed if forced + if (size > 0 && isMemoryHostAccessible() && (evenIfCoherent || !isMemoryHostCoherent()) ) { + VkDeviceSize memSize = adjustMemorySize(size, offset); + for (auto& rez : _resources) { rez->flushToDevice(offset, memSize); } + } + return VK_SUCCESS; +} + +VkResult MVKDeviceMemory::pullFromDevice(VkDeviceSize offset, VkDeviceSize size, bool evenIfCoherent) { + // Coherent memory is flushed on unmap(), so it is only flushed if forced + VkDeviceSize memSize = adjustMemorySize(size, offset); + if (memSize > 0 && isMemoryHostAccessible() && (evenIfCoherent || !isMemoryHostCoherent()) ) { + for (auto& rez : _resources) { rez->pullFromDevice(offset, memSize); } + } + return VK_SUCCESS; +} + +/** + * If the size parameter is the special constant VK_WHOLE_SIZE, returns the size of memory + * between offset and the end of the buffer, otherwise simply returns size. + */ +VkDeviceSize MVKDeviceMemory::adjustMemorySize(VkDeviceSize size, VkDeviceSize offset) { + return (size == VK_WHOLE_SIZE) ? (_allocationSize - offset) : size; +} + +void MVKDeviceMemory::addResource(MVKResource* rez) { _resources.push_back(rez); } + +void MVKDeviceMemory::removeResource(MVKResource* rez) { mvkRemoveAllOccurances(_resources, rez); } + +MVKDeviceMemory::MVKDeviceMemory(MVKDevice* device, + const VkMemoryAllocateInfo* pAllocateInfo, + const VkAllocationCallbacks* pAllocator) : MVKBaseDeviceObject(device) { + _allocationSize = pAllocateInfo->allocationSize; + _mtlBuffer = nil; + _mapOffset = 0; + _mapSize = 0; + + _pMappedHostAllocation = VK_NULL_HANDLE; + _pMappedMemory = VK_NULL_HANDLE; + _pLogicalMappedMemory = VK_NULL_HANDLE; + + // Set Metal memory parameters + VkMemoryPropertyFlags vkMemProps = _device->_pMemoryProperties->memoryTypes[pAllocateInfo->memoryTypeIndex].propertyFlags; + _mtlResourceOptions = mvkMTLResourceOptionsFromVkMemoryPropertyFlags(vkMemProps); + _mtlStorageMode = mvkMTLStorageModeFromVkMemoryPropertyFlags(vkMemProps); + _mtlCPUCacheMode = mvkMTLCPUCacheModeFromVkMemoryPropertyFlags(vkMemProps); +} + +MVKDeviceMemory::~MVKDeviceMemory() { + // Unbind any resources that are using me. Iterate a copy of the collection, + // to allow the resource to callback to remove itself from the collection. + auto rezCopies = _resources; + for (auto& rez : rezCopies) { rez->bindDeviceMemory(nullptr, 0); } + + [_mtlBuffer release]; + _mtlBuffer = nil; +} diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.h b/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.h new file mode 100644 index 00000000..1de6f203 --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.h @@ -0,0 +1,49 @@ +/* + * MVKFramebuffer.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKDevice.h" +#include "MVKImage.h" +#include + + +#pragma mark MVKFramebuffer + +/** Represents a Vulkan framebuffer. */ +class MVKFramebuffer : public MVKBaseDeviceObject { + +public: + + /** Returns the dimensions of this framebuffer. */ + inline VkExtent2D getExtent2D() { return _extent; } + + /** Returns the attachment at the specified index. */ + inline MVKImageView* getAttachment(uint32_t index) { return _attachments[index]; } + + +#pragma mark Construction + + /** Constructs an instance for the specified device. */ + MVKFramebuffer(MVKDevice* device, const VkFramebufferCreateInfo* pCreateInfo); + +protected: + VkExtent2D _extent; + std::vector _attachments; +}; + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.mm b/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.mm new file mode 100644 index 00000000..ad53ce52 --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.mm @@ -0,0 +1,36 @@ +/* + * MVKFramebuffer.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKFramebuffer.h" + + +#pragma mark MVKFramebuffer + +#pragma mark Construction + +MVKFramebuffer::MVKFramebuffer(MVKDevice* device, + const VkFramebufferCreateInfo* pCreateInfo) : MVKBaseDeviceObject(device) { + _extent = { .width = pCreateInfo->width, .height = pCreateInfo->height }; + + // Add clear values + _attachments.reserve(pCreateInfo->attachmentCount); + for (uint32_t i = 0; i < pCreateInfo->attachmentCount; i++) { + _attachments.push_back((MVKImageView*)pCreateInfo->pAttachments[i]); + } +} + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h new file mode 100644 index 00000000..9db7b7d7 --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h @@ -0,0 +1,386 @@ +/* + * MVKImage.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKResource.h" +#include "MVKSync.h" +#include +#include + +#import + +class MVKImageView; +class MVKSwapchain; +class MVKCommandEncoder; +struct MVKImageDescriptorData_t; +typedef MVKImageDescriptorData_t MVKImageDescriptorData; + + +/** Tracks the state of an image subresource. */ +typedef struct { + VkImageSubresource subresource; + VkSubresourceLayout layout; + VkImageLayout layoutState; +} MVKImageSubresource; + + +#pragma mark - +#pragma mark MVKImage + +/** Represents a Vulkan image. */ +class MVKImage : public MVKResource { + +public: + + /** Returns the Vulkan image type of this image. */ + VkImageType getImageType(); + + /** Returns the Vulkan image format of this image. */ + VkFormat getVkFormat(); + + /** + * Returns the 3D extent of this image at the base mipmap level. + * For 2D or cube images, the Z component will be 1. + */ + inline VkExtent3D getExtent3D() { return _extent; } + + /** + * 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); + + /** Returns the number of mipmap levels in this image. */ + inline uint32_t getMipLevelCount() { return _mipLevels; } + + /** + * Returns the number of layers at each mipmap level. For an array image type, this is + * the number of elements in the array. For cube image type, this is a multiple of 6. + * For a 3D image type, this is the depth of the image. Formally, this value is calculated + * as the multiple of depth and array size. + */ + inline uint32_t getLayerCount() { return _extent.depth * _arrayLayers; } + + /** Returns the number of samples for each pixel of this image. */ + inline uint32_t getSampleCount() { return _samples; } + + /** + * Returns the number of bytes per image row at the specified zero-based mip level. + * For non-compressed formats, this is the number of bytes in a row of texels. + * 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); + + /** + * Returns the number of bytes per image layer (for cube, array, or 3D images) + * at the specified zero-based mip level. This value will normally be the number + * of bytes per row (as returned by the getBytesPerRow() function, multiplied by + * the height of each 2D image. + */ + VkDeviceSize getBytesPerLayer(uint32_t mipLevel); + + /** Populates the specified layout for the specified sub-resource. */ + VkResult getSubresourceLayout(const VkImageSubresource* pSubresource, + VkSubresourceLayout* pLayout); + + /** Populates the specified transfer image descriptor data structure. */ + void getTransferDescriptorData(MVKImageDescriptorData& imgData); + + +#pragma mark Resource memory + + /** Returns the memory requirements of this resource by populating the specified structure. */ + VkResult getMemoryRequirements(VkMemoryRequirements* pMemoryRequirements) override; + + /** Applies the specified global memory barrier. */ + void applyMemoryBarrier(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkMemoryBarrier* pMemoryBarrier, + MVKCommandEncoder* cmdEncoder, + MVKCommandUse cmdUse) override; + + /** Applies the specified image memory barrier. */ + void applyImageMemoryBarrier(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkImageMemoryBarrier* pImageMemoryBarrier, + MVKCommandEncoder* cmdEncoder, + MVKCommandUse cmdUse); + +#pragma mark Metal + + /** Returns the Metal texture underlying this image. */ + id getMTLTexture(); + + /** + * Sets this image to use the specified MTLTexture. + * + * Any differences in the properties of mtlTexture and this image will modify the + * properties of this image. + * + * If a MTLTexture has already been created for this image, it will be destroyed. + */ + VkResult setMTLTexture(id mtlTexture); + + /** + * Indicates that this VkImage should use an IOSurface to underlay the Metal texture. + * + * If ioSurface is provided and is not nil, it will be used as the IOSurface. + * + * If ioSurface is not provided, or is nil, this image will create and use an IOSurface + * whose properties are compatible with the properties of this image. + * + * If a MTLTexture has already been created for this image, it will be destroyed. + * + * Returns: + * - VK_SUCCESS. + * - VK_ERROR_FEATURE_NOT_PRESENT if IOSurfaces are not supported on the platform. + * - VK_ERROR_INITIALIZATION_FAILED if ioSurface is specified and is not compatible with this VkImage. + */ + VkResult useIOSurface(IOSurfaceRef ioSurface = nil); + + /** + * Returns the IOSurface underlying the MTLTexture, + * or nil if no IOSurface has been set via useIOSurface(). + */ + IOSurfaceRef getIOSurface(); + + /** Returns the Metal pixel format of this image. */ + inline MTLPixelFormat getMTLPixelFormat() { return _mtlPixelFormat; } + + /** Returns the Metal texture type of this image. */ + inline MTLTextureType getMTLTextureType() { return _mtlTextureType; } + + /** + * Returns whether the Metal texel size is the same as the Vulkan texel size. + * + * If a different MTLPixelFormat was substituted for the desired VkFormat, the texel + * size may be different. This can occur for certain depth formats when the format + * is not supported on a platform, and the application has not verified this. + * In this case, a different depth format will automatically be substituted. + * With depth formats, this is usually accpetable, but can cause problems when + * attempting to copy a depth image with a substituted format to and from a buffer. + */ + inline bool hasExpectedTexelSize() { return _hasExpectedTexelSize; } + + /** Returns the Metal resource options for this image. */ + MTLStorageMode getMTLStorageMode(); + + /** Returns the Metal CPU cache mode used by this image. */ + inline MTLCPUCacheMode getMTLCPUCacheMode() { return _deviceMemory->getMTLCPUCacheMode(); } + + +#pragma mark Construction + + MVKImage(MVKDevice* device, const VkImageCreateInfo* pCreateInfo); + + ~MVKImage() override; + +protected: + friend class MVKImageView; + using MVKResource::needsHostReadSync; + + MVKImageSubresource* getSubresource(uint32_t mipLevel, uint32_t arrayLayer); + void initMTLTextureViewSupport(); + void initSubresources(const VkImageCreateInfo* pCreateInfo); + void initSubresourceLayout(MVKImageSubresource& imgSubRez); + virtual id newMTLTexture(); + void resetMTLTexture(); + void resetIOSurface(); + MTLTextureDescriptor* getMTLTextureDescriptor(); + void updateMTLTextureContent(MVKImageSubresource& subresource, VkDeviceSize offset, VkDeviceSize size); + void getMTLTextureContent(MVKImageSubresource& subresource, VkDeviceSize offset, VkDeviceSize size); + void* map(VkDeviceSize offset, VkDeviceSize size) override; + VkResult flushToDevice(VkDeviceSize offset, VkDeviceSize size) override; + VkResult pullFromDevice(VkDeviceSize offset, VkDeviceSize size) override; + bool needsHostReadSync(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkImageMemoryBarrier* pImageMemoryBarrier); + + std::vector _subresources; + VkExtent3D _extent; + uint32_t _mipLevels; + uint32_t _arrayLayers; + VkSampleCountFlagBits _samples; + VkImageUsageFlags _usage; + MTLPixelFormat _mtlPixelFormat; + MTLTextureType _mtlTextureType; + id _mtlTexture; + IOSurfaceRef _ioSurface; + bool _isDepthStencilAttachment; + bool _canSupportMTLTextureView; + bool _hasExpectedTexelSize; +}; + + +#pragma mark - +#pragma mark MVKImageView + +/** Represents a Vulkan image view. */ +class MVKImageView : public MVKBaseDeviceObject { + +public: + + +#pragma mark Metal + + /** Returns the Metal texture underlying this image view. */ + id getMTLTexture(); + + /** Returns the Metal pixel format of this image view. */ + inline MTLPixelFormat getMTLPixelFormat() { return _mtlPixelFormat; } + + /** Returns the Metal texture type of this image view. */ + inline MTLTextureType getMTLTextureType() { return _mtlTextureType; } + + /** + * Populates the texture of the specified render pass descriptor + * with the Metal texture underlying this image. + */ + void populateMTLRenderPassAttachmentDescriptor(MTLRenderPassAttachmentDescriptor* mtlAttDesc); + + /** + * Populates the resolve texture of the specified render pass descriptor + * with the Metal texture underlying this image. + */ + void populateMTLRenderPassAttachmentDescriptorResolve(MTLRenderPassAttachmentDescriptor* mtlAttDesc); + + +#pragma mark Construction + + MVKImageView(MVKDevice* device, const VkImageViewCreateInfo* pCreateInfo); + + ~MVKImageView() override; + +protected: + id newMTLTexture(); + void initMTLTextureViewSupport(); + MTLPixelFormat getSwizzledMTLPixelFormat(VkFormat format, VkComponentMapping components); + bool matchesSwizzle(VkComponentMapping components, VkComponentMapping pattern); + const char* getSwizzleName(VkComponentSwizzle swizzle); + void setSwizzleFormatError(VkFormat format, VkComponentMapping components); + + MVKImage* _image; + VkImageSubresourceRange _subresourceRange; + id _mtlTexture; + MTLPixelFormat _mtlPixelFormat; + MTLTextureType _mtlTextureType; + bool _useMTLTextureView; +}; + + +#pragma mark - +#pragma mark MVKSampler + +/** Represents a Vulkan sampler. */ +class MVKSampler : public MVKBaseDeviceObject { + +public: + + /** Returns the Metal sampler state. */ + inline id getMTLSamplerState() { return _mtlSamplerState; } + + MVKSampler(MVKDevice* device, const VkSamplerCreateInfo* pCreateInfo); + + ~MVKSampler() override; + +protected: + MTLSamplerDescriptor* getMTLSamplerDescriptor(const VkSamplerCreateInfo* pCreateInfo); + + id _mtlSamplerState; +}; + + +#pragma mark - +#pragma mark MVKSwapchainImage + +/** Indicates the relative availability of each image in the swapchain. */ +typedef struct MVKSwapchainImageAvailability_t { + uint64_t acquisitionID; /**< When this image was last made available, relative to the other images in the swapchain. Smaller value is earlier. */ + uint32_t waitCount; /**< The number of semaphores already waiting for this image. */ + bool isAvailable; /**< Indicates whether this image is currently available. */ + + bool operator< (const MVKSwapchainImageAvailability_t& rhs) const; +} MVKSwapchainImageAvailability; + +/** Represents a Vulkan image used as a rendering destination within a swapchain. */ +class MVKSwapchainImage : public MVKImage { + +public: + + /** Returns the index of this image within the encompassing swapchain. */ + inline uint32_t getSwapchainIndex() { return _swapchainIndex; } + + /** + * Makes this image available for acquisition by the app. + * + * If any semaphores are waiting to be signaled when this image becomes available, the + * earliest semaphore is signaled, and this image remains unavailable for other uses. + */ + void makeAvailable(); + + /** + * Registers a semaphore and/or fence that will be signaled when this image becomes available. + * This function accepts both a semaphore and a fence, and either none, one, or both may be provided. + * If this image is available already, the semaphore and fence are immediately signaled. + */ + void signalWhenAvailable(MVKSemaphore* semaphore, MVKFence* fence); + + /** Returns the availability status of this image, relative to other images in the swapchain. */ + const MVKSwapchainImageAvailability* getAvailability(); + + +#pragma mark Metal + + /** + * Presents the contained drawable to the OS, releases the Metal drawable and its + * texture back to the Metal layer's pool, and makes this image available for new use. + * + * If mtlCmdBuff is not nil, the contained drawable is scheduled for presentation using + * the presentDrawable: method of the command buffer. If mtlCmdBuff is nil, the contained + * drawable is presented immediately using the present method of the drawable. + */ + void presentCAMetalDrawable(id mtlCmdBuff); + + +#pragma mark Construction + + /** Constructs an instance for the specified device and swapchain. */ + MVKSwapchainImage(MVKDevice* device, + const VkImageCreateInfo* pCreateInfo, + MVKSwapchain* swapchain); + + ~MVKSwapchainImage() override; + +protected: + id newMTLTexture() override; + id getCAMetalDrawable(); + void resetCAMetalDrawable(); + void resetMetalSurface(); + void signal(std::pair tracker); + void renderWatermark(id mtlCmdBuff); + + MVKSwapchain* _swapchain; + uint32_t _swapchainIndex; + id _mtlDrawable; + std::mutex _availabilityLock; + std::list> _availabilityTrackers; + MVKSwapchainImageAvailability _availability; +}; + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm new file mode 100644 index 00000000..f7d3edf6 --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm @@ -0,0 +1,901 @@ +/* + * MVKImage.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKImage.h" +#include "MVKSwapchain.h" +#include "MVKCommandBuffer.h" +#include "MVKOSExtensions.h" +#include "mvk_datatypes.h" +#include "MVKFoundation.h" +#include "MVKLogging.h" + +using namespace std; + + +#pragma mark MVKImage + +VkImageType MVKImage::getImageType() { return mvkVkImageTypeFromMTLTextureType(_mtlTextureType); } + +VkFormat MVKImage::getVkFormat() { return mvkVkFormatFromMTLPixelFormat(_mtlPixelFormat); } + +VkExtent3D MVKImage::getExtent3D(uint32_t mipLevel) { + VkExtent2D baseExtent = { _extent.width, _extent.height }; + VkExtent2D mipLvlExt = mvkMipmapLevelSizeFromBaseSize(baseExtent, mipLevel); + + VkExtent3D extent; + extent.width = mipLvlExt.width; + extent.height = mipLvlExt.height; + extent.depth = _extent.depth; + return extent; +} + +VkDeviceSize MVKImage::getBytesPerRow(uint32_t mipLevel) { + VkExtent2D baseExtent = { _extent.width, _extent.height }; + VkExtent2D mipLvlExt = mvkMipmapLevelSizeFromBaseSize(baseExtent, mipLevel); + size_t bytesPerRow = mvkMTLPixelFormatBytesPerRow(_mtlPixelFormat, mipLvlExt.width); + return (uint32_t)mvkAlignByteOffset(bytesPerRow, _byteAlignment); +} + +VkDeviceSize MVKImage::getBytesPerLayer(uint32_t mipLevel) { + VkExtent2D baseExtent = { _extent.width, _extent.height }; + VkExtent2D mipLvlExt = mvkMipmapLevelSizeFromBaseSize(baseExtent, mipLevel); + return mvkMTLPixelFormatBytesPerLayer(_mtlPixelFormat, getBytesPerRow(mipLevel), mipLvlExt.height); +} + +VkResult MVKImage::getSubresourceLayout(const VkImageSubresource* pSubresource, + VkSubresourceLayout* pLayout) { + MVKImageSubresource* pImgRez = getSubresource(pSubresource->mipLevel, + pSubresource->arrayLayer); + if ( !pImgRez ) { return VK_INCOMPLETE; } + + *pLayout = pImgRez->layout; + return VK_SUCCESS; +} + +void MVKImage::getTransferDescriptorData(MVKImageDescriptorData& imgData) { + imgData.imageType = getImageType(); + imgData.format = getVkFormat(); + imgData.extent = _extent; + imgData.mipLevels = _mipLevels; + imgData.arrayLayers = _arrayLayers; + imgData.samples = _samples; + imgData.usage = _usage; +} + + +#pragma mark Resource memory + +void MVKImage::applyMemoryBarrier(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkMemoryBarrier* pMemoryBarrier, + MVKCommandEncoder* cmdEncoder, + MVKCommandUse cmdUse) { +#if MVK_MACOS + if ( needsHostReadSync(srcStageMask, dstStageMask, pMemoryBarrier) ) { + [cmdEncoder->getMTLBlitEncoder(cmdUse) synchronizeResource: getMTLTexture()]; + } +#endif +} + +void MVKImage::applyImageMemoryBarrier(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkImageMemoryBarrier* pImageMemoryBarrier, + MVKCommandEncoder* cmdEncoder, + MVKCommandUse cmdUse) { + const VkImageSubresourceRange& srRange = pImageMemoryBarrier->subresourceRange; + + // Extract the mipmap levels that are to be updated + uint32_t mipLvlStart = srRange.baseMipLevel; + uint32_t mipLvlCnt = srRange.levelCount; + uint32_t mipLvlEnd = (mipLvlCnt == VK_REMAINING_MIP_LEVELS + ? getMipLevelCount() + : (mipLvlStart + mipLvlCnt)); + + // Extract the cube or array layers (slices) that are to be updated + uint32_t layerStart = srRange.baseArrayLayer; + uint32_t layerCnt = srRange.layerCount; + uint32_t layerEnd = (layerCnt == VK_REMAINING_ARRAY_LAYERS + ? getLayerCount() + : (layerStart + layerCnt)); + +#if MVK_MACOS + bool needsSync = needsHostReadSync(srcStageMask, dstStageMask, pImageMemoryBarrier); + 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 = pImageMemoryBarrier->newLayout; } +#if MVK_MACOS + if (needsSync) { [mtlBlitEncoder synchronizeTexture: mtlTex slice: layer level: mipLvl]; } +#endif + } + } +} + +/** + * 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, + VkImageMemoryBarrier* pImageMemoryBarrier) { +#if MVK_IOS + return false; +#endif +#if MVK_MACOS + return ((pImageMemoryBarrier->newLayout == VK_IMAGE_LAYOUT_GENERAL) && + mvkIsAnyFlagEnabled(dstStageMask, (VK_PIPELINE_STAGE_HOST_BIT)) && + mvkIsAnyFlagEnabled(pImageMemoryBarrier->dstAccessMask, (VK_ACCESS_HOST_READ_BIT)) && + _deviceMemory->isMemoryHostAccessible() && !_deviceMemory->isMemoryHostCoherent()); +#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()); + return VK_SUCCESS; +} + +/** + * 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) { + for (auto& subRez : _subresources) { + switch (subRez.layoutState) { + case VK_IMAGE_LAYOUT_UNDEFINED: // TODO: VK_IMAGE_LAYOUT_UNDEFINED should be illegal + 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) { + for (auto& subRez : _subresources) { + switch (subRez.layoutState) { + case VK_IMAGE_LAYOUT_GENERAL: { + getMTLTextureContent(subRez, offset, size); + break; + } + default: + break; + } + } + return VK_SUCCESS; +} + +void* MVKImage::map(VkDeviceSize offset, VkDeviceSize size) { +// MVKLogDebug("Comparing map to image %p with memory offset %d and size %d.", this, _deviceMemoryOffset, _byteCount); +// if (doesContain(offset, size)) { MVKLogDebug("Mapping %d bytes to single image %p.", size, this); } + + return (doesContain(offset, size) + ? _deviceMemory->allocateMappedMemory(offset, size) + : VK_NULL_HANDLE); +} + + +#pragma mark Metal + +id MVKImage::getMTLTexture() { + if ( !_mtlTexture && _mtlPixelFormat ) { _mtlTexture = newMTLTexture(); } // retained + return _mtlTexture; +} + +VkResult MVKImage::setMTLTexture(id mtlTexture) { + resetMTLTexture(); + resetIOSurface(); + + _mtlTexture = mtlTexture; + + _mtlPixelFormat = _mtlTexture.pixelFormat; + _mtlTextureType = _mtlTexture.textureType; + _extent.width = uint32_t(_mtlTexture.width); + _extent.height = uint32_t(_mtlTexture.height); + _extent.depth = uint32_t(_mtlTexture.depth); + _mipLevels = uint32_t(_mtlTexture.mipmapLevelCount); + _samples = mvkVkSampleCountFlagBitsFromSampleCount(_mtlTexture.sampleCount); + _arrayLayers = uint32_t(_mtlTexture.arrayLength); + _usage = mvkVkImageUsageFlagsFromMTLTextureUsage(_mtlTexture.usage, _mtlPixelFormat); + + if (_device->_pMetalFeatures->ioSurfaces) { + _ioSurface = mtlTexture.iosurface; + CFRetain(_ioSurface); + } + + return VK_SUCCESS; +} + +/** + * Creates and returns a retained Metal texture suitable for use in this instance. + * + * This implementation creates a new MTLTexture from a MTLTextureDescriptor and possible IOSurface. + * Subclasses may override this function to create the MTLTexture in a different manner. + */ +id MVKImage::newMTLTexture() { + if (_ioSurface) { + return [getMTLDevice() newTextureWithDescriptor: getMTLTextureDescriptor() iosurface: _ioSurface plane: 0]; + } else { + return [getMTLDevice() newTextureWithDescriptor: getMTLTextureDescriptor()]; + } +} + +/** Removes and releases the MTLTexture object, so that it can be lazily created by getMTLTexture(). */ +void MVKImage::resetMTLTexture() { + [_mtlTexture release]; + _mtlTexture = nil; +} + +void MVKImage::resetIOSurface() { + if (_ioSurface) { + CFRelease(_ioSurface); + _ioSurface = nil; + } +} + +IOSurfaceRef MVKImage::getIOSurface() { return _ioSurface; } + +VkResult MVKImage::useIOSurface(IOSurfaceRef ioSurface) { + + if (!_device->_pMetalFeatures->ioSurfaces) { return mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "vkUseIOSurfaceMVK() : IOSurfaces are not supported on this platform."); } + + resetMTLTexture(); + resetIOSurface(); + + if (ioSurface) { + if (IOSurfaceGetWidth(ioSurface) != _extent.width) { return mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "vkUseIOSurfaceMVK() : IOSurface width %d does not match VkImage width %d.", IOSurfaceGetWidth(ioSurface), _extent.width); } + if (IOSurfaceGetHeight(ioSurface) != _extent.height) { return mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "vkUseIOSurfaceMVK() : IOSurface height %d does not match VkImage height %d.", IOSurfaceGetHeight(ioSurface), _extent.height); } + if (IOSurfaceGetBytesPerElement(ioSurface) != mvkMTLPixelFormatBytesPerBlock(_mtlPixelFormat)) { return mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "vkUseIOSurfaceMVK() : IOSurface bytes per element %d does not match VkImage bytes per element %d.", IOSurfaceGetBytesPerElement(ioSurface), mvkMTLPixelFormatBytesPerBlock(_mtlPixelFormat)); } + if (IOSurfaceGetElementWidth(ioSurface) != mvkMTLPixelFormatBlockTexelSize(_mtlPixelFormat).width) { return mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "vkUseIOSurfaceMVK() : IOSurface element width %d does not match VkImage element width %d.", IOSurfaceGetElementWidth(ioSurface), mvkMTLPixelFormatBlockTexelSize(_mtlPixelFormat).width); } + if (IOSurfaceGetElementHeight(ioSurface) != mvkMTLPixelFormatBlockTexelSize(_mtlPixelFormat).height) { return mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "vkUseIOSurfaceMVK() : IOSurface element height %d does not match VkImage element height %d.", IOSurfaceGetElementHeight(ioSurface), mvkMTLPixelFormatBlockTexelSize(_mtlPixelFormat).height); } + + _ioSurface = ioSurface; + CFRetain(_ioSurface); + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + _ioSurface = IOSurfaceCreate((CFDictionaryRef)@{ + (id)kIOSurfaceWidth: @(_extent.width), + (id)kIOSurfaceHeight: @(_extent.height), + (id)kIOSurfaceBytesPerElement: @(mvkMTLPixelFormatBytesPerBlock(_mtlPixelFormat)), + (id)kIOSurfaceElementWidth: @(mvkMTLPixelFormatBlockTexelSize(_mtlPixelFormat).width), + (id)kIOSurfaceElementHeight: @(mvkMTLPixelFormatBlockTexelSize(_mtlPixelFormat).height), + (id)kIOSurfaceIsGlobal: @(true), // Deprecated but needed for interprocess transfers + }); +#pragma clang diagnostic pop + + } + + return VK_SUCCESS; +} + +/** Returns an autoreleased Metal texture descriptor constructed from the properties of this image. */ +MTLTextureDescriptor* MVKImage::getMTLTextureDescriptor() { + MTLTextureDescriptor* mtlTexDesc = [[MTLTextureDescriptor alloc] init]; + mtlTexDesc.pixelFormat = _mtlPixelFormat; + 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 = mvkMTLTextureUsageFromVkImageUsageFlags(_usage); + mtlTexDesc.storageModeMVK = getMTLStorageMode(); + mtlTexDesc.cpuCacheMode = getMTLCPUCacheMode(); + + return [mtlTexDesc autorelease]; +} + +MTLStorageMode MVKImage::getMTLStorageMode() { + // For macOS, textures cannot use Shared storage mode, so change to Managed storage mode. + MTLStorageMode stgMode = _deviceMemory->getMTLStorageMode(); + + if (_ioSurface && stgMode == MTLStorageModePrivate) { stgMode = MTLStorageModeShared; } + +#if MVK_MACOS + if (stgMode == MTLStorageModeShared) { stgMode = MTLStorageModeManaged; } +#endif + return stgMode; +} + +/** + * 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) { + // Check if subresource overlaps the memory range. + VkDeviceSize memStart = offset; + VkDeviceSize memEnd = offset + size; + VkDeviceSize imgStart = subresource.layout.offset; + VkDeviceSize imgEnd = subresource.layout.offset + subresource.layout.size; + if (imgStart >= memEnd || imgEnd <= memStart) { return; } + + VkImageSubresource& imgSubRez = subresource.subresource; + VkSubresourceLayout& imgLayout = subresource.layout; + + uint32_t mipLvl = imgSubRez.mipLevel; + uint32_t layer = imgSubRez.arrayLayer; + + VkExtent3D mipExtent = getExtent3D(mipLvl); + VkImageType imgType = getImageType(); + void* pImgBytes = (void*)((uintptr_t)getLogicalMappedMemory() + imgLayout.offset); + + MTLRegion mtlRegion; + mtlRegion.origin = MTLOriginMake(0, 0, 0); + mtlRegion.size = mvkMTLSizeFromVkExtent3D(mipExtent); + + [getMTLTexture() replaceRegion: mtlRegion + mipmapLevel: mipLvl + slice: layer + withBytes: pImgBytes + bytesPerRow: (imgType != VK_IMAGE_TYPE_1D ? imgLayout.rowPitch : 0) + bytesPerImage: (imgType == VK_IMAGE_TYPE_3D ? imgLayout.depthPitch : 0)]; +} + +/** + * 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) { + // Check if subresource overlaps the memory range. + VkDeviceSize memStart = offset; + VkDeviceSize memEnd = offset + size; + VkDeviceSize imgStart = subresource.layout.offset; + VkDeviceSize imgEnd = subresource.layout.offset + subresource.layout.size; + if (imgStart >= memEnd || imgEnd <= memStart) { return; } + + VkImageSubresource& imgSubRez = subresource.subresource; + VkSubresourceLayout& imgLayout = subresource.layout; + + uint32_t mipLvl = imgSubRez.mipLevel; + uint32_t layer = imgSubRez.arrayLayer; + + VkExtent3D mipExtent = getExtent3D(mipLvl); + VkImageType imgType = getImageType(); + void* pImgBytes = (void*)((uintptr_t)getLogicalMappedMemory() + 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: mipLvl + slice: layer]; +} + + +#pragma mark Construction + +MVKImage::MVKImage(MVKDevice* device, const VkImageCreateInfo* pCreateInfo) : MVKResource(device) { + + _byteAlignment = _device->_pProperties->limits.minTexelBufferOffsetAlignment; + + // 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. Adjust samples and miplevels for the right texture type. + uint32_t minDim = 1; + _usage = pCreateInfo->usage; + _extent.width = max(pCreateInfo->extent.width, minDim); + _extent.height = max(pCreateInfo->extent.height, minDim); + _extent.depth = max(pCreateInfo->extent.depth, minDim); + _arrayLayers = max(pCreateInfo->arrayLayers, minDim); + + _mipLevels = max(pCreateInfo->mipLevels, minDim); + if ( (_mipLevels > 1) && (pCreateInfo->imageType == VK_IMAGE_TYPE_1D) ) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, 1D images cannot use mipmaps. Setting mip levels to 1.")); + _mipLevels = 1; + } + + _mtlTexture = nil; + _ioSurface = nil; + _mtlPixelFormat = mtlPixelFormatFromVkFormat(pCreateInfo->format); + _mtlTextureType = mvkMTLTextureTypeFromVkImageType(pCreateInfo->imageType, + _arrayLayers, + (pCreateInfo->samples > 1)); + _samples = pCreateInfo->samples; + if ( (_samples > 1) && (_mtlTextureType != MTLTextureType2DMultisample) ) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, multisampling can only be used with a 2D image type with an array length of 1. Setting sample count to 1.")); + _samples = VK_SAMPLE_COUNT_1_BIT; + } + + _isDepthStencilAttachment = (mvkAreFlagsEnabled(pCreateInfo->usage, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) || + mvkAreFlagsEnabled(mvkVkFormatProperties(pCreateInfo->format).optimalTilingFeatures, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)); + + _hasExpectedTexelSize = (mvkMTLPixelFormatBytesPerBlock(_mtlPixelFormat) == mvkVkFormatBytesPerBlock(pCreateInfo->format)); + + // Calc _byteCount after _mtlTexture & _byteAlignment + uint32_t layerCount = getLayerCount(); // Combines depth & arrays + for (uint32_t mipLvl = 0; mipLvl < _mipLevels; mipLvl++) { + _byteCount += getBytesPerLayer(mipLvl) * layerCount; + } + + initSubresources(pCreateInfo); + initMTLTextureViewSupport(); +} + +/** 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); + uint32_t layCnt = getLayerCount(); + + // 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) * layCnt); + } + offset += (bytesPerLayerCurrLevel * currArrayLayer); + + VkSubresourceLayout& layout = imgSubRez.layout; + layout.offset = offset; + layout.size = bytesPerLayerCurrLevel; + layout.rowPitch = getBytesPerRow(currMipLevel); + layout.depthPitch = bytesPerLayerCurrLevel; +} + +/** + * Determines whether this image can support Metal texture views, + * and sets the _canSupportMTLTextureView variable appropriately. + */ +void MVKImage::initMTLTextureViewSupport() { + _canSupportMTLTextureView = !_isDepthStencilAttachment; +} + +MVKImage::~MVKImage() { + resetMTLTexture(); + resetIOSurface(); +} + + +#pragma mark - +#pragma mark MVKImageView + + +void MVKImageView::populateMTLRenderPassAttachmentDescriptor(MTLRenderPassAttachmentDescriptor* mtlAttDesc) { + mtlAttDesc.texture = _image->getMTLTexture(); // Use underlying image texture, since RP attachment handles subresource + mtlAttDesc.level = _subresourceRange.baseMipLevel; + mtlAttDesc.slice = _subresourceRange.baseArrayLayer; + mtlAttDesc.depthPlane = 0; +} + +void MVKImageView::populateMTLRenderPassAttachmentDescriptorResolve(MTLRenderPassAttachmentDescriptor* mtlAttDesc) { + mtlAttDesc.resolveTexture = _image->getMTLTexture(); // Use underlying image texture, since RP attachment handles subresource + mtlAttDesc.resolveLevel = _subresourceRange.baseMipLevel; + mtlAttDesc.resolveSlice = _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 ) { _mtlTexture = newMTLTexture(); } // retained + 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() { + return [_image->getMTLTexture() newTextureViewWithPixelFormat: _mtlPixelFormat + textureType: _mtlTextureType + levels: NSMakeRange(_subresourceRange.baseMipLevel, _subresourceRange.levelCount) + slices: NSMakeRange(_subresourceRange.baseArrayLayer, _subresourceRange.layerCount)]; // retained +} + + +#pragma mark Construction + +MVKImageView::MVKImageView(MVKDevice* device, const VkImageViewCreateInfo* pCreateInfo) : MVKBaseDeviceObject(device) { + + _image = (MVKImage*)pCreateInfo->image; + + // Remember the subresource range, and determine the actual number of mip levels and texture slices + _subresourceRange = pCreateInfo->subresourceRange; + if (_subresourceRange.levelCount == VK_REMAINING_MIP_LEVELS) { + _subresourceRange.levelCount = _image->getMipLevelCount() - _subresourceRange.baseMipLevel; + } + if (_subresourceRange.layerCount == VK_REMAINING_ARRAY_LAYERS) { + _subresourceRange.layerCount = _image->getLayerCount() - _subresourceRange.baseArrayLayer; + } + + _mtlTexture = nil; + _mtlPixelFormat = getSwizzledMTLPixelFormat(pCreateInfo->format, pCreateInfo->components); + _mtlTextureType = mvkMTLTextureTypeFromVkImageViewType(pCreateInfo->viewType, (_image->getSampleCount() > 1)); + initMTLTextureViewSupport(); +} + +// Returns a MTLPixelFormat, based on the original MTLPixelFormat, as converted from the VkFormat, +// but possibly modified by the swizzles defined in the VkComponentMapping of the VkImageViewCreateInfo. +// Metal does not support general per-texture swizzles, and so this function relies on a few coincidental +// alignments of existing MTLPixelFormats of the same structure. If swizzling is not possible for a +// particular combination of format and swizzle spec, the original MTLPixelFormat is returned. +MTLPixelFormat MVKImageView::getSwizzledMTLPixelFormat(VkFormat format, VkComponentMapping components) { + MTLPixelFormat mtlPF = mtlPixelFormatFromVkFormat(format); + + switch (mtlPF) { + case MTLPixelFormatR8Unorm: + if (matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_R} ) ) { + return MTLPixelFormatA8Unorm; + } + break; + + case MTLPixelFormatR8Snorm: +#if MVK_IOS + case MTLPixelFormatR8Unorm_sRGB: +#endif + if (matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_R} ) ) { + setSwizzleFormatError(format, components); + return MTLPixelFormatA8Unorm; + } + break; + + case MTLPixelFormatA8Unorm: + if (matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_A, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_ZERO} ) ) { + return MTLPixelFormatR8Unorm; + } + break; + + case MTLPixelFormatRGBA8Unorm: + if (matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_A} ) ) { + return MTLPixelFormatBGRA8Unorm; + } + break; + + case MTLPixelFormatRGBA8Unorm_sRGB: + if (matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_A} ) ) { + return MTLPixelFormatBGRA8Unorm_sRGB; + } + break; + + case MTLPixelFormatRGBA8Snorm: + if (matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_A} ) ) { + setSwizzleFormatError(format, components); + return MTLPixelFormatBGRA8Unorm; + } + break; + + case MTLPixelFormatBGRA8Unorm: + if (matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_A} ) ) { + return MTLPixelFormatRGBA8Unorm; + } + break; + + case MTLPixelFormatBGRA8Unorm_sRGB: + if (matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_A} ) ) { + return MTLPixelFormatRGBA8Unorm_sRGB; + } + break; + + default: + break; + } + + if ( !matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A} ) ) { + setSwizzleFormatError(format, components); + } + return mtlPF; +} + +const char* MVKImageView::getSwizzleName(VkComponentSwizzle swizzle) { + switch (swizzle) { + case VK_COMPONENT_SWIZZLE_IDENTITY: return "VK_COMPONENT_SWIZZLE_IDENTITY"; + case VK_COMPONENT_SWIZZLE_ZERO: return "VK_COMPONENT_SWIZZLE_ZERO"; + case VK_COMPONENT_SWIZZLE_ONE: return "VK_COMPONENT_SWIZZLE_ONE"; + case VK_COMPONENT_SWIZZLE_R: return "VK_COMPONENT_SWIZZLE_R"; + case VK_COMPONENT_SWIZZLE_G: return "VK_COMPONENT_SWIZZLE_G"; + case VK_COMPONENT_SWIZZLE_B: return "VK_COMPONENT_SWIZZLE_B"; + case VK_COMPONENT_SWIZZLE_A: return "VK_COMPONENT_SWIZZLE_A"; + default: return "VK_COMPONENT_SWIZZLE_UNKNOWN"; + } +} + +// Sets a standard swizzle format error during instance construction. +void MVKImageView::setSwizzleFormatError(VkFormat format, VkComponentMapping components) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FORMAT_NOT_SUPPORTED, + "VkImageView format %s and swizzle (%s, %s, %s, %s) does not map to a valid MTLPixelFormat.\n", + mvkVkFormatName(format), + getSwizzleName(components.r), + getSwizzleName(components.g), + getSwizzleName(components.b), + getSwizzleName(components.a))); +} + +// Returns whether the swizzle components of the internal VkComponentMapping matches the +// swizzle pattern, by comparing corresponding elements of the two structures. The pattern +// supports wildcards, in that any element of pattern can be set to VK_COMPONENT_SWIZZLE_MAX_ENUM +// to indicate that any value in the corresponding element of components. +bool MVKImageView::matchesSwizzle(VkComponentMapping components, VkComponentMapping pattern) { + if ( !((pattern.r == VK_COMPONENT_SWIZZLE_MAX_ENUM) || (pattern.r == components.r) || + ((pattern.r == VK_COMPONENT_SWIZZLE_R) && (components.r == VK_COMPONENT_SWIZZLE_IDENTITY))) ) { return false; } + if ( !((pattern.g == VK_COMPONENT_SWIZZLE_MAX_ENUM) || (pattern.g == components.g) || + ((pattern.g == VK_COMPONENT_SWIZZLE_G) && (components.g == VK_COMPONENT_SWIZZLE_IDENTITY))) ) { return false; } + if ( !((pattern.b == VK_COMPONENT_SWIZZLE_MAX_ENUM) || (pattern.b == components.b) || + ((pattern.b == VK_COMPONENT_SWIZZLE_B) && (components.b == VK_COMPONENT_SWIZZLE_IDENTITY))) ) { return false; } + if ( !((pattern.a == VK_COMPONENT_SWIZZLE_MAX_ENUM) || (pattern.a == components.a) || + ((pattern.a == VK_COMPONENT_SWIZZLE_A) && (components.a == VK_COMPONENT_SWIZZLE_IDENTITY))) ) { return false; } + + return true; +} + +/** + * Determine whether this image view should use a Metal texture view, + * and set the _useMTLTextureView variable appropriately. + */ +void MVKImageView::initMTLTextureViewSupport() { + _useMTLTextureView = _image->_canSupportMTLTextureView; + + // If the view is identical to underlying image, don't bother using a Metal view + if (_mtlPixelFormat == _image->_mtlPixelFormat && + _mtlTextureType == _image->_mtlTextureType && + _subresourceRange.levelCount == _image->_mipLevels && + _subresourceRange.layerCount == _image->_arrayLayers) { + _useMTLTextureView = false; + } +} + +MVKImageView::~MVKImageView() { + [_mtlTexture release]; +} + + +#pragma mark - +#pragma mark MVKSampler + +/** Returns an autoreleased Metal sampler descriptor constructed from the properties of this image. */ +MTLSamplerDescriptor* MVKSampler::getMTLSamplerDescriptor(const VkSamplerCreateInfo* pCreateInfo) { + + MTLSamplerDescriptor* mtlSampDesc = [[MTLSamplerDescriptor alloc] init]; + mtlSampDesc.sAddressMode = mvkMTLSamplerAddressModeFromVkSamplerAddressMode(pCreateInfo->addressModeU); + mtlSampDesc.tAddressMode = mvkMTLSamplerAddressModeFromVkSamplerAddressMode(pCreateInfo->addressModeV); + mtlSampDesc.rAddressMode = mvkMTLSamplerAddressModeFromVkSamplerAddressMode(pCreateInfo->addressModeW); + mtlSampDesc.minFilter = mvkMTLSamplerMinMagFilterFromVkFilter(pCreateInfo->minFilter); + mtlSampDesc.magFilter = mvkMTLSamplerMinMagFilterFromVkFilter(pCreateInfo->magFilter); + mtlSampDesc.mipFilter = (pCreateInfo->unnormalizedCoordinates + ? MTLSamplerMipFilterNotMipmapped + : mvkMTLSamplerMipFilterFromVkSamplerMipmapMode(pCreateInfo->mipmapMode)); + mtlSampDesc.lodMinClamp = pCreateInfo->minLod; + mtlSampDesc.lodMaxClamp = pCreateInfo->maxLod; + mtlSampDesc.maxAnisotropy = (pCreateInfo->anisotropyEnable + ? mvkClamp(pCreateInfo->maxAnisotropy, 1.0f, _device->_pProperties->limits.maxSamplerAnisotropy) + : 1); + mtlSampDesc.normalizedCoordinates = !pCreateInfo->unnormalizedCoordinates; + mtlSampDesc.compareFunctionMVK = (pCreateInfo->compareEnable + ? mvkMTLCompareFunctionFromVkCompareOp(pCreateInfo->compareOp) + : MTLCompareFunctionNever); + return [mtlSampDesc autorelease]; +} + +/** Constructs an instance on the specified image. */ +MVKSampler::MVKSampler(MVKDevice* device, const VkSamplerCreateInfo* pCreateInfo) : MVKBaseDeviceObject(device) { + _mtlSamplerState = [getMTLDevice() newSamplerStateWithDescriptor: getMTLSamplerDescriptor(pCreateInfo)]; +} + +MVKSampler::~MVKSampler() { + [_mtlSamplerState release]; +} + + +#pragma mark - +#pragma mark MVKSwapchainImage + +bool MVKSwapchainImageAvailability_t::operator< (const MVKSwapchainImageAvailability_t& rhs) const { + if ( isAvailable && !rhs.isAvailable) { return true; } + if ( !isAvailable && rhs.isAvailable) { return false; } + + if (waitCount < rhs.waitCount) { return true; } + if (waitCount > rhs.waitCount) { return false; } + + return acquisitionID < rhs.acquisitionID; +} + +void MVKSwapchainImage::makeAvailable() { + lock_guard lock(_availabilityLock); + + // Mark when this event happened, relative to that of other images + _availability.acquisitionID = _swapchain->getNextAcquisitionID(); + + // If there are no availability trackers waiting, mark this image as available. + // Otherwise, mark this image as unavailable, and extract and signal the first semaphore and fence. + if ( _availabilityTrackers.empty() ) { + _availability.isAvailable = true; + } else { + _availability.isAvailable = false; + auto tracker = _availabilityTrackers.front(); + _availabilityTrackers.pop_front(); + signal(tracker); + } + +// MVKLogDebug("Finished presentation of MVKSwapchainImage %p. Elapsed time: %.6f ms.", this, mvkGetElapsedMilliseconds()); + +} + +void MVKSwapchainImage::signalWhenAvailable(MVKSemaphore* semaphore, MVKFence* fence) { + lock_guard lock(_availabilityLock); +// MVKLogDebug("Requesting signal when MVKSwapchain image %p is free to semaphore %p and fence %p.", this, semaphore, fence); + auto tracker = make_pair(semaphore, fence); + if (_availability.isAvailable) { + _availability.isAvailable = false; + signal(tracker); + } else { + _availabilityTrackers.push_back(tracker); + } +} + +/** Signal either or both of the semaphore and fence in the specified tracker pair. */ +void MVKSwapchainImage::signal(pair tracker) { + + MVKSemaphore* semaphore = tracker.first; + if (semaphore) { semaphore->signal(); } + + MVKFence* fence = tracker.second; + if (fence) { fence->signal(); } + +// MVKLogDebug("MVKSwapchainImage %p signalling semaphore %p and fence %p.", this, semaphore, fence); +} + +const MVKSwapchainImageAvailability* MVKSwapchainImage::getAvailability() { + lock_guard lock(_availabilityLock); + _availability.waitCount = (uint32_t)_availabilityTrackers.size(); + return &_availability; +} + + +#pragma mark Metal + +/** + * Creates and returns a retained Metal texture suitable for use in this instance. + * + * This implementation retrieves a MTLTexture from the CAMetalDrawable. + */ +id MVKSwapchainImage::newMTLTexture() { +// return [[getCAMetalDrawable() texture] retain]; + id mtlDrwbl = getCAMetalDrawable(); + id mtlTex = [[mtlDrwbl texture] retain]; +// MVKLogDebug("Retrieved MTLTexture %p from CAMetalDrawable %p in MVKSwapchain image: %p. Elapsed time: %.6f ms.", +// mtlTex, mtlDrwbl, this, mvkGetElapsedMilliseconds()); + return mtlTex; +} + +id MVKSwapchainImage::getCAMetalDrawable() { + if ( !_mtlDrawable ) { + @autoreleasepool { // Allow auto-released drawable object to be reclaimed before end of loop + _mtlDrawable = [_swapchain->getNextCAMetalDrawable() retain]; // retained + } + MVKAssert(_mtlDrawable, "Could not aquire an available CAMetalDrawable from the CAMetalLayer in MVKSwapchain image: %p.", this); +// MVKLogDebug("Retrieved CAMetalDrawable %p with retain count %d in MVKSwapchain image: %p. Elapsed time: %.6f ms.", +// _mtlDrawable, _mtlDrawable.retainCount, this, mvkGetElapsedMilliseconds()); + } + return _mtlDrawable; +} + +void MVKSwapchainImage::presentCAMetalDrawable(id mtlCmdBuff) { +// MVKLogDebug("Presenting CAMetalDrawable: %s with retain count %d in MVKSwapchain image: %p. Elapsed time: %.6f ms.", +// _mtlDrawable.description.UTF8String, _mtlDrawable.retainCount, this, mvkGetElapsedMilliseconds()); + + id mtlDrawable = getCAMetalDrawable(); + _swapchain->willPresentSurface(getMTLTexture(), mtlCmdBuff); + + // If using a command buffer, present the drawable through it, + // and make myself available only once the command buffer has completed. + // Otherwise, immediately present the drawable and make myself available. + if (mtlCmdBuff) { + [mtlCmdBuff presentDrawable: mtlDrawable]; + resetMetalSurface(); + [mtlCmdBuff addCompletedHandler: ^(id mcb) { makeAvailable(); }]; + } else { + [mtlDrawable present]; + resetMetalSurface(); + makeAvailable(); + } +} + +/** Removes and releases the Metal drawable object, so that it can be lazily created by getCAMetalDrawable(). */ +void MVKSwapchainImage::resetCAMetalDrawable() { +// MVKLogDebug("Releasing MTLDrawable: %s with retain count %d in MVKSwapchain image: %p.", +// _mtlDrawable.description.UTF8String, _mtlDrawable.retainCount, this); + [_mtlDrawable release]; + _mtlDrawable = nil; +} + +/** Resets the MTLTexture and CAMetalDrawable underlying this image. */ +void MVKSwapchainImage::resetMetalSurface() { +// MVKLogDebug("Resetting MTLTexture: %p and CAMetalDrawable %p in MVKSwapchain image: %p. Elapsed time: %.6f ms.", +// _mtlTexture, _mtlDrawable, this, mvkGetElapsedMilliseconds()); + resetMTLTexture(); // Release texture first so drawable will be last to release it + resetCAMetalDrawable(); +} + + +#pragma mark Construction + +MVKSwapchainImage::MVKSwapchainImage(MVKDevice* device, + const VkImageCreateInfo* pCreateInfo, + MVKSwapchain* swapchain) : MVKImage(device, pCreateInfo) { + _swapchain = swapchain; + _swapchainIndex = _swapchain->getImageCount(); + _availability.acquisitionID = _swapchain->getNextAcquisitionID(); + _availability.isAvailable = true; + _mtlDrawable = nil; + _canSupportMTLTextureView = false; // Override...swapchains never support Metal image view. + +// MVKLogDebug("Created MVKSwapchain image %p.", this); +} + +MVKSwapchainImage::~MVKSwapchainImage() { + resetCAMetalDrawable(); +} + + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h new file mode 100644 index 00000000..6e7ac526 --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h @@ -0,0 +1,91 @@ +/* + * MVKInstance.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKSurface.h" +#include "MVKBaseObject.h" +#include +#include +#include + +class MVKPhysicalDevice; + + +#pragma mark - +#pragma mark MVKInstance + +/** Represents a Vulkan instance. */ +class MVKInstance : public MVKConfigurableObject { + +public: + + /** Returns the function pointer corresponding to the specified named entry point. */ + inline PFN_vkVoidFunction getProcAddr(const char* pName) { return _procAddrMap[pName]; } + + /** + * If pPhysicalDevices is null, the value of pCount is updated with the number of + * physical devices supported by this instance. + * + * If pPhysicalDevices is not null, then pCount physical devices are copied into the array. + * If the number of available physical devices is less than pCount, the value of pCount is + * updated to indicate the number of physical devices actually returned in the array. + * + * Returns VK_SUCCESS if successful. Returns VK_INCOMPLETE if the number of physical + * devices available in this instance is larger than the specified pCount. Returns other + * values if an error occurs. + */ + VkResult getPhysicalDevices(uint32_t* pCount, VkPhysicalDevice* pPhysicalDevices); + + /** + * Verifies that the list of layers are available, + * and returns VK_SUCCESS or VK_ERROR_LAYER_NOT_PRESENT. + */ + VkResult verifyLayers(uint32_t count, const char* const* names); + + /** + * Verifies that the list of extensions are available, + * and returns VK_SUCCESS or VK_ERROR_EXTENSION_NOT_PRESENT. + */ + VkResult verifyExtensions(uint32_t count, const char* const* names); + + /** Creates and returns a new object. */ + MVKSurface* createSurface(const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + + /** Destroys the specified object. */ + void destroySurface(MVKSurface* mvkSrfc, + const VkAllocationCallbacks* pAllocator); + + +#pragma mark Object Creation + + /** Constructs an instance from the specified instance config. */ + MVKInstance(const VkInstanceCreateInfo* pCreateInfo); + + ~MVKInstance() override; + +protected: + void initProcAddrs(); + void logVersions(); + + VkApplicationInfo _appInfo; + std::vector _physicalDevices; + std::unordered_map _procAddrMap; +}; + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm new file mode 100644 index 00000000..1fb701f9 --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm @@ -0,0 +1,319 @@ +/* + * MVKInstance.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "MVKInstance.h" +#include "MVKLayers.h" +#include "MVKDevice.h" +#include "MVKFoundation.h" +#include "MVKEnvironment.h" +#include "MVKSurface.h" + +using namespace std; + + +#pragma mark - +#pragma mark MVKInstance + +VkResult MVKInstance::getPhysicalDevices(uint32_t* pCount, VkPhysicalDevice* pPhysicalDevices) { + + // Get the number of physical devices + uint32_t pdCnt = (uint32_t)_physicalDevices.size(); + + // If properties aren't actually being requested yet, simply update the returned count + if ( !pPhysicalDevices ) { + *pCount = pdCnt; + return VK_SUCCESS; + } + + // Othewise, determine how many physical devices we'll return, and return that count + VkResult result = (*pCount <= pdCnt) ? VK_SUCCESS : VK_INCOMPLETE; + *pCount = min(pdCnt, *pCount); + + // Now populate the devices + for (uint32_t pdIdx = 0; pdIdx < *pCount; pdIdx++) { + pPhysicalDevices[pdIdx] = (VkPhysicalDevice)_physicalDevices[pdIdx]; + } + + return result; +} + +VkResult MVKInstance::verifyLayers(uint32_t count, const char* const* names) { + VkResult result = VK_SUCCESS; + for (uint32_t i = 0; i < count; i++) { + if ( !MVKLayerManager::globalManager()->getLayerNamed(names[i]) ) { + result = mvkNotifyErrorWithText(VK_ERROR_LAYER_NOT_PRESENT, "Vulkan layer %s is not supported.", names[i]); + } + } + return result; +} + +VkResult MVKInstance::verifyExtensions(uint32_t count, const char* const* names) { + VkResult result = VK_SUCCESS; + MVKLayer* driverLayer = MVKLayerManager::globalManager()->getDriverLayer(); + for (uint32_t i = 0; i < count; i++) { + if (!driverLayer->hasExtensionNamed(names[i])) { + result = mvkNotifyErrorWithText(VK_ERROR_EXTENSION_NOT_PRESENT, "Vulkan extension %s is not supported.", names[i]); + } + } + return result; +} + +MVKSurface* MVKInstance::createSurface(const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + return new MVKSurface(this, pCreateInfo, pAllocator); +} + +void MVKInstance::destroySurface(MVKSurface* mvkSrfc, + const VkAllocationCallbacks* pAllocator) { + delete mvkSrfc; +} + + +#pragma mark Object Creation + +#define ADD_PROC_ADDR(entrypoint) _procAddrMap[""#entrypoint] = (PFN_vkVoidFunction)&entrypoint; + +/** Initializes the function pointer map. */ +void MVKInstance::initProcAddrs() { + + // Instance functions + ADD_PROC_ADDR(vkDestroyInstance); + ADD_PROC_ADDR(vkEnumeratePhysicalDevices); + ADD_PROC_ADDR(vkGetPhysicalDeviceFeatures); + ADD_PROC_ADDR(vkGetPhysicalDeviceFormatProperties); + ADD_PROC_ADDR(vkGetPhysicalDeviceImageFormatProperties); + ADD_PROC_ADDR(vkGetPhysicalDeviceProperties); + ADD_PROC_ADDR(vkGetPhysicalDeviceQueueFamilyProperties); + ADD_PROC_ADDR(vkGetPhysicalDeviceMemoryProperties); + ADD_PROC_ADDR(vkGetInstanceProcAddr); + ADD_PROC_ADDR(vkGetDeviceProcAddr); + ADD_PROC_ADDR(vkCreateDevice); + ADD_PROC_ADDR(vkEnumerateDeviceExtensionProperties); + ADD_PROC_ADDR(vkEnumerateDeviceLayerProperties); + ADD_PROC_ADDR(vkGetPhysicalDeviceSparseImageFormatProperties); + + // Device functions: + ADD_PROC_ADDR(vkDestroyDevice); + ADD_PROC_ADDR(vkGetDeviceQueue); + ADD_PROC_ADDR(vkQueueSubmit); + ADD_PROC_ADDR(vkQueueWaitIdle); + ADD_PROC_ADDR(vkDeviceWaitIdle); + ADD_PROC_ADDR(vkAllocateMemory); + ADD_PROC_ADDR(vkFreeMemory); + ADD_PROC_ADDR(vkMapMemory); + ADD_PROC_ADDR(vkUnmapMemory); + ADD_PROC_ADDR(vkFlushMappedMemoryRanges); + ADD_PROC_ADDR(vkInvalidateMappedMemoryRanges); + ADD_PROC_ADDR(vkGetDeviceMemoryCommitment); + ADD_PROC_ADDR(vkBindBufferMemory); + ADD_PROC_ADDR(vkBindImageMemory); + ADD_PROC_ADDR(vkGetBufferMemoryRequirements); + ADD_PROC_ADDR(vkGetImageMemoryRequirements); + ADD_PROC_ADDR(vkGetImageSparseMemoryRequirements); + ADD_PROC_ADDR(vkQueueBindSparse); + ADD_PROC_ADDR(vkCreateFence); + ADD_PROC_ADDR(vkDestroyFence); + ADD_PROC_ADDR(vkResetFences); + ADD_PROC_ADDR(vkGetFenceStatus); + ADD_PROC_ADDR(vkWaitForFences); + ADD_PROC_ADDR(vkCreateSemaphore); + ADD_PROC_ADDR(vkDestroySemaphore); + ADD_PROC_ADDR(vkCreateEvent); + ADD_PROC_ADDR(vkDestroyEvent); + ADD_PROC_ADDR(vkGetEventStatus); + ADD_PROC_ADDR(vkSetEvent); + ADD_PROC_ADDR(vkResetEvent); + ADD_PROC_ADDR(vkCreateQueryPool); + ADD_PROC_ADDR(vkDestroyQueryPool); + ADD_PROC_ADDR(vkGetQueryPoolResults); + ADD_PROC_ADDR(vkCreateBuffer); + ADD_PROC_ADDR(vkDestroyBuffer); + ADD_PROC_ADDR(vkCreateBufferView); + ADD_PROC_ADDR(vkDestroyBufferView); + ADD_PROC_ADDR(vkCreateImage); + ADD_PROC_ADDR(vkDestroyImage); + ADD_PROC_ADDR(vkGetImageSubresourceLayout); + ADD_PROC_ADDR(vkCreateImageView); + ADD_PROC_ADDR(vkDestroyImageView); + ADD_PROC_ADDR(vkCreateShaderModule); + ADD_PROC_ADDR(vkDestroyShaderModule); + ADD_PROC_ADDR(vkCreatePipelineCache); + ADD_PROC_ADDR(vkDestroyPipelineCache); + ADD_PROC_ADDR(vkGetPipelineCacheData); + ADD_PROC_ADDR(vkMergePipelineCaches); + ADD_PROC_ADDR(vkCreateGraphicsPipelines); + ADD_PROC_ADDR(vkCreateComputePipelines); + ADD_PROC_ADDR(vkDestroyPipeline); + ADD_PROC_ADDR(vkCreatePipelineLayout); + ADD_PROC_ADDR(vkDestroyPipelineLayout); + ADD_PROC_ADDR(vkCreateSampler); + ADD_PROC_ADDR(vkDestroySampler); + ADD_PROC_ADDR(vkCreateDescriptorSetLayout); + ADD_PROC_ADDR(vkDestroyDescriptorSetLayout); + ADD_PROC_ADDR(vkCreateDescriptorPool); + ADD_PROC_ADDR(vkDestroyDescriptorPool); + ADD_PROC_ADDR(vkResetDescriptorPool); + ADD_PROC_ADDR(vkAllocateDescriptorSets); + ADD_PROC_ADDR(vkFreeDescriptorSets); + ADD_PROC_ADDR(vkUpdateDescriptorSets); + ADD_PROC_ADDR(vkCreateFramebuffer); + ADD_PROC_ADDR(vkDestroyFramebuffer); + ADD_PROC_ADDR(vkCreateRenderPass); + ADD_PROC_ADDR(vkDestroyRenderPass); + ADD_PROC_ADDR(vkGetRenderAreaGranularity); + ADD_PROC_ADDR(vkCreateCommandPool); + ADD_PROC_ADDR(vkDestroyCommandPool); + ADD_PROC_ADDR(vkResetCommandPool); + ADD_PROC_ADDR(vkAllocateCommandBuffers); + ADD_PROC_ADDR(vkFreeCommandBuffers); + ADD_PROC_ADDR(vkBeginCommandBuffer); + ADD_PROC_ADDR(vkEndCommandBuffer); + ADD_PROC_ADDR(vkResetCommandBuffer); + ADD_PROC_ADDR(vkCmdBindPipeline); + ADD_PROC_ADDR(vkCmdSetViewport); + ADD_PROC_ADDR(vkCmdSetScissor); + ADD_PROC_ADDR(vkCmdSetLineWidth); + ADD_PROC_ADDR(vkCmdSetDepthBias); + ADD_PROC_ADDR(vkCmdSetBlendConstants); + ADD_PROC_ADDR(vkCmdSetDepthBounds); + ADD_PROC_ADDR(vkCmdSetStencilCompareMask); + ADD_PROC_ADDR(vkCmdSetStencilWriteMask); + ADD_PROC_ADDR(vkCmdSetStencilReference); + ADD_PROC_ADDR(vkCmdBindDescriptorSets); + ADD_PROC_ADDR(vkCmdBindIndexBuffer); + ADD_PROC_ADDR(vkCmdBindVertexBuffers); + ADD_PROC_ADDR(vkCmdDraw); + ADD_PROC_ADDR(vkCmdDrawIndexed); + ADD_PROC_ADDR(vkCmdDrawIndirect); + ADD_PROC_ADDR(vkCmdDrawIndexedIndirect); + ADD_PROC_ADDR(vkCmdDispatch); + ADD_PROC_ADDR(vkCmdDispatchIndirect); + ADD_PROC_ADDR(vkCmdCopyBuffer); + ADD_PROC_ADDR(vkCmdCopyImage); + ADD_PROC_ADDR(vkCmdBlitImage); + ADD_PROC_ADDR(vkCmdCopyBufferToImage); + ADD_PROC_ADDR(vkCmdCopyImageToBuffer); + ADD_PROC_ADDR(vkCmdUpdateBuffer); + ADD_PROC_ADDR(vkCmdFillBuffer); + ADD_PROC_ADDR(vkCmdClearColorImage); + ADD_PROC_ADDR(vkCmdClearDepthStencilImage); + ADD_PROC_ADDR(vkCmdClearAttachments); + ADD_PROC_ADDR(vkCmdResolveImage); + ADD_PROC_ADDR(vkCmdSetEvent); + ADD_PROC_ADDR(vkCmdResetEvent); + ADD_PROC_ADDR(vkCmdWaitEvents); + ADD_PROC_ADDR(vkCmdPipelineBarrier); + ADD_PROC_ADDR(vkCmdBeginQuery); + ADD_PROC_ADDR(vkCmdEndQuery); + ADD_PROC_ADDR(vkCmdResetQueryPool); + ADD_PROC_ADDR(vkCmdWriteTimestamp); + ADD_PROC_ADDR(vkCmdCopyQueryPoolResults); + ADD_PROC_ADDR(vkCmdPushConstants); + ADD_PROC_ADDR(vkCmdBeginRenderPass); + ADD_PROC_ADDR(vkCmdNextSubpass); + ADD_PROC_ADDR(vkCmdEndRenderPass); + ADD_PROC_ADDR(vkCmdExecuteCommands); + + // Supported extensions: + ADD_PROC_ADDR(vkDestroySurfaceKHR); + ADD_PROC_ADDR(vkGetPhysicalDeviceSurfaceSupportKHR); + ADD_PROC_ADDR(vkGetPhysicalDeviceSurfaceCapabilitiesKHR); + ADD_PROC_ADDR(vkGetPhysicalDeviceSurfaceFormatsKHR); + ADD_PROC_ADDR(vkGetPhysicalDeviceSurfacePresentModesKHR); + ADD_PROC_ADDR(vkCreateSwapchainKHR); + ADD_PROC_ADDR(vkDestroySwapchainKHR); + ADD_PROC_ADDR(vkGetSwapchainImagesKHR); + ADD_PROC_ADDR(vkAcquireNextImageKHR); + ADD_PROC_ADDR(vkQueuePresentKHR); + ADD_PROC_ADDR(vkGetMoltenVKDeviceConfigurationMVK); + ADD_PROC_ADDR(vkSetMoltenVKDeviceConfigurationMVK); + ADD_PROC_ADDR(vkGetPhysicalDeviceMetalFeaturesMVK); + ADD_PROC_ADDR(vkGetSwapchainPerformanceMVK); + ADD_PROC_ADDR(vkGetShaderCompilationPerformanceMVK); + ADD_PROC_ADDR(vkGetVersionStringsMVK); + ADD_PROC_ADDR(vkGetMTLDeviceMVK); + ADD_PROC_ADDR(vkSetMTLTextureMVK); + ADD_PROC_ADDR(vkGetMTLTextureMVK); + ADD_PROC_ADDR(vkUseIOSurfaceMVK); + ADD_PROC_ADDR(vkGetIOSurfaceMVK); + +#ifdef VK_USE_PLATFORM_IOS_MVK + ADD_PROC_ADDR(vkCreateIOSSurfaceMVK); +#endif +#ifdef VK_USE_PLATFORM_MACOS_MVK + ADD_PROC_ADDR(vkCreateMacOSSurfaceMVK); +#endif + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + ADD_PROC_ADDR(vkActivateMoltenVKLicenseMVK); // Deprecated + ADD_PROC_ADDR(vkActivateMoltenVKLicensesMVK); // Deprecated +#pragma clang diagnostic pop +} + +void MVKInstance::logVersions() { + uint32_t buffLen = 32; + char mvkVer[buffLen]; + char vkVer[buffLen]; + vkGetVersionStringsMVK(mvkVer, buffLen, vkVer, buffLen); + MVKLogInfo("MoltenVK version %s. Vulkan version %s.", mvkVer, vkVer); +} + +/** Returns an autorelease array containing the MTLDevices available on this system. */ +static NSArray>* getAvailableMTLDevices() { +#if MVK_MACOS + return [MTLCopyAllDevices() autorelease]; +#endif +#if MVK_IOS + return [NSArray arrayWithObject: MTLCreateSystemDefaultDevice()]; +#endif +} + +MVKInstance::MVKInstance(const VkInstanceCreateInfo* pCreateInfo) { + + _appInfo.apiVersion = MVK_VULKAN_API_VERSION; // Default + mvkSetOrClear(&_appInfo, pCreateInfo->pApplicationInfo); + + logVersions(); // Log the MoltenVK and Vulkan versions + initProcAddrs(); // Init function pointers + + // Populate the array of physical GPU devices + NSArray>* mtlDevices = getAvailableMTLDevices(); + _physicalDevices.reserve(mtlDevices.count); + for (id mtlDev in mtlDevices) { +#if (MVK_MACOS && defined(MVK_FORCE_LOW_POWER_GPU)) + if (mtlDev.isLowPower) { _physicalDevices.push_back(new MVKPhysicalDevice(this, mtlDev)); } +#else + _physicalDevices.push_back(new MVKPhysicalDevice(this, mtlDev)); +#endif + } + + if (MVK_VULKAN_API_VERSION_CONFORM(MVK_VULKAN_API_VERSION) < + MVK_VULKAN_API_VERSION_CONFORM(_appInfo.apiVersion)) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INCOMPATIBLE_DRIVER, "Request for driver version %x is not compatible with provided version %x.", _appInfo.apiVersion, MVK_VULKAN_API_VERSION)); + } + + setConfigurationResult(verifyLayers(pCreateInfo->enabledLayerCount, pCreateInfo->ppEnabledLayerNames)); + setConfigurationResult(verifyExtensions(pCreateInfo->enabledExtensionCount, pCreateInfo->ppEnabledExtensionNames)); +} + +MVKInstance::~MVKInstance() { + mvkDestroyContainerContents(_physicalDevices); +} + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h new file mode 100644 index 00000000..aef48ebd --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h @@ -0,0 +1,179 @@ +/* + * MVKPipeline.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKDevice.h" +#include "MVKDescriptorSet.h" +#include "MVKShaderModule.h" +#include +#include +#include + +#import + +class MVKCommandEncoder; +class MVKPipelineCache; + + +#pragma mark - +#pragma mark MVKPipelineLayout + +/** Represents a Vulkan pipeline layout. */ +class MVKPipelineLayout : public MVKBaseDeviceObject { + +public: + + /** Binds descriptor sets to a command encoder. */ + void bindDescriptorSets(MVKCommandEncoder* cmdEncoder, + std::vector& descriptorSets, + uint32_t firstSet, + std::vector& dynamicOffsets); + + /** Populates the specified shader converter context. */ + void populateShaderConverterContext(SPIRVToMSLConverterContext& context); + + /** Constructs an instance for the specified device. */ + MVKPipelineLayout(MVKDevice* device, const VkPipelineLayoutCreateInfo* pCreateInfo); + +private: + std::vector _descriptorSetLayouts; + std::vector _dslMTLResourceIndexOffsets; + std::vector _pushConstants; + MVKShaderResourceBinding _pushConstantsMTLResourceIndexOffsets; +}; + + +#pragma mark - +#pragma mark MVKPipeline + +/** Represents an abstract Vulkan pipeline. */ +class MVKPipeline : public MVKBaseDeviceObject { + +public: + + /** Binds this pipeline to the specified command encoder. */ + virtual void encode(MVKCommandEncoder* cmdEncoder) = 0; + + /** Constructs an instance for the device. layout, and parent (which may be NULL). */ + MVKPipeline(MVKDevice* device, MVKPipelineCache* pipelineCache, MVKPipeline* parent) : MVKBaseDeviceObject(device) {} +}; + + +#pragma mark - +#pragma mark MVKGraphicsPipeline + +/** Represents an Vulkan graphics pipeline. */ +class MVKGraphicsPipeline : public MVKPipeline { + +public: + + /** Binds this pipeline to the specified command encoder. */ + void encode(MVKCommandEncoder* cmdEncoder) override; + + /** Returns whether this pipeline permits dynamic setting of the specifie state. */ + bool supportsDynamicState(VkDynamicState state); + + /** Constructs an instance for the device and parent (which may be NULL). */ + MVKGraphicsPipeline(MVKDevice* device, + MVKPipelineCache* pipelineCache, + MVKPipeline* parent, + const VkGraphicsPipelineCreateInfo* pCreateInfo); + + ~MVKGraphicsPipeline() override; + +protected: + void initMTLRenderPipelineState(const VkGraphicsPipelineCreateInfo* pCreateInfo); + void initMVKShaderConverterContext(SPIRVToMSLConverterContext& _shaderContext, + const VkGraphicsPipelineCreateInfo* pCreateInfo); + MTLRenderPipelineDescriptor* getMTLRenderPipelineDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo); + + VkPipelineRasterizationStateCreateInfo _rasterInfo; + VkPipelineDepthStencilStateCreateInfo _depthStencilInfo; + + std::vector _mtlViewports; + std::vector _mtlScissors; + + id _mtlPipelineState; + MTLCullMode _mtlCullMode; + MTLWinding _mtlFrontWinding; + MTLTriangleFillMode _mtlFillMode; + MTLDepthClipMode _mtlDepthClipMode; + MTLPrimitiveType _mtlPrimitiveType; + + float _blendConstants[4] = { 0.0, 0.0, 0.0, 1.0 }; + + bool _dynamicStateEnabled[VK_DYNAMIC_STATE_RANGE_SIZE]; + bool _hasDepthStencilInfo; +}; + + +#pragma mark - +#pragma mark MVKComputePipeline + +/** Represents an Vulkan compute pipeline. */ +class MVKComputePipeline : public MVKPipeline { + +public: + + /** Binds this pipeline to the specified command encoder. */ + void encode(MVKCommandEncoder* cmdEncoder) override; + + /** Constructs an instance for the device and parent (which may be NULL). */ + MVKComputePipeline(MVKDevice* device, + MVKPipelineCache* pipelineCache, + MVKPipeline* parent, + const VkComputePipelineCreateInfo* pCreateInfo); + + ~MVKComputePipeline() override; + +protected: + MVKMTLFunction getMTLFunction(const VkComputePipelineCreateInfo* pCreateInfo); + + id _mtlPipelineState; + MTLSize _mtlThreadgroupSize; +}; + + +#pragma mark - +#pragma mark MVKPipelineCache + +/** Represents a Vulkan pipeline cache. */ +class MVKPipelineCache : public MVKBaseDeviceObject { + +public: + + /** + * If pData is not null, serializes at most pDataSize bytes of the contents of the cache + * into that memory location, and returns the number of bytes serialized in pDataSize. + * If pData is null, returns the number of bytes required to serialize the contents of + */ + inline VkResult getData(size_t* pDataSize, void* pData) { + *pDataSize = 0; + return VK_SUCCESS; + } + + /** Merges the contents of the specified number of pipeline caches into this cache. */ + inline VkResult mergePipelineCaches(uint32_t srcCacheCount, const VkPipelineCache* pSrcCaches) { return VK_SUCCESS; } + +#pragma mark Construction + + /** Constructs an instance for the specified device. */ + MVKPipelineCache(MVKDevice* device, const VkPipelineCacheCreateInfo* pCreateInfo) : MVKBaseDeviceObject(device) {} + +}; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm new file mode 100644 index 00000000..45f10e2a --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm @@ -0,0 +1,446 @@ +/* + * MVKPipeline.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKPipeline.h" +#include "MVKRenderPass.h" +#include "MVKCommandBuffer.h" +#include "MVKFoundation.h" +#include "mvk_datatypes.h" + +using namespace std; + + +#pragma mark MVKPipelineLayout + +void MVKPipelineLayout::bindDescriptorSets(MVKCommandEncoder* cmdEncoder, + vector& descriptorSets, + uint32_t firstSet, + vector& dynamicOffsets) { + + uint32_t pDynamicOffsetIndex = 0; + uint32_t dsCnt = (uint32_t)descriptorSets.size(); + for (uint32_t dsIdx = 0; dsIdx < dsCnt; dsIdx++) { + MVKDescriptorSet* descSet = descriptorSets[dsIdx]; + uint32_t dslIdx = firstSet + dsIdx; + _descriptorSetLayouts[dslIdx].bindDescriptorSet(cmdEncoder, descSet, + _dslMTLResourceIndexOffsets[dslIdx], + dynamicOffsets, &pDynamicOffsetIndex); + } + cmdEncoder->getPushConstants(VK_SHADER_STAGE_VERTEX_BIT)->setMTLBufferIndex(_pushConstantsMTLResourceIndexOffsets.vertexStage.bufferIndex); + cmdEncoder->getPushConstants(VK_SHADER_STAGE_FRAGMENT_BIT)->setMTLBufferIndex(_pushConstantsMTLResourceIndexOffsets.fragmentStage.bufferIndex); + cmdEncoder->getPushConstants(VK_SHADER_STAGE_COMPUTE_BIT)->setMTLBufferIndex(_pushConstantsMTLResourceIndexOffsets.computeStage.bufferIndex); +} + +void MVKPipelineLayout::populateShaderConverterContext(SPIRVToMSLConverterContext& context) { + context.resourceBindings.clear(); + + // Add resource bindings defined in the descriptor set layouts + uint32_t dslCnt = (uint32_t)_descriptorSetLayouts.size(); + for (uint32_t dslIdx = 0; dslIdx < dslCnt; dslIdx++) { + _descriptorSetLayouts[dslIdx].populateShaderConverterContext(context, + _dslMTLResourceIndexOffsets[dslIdx], + dslIdx); + } + + // Add any resource bindings used by push-constants + mvkPopulateShaderConverterContext(context, + _pushConstantsMTLResourceIndexOffsets.vertexStage, + spv::ExecutionModelVertex, + kPushConstDescSet, + kPushConstBinding); + + mvkPopulateShaderConverterContext(context, + _pushConstantsMTLResourceIndexOffsets.fragmentStage, + spv::ExecutionModelFragment, + kPushConstDescSet, + kPushConstBinding); + + mvkPopulateShaderConverterContext(context, + _pushConstantsMTLResourceIndexOffsets.computeStage, + spv::ExecutionModelGLCompute, + kPushConstDescSet, + kPushConstBinding); +} + +MVKPipelineLayout::MVKPipelineLayout(MVKDevice* device, + const VkPipelineLayoutCreateInfo* pCreateInfo) : MVKBaseDeviceObject(device) { + + // Add descriptor set layouts, accumulating the resource index offsets used by the + // corresponding DSL, and associating the current accumulated resource index offsets + // with each DSL as it is added. The final accumulation of resource index offsets + // becomes the resource index offsets that will be used for push contants. + + // According to the Vulkan spec, VkDescriptorSetLayout is intended to be consumed when + // passed to any Vulkan function, and may be safely destroyed by app immediately after. + // In order for this pipeline layout to retain the content of a VkDescriptorSetLayout, + // this pipeline holds onto copies of the MVKDescriptorSetLayout instances, so that the + // originals created by the app can be safely destroyed. + + _descriptorSetLayouts.reserve(pCreateInfo->setLayoutCount); + for (uint32_t i = 0; i < pCreateInfo->setLayoutCount; i++) { + MVKDescriptorSetLayout* pDescSetLayout = (MVKDescriptorSetLayout*)pCreateInfo->pSetLayouts[i]; + _descriptorSetLayouts.push_back(*pDescSetLayout); + _dslMTLResourceIndexOffsets.push_back(_pushConstantsMTLResourceIndexOffsets); + _pushConstantsMTLResourceIndexOffsets += pDescSetLayout->_mtlResourceCounts; + } + + // Add push constants + _pushConstants.reserve(pCreateInfo->pushConstantRangeCount); + for (uint32_t i = 0; i < pCreateInfo->pushConstantRangeCount; i++) { + _pushConstants.push_back(pCreateInfo->pPushConstantRanges[i]); + } +} + + +#pragma mark - +#pragma mark MVKGraphicsPipeline + +void MVKGraphicsPipeline::encode(MVKCommandEncoder* cmdEncoder) { + + id mtlCmdEnc = cmdEncoder->_mtlRenderEncoder; + if ( !mtlCmdEnc ) { return; } // Pre-renderpass. Come back later. + + // Render pipeline state + [mtlCmdEnc setRenderPipelineState: _mtlPipelineState]; + + // Depth stencil state + if (_hasDepthStencilInfo) { + cmdEncoder->_depthStencilState.setDepthStencilState(_depthStencilInfo); + cmdEncoder->_stencilReferenceValueState.setReferenceValues(_depthStencilInfo); + } else { + cmdEncoder->_depthStencilState.reset(); + cmdEncoder->_stencilReferenceValueState.reset(); + } + + // Rasterization + cmdEncoder->_blendColorState.setBlendColor(_blendConstants[0], _blendConstants[1], + _blendConstants[2], _blendConstants[3], false); + cmdEncoder->_depthBiasState.setDepthBias(_rasterInfo); + cmdEncoder->_viewportState.setViewports(_mtlViewports); + cmdEncoder->_scissorState.setScissors(_mtlScissors); + cmdEncoder->_mtlPrimitiveType = _mtlPrimitiveType; + + [mtlCmdEnc setCullMode: _mtlCullMode]; + [mtlCmdEnc setFrontFacingWinding: _mtlFrontWinding]; + [mtlCmdEnc setTriangleFillMode: _mtlFillMode]; + + if (_device->_pMetalFeatures->depthClipMode) { + [mtlCmdEnc setDepthClipMode: _mtlDepthClipMode]; + } +} + +bool MVKGraphicsPipeline::supportsDynamicState(VkDynamicState state) { + + // First test if this dynamic state is explicitly turned off + if ( (state >= VK_DYNAMIC_STATE_RANGE_SIZE) || !_dynamicStateEnabled[state] ) { return false; } + + // Some dynamic states have other restrictions + switch (state) { + case VK_DYNAMIC_STATE_DEPTH_BIAS: + return _rasterInfo.depthBiasEnable; + default: + return true; + } +} + + +#pragma mark Construction + +MVKGraphicsPipeline::MVKGraphicsPipeline(MVKDevice* device, + MVKPipelineCache* pipelineCache, + MVKPipeline* parent, + const VkGraphicsPipelineCreateInfo* pCreateInfo) : MVKPipeline(device, pipelineCache, parent) { + + // Track dynamic state in _dynamicStateEnabled array + memset(&_dynamicStateEnabled, false, sizeof(_dynamicStateEnabled)); // start with all dynamic state disabled + const VkPipelineDynamicStateCreateInfo* pDS = pCreateInfo->pDynamicState; + if (pDS) { + for (uint32_t i = 0; i < pDS->dynamicStateCount; i++) { + VkDynamicState ds = pDS->pDynamicStates[i]; + _dynamicStateEnabled[ds] = true; + } + } + + if (pCreateInfo->pColorBlendState) { + memcpy(&_blendConstants, &pCreateInfo->pColorBlendState->blendConstants, sizeof(_blendConstants)); + } + + if (pCreateInfo->pInputAssemblyState) { + _mtlPrimitiveType = mvkMTLPrimitiveTypeFromVkPrimitiveTopology(pCreateInfo->pInputAssemblyState->topology); + } + + // Add raster content - must occur before initMTLRenderPipelineState() for rasterizerDiscardEnable + _mtlCullMode = MTLCullModeNone; + _mtlFrontWinding = MTLWindingCounterClockwise; + _mtlFillMode = MTLTriangleFillModeFill; + _mtlDepthClipMode = MTLDepthClipModeClip; + bool hasRasterInfo = mvkSetOrClear(&_rasterInfo, pCreateInfo->pRasterizationState); + if (hasRasterInfo) { + _mtlCullMode = mvkMTLCullModeFromVkCullModeFlags(_rasterInfo.cullMode); + _mtlFrontWinding = mvkMTLWindingFromVkFrontFace(_rasterInfo.frontFace); + _mtlFillMode = mvkMTLTriangleFillModeFromVkPolygonMode(_rasterInfo.polygonMode); + if (_rasterInfo.depthClampEnable) { + if (_device->_pMetalFeatures->depthClipMode) { + _mtlDepthClipMode = MTLDepthClipModeClamp; + } else { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "This device does not support depth clamping.")); + } + } + } + + // Render pipeline state + initMTLRenderPipelineState(pCreateInfo); + + // Depth stencil content + _hasDepthStencilInfo = mvkSetOrClear(&_depthStencilInfo, pCreateInfo->pDepthStencilState); + + // Add viewports and scissors + if (pCreateInfo->pViewportState) { + _mtlViewports.reserve(pCreateInfo->pViewportState->viewportCount); + for (uint32_t i = 0; i < pCreateInfo->pViewportState->viewportCount; i++) { + // If viewport is dyanamic, we still add a dummy so that the count will be tracked. + MTLViewport mtlVP; + if ( !_dynamicStateEnabled[VK_DYNAMIC_STATE_VIEWPORT] ) { + mtlVP = mvkMTLViewportFromVkViewport(pCreateInfo->pViewportState->pViewports[i]); + } + _mtlViewports.push_back(mtlVP); + } + _mtlScissors.reserve(pCreateInfo->pViewportState->scissorCount); + for (uint32_t i = 0; i < pCreateInfo->pViewportState->scissorCount; i++) { + // If scissor is dyanamic, we still add a dummy so that the count will be tracked. + MTLScissorRect mtlSc; + if ( !_dynamicStateEnabled[VK_DYNAMIC_STATE_SCISSOR] ) { + mtlSc = mvkMTLScissorRectFromVkRect2D(pCreateInfo->pViewportState->pScissors[i]); + } + _mtlScissors.push_back(mtlSc); + } + } +} + +/** Constructs the underlying Metal render pipeline. */ +void MVKGraphicsPipeline::initMTLRenderPipelineState(const VkGraphicsPipelineCreateInfo* pCreateInfo) { + @autoreleasepool { + _mtlPipelineState = nil; + + MTLRenderPipelineDescriptor* plDesc = getMTLRenderPipelineDescriptor(pCreateInfo); + if (plDesc) { + NSTimeInterval startTime = _device->getPerformanceTimestamp(); + NSError* psError = nil; + _mtlPipelineState = [getMTLDevice() newRenderPipelineStateWithDescriptor: plDesc error: &psError]; // retained + if (psError) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "Could not create render pipeline:\n%s.", psError.description.UTF8String)); + } + _device->addShaderCompilationEventPerformance(_device->_shaderCompilationPerformance.pipelineCompile, startTime); + } + } +} + +// Returns a MTLRenderPipelineDescriptor constructed from this instance, or nil if an error occurs. +MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLRenderPipelineDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo) { + SPIRVToMSLConverterContext shaderContext; + initMVKShaderConverterContext(shaderContext, pCreateInfo); + + // Retrieve the render subpass for which this pipeline is being constructed + MVKRenderPass* mvkRendPass = (MVKRenderPass*)pCreateInfo->renderPass; + MVKRenderSubpass* mvkRenderSubpass = mvkRendPass->getSubpass(pCreateInfo->subpass); + + MTLRenderPipelineDescriptor* plDesc = [[MTLRenderPipelineDescriptor new] autorelease]; + + // Add shader stages + for (uint32_t i = 0; i < pCreateInfo->stageCount; i++) { + const VkPipelineShaderStageCreateInfo* pSS = &pCreateInfo->pStages[i]; + + MVKShaderModule* mvkShdrMod = (MVKShaderModule*)pSS->module; + + // Vertex shader + if (mvkAreFlagsEnabled(pSS->stage, VK_SHADER_STAGE_VERTEX_BIT)) { + plDesc.vertexFunction = mvkShdrMod->getMTLFunction(pSS, &shaderContext).mtlFunction; + } + + // Fragment shader + if (mvkAreFlagsEnabled(pSS->stage, VK_SHADER_STAGE_FRAGMENT_BIT)) { + // If no color attachments (e.g. depth-only rendering), don't set fragment function. + if (mvkRenderSubpass->getColorAttachmentCount() > 0) { + plDesc.fragmentFunction = mvkShdrMod->getMTLFunction(pSS, &shaderContext).mtlFunction; + } + } + } + + MTLVertexDescriptor* vtxDesc = plDesc.vertexDescriptor; + + // Vertex attributes + MTLVertexAttributeDescriptorArray* vaArray = vtxDesc.attributes; + uint32_t vaCnt = pCreateInfo->pVertexInputState->vertexAttributeDescriptionCount; + for (uint32_t i = 0; i < vaCnt; i++) { + const VkVertexInputAttributeDescription* pVKVA = &pCreateInfo->pVertexInputState->pVertexAttributeDescriptions[i]; + if (shaderContext.isVertexAttributeLocationUsed(pVKVA->location)) { + MTLVertexAttributeDescriptor* vaDesc = vaArray[pVKVA->location]; + vaDesc.format = mvkMTLVertexFormatFromVkFormat(pVKVA->format); + vaDesc.bufferIndex = _device->getMetalBufferIndexForVertexAttributeBinding(pVKVA->binding); + vaDesc.offset = pVKVA->offset; + } + } + + // Vertex buffer bindings + MTLVertexBufferLayoutDescriptorArray* vbArray = vtxDesc.layouts; + uint32_t vbCnt = pCreateInfo->pVertexInputState->vertexBindingDescriptionCount; + for (uint32_t i = 0; i < vbCnt; i++) { + const VkVertexInputBindingDescription* pVKVB = &pCreateInfo->pVertexInputState->pVertexBindingDescriptions[i]; + uint32_t vbIdx = _device->getMetalBufferIndexForVertexAttributeBinding(pVKVB->binding); + if (shaderContext.isVertexBufferUsed(vbIdx)) { + MTLVertexBufferLayoutDescriptor* vbDesc = vbArray[vbIdx]; + vbDesc.stride = (pVKVB->stride == 0) ? sizeof(simd::float4) : pVKVB->stride; // Vulkan allows zero stride but Metal doesn't. Default to float4 + vbDesc.stepFunction = mvkMTLVertexStepFunctionFromVkVertexInputRate(pVKVB->inputRate); + vbDesc.stepRate = 1; + } + } + + // Color attachments + if (pCreateInfo->pColorBlendState) { + for (uint32_t caIdx = 0; caIdx < pCreateInfo->pColorBlendState->attachmentCount; caIdx++) { + const VkPipelineColorBlendAttachmentState* pCA = &pCreateInfo->pColorBlendState->pAttachments[caIdx]; + + MTLRenderPipelineColorAttachmentDescriptor* colorDesc = plDesc.colorAttachments[caIdx]; + colorDesc.pixelFormat = mtlPixelFormatFromVkFormat(mvkRenderSubpass->getColorAttachmentFormat(caIdx)); + colorDesc.writeMask = mvkMTLColorWriteMaskFromVkChannelFlags(pCA->colorWriteMask); + colorDesc.blendingEnabled = pCA->blendEnable; + colorDesc.rgbBlendOperation = mvkMTLBlendOperationFromVkBlendOp(pCA->colorBlendOp); + colorDesc.sourceRGBBlendFactor = mvkMTLBlendFactorFromVkBlendFactor(pCA->srcColorBlendFactor); + colorDesc.destinationRGBBlendFactor = mvkMTLBlendFactorFromVkBlendFactor(pCA->dstColorBlendFactor); + colorDesc.alphaBlendOperation = mvkMTLBlendOperationFromVkBlendOp(pCA->alphaBlendOp); + colorDesc.sourceAlphaBlendFactor = mvkMTLBlendFactorFromVkBlendFactor(pCA->srcAlphaBlendFactor); + colorDesc.destinationAlphaBlendFactor = mvkMTLBlendFactorFromVkBlendFactor(pCA->dstAlphaBlendFactor); + } + } + + // Depth & stencil attachments + MTLPixelFormat mtlDSFormat = mtlPixelFormatFromVkFormat(mvkRenderSubpass->getDepthStencilFormat()); + if (mvkMTLPixelFormatIsDepthFormat(mtlDSFormat)) { plDesc.depthAttachmentPixelFormat = mtlDSFormat; } + if (mvkMTLPixelFormatIsStencilFormat(mtlDSFormat)) { plDesc.stencilAttachmentPixelFormat = mtlDSFormat; } + + // Rasterization + plDesc.rasterizationEnabled = !_rasterInfo.rasterizerDiscardEnable; + if (pCreateInfo->pMultisampleState) { + plDesc.sampleCount = mvkSampleCountFromVkSampleCountFlagBits(pCreateInfo->pMultisampleState->rasterizationSamples); + plDesc.alphaToCoverageEnabled = pCreateInfo->pMultisampleState->alphaToCoverageEnable; + plDesc.alphaToOneEnabled = pCreateInfo->pMultisampleState->alphaToOneEnable; + } + +#if MVK_MACOS + if (pCreateInfo->pInputAssemblyState) { + plDesc.inputPrimitiveTopology = mvkMTLPrimitiveTopologyClassFromVkPrimitiveTopology(pCreateInfo->pInputAssemblyState->topology); + } +#endif + + return plDesc; +} + +/** Initializes the context used to prepare the MSL library used by this pipeline. */ +void MVKGraphicsPipeline::initMVKShaderConverterContext(SPIRVToMSLConverterContext& shaderContext, + const VkGraphicsPipelineCreateInfo* pCreateInfo) { + + shaderContext.options.mslVersion = _device->_pMetalFeatures->mslVersion; + + MVKPipelineLayout* layout = (MVKPipelineLayout*)pCreateInfo->layout; + layout->populateShaderConverterContext(shaderContext); + + shaderContext.options.isRenderingPoints = (pCreateInfo->pInputAssemblyState && (pCreateInfo->pInputAssemblyState->topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST)); + shaderContext.options.shouldFlipVertexY = _device->_mvkConfig.shaderConversionFlipVertexY; + + // Set the shader context vertex attribute information + shaderContext.vertexAttributes.clear(); + uint32_t vaCnt = pCreateInfo->pVertexInputState->vertexAttributeDescriptionCount; + for (uint32_t vaIdx = 0; vaIdx < vaCnt; vaIdx++) { + const VkVertexInputAttributeDescription* pVKVA = &pCreateInfo->pVertexInputState->pVertexAttributeDescriptions[vaIdx]; + + // Set binding and offset from Vulkan vertex attribute + MSLVertexAttribute va; + va.location = pVKVA->location; + va.mslBuffer = _device->getMetalBufferIndexForVertexAttributeBinding(pVKVA->binding); + va.mslOffset = pVKVA->offset; + + // Set stride and input rate of vertex attribute from corresponding Vulkan vertex bindings + uint32_t vbCnt = pCreateInfo->pVertexInputState->vertexBindingDescriptionCount; + for (uint32_t vbIdx = 0; vbIdx < vbCnt; vbIdx++) { + const VkVertexInputBindingDescription* pVKVB = &pCreateInfo->pVertexInputState->pVertexBindingDescriptions[vbIdx]; + if (pVKVB->binding == pVKVA->binding) { + va.mslStride = pVKVB->stride; + va.isPerInstance = (pVKVB->inputRate == VK_VERTEX_INPUT_RATE_INSTANCE); + break; + } + } + + shaderContext.vertexAttributes.push_back(va); + } +} + +MVKGraphicsPipeline::~MVKGraphicsPipeline() { + [_mtlPipelineState release]; +} + + +#pragma mark - +#pragma mark MVKComputePipeline + +void MVKComputePipeline::encode(MVKCommandEncoder* cmdEncoder) { + id mtlCmdEnc = cmdEncoder->getMTLComputeEncoder(); + [mtlCmdEnc setComputePipelineState: _mtlPipelineState]; + + cmdEncoder->_mtlThreadgroupSize = _mtlThreadgroupSize; +} + +MVKComputePipeline::MVKComputePipeline(MVKDevice* device, + MVKPipelineCache* pipelineCache, + MVKPipeline* parent, + const VkComputePipelineCreateInfo* pCreateInfo) : MVKPipeline(device, pipelineCache, parent) { + @autoreleasepool { + MVKMTLFunction shaderFunc = getMTLFunction(pCreateInfo); + _mtlThreadgroupSize = shaderFunc.threadGroupSize; + _mtlPipelineState = nil; + + NSError* psError = nil; + NSTimeInterval startTime = _device->getPerformanceTimestamp(); + _mtlPipelineState = [getMTLDevice() newComputePipelineStateWithFunction: shaderFunc.mtlFunction error: &psError]; // retained + if (psError) { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "Could not create compute pipeline:\n%s.", psError.description.UTF8String)); + } + _device->addShaderCompilationEventPerformance(_device->_shaderCompilationPerformance.pipelineCompile, startTime); + + } +} + +// Returns a MTLFunction to use when creating the MTLComputePipelineState. +MVKMTLFunction MVKComputePipeline::getMTLFunction(const VkComputePipelineCreateInfo* pCreateInfo) { + + const VkPipelineShaderStageCreateInfo* pSS = &pCreateInfo->stage; + if ( !mvkAreFlagsEnabled(pSS->stage, VK_SHADER_STAGE_COMPUTE_BIT) ) { return MVKMTLFunctionNull; } + + SPIRVToMSLConverterContext shaderContext; + shaderContext.options.mslVersion = _device->_pMetalFeatures->mslVersion; + + MVKPipelineLayout* layout = (MVKPipelineLayout*)pCreateInfo->layout; + layout->populateShaderConverterContext(shaderContext); + + MVKShaderModule* mvkShdrMod = (MVKShaderModule*)pSS->module; + return mvkShdrMod->getMTLFunction(pSS, &shaderContext); +} + + +MVKComputePipeline::~MVKComputePipeline() { + [_mtlPipelineState release]; +} diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.h b/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.h new file mode 100644 index 00000000..64b0ba06 --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.h @@ -0,0 +1,163 @@ +/* + * MVKQueryPool.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKDevice.h" +#include +#include +#include + +class MVKBuffer; +class MVKCommandBuffer; +class MVKCommandEncoder; + +// The size of one query slot in bytes +#define kMVKQuerySlotSizeInBytes sizeof(uint64_t) + + +#pragma mark - +#pragma mark MVKQueryPool + +/** + * Abstract class representing a Vulkan query pool. + * Subclasses are specialized for specific query types. + * Subclasses will generally override the beginQuery(), endQuery(), and getResult(uint32_t, void*, bool) member functions. + */ +class MVKQueryPool : public MVKBaseDeviceObject { + +public: + + /** Begins the specified query. */ + virtual void beginQuery(uint32_t query, VkQueryControlFlags flags, MVKCommandEncoder* cmdEncoder) {} + + /** Ends the specified query. */ + virtual void endQuery(uint32_t query, MVKCommandEncoder* cmdEncoder) {} + + /** Finishes the specified queries and marks them as available. */ + virtual void finishQueries(std::vector& queries); + + /** Resets the results and availability status of the specified queries. */ + virtual void resetResults(uint32_t firstQuery, uint32_t queryCount, MVKCommandEncoder* cmdEncoder); + + /** Copies the results of the specified queries into host memory. */ + VkResult getResults(uint32_t firstQuery, + uint32_t queryCount, + size_t dataSize, + void* pData, + VkDeviceSize stride, + VkQueryResultFlags flags); + + /** Copies the results of the specified queries into a buffer. */ + virtual void copyQueryPoolResults(uint32_t firstQuery, + uint32_t queryCount, + MVKBuffer* destBuffer, + VkDeviceSize destOffset, + VkDeviceSize destStride, + VkQueryResultFlags flags); + + /** Called from the MVKCmdBeginQuery command when it is added to the command buffer */ + virtual void beginQueryAddedTo(uint32_t query, MVKCommandBuffer* cmdBuffer) {}; + + +#pragma mark Construction + + MVKQueryPool(MVKDevice* device, + const VkQueryPoolCreateInfo* pCreateInfo, + const uint32_t queryElementCount) : MVKBaseDeviceObject(device), + _availability(pCreateInfo->queryCount), + _queryElementCount(queryElementCount) {} + +protected: + bool areQueriesAvailable(uint32_t firstQuery, uint32_t endQuery); + VkResult getResult(uint32_t query, void* pQryData, VkQueryResultFlags flags); + virtual void getResult(uint32_t query, void* pQryData, bool shouldOutput64Bit) {} + + std::vector _availability; + uint32_t _queryElementCount; + std::mutex _availabilityLock; + std::condition_variable _availabilityBlocker; +}; + + +#pragma mark - +#pragma mark MVKTimestampQueryPool + +/** A Vulkan query pool for timestamp queries. */ +class MVKTimestampQueryPool : public MVKQueryPool { + +public: + void finishQueries(std::vector& queries) override; + + +#pragma mark Construction + + MVKTimestampQueryPool(MVKDevice* device, const VkQueryPoolCreateInfo* pCreateInfo); + +protected: + void getResult(uint32_t query, void* pQryData, bool shouldOutput64Bit) override; + + std::vector _timestamps; +}; + + +#pragma mark - +#pragma mark MVKOcclusionQueryPool + +/** A Vulkan query pool for occlusion queries. */ +class MVKOcclusionQueryPool : public MVKQueryPool { + +public: + + /** Returns the MTLBuffer used to hold occlusion query results. */ + id getVisibilityResultMTLBuffer(); + + /** Returns the offset of the specified query in the visibility MTLBuffer. */ + NSUInteger getVisibilityResultOffset(uint32_t query); + + void beginQuery(uint32_t query, VkQueryControlFlags flags, MVKCommandEncoder* cmdEncoder) override; + void endQuery(uint32_t query, MVKCommandEncoder* cmdEncoder) override; + void resetResults(uint32_t firstQuery, uint32_t queryCount, MVKCommandEncoder* cmdEncoder) override; + void beginQueryAddedTo(uint32_t query, MVKCommandBuffer* cmdBuffer) override; + + +#pragma mark Construction + + MVKOcclusionQueryPool(MVKDevice* device, const VkQueryPoolCreateInfo* pCreateInfo); + + ~MVKOcclusionQueryPool() override; + +protected: + void getResult(uint32_t query, void* pQryData, bool shouldOutput64Bit) override; + + id _visibilityResultMTLBuffer; + uint32_t _queryIndexOffset; +}; + + +#pragma mark - +#pragma mark MVKUnsupportedQueryPool + +/** A Vulkan query pool for a query pool type that is unsupported in Metal. */ +class MVKUnsupportedQueryPool : public MVKQueryPool { + +public: + MVKUnsupportedQueryPool(MVKDevice* device, const VkQueryPoolCreateInfo* pCreateInfo); + +}; + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.mm b/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.mm new file mode 100644 index 00000000..ae264c93 --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.mm @@ -0,0 +1,248 @@ +/* + * MVKQueryPool.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKQueryPool.h" +#include "MVKBuffer.h" +#include "MVKCommandBuffer.h" +#include "MVKFoundation.h" +#include "MVKLogging.h" + +using namespace std; + + +#pragma mark MVKQueryPool + +// Mark queries as available +void MVKQueryPool::finishQueries(vector& queries) { + lock_guard lock(_availabilityLock); + for (uint32_t qry : queries) { _availability[qry] = true; } + _availabilityBlocker.notify_all(); // Predicate of each wait() call will check whether all required queries are available +} + +void MVKQueryPool::resetResults(uint32_t firstQuery, uint32_t queryCount, MVKCommandEncoder* cmdEncoder) { + lock_guard lock(_availabilityLock); + uint32_t endQuery = firstQuery + queryCount; + for (uint32_t query = firstQuery; query < endQuery; query++) { + _availability[query] = false; + } +} + +VkResult MVKQueryPool::getResults(uint32_t firstQuery, + uint32_t queryCount, + size_t dataSize, + void* pData, + VkDeviceSize stride, + VkQueryResultFlags flags) { + unique_lock lock(_availabilityLock); + + uint32_t endQuery = firstQuery + queryCount; + + if (mvkAreFlagsEnabled(flags, VK_QUERY_RESULT_WAIT_BIT)) { + _availabilityBlocker.wait(lock, [this, firstQuery, endQuery]{ + return areQueriesAvailable(firstQuery, endQuery); + }); + } + + VkResult rqstRslt = VK_SUCCESS; + uintptr_t pQryData = (uintptr_t)pData; + for (uint32_t query = firstQuery; query < endQuery; query++, pQryData += stride) { + VkResult qryRslt = getResult(query, (void*)pQryData, flags); + if (rqstRslt == VK_SUCCESS) { rqstRslt = qryRslt; } + } + return rqstRslt; +} + +// Returns whether all the queries between the start (inclusive) and end (exclusive) queries are available. +bool MVKQueryPool::areQueriesAvailable(uint32_t firstQuery, uint32_t endQuery) { + for (uint32_t query = firstQuery; query < endQuery; query++) { + if ( !_availability[query] ) { return false; } + } + return true; +} + +VkResult MVKQueryPool::getResult(uint32_t query, void* pQryData, VkQueryResultFlags flags) { + + bool isAvailable = _availability[query]; + bool shouldOutput = (isAvailable || mvkAreFlagsEnabled(flags, VK_QUERY_RESULT_PARTIAL_BIT)); + bool shouldOutput64Bit = mvkAreFlagsEnabled(flags, VK_QUERY_RESULT_64_BIT); + + // Output the results of this query + if (shouldOutput) { getResult(query, pQryData, shouldOutput64Bit); } + + // If requested, output the availability bit + if (mvkAreFlagsEnabled(flags, VK_QUERY_RESULT_WITH_AVAILABILITY_BIT)) { + if (shouldOutput64Bit) { + uintptr_t pAvailability = (uintptr_t)pQryData + (_queryElementCount * sizeof(uint64_t)); + *(uint64_t*)pAvailability = isAvailable; + } else { + uintptr_t pAvailability = (uintptr_t)pQryData + (_queryElementCount * sizeof(uint32_t)); + *(uint32_t*)pAvailability = isAvailable; + } + } + + return shouldOutput ? VK_SUCCESS : VK_NOT_READY; +} + +void MVKQueryPool::copyQueryPoolResults(uint32_t firstQuery, + uint32_t queryCount, + MVKBuffer* destBuffer, + VkDeviceSize destOffset, + VkDeviceSize destStride, + VkQueryResultFlags flags) { + if (destBuffer->isMemoryHostAccessible()) { + void* pData = (void*)((uintptr_t)destBuffer->getMTLBuffer().contents + destBuffer->getMTLBufferOffset() + destOffset); + size_t dataSize = destStride * queryCount; + getResults(firstQuery, queryCount, dataSize, pData, destStride, flags); + } else { + mvkNotifyErrorWithText(VK_ERROR_MEMORY_MAP_FAILED, "Private GPU-only memory cannot be used for query pool results."); + } +} + + +#pragma mark - +#pragma mark MVKTimestampQueryPool + +// Update timestamp values, then mark queries as available +void MVKTimestampQueryPool::finishQueries(vector& queries) { + uint64_t ts = mvkGetTimestamp(); + for (uint32_t qry : queries) { _timestamps[qry] = ts; } + + MVKQueryPool::finishQueries(queries); +} + +void MVKTimestampQueryPool::getResult(uint32_t query, void* pQryData, bool shouldOutput64Bit) { + if (shouldOutput64Bit) { + *(uint64_t*)pQryData = _timestamps[query]; + } else { + *(uint32_t*)pQryData = (uint32_t)_timestamps[query]; + } +} + + +#pragma mark Construction + +MVKTimestampQueryPool::MVKTimestampQueryPool(MVKDevice* device, const VkQueryPoolCreateInfo* pCreateInfo) + : MVKQueryPool(device, pCreateInfo, 1), _timestamps(pCreateInfo->queryCount) { +} + + +#pragma mark - +#pragma mark MVKOcclusionQueryPool + +// If a dedicated visibility buffer has been established, use it, otherwise fetch the +// current global visibility buffer, but don't cache it because it could be replaced later. +id MVKOcclusionQueryPool::getVisibilityResultMTLBuffer() { + return _visibilityResultMTLBuffer ? _visibilityResultMTLBuffer : _device->getGlobalVisibilityResultMTLBuffer(); +} + +NSUInteger MVKOcclusionQueryPool::getVisibilityResultOffset(uint32_t query) { + return (NSUInteger)(_queryIndexOffset + query) * kMVKQuerySlotSizeInBytes; +} + +void MVKOcclusionQueryPool::beginQuery(uint32_t query, VkQueryControlFlags flags, MVKCommandEncoder* cmdEncoder) { + cmdEncoder->beginOcclusionQuery(this, query, flags); +} + +void MVKOcclusionQueryPool::endQuery(uint32_t query, MVKCommandEncoder* cmdEncoder) { + cmdEncoder->endOcclusionQuery(this, query); +} + +void MVKOcclusionQueryPool::resetResults(uint32_t firstQuery, uint32_t queryCount, MVKCommandEncoder* cmdEncoder) { + MVKQueryPool::resetResults(firstQuery, queryCount, cmdEncoder); + + id blitEncoder = cmdEncoder->getMTLBlitEncoder(kMVKCommandUseResetQueryPool); + NSUInteger firstOffset = getVisibilityResultOffset(firstQuery); + NSUInteger lastOffset = getVisibilityResultOffset(firstQuery + queryCount); + + [blitEncoder fillBuffer: getVisibilityResultMTLBuffer() + range: NSMakeRange(firstOffset, lastOffset - firstOffset) + value: 0]; +} + +void MVKOcclusionQueryPool::getResult(uint32_t query, void* pQryData, bool shouldOutput64Bit) { + NSUInteger mtlBuffOffset = getVisibilityResultOffset(query); + uint64_t* pData = (uint64_t*)((uintptr_t)getVisibilityResultMTLBuffer().contents + mtlBuffOffset); + + if (shouldOutput64Bit) { + *(uint64_t*)pQryData = *pData; + } else { + *(uint32_t*)pQryData = (uint32_t)(*pData); + } +} + +void MVKOcclusionQueryPool::beginQueryAddedTo(uint32_t query, MVKCommandBuffer* cmdBuffer) { + NSUInteger offset = getVisibilityResultOffset(query); + NSUInteger maxOffset = getDevice()->_pMetalFeatures->maxQueryBufferSize - kMVKQuerySlotSizeInBytes; + if (offset > maxOffset) { + cmdBuffer->recordResult(mvkNotifyErrorWithText(VK_ERROR_OUT_OF_DEVICE_MEMORY, "vkCmdBeginQuery(): The query offset value %lu is larger than the maximum offset value %lu available on this device.", offset, maxOffset)); + } + + if (cmdBuffer->_initialVisibilityResultMTLBuffer == nil) { + cmdBuffer->_initialVisibilityResultMTLBuffer = getVisibilityResultMTLBuffer(); + } +} + + +#pragma mark Construction + +MVKOcclusionQueryPool::MVKOcclusionQueryPool(MVKDevice* device, + const VkQueryPoolCreateInfo* pCreateInfo) : MVKQueryPool(device, pCreateInfo, 1) { + + if (_device->_mvkConfig.supportLargeQueryPools) { + _queryIndexOffset = 0; + + // Ensure we don't overflow the maximum number of queries + uint32_t queryCount = pCreateInfo->queryCount; + VkDeviceSize reqBuffLen = (VkDeviceSize)queryCount * kMVKQuerySlotSizeInBytes; + VkDeviceSize maxBuffLen = _device->_pMetalFeatures->maxQueryBufferSize; + VkDeviceSize newBuffLen = min(reqBuffLen, maxBuffLen); + queryCount = uint32_t(newBuffLen / kMVKQuerySlotSizeInBytes); + + if (reqBuffLen > maxBuffLen) { + mvkNotifyErrorWithText(VK_ERROR_OUT_OF_DEVICE_MEMORY, "vkCreateQueryPool(): Each query pool can support a maximum of %d queries.", queryCount); + } + + NSUInteger mtlBuffLen = mvkAlignByteOffset(newBuffLen, _device->_pMetalFeatures->mtlBufferAlignment); + MTLResourceOptions mtlBuffOpts = MTLResourceStorageModeShared | MTLResourceCPUCacheModeDefaultCache; + _visibilityResultMTLBuffer = [getMTLDevice() newBufferWithLength: mtlBuffLen options: mtlBuffOpts]; // retained + + } else { + _queryIndexOffset = _device->expandVisibilityResultMTLBuffer(pCreateInfo->queryCount); + _visibilityResultMTLBuffer = nil; // Will delegate to global buffer in device on access + } +} + +MVKOcclusionQueryPool::~MVKOcclusionQueryPool() { + [_visibilityResultMTLBuffer release]; +}; + + +#pragma mark - +#pragma mark MVKUnsupportedQueryPool + +MVKUnsupportedQueryPool::MVKUnsupportedQueryPool(MVKDevice* device, const VkQueryPoolCreateInfo* pCreateInfo) + : MVKQueryPool(device, pCreateInfo, 1) { + + switch (pCreateInfo->queryType) { + case VK_QUERY_TYPE_PIPELINE_STATISTICS: + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateQueryPool: VK_QUERY_TYPE_PIPELINE_STATISTICS is not supported.")); + default: + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "vkCreateQueryPool: Unsupported query pool type: %d.", pCreateInfo->queryType)); + } +} + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h new file mode 100644 index 00000000..b49c1b74 --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h @@ -0,0 +1,268 @@ +/* + * MVKQueue.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKDevice.h" +#include "MVKCommandBuffer.h" +#include "MVKCommandEncodingPool.h" +#include "MVKImage.h" +#include "MVKSync.h" +#include +#include + +#import + +class MVKQueue; +class MVKQueueSubmission; + + +#pragma mark - +#pragma mark MVKQueueFamily + +/** Represents a Vulkan queue family. */ +class MVKQueueFamily : public MVKBaseDeviceObject { + +public: + + /** Returns the index of this queue family. */ + inline uint32_t getIndex() { return _queueFamilyIndex; } + + /** Returns the number of queues allocated for this family. */ + inline uint32_t getQueueCount() { return uint32_t(_queues.size()); } + + /** Returns the queue at the specified index. */ + inline MVKQueue* getQueue(uint32_t queueIndex) { return _queues[queueIndex]; } + + /** Constructs an instance with the specified number of queues for the specified device. */ + MVKQueueFamily(MVKDevice* device, + const VkDeviceQueueCreateInfo* pCreateInfo, + const VkQueueFamilyProperties* pProperties); + + ~MVKQueueFamily() override; + +protected: + uint32_t _queueFamilyIndex; + VkQueueFamilyProperties _properties; + std::vector _queues; + std::mutex _lock; +}; + + +#pragma mark - +#pragma mark MVKQueue + +/** Represents a Vulkan queue. */ +class MVKQueue : public MVKBaseDeviceObject { + +public: + +#pragma mark Queue submissions + + /** Submits the specified command buffers to the queue. */ + VkResult submit(uint32_t submitCount, const VkSubmitInfo* pSubmits, + VkFence fence, MVKCommandUse cmdBuffUse); + + /** Submits the specified presentation command to the queue. */ + VkResult submitPresentKHR(const VkPresentInfoKHR* pPresentInfo); + + /** Block the current thread until this queue is idle. */ + VkResult waitIdle(MVKCommandUse cmdBuffUse); + + /** + * Retrieves a MTLCommandBuffer instance from the contained MTLCommandQueue, adds a + * completion handler to it so that the mtlCommandBufferHasCompleted() function will + * be called when the MTLCommandBuffer completes, and returns the MTLCommandBuffer. + */ + id getNextMTLCommandBuffer(NSString* mtlCmdBuffLabel, + MVKCommandBuffer* mvkCmdBuff = nullptr); + + /** Called automatically when the specified MTLCommandBuffer with the specified ID has completed. */ + void mtlCommandBufferHasCompleted(id mtlCmdBuff, + MVKMTLCommandBufferID mtlCmdBuffID, + MVKCommandBuffer* mvkCmdBuff); + + /** + * Registers the specified countdown object. This function sets the count value + * of the countdown object to the current number of incomplete MTLCommandBuffers, + * and marks the countdown object with the ID of the most recently registered + * MTLCommandBuffer. The countdown object will be decremented each time any + * MTLCommandBuffer with an ID less than the ID of the most recent MTLCommandBuffer + * at the time the countdown object was registered. + * + * If the current number of incomplete MTLCommandBuffers is zero, the countdown + * object will indicate that it is already completed, and will not be registered. + */ + void registerMTLCommandBufferCountdown(MVKMTLCommandBufferCountdown* countdown); + + /** Returns the command encoding pool. */ + inline MVKCommandEncodingPool* getCommandEncodingPool() { return &_commandEncodingPool; } + + +#pragma mark Metal + + /** Returns the Metal queue underlying this queue. */ + inline id getMTLCommandQueue() { return _mtlQueue; } + +#pragma mark Construction + + /** Constructs an instance for the device and queue family. */ + MVKQueue(MVKDevice* device, MVKQueueFamily* queueFamily, uint32_t index, float priority); + + ~MVKQueue() override; + +protected: + friend class MVKQueueSubmission; + + void initExecQueue(); + void initMTLCommandQueue(); + void destroyExecQueue(); + void submit(MVKQueueSubmission* qSubmit); + + MVKQueueFamily* _queueFamily; + uint32_t _index; + float _priority; + dispatch_queue_t _execQueue; + id _mtlQueue; + std::vector _completionCountdowns; + std::mutex _completionLock; + uint32_t _activeMTLCommandBufferCount; + MVKMTLCommandBufferID _nextMTLCmdBuffID; + MVKCommandEncodingPool _commandEncodingPool; +}; + + +#pragma mark - +#pragma mark MVKQueueSubmission + +/** This is an abstract class for an operation that can be submitted to an MVKQueue. */ +class MVKQueueSubmission : public MVKBaseDeviceObject { + +public: + + /** + * Executes this action on the queue and then disposes of this instance. + * + * Upon completion of this function, no further calls should be made to this instance. + */ + virtual void execute() = 0; + + MVKQueueSubmission(MVKDevice* device, MVKQueue* queue); + +protected: + friend class MVKQueue; + + void recordResult(VkResult vkResult); + + MVKQueue* _queue; + MVKQueueSubmission* _prev; + MVKQueueSubmission* _next; + VkResult _submissionResult; +}; + + +#pragma mark - +#pragma mark MVKQueueCommandBufferSubmissionCountdown + +/** Counts down MTLCommandBuffers on behalf of an MVKQueueCommandBufferSubmission instance. */ +class MVKQueueCommandBufferSubmissionCountdown : public MVKMTLCommandBufferCountdown { + +public: + + /** Constructs an instance. */ + MVKQueueCommandBufferSubmissionCountdown(MVKQueueCommandBufferSubmission* qSub); + +protected: + + /** Performs the action to take when the count has reached zero. */ + virtual void finish(); + + MVKQueueCommandBufferSubmission* _qSub; +}; + + +#pragma mark - +#pragma mark MVKQueueCommandBufferSubmission + +/** Submits the commands in a set of command buffers to the queue. */ +class MVKQueueCommandBufferSubmission : public MVKQueueSubmission { + +public: + + /** + * Executes this action on the queue and then disposes of this instance. + * + * Upon completion of this function, no further calls should be made to this instance. + */ + virtual void execute(); + + /** Automatically called once all the MTLCommandBuffers have completed execution. */ + void finish(); + + /** + * Constructs an instance for the device and queue. + * pSubmit may be VK_NULL_HANDLE to create an instance that triggers a fence without submitting any actual command buffers. + */ + MVKQueueCommandBufferSubmission(MVKDevice* device, + MVKQueue* queue, + const VkSubmitInfo* pSubmit, + VkFence fence, + MVKCommandUse cmdBuffUse); + + /** Constructs an instance for the device and queue, with a fence, but without actual command buffers. */ + MVKQueueCommandBufferSubmission(MVKDevice* device, MVKQueue* queue, VkFence fence); + +protected: + friend MVKCommandEncoder; + + NSString* getMTLCommandBufferName(); + + MVKQueueCommandBufferSubmissionCountdown _cmdBuffCountdown; + std::vector _cmdBuffers; + std::vector _waitSemaphores; + std::vector _signalSemaphores; + MVKFence* _fence; + MVKCommandUse _cmdBuffUse; +}; + + +#pragma mark - +#pragma mark MVKQueuePresentSurfaceSubmission + +/** Presents a swapchain surface image to the OS. */ +class MVKQueuePresentSurfaceSubmission : public MVKQueueSubmission { + +public: + + /** + * Executes this action on the queue and then disposes of this instance. + * + * Upon completion of this function, no further calls should be made to this instance. + */ + virtual void execute(); + + /** Constructs an instance for the device and queue. */ + MVKQueuePresentSurfaceSubmission(MVKDevice* device, + MVKQueue* queue, + const VkPresentInfoKHR* pPresentInfo); + +protected: + std::vector _waitSemaphores; + std::vector _surfaceImages; +}; + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm new file mode 100644 index 00000000..c212230e --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm @@ -0,0 +1,418 @@ +/* + * MVKQueue.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKQueue.h" +#include "MVKSwapchain.h" +#include "MVKSync.h" +#include "MVKFoundation.h" +#include "MVKLogging.h" + +using namespace std; + + +#pragma mark - +#pragma mark MVKQueueFamily + +MVKQueueFamily::MVKQueueFamily(MVKDevice* device, + const VkDeviceQueueCreateInfo* pCreateInfo, + const VkQueueFamilyProperties* pProperties) : MVKBaseDeviceObject(device) { + _properties = *pProperties; + + // Create the queues + uint32_t qCnt = pCreateInfo->queueCount; + _queues.reserve(qCnt); + for (uint32_t qIdx = 0; qIdx < qCnt; qIdx++) { + _queues.push_back(new MVKQueue(_device, this, qIdx, pCreateInfo->pQueuePriorities[qIdx])); + } +} + +MVKQueueFamily::~MVKQueueFamily() { + mvkDestroyContainerContents(_queues); +} + + +#pragma mark - +#pragma mark MVKQueue + + +#pragma mark Queue submissions + +/** Submits the specified submission object to the execution queue. */ +void MVKQueue::submit(MVKQueueSubmission* qSubmit) { + if ( !qSubmit ) { return; } // Ignore nils + dispatch_async( _execQueue, ^{ qSubmit->execute(); } ); +// MVKLogDebug("Dispatched MVKQueueSubmission %p on thread '%s'.", qSubmit, [NSThread currentThread].name.UTF8String); +} + +VkResult MVKQueue::submit(uint32_t submitCount, const VkSubmitInfo* pSubmits, + VkFence fence, MVKCommandUse cmdBuffUse) { + VkResult rslt = VK_SUCCESS; + for (uint32_t sIdx = 0; sIdx < submitCount; sIdx++) { +// MVKLogDebug("Submitting command buffer on thread '%s'. Elapsed time: %.6f ms.", [NSThread currentThread].name.UTF8String, mvkGetElapsedMilliseconds()); + VkFence fenceOrNil = (sIdx == (submitCount - 1)) ? fence : VK_NULL_HANDLE; // last one gets the fence + MVKQueueSubmission* qSub = new MVKQueueCommandBufferSubmission(_device, this, &pSubmits[sIdx], fenceOrNil, cmdBuffUse); + if (rslt == VK_SUCCESS) { rslt = qSub->_submissionResult; } // Extract result before submission to avoid race condition with early destruction + submit(qSub); + } + + // Support fence-only submission + if (submitCount == 0 && fence) { +// MVKLogDebug("Submitting fence-only command buffer. Elapsed time: %.6f ms.", mvkGetElapsedMilliseconds()); + MVKQueueSubmission* qSub = new MVKQueueCommandBufferSubmission(_device, this, VK_NULL_HANDLE, fence, cmdBuffUse); + if (rslt == VK_SUCCESS) { rslt = qSub->_submissionResult; } // Extract result before submission to avoid race condition with early destruction + submit(qSub); + } + + return rslt; +} + +VkResult MVKQueue::submitPresentKHR(const VkPresentInfoKHR* pPresentInfo) { + MVKQueueSubmission* qSub = new MVKQueuePresentSurfaceSubmission(_device, this, pPresentInfo); + VkResult rslt = qSub->_submissionResult; // Extract result before submission to avoid race condition with early destruction + submit(qSub); + return rslt; +} + +VkResult MVKQueue::waitIdle(MVKCommandUse cmdBuffUse) { + + // Create submit struct including a temp Vulkan reference to a semaphore + VkSemaphore vkSem4; + VkSubmitInfo vkSbmtInfo = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = NULL, + .waitSemaphoreCount = 0, + .pWaitSemaphores = VK_NULL_HANDLE, + .commandBufferCount = 0, + .pCommandBuffers = VK_NULL_HANDLE, + .signalSemaphoreCount = 1, + .pSignalSemaphores = &vkSem4 + }; + + VkSemaphoreCreateInfo vkSemInfo = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .pNext = NULL, + .flags = 0, + }; + + MVKSemaphore mvkSem4(_device, &vkSemInfo); // Construct MVKSemaphore + vkSem4 = (VkSemaphore)&mvkSem4; // Set reference to MVKSemaphore in submit struct + submit(1, &vkSbmtInfo, VK_NULL_HANDLE, cmdBuffUse); // Submit semaphore queue + mvkSem4.wait(); // Wait on the semaphore + + return VK_SUCCESS; +} + +// This function is guarded against conflict with the mtlCommandBufferHasCompleted() +// function, but is not theadsafe against calls to this function itself, or to the +// registerMTLCommandBufferCountdown() function from multiple threads. It is assumed +// that this function and the registerMTLCommandBufferCountdown() function are called +// from a single thread. +id MVKQueue::getNextMTLCommandBuffer(NSString* mtlCmdBuffLabel, + MVKCommandBuffer* mvkCmdBuff) { + + // Retrieve a MTLCommandBuffer from the MTLQueue. + id mtlCmdBuffer = [_mtlQueue commandBufferWithUnretainedReferences]; + mtlCmdBuffer.label = mtlCmdBuffLabel; + + if (mvkCmdBuff) { mvkCmdBuff->mtlCommandBufferHasStarted(mtlCmdBuffer); } + + // Assign a unique ID to the MTLCommandBuffer, and track when it completes. + MVKMTLCommandBufferID mtlCmdBuffID = _nextMTLCmdBuffID++; + [mtlCmdBuffer addCompletedHandler: ^(id mtlCmdBuff) { + this->mtlCommandBufferHasCompleted(mtlCmdBuff, mtlCmdBuffID, mvkCmdBuff); + }]; + + // Keep a running count of the active MTLCommandBuffers. + // This needs to be guarded against a race condition with a MTLCommandBuffer completing. + lock_guard lock(_completionLock); + _activeMTLCommandBufferCount++; + +// MVKLogDebug("MVKQueue %p created MTLCommandBuffer %d with now %d active MTLCommandBuffers.", this, mtlCmdBuffID, _activeMTLCommandBufferCount); + + return mtlCmdBuffer; +} + +// This function must be called after all corresponding calls to getNextMTLCommandBuffer() and from the same thead. +void MVKQueue::registerMTLCommandBufferCountdown(MVKMTLCommandBufferCountdown* countdown) { + lock_guard lock(_completionLock); + + if ( !countdown->setActiveMTLCommandBufferCount(_activeMTLCommandBufferCount, _nextMTLCmdBuffID) ) { + _completionCountdowns.push_back(countdown); + } +// MVKLogDebug("Queue %p adding MTLCommandBufferCountdown for %d active MTLCommandBuffers and MTLCommandBuffer ID's below %d.", this, _activeMTLCommandBufferCount, _nextMTLCmdBuffID); +} + +void MVKQueue::mtlCommandBufferHasCompleted(id mtlCmdBuff, + MVKMTLCommandBufferID mtlCmdBuffID, + MVKCommandBuffer* mvkCmdBuff) { + lock_guard lock(_completionLock); + + _activeMTLCommandBufferCount--; + + if (mvkCmdBuff) { mvkCmdBuff->mtlCommandBufferHasCompleted(mtlCmdBuff); } + +// MVKLogDebug("Queue %p completing MTLCommandBuffer %d (%s) with now %d active MTLCommandBuffers. Iterating %d MTLCommandBufferCountdowns. Elapsed time: %.6f ms.", +// this, mtlCmdBuffID, mtlCmdBuff.label.UTF8String, _activeMTLCommandBufferCount, _completionCountdowns.size(), mvkGetElapsedMilliseconds()); + + // Iterate through the countdowns, letting them know about the completion, and + // remove any countdowns that have completed by eliding them out of the array. + uint32_t ccCnt = (uint32_t)_completionCountdowns.size(); + uint32_t currCCIdx = 0; + for (uint32_t ccIdx = 0; ccIdx < ccCnt; ccIdx++) { + MVKMTLCommandBufferCountdown* mvkCD = _completionCountdowns[ccIdx]; + if ( !mvkCD->mtlCommandBufferHasCompleted(mtlCmdBuffID) ) { + // Only retain the countdown if it has not just completed. + // Move it forward in the array if any preceding countdowns have been removed. + if (currCCIdx != ccIdx) { _completionCountdowns[currCCIdx] = mvkCD; } + currCCIdx++; + } + } + // If any countdowns were removed, clear out the extras at the end + if (currCCIdx < ccCnt) { _completionCountdowns.resize(currCCIdx); } +} + + +#pragma mark Construction + +#define MVK_DISPATCH_QUEUE_QOS_CLASS QOS_CLASS_USER_INITIATED + +MVKQueue::MVKQueue(MVKDevice* device, MVKQueueFamily* queueFamily, uint32_t index, float priority) + : MVKBaseDeviceObject(device), _commandEncodingPool(device) { + + _queueFamily = queueFamily; + _index = index; + _priority = priority; + initExecQueue(); + initMTLCommandQueue(); + _activeMTLCommandBufferCount = 0; + _nextMTLCmdBuffID = 1; + +// MVKLogDebug("Queue %p created.", this); +} + +/** Creates and initializes the execution dispatch queue. */ +void MVKQueue::initExecQueue() { + + // Create a name for the dispatch queue + const char* dqNameFmt = "MoltenVKDispatchQueue-%d-%d-%.1f"; + char dqName[strlen(dqNameFmt) + 32]; + sprintf(dqName, dqNameFmt, _queueFamily->getIndex(), _index, _priority); + + // Determine the dispatch queue priority + dispatch_qos_class_t dqQOS = MVK_DISPATCH_QUEUE_QOS_CLASS; + int dqPriority = (1.0 - _priority) * QOS_MIN_RELATIVE_PRIORITY; + dispatch_queue_attr_t dqAttr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, dqQOS, dqPriority); + + // Create the dispatch queue + _execQueue = dispatch_queue_create(dqName, dqAttr); // retained +} + +/** Creates and initializes the Metal queue. */ +void MVKQueue::initMTLCommandQueue() { + _mtlQueue = [_device->getMTLDevice() newCommandQueue]; // retained + [_mtlQueue insertDebugCaptureBoundary]; // Allow Xcode to capture the first frame if desired. +} + +MVKQueue::~MVKQueue() { + // Delay destroying this queue until registerMTLCommandBufferCountdown() is done. + // registerMTLCommandBufferCountdown() can trigger a queue submission to finish(), + // which may trigger semaphores that control a queue waitIdle(). If that waitIdle() + // is being called by the app just prior to device and queue destruction, a rare race + // condition exists if registerMTLCommandBufferCountdown() does not complete before + // this queue is destroyed. If _completionLock is destroyed along with this queue, + // before registerMTLCommandBufferCountdown() completes, a SIGABRT crash will arise + // in the destructor of the lock created in registerMTLCommandBufferCountdown(). + lock_guard lock(_completionLock); + destroyExecQueue(); + [_mtlQueue release]; +} + +/** Destroys the execution dispatch queue. */ +void MVKQueue::destroyExecQueue() { + dispatch_release(_execQueue); +} + + +#pragma mark - +#pragma mark MVKQueueSubmission + +MVKQueueSubmission::MVKQueueSubmission(MVKDevice* device, MVKQueue* queue) : MVKBaseDeviceObject(device) { + _queue = queue; + _prev = VK_NULL_HANDLE; + _next = VK_NULL_HANDLE; + _submissionResult = VK_SUCCESS; +} + +void MVKQueueSubmission::recordResult(VkResult vkResult) { + if (_submissionResult == VK_SUCCESS) { _submissionResult = vkResult; } +} + + +#pragma mark - +#pragma mark MVKQueueCommandBufferSubmissionCountdown + +MVKQueueCommandBufferSubmissionCountdown::MVKQueueCommandBufferSubmissionCountdown(MVKQueueCommandBufferSubmission* qSub) { + _qSub = qSub; +} + +void MVKQueueCommandBufferSubmissionCountdown::finish() { _qSub->finish(); } + + +#pragma mark - +#pragma mark MVKQueueCommandBufferSubmission + +void MVKQueueCommandBufferSubmission::execute() { + +// MVKLogDebug("Executing MVKQueueCommandBufferSubmission %p with %d command buffers, %d wait semaphores, %d signal semaphores, and fence %p on thread '%s'.", +// this, _cmdBuffers.size(), _waitSemaphores.size(), _signalSemaphores.size(), _fence, [NSThread currentThread].name.UTF8String); + + // Wait on each wait semaphore in turn. It doesn't matter which order they are signalled. + for (auto& ws : _waitSemaphores) { ws->wait(); } + +// MVKLogDebug("Continuing MVKQueueCommandBufferSubmission %p after waiting for %d wait semaphores on thread '%s'.", +// this, _waitSemaphores.size(), [NSThread currentThread].name.UTF8String); + + // Execute each command buffer, or if no real command buffers, + // create an empty MTLCommandBuffer to trigger the semaphores and fence if needed. + if ( _cmdBuffers.empty() ) { + if ( !_fence && _signalSemaphores.empty() ) { return; } // Nothing to do + + id mtlCmdBuff = _queue->getNextMTLCommandBuffer(getMTLCommandBufferName()); + [mtlCmdBuff commit]; + } else { + MVKCommandBufferBatchPosition cmdBuffPos = {1, uint32_t(_cmdBuffers.size()), _cmdBuffUse}; + for (auto& cb : _cmdBuffers) { + cb->execute(this, cmdBuffPos); + cmdBuffPos.index++; + } + } + + // Register for callback when MTLCommandBuffers have completed + _queue->registerMTLCommandBufferCountdown(&_cmdBuffCountdown); +} + +// Returns an NSString suitable for use as a label +NSString* MVKQueueCommandBufferSubmission::getMTLCommandBufferName() { + switch (_cmdBuffUse) { + case kMVKCommandUseQueueSubmit: + return [NSString stringWithFormat: @"%@ (virtual for sync)", mvkMTLCommandBufferLabel(_cmdBuffUse)]; + default: + return mvkMTLCommandBufferLabel(_cmdBuffUse); + } +} + +void MVKQueueCommandBufferSubmission::finish() { + +// MVKLogDebug("Finishing MVKQueueCommandBufferSubmission %p with %d wait semaphores, %d signal semaphores, and fence %p. Elapsed time: %.6f ms.", +// this, _waitSemaphores.size(), _signalSemaphores.size(), _fence, mvkGetElapsedMilliseconds()); + + // Signal each of the signal semaphores. + for (auto& ss : _signalSemaphores) { ss->signal(); } + + // If a fence exists, signal it. + if (_fence) { _fence->signal(); } + + delete this; +} + +MVKQueueCommandBufferSubmission::MVKQueueCommandBufferSubmission(MVKDevice* device, + MVKQueue* queue, + const VkSubmitInfo* pSubmit, + VkFence fence, + MVKCommandUse cmdBuffUse) + : MVKQueueSubmission(device, queue), _cmdBuffCountdown(this) { + + // pSubmit can be null if just tracking the fence alone + if (pSubmit) { + uint32_t cbCnt = pSubmit->commandBufferCount; + _cmdBuffers.reserve(cbCnt); + for (uint32_t i = 0; i < cbCnt; i++) { + MVKCommandBuffer* cb = (MVKCommandBuffer*)pSubmit->pCommandBuffers[i]; + _cmdBuffers.push_back(cb); + recordResult(cb->getRecordingResult()); + } + + uint32_t wsCnt = pSubmit->waitSemaphoreCount; + _waitSemaphores.reserve(wsCnt); + for (uint32_t i = 0; i < wsCnt; i++) { + _waitSemaphores.push_back((MVKSemaphore*)pSubmit->pWaitSemaphores[i]); + } + + uint32_t ssCnt = pSubmit->signalSemaphoreCount; + _signalSemaphores.reserve(ssCnt); + for (uint32_t i = 0; i < ssCnt; i++) { + _signalSemaphores.push_back((MVKSemaphore*)pSubmit->pSignalSemaphores[i]); + } + } + + _fence = (MVKFence*)fence; + _cmdBuffUse= cmdBuffUse; + +// MVKLogDebug("Adding MVKQueueCommandBufferSubmission %p with %d command buffers, %d wait semaphores, %d signal semaphores, and fence %p. Elapsed time: %.6f ms.", +// this, _cmdBuffers.size(), _waitSemaphores.size(), _signalSemaphores.size(), _fence, mvkGetElapsedMilliseconds()); +} + + +#pragma mark - +#pragma mark MVKQueuePresentSurfaceSubmission + +#define MVK_PRESENT_VIA_CMD_BUFFER 0 + +void MVKQueuePresentSurfaceSubmission::execute() { + +// MVKLogDebug("Executing MVKQueuePresentSurfaceSubmission %p with %d wait semaphores.", this, _waitSemaphores.size()); + + // Wait on each of the wait semaphores in turn. It doesn't matter which order they are signalled. + for (auto& ws : _waitSemaphores) { ws->wait(); } + + id mtlQ = _queue->getMTLCommandQueue(); + id mtlCmdBuff = ((_device->_mvkConfig.displayWatermark || MVK_PRESENT_VIA_CMD_BUFFER) + ? [mtlQ commandBufferWithUnretainedReferences] + : nil); + mtlCmdBuff.label = mvkMTLCommandBufferLabel(kMVKCommandUseQueuePresent); + + for (auto& si : _surfaceImages) { si->presentCAMetalDrawable(mtlCmdBuff); } + [mtlCmdBuff commit]; + + // Let Xcode know the frame is done, in case command buffer is not used + if (_device->_mvkConfig.debugMode) { [mtlQ insertDebugCaptureBoundary]; } + + delete this; +} + +MVKQueuePresentSurfaceSubmission::MVKQueuePresentSurfaceSubmission(MVKDevice* device, + MVKQueue* queue, + const VkPresentInfoKHR* pPresentInfo) : MVKQueueSubmission(device, queue) { + uint32_t wsCnt = pPresentInfo->waitSemaphoreCount; + _waitSemaphores.reserve(wsCnt); + for (uint32_t i = 0; i < wsCnt; i++) { + _waitSemaphores.push_back((MVKSemaphore*)pPresentInfo->pWaitSemaphores[i]); + } + + // Populate the array of swapchain images, testing each one for a change in surface size + _surfaceImages.reserve(pPresentInfo->swapchainCount); + for (uint32_t i = 0; i < pPresentInfo->swapchainCount; i++) { + MVKSwapchain* mvkSC = (MVKSwapchain*)pPresentInfo->pSwapchains[i]; + _surfaceImages.push_back(mvkSC->getImage(pPresentInfo->pImageIndices[i])); + if (mvkSC->getHasSurfaceSizeChanged()) { + _submissionResult = VK_SUBOPTIMAL_KHR; + } + } +// MVKLogDebug("Adding MVKQueuePresentSurfaceSubmission %p with %d wait semaphores.", this, _waitSemaphores.size()); +} + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h new file mode 100644 index 00000000..aca4f4ec --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h @@ -0,0 +1,147 @@ +/* + * MVKRenderPass.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKDevice.h" +#include + +#import + +class MVKRenderPass; +class MVKFramebuffer; + + +#pragma mark - +#pragma mark MVKRenderSubpass + +/** Represents a Vulkan render subpass. */ +class MVKRenderSubpass : public MVKBaseObject { + +public: + + /** Returns the number of color attachments, which may be zero for depth-only rendering. */ + inline uint32_t getColorAttachmentCount() { return uint32_t(_colorAttachments.size()); } + + /** Returns the format of the color attachment at the specified index. */ + VkFormat getColorAttachmentFormat(uint32_t colorAttIdx); + + /** Returns the format of the depth/stencil attachment. */ + VkFormat getDepthStencilFormat(); + + /** + * Populates the specified Metal MTLRenderPassDescriptor with content from this + * instance, the specified framebuffer, and the specified array of clear values. + */ + void populateMTLRenderPassDescriptor(MTLRenderPassDescriptor* mtlRPDesc, + MVKFramebuffer* framebuffer, + std::vector& clearValues, + bool isRenderingEntireAttachment); + + /** + * Populates the specified vector with the attachments that need to be cleared + * when the render area is smaller than the full framebuffer size. + */ + void populateClearAttachments(std::vector& clearAtts, + std::vector& clearValues); + + /** Constructs an instance for the specified parent renderpass. */ + MVKRenderSubpass(MVKRenderPass* renderPass, const VkSubpassDescription* pCreateInfo); + +private: + + friend class MVKRenderPass; + friend class MVKRenderPassAttachment; + + bool isUsingAttachmentAt(uint32_t rpAttIdx); + + MVKRenderPass* _renderPass; + uint32_t _subpassIndex; + std::vector _inputAttachments; + std::vector _colorAttachments; + std::vector _resolveAttachments; + std::vector _preserveAttachments; + VkAttachmentReference _depthStencilAttachment; +}; + + +#pragma mark - +#pragma mark MVKRenderPassAttachment + +/** Represents an attachment within a Vulkan render pass. */ +class MVKRenderPassAttachment : public MVKBaseObject { + +public: + friend MVKRenderSubpass; + + /** Returns the Vulkan format of this attachment. */ + VkFormat getFormat(); + + /** + * Populates the specified Metal color attachment description with the load and store actions for + * the specified render subpass, and returns whether the load action will clear the attachment. + */ + bool populateMTLRenderPassAttachmentDescriptor(MTLRenderPassAttachmentDescriptor* mtlAttDesc, + MVKRenderSubpass* subpass, + bool isRenderingEntireAttachment, + bool hasResolveAttachment); + + /** Returns whether this attachment should be cleared in the subpass. */ + bool shouldUseClearAttachment(MVKRenderSubpass* subpass); + + /** Constructs an instance for the specified parent renderpass. */ + MVKRenderPassAttachment(MVKRenderPass* renderPass, + const VkAttachmentDescription* pCreateInfo); + +protected: + VkAttachmentDescription _info; + MVKRenderPass* _renderPass; + uint32_t _attachmentIndex; + uint32_t _firstUseSubpassIdx; + uint32_t _lastUseSubpassIdx; +}; + + +#pragma mark - +#pragma mark MVKRenderPass + +/** Represents a Vulkan render pass. */ +class MVKRenderPass : public MVKBaseDeviceObject { + +public: + + /** Returns the granularity of the render area of this instance. */ + VkExtent2D getRenderAreaGranularity(); + + /** Returns the format of the color attachment at the specified index. */ + MVKRenderSubpass* getSubpass(uint32_t subpassIndex); + + /** Constructs an instance for the specified device. */ + MVKRenderPass(MVKDevice* device, const VkRenderPassCreateInfo* pCreateInfo); + +private: + + friend class MVKRenderSubpass; + friend class MVKRenderPassAttachment; + + std::vector _subpasses; + std::vector _attachments; + std::vector _subpassDependencies; + +}; + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm new file mode 100644 index 00000000..271586f7 --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm @@ -0,0 +1,279 @@ +/* + * MVKRenderPass.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKRenderPass.h" +#include "MVKFramebuffer.h" +#include "MVKCommandBuffer.h" +#include "MVKFoundation.h" +#include "mvk_datatypes.h" + +using namespace std; + + +#pragma mark - +#pragma mark MVKRenderSubpass + +VkFormat MVKRenderSubpass::getColorAttachmentFormat(uint32_t colorAttIdx) { + if (colorAttIdx < _colorAttachments.size()) { + uint32_t rpAttIdx = _colorAttachments[colorAttIdx].attachment; + if (rpAttIdx == VK_ATTACHMENT_UNUSED) { return VK_FORMAT_UNDEFINED; } + return _renderPass->_attachments[rpAttIdx].getFormat(); + } + return VK_FORMAT_UNDEFINED; +} + +VkFormat MVKRenderSubpass::getDepthStencilFormat() { + uint32_t rpAttIdx = _depthStencilAttachment.attachment; + if (rpAttIdx == VK_ATTACHMENT_UNUSED) { return VK_FORMAT_UNDEFINED; } + return _renderPass->_attachments[rpAttIdx].getFormat(); +} + +void MVKRenderSubpass::populateMTLRenderPassDescriptor(MTLRenderPassDescriptor* mtlRPDesc, + MVKFramebuffer* framebuffer, + vector& clearValues, + bool isRenderingEntireAttachment) { + // Populate the Metal color attachments + uint32_t caCnt = getColorAttachmentCount(); + for (uint32_t caIdx = 0; caIdx < caCnt; caIdx++) { + uint32_t clrRPAttIdx = _colorAttachments[caIdx].attachment; + if (clrRPAttIdx != VK_ATTACHMENT_UNUSED) { + MTLRenderPassColorAttachmentDescriptor* mtlColorAttDesc = mtlRPDesc.colorAttachments[caIdx]; + + // If it exists, configure the resolve attachment first, + // as it affects how the store action of the color attachment. + uint32_t rslvRPAttIdx = _resolveAttachments.empty() ? VK_ATTACHMENT_UNUSED : _resolveAttachments[caIdx].attachment; + bool hasResolveAttachment = (rslvRPAttIdx != VK_ATTACHMENT_UNUSED); + if (hasResolveAttachment) { + framebuffer->getAttachment(rslvRPAttIdx)->populateMTLRenderPassAttachmentDescriptorResolve(mtlColorAttDesc); + } + + // Configure the color attachment + MVKRenderPassAttachment* clrMVKRPAtt = &_renderPass->_attachments[clrRPAttIdx]; + framebuffer->getAttachment(clrRPAttIdx)->populateMTLRenderPassAttachmentDescriptor(mtlColorAttDesc); + if (clrMVKRPAtt->populateMTLRenderPassAttachmentDescriptor(mtlColorAttDesc, this, + isRenderingEntireAttachment, + hasResolveAttachment)) { + mtlColorAttDesc.clearColor = mvkMTLClearColorFromVkClearValue(clearValues[clrRPAttIdx], clrMVKRPAtt->getFormat()); + } + + } + } + + // Populate the Metal depth and stencil attachments + uint32_t dsRPAttIdx = _depthStencilAttachment.attachment; + if (dsRPAttIdx != VK_ATTACHMENT_UNUSED) { + MVKRenderPassAttachment* dsMVKRPAtt = &_renderPass->_attachments[dsRPAttIdx]; + MVKImageView* dsImage = framebuffer->getAttachment(dsRPAttIdx); + MTLPixelFormat mtlDSFormat = dsImage->getMTLPixelFormat(); + + if (mvkMTLPixelFormatIsDepthFormat(mtlDSFormat)) { + MTLRenderPassDepthAttachmentDescriptor* mtlDepthAttDesc = mtlRPDesc.depthAttachment; + dsImage->populateMTLRenderPassAttachmentDescriptor(mtlDepthAttDesc); + if (dsMVKRPAtt->populateMTLRenderPassAttachmentDescriptor(mtlDepthAttDesc, this, + isRenderingEntireAttachment, + false)) { + mtlDepthAttDesc.clearDepth = mvkMTLClearDepthFromVkClearValue(clearValues[dsRPAttIdx]); + } + } + if (mvkMTLPixelFormatIsStencilFormat(mtlDSFormat)) { + MTLRenderPassStencilAttachmentDescriptor* mtlStencilAttDesc = mtlRPDesc.stencilAttachment; + dsImage->populateMTLRenderPassAttachmentDescriptor(mtlStencilAttDesc); + if (dsMVKRPAtt->populateMTLRenderPassAttachmentDescriptor(mtlStencilAttDesc, this, + isRenderingEntireAttachment, + false)) { + mtlStencilAttDesc.clearStencil = mvkMTLClearStencilFromVkClearValue(clearValues[dsRPAttIdx]); + } + } + } +} + +void MVKRenderSubpass::populateClearAttachments(vector& clearAtts, + vector& clearValues) { + VkClearAttachment cAtt; + + uint32_t attIdx; + uint32_t caCnt = getColorAttachmentCount(); + for (uint32_t caIdx = 0; caIdx < caCnt; caIdx++) { + attIdx = _colorAttachments[caIdx].attachment; + if ((attIdx != VK_ATTACHMENT_UNUSED) && _renderPass->_attachments[attIdx].shouldUseClearAttachment(this)) { + cAtt.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + cAtt.colorAttachment = caIdx; + cAtt.clearValue = clearValues[attIdx]; + clearAtts.push_back(cAtt); + } + } + + attIdx = _depthStencilAttachment.attachment; + if ((attIdx != VK_ATTACHMENT_UNUSED) && _renderPass->_attachments[attIdx].shouldUseClearAttachment(this)) { + cAtt.aspectMask = 0; + cAtt.colorAttachment = 0; + cAtt.clearValue = clearValues[attIdx]; + + MTLPixelFormat mtlDSFmt = _renderPass->mtlPixelFormatFromVkFormat(getDepthStencilFormat()); + if (mvkMTLPixelFormatIsDepthFormat(mtlDSFmt)) { cAtt.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; } + if (mvkMTLPixelFormatIsStencilFormat(mtlDSFmt)) { cAtt.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; } + if (cAtt.aspectMask) { clearAtts.push_back(cAtt); } + } +} + +/** + * Returns whether this subpass uses the attachment at the specified index within the + * parent renderpass, as any of input, color, resolve, or depth/stencil attachment type. + */ +bool MVKRenderSubpass::isUsingAttachmentAt(uint32_t rpAttIdx) { + + for (auto& att : _inputAttachments) { + if (att.attachment == rpAttIdx) { return true; } + } + for (auto& att : _colorAttachments) { + if (att.attachment == rpAttIdx) { return true; } + } + for (auto& att : _resolveAttachments) { + if (att.attachment == rpAttIdx) { return true; } + } + if (_depthStencilAttachment.attachment == rpAttIdx) { return true; } + + return false; +} + +MVKRenderSubpass::MVKRenderSubpass(MVKRenderPass* renderPass, + const VkSubpassDescription* pCreateInfo) : MVKBaseObject() { + _renderPass = renderPass; + _subpassIndex = (uint32_t)_renderPass->_subpasses.size(); + + // Add attachments + _inputAttachments.reserve(pCreateInfo->inputAttachmentCount); + for (uint32_t i = 0; i < pCreateInfo->inputAttachmentCount; i++) { + _inputAttachments.push_back(pCreateInfo->pInputAttachments[i]); + } + + _colorAttachments.reserve(pCreateInfo->colorAttachmentCount); + for (uint32_t i = 0; i < pCreateInfo->colorAttachmentCount; i++) { + _colorAttachments.push_back(pCreateInfo->pColorAttachments[i]); + } + + if (pCreateInfo->pResolveAttachments) { + _resolveAttachments.reserve(pCreateInfo->colorAttachmentCount); + for (uint32_t i = 0; i < pCreateInfo->colorAttachmentCount; i++) { + _resolveAttachments.push_back(pCreateInfo->pResolveAttachments[i]); + } + } + + if (pCreateInfo->pDepthStencilAttachment) { + _depthStencilAttachment = *pCreateInfo->pDepthStencilAttachment; + } else { + _depthStencilAttachment.attachment = VK_ATTACHMENT_UNUSED; + } + + _preserveAttachments.reserve(pCreateInfo->preserveAttachmentCount); + for (uint32_t i = 0; i < pCreateInfo->preserveAttachmentCount; i++) { + _preserveAttachments.push_back(pCreateInfo->pPreserveAttachments[i]); + } +} + + +#pragma mark - +#pragma mark MVKRenderPassAttachment + +VkFormat MVKRenderPassAttachment::getFormat() { return _info.format; } + +bool MVKRenderPassAttachment::populateMTLRenderPassAttachmentDescriptor(MTLRenderPassAttachmentDescriptor* mtlAttDesc, + MVKRenderSubpass* subpass, + bool isRenderingEntireAttachment, + bool hasResolveAttachment) { + + bool willClear = false; // Assume the attachment won't be cleared + + // Only allow clearing of entire attachment if we're actually rendering to the entire + // attachment AND we're in the first subpass. + if ( isRenderingEntireAttachment && (subpass->_subpassIndex == _firstUseSubpassIdx) ) { + mtlAttDesc.loadAction = mvkMTLLoadActionFromVkAttachmentLoadOp(_info.loadOp); + willClear = (_info.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR); + } else { + mtlAttDesc.loadAction = MTLLoadActionLoad; + } + + // If a resolve attachment exists, this attachment must resolve once complete. + // Otherwise only allow the attachment to be discarded if we're actually rendering + // to the entire attachment and we're in the last subpass. + if (hasResolveAttachment) { + mtlAttDesc.storeAction = MTLStoreActionMultisampleResolve; + } else if ( isRenderingEntireAttachment && (subpass->_subpassIndex == _lastUseSubpassIdx) ) { + mtlAttDesc.storeAction = mvkMTLStoreActionFromVkAttachmentStoreOp(_info.storeOp); + } else { + mtlAttDesc.storeAction = MTLStoreActionStore; + } + return willClear; +} + +bool MVKRenderPassAttachment::shouldUseClearAttachment(MVKRenderSubpass* subpass) { + + // If the subpass is not the first subpass to use this attachment, don't clear this attachment + if (subpass->_subpassIndex != _firstUseSubpassIdx) { return false; } + + return (_info.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR); +} + +MVKRenderPassAttachment::MVKRenderPassAttachment(MVKRenderPass* renderPass, + const VkAttachmentDescription* pCreateInfo) : MVKBaseObject() { + _renderPass = renderPass; + _attachmentIndex = uint32_t(_renderPass->_attachments.size()); + + // Determine the indices of the first and last render subpasses to use that attachment. + _firstUseSubpassIdx = kMVKMaxUnsigned; + _lastUseSubpassIdx = 0; + for (auto& subPass : _renderPass->_subpasses) { + if (subPass.isUsingAttachmentAt(_attachmentIndex)) { + uint32_t spIdx = subPass._subpassIndex; + _firstUseSubpassIdx = MIN(spIdx, _firstUseSubpassIdx); + _lastUseSubpassIdx = MAX(spIdx, _lastUseSubpassIdx); + } + } + + _info = *pCreateInfo; +} + + +#pragma mark - +#pragma mark MVKRenderPass + +VkExtent2D MVKRenderPass::getRenderAreaGranularity() { return { 1, 1 }; } + +MVKRenderSubpass* MVKRenderPass::getSubpass(uint32_t subpassIndex) { return &_subpasses[subpassIndex]; } + +MVKRenderPass::MVKRenderPass(MVKDevice* device, + const VkRenderPassCreateInfo* pCreateInfo) : MVKBaseDeviceObject(device) { + + // Add subpasses and dependencies first + _subpasses.reserve(pCreateInfo->subpassCount); + for (uint32_t i = 0; i < pCreateInfo->subpassCount; i++) { + _subpasses.push_back(MVKRenderSubpass(this, &pCreateInfo->pSubpasses[i])); + } + _subpassDependencies.reserve(pCreateInfo->dependencyCount); + for (uint32_t i = 0; i < pCreateInfo->dependencyCount; i++) { + _subpassDependencies.push_back(pCreateInfo->pDependencies[i]); + } + + // Add attachments after subpasses, so each attachment can link to subpasses + _attachments.reserve(pCreateInfo->attachmentCount); + for (uint32_t i = 0; i < pCreateInfo->attachmentCount; i++) { + _attachments.push_back(MVKRenderPassAttachment(this, &pCreateInfo->pAttachments[i])); + } +} + + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKResource.h b/MoltenVK/MoltenVK/GPUObjects/MVKResource.h new file mode 100644 index 00000000..ec37704f --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKResource.h @@ -0,0 +1,104 @@ +/* + * MVKResource.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKDevice.h" +#include "MVKDeviceMemory.h" + +class MVKCommandEncoder; + + +#pragma mark - +#pragma mark MVKResource + +/** Represents an abstract Vulkan resource. Specialized subclasses include MVKBuffer and MVKImage. */ +class MVKResource : public MVKBaseDeviceObject { + +public: + + /** Returns the number of bytes required for the entire resource. */ + inline VkDeviceSize getByteCount() { return _byteCount; } + + /** Returns the byte offset in the bound device memory. */ + inline VkDeviceSize getDeviceMemoryOffset() { return _deviceMemoryOffset; } + + /** Returns the byte alignment required for this resource. */ + inline VkDeviceSize getByteAlignment() { return _byteAlignment; } + + /** Returns the memory requirements of this resource by populating the specified structure. */ + virtual VkResult getMemoryRequirements(VkMemoryRequirements* pMemoryRequirements) = 0; + + /** Binds this resource to the specified offset within the specified memory allocation. */ + VkResult bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset); + + /** Returns the device memory underlying this resource. */ + inline MVKDeviceMemory* getDeviceMemory() { return _deviceMemory; } + + /** Returns whether the memory is accessible from the host. */ + inline bool isMemoryHostAccessible() { + return (_deviceMemory ? _deviceMemory->isMemoryHostAccessible() : false); + } + + /** + * Returns the host memory address that represents what would be the beginning of + * the host address space that this resource is mapped to by a vkMapMemory() call. + * + * The returnd value only has physical meaning if the mapped memory overlaps the + * beginning of the memory used by this resource, otherwise it is a logical address + * used to calculate resource offsets. + * + * This function must only be called between vkMapMemory() and vkUnmapMemory() calls. + */ + inline void* getLogicalMappedMemory() { + return (_deviceMemory ? (void*)((uintptr_t)_deviceMemory->getLogicalMappedMemory() + _deviceMemoryOffset) : nullptr); + } + + /** Applies the specified global memory barrier. */ + virtual void applyMemoryBarrier(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkMemoryBarrier* pMemoryBarrier, + MVKCommandEncoder* cmdEncoder, + MVKCommandUse cmdUse) = 0; + + +#pragma mark Construction + + /** Constructs an instance for the specified device. */ + MVKResource(MVKDevice* device) : MVKBaseDeviceObject(device) {} + + /** Destructor. */ + ~MVKResource() override; + +protected: + friend MVKDeviceMemory; + + virtual void* map(VkDeviceSize offset, VkDeviceSize size) = 0; + virtual VkResult flushToDevice(VkDeviceSize offset, VkDeviceSize size) = 0; + virtual VkResult pullFromDevice(VkDeviceSize offset, VkDeviceSize size) = 0; + virtual bool needsHostReadSync(VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkMemoryBarrier* pMemoryBarrier); + bool doesOverlap(VkDeviceSize offset, VkDeviceSize size); + bool doesContain(VkDeviceSize offset, VkDeviceSize size); + + MVKDeviceMemory* _deviceMemory = nullptr; + VkDeviceSize _deviceMemoryOffset = 0; + VkDeviceSize _byteCount = 0; + VkDeviceSize _byteAlignment = 0; +}; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKResource.mm b/MoltenVK/MoltenVK/GPUObjects/MVKResource.mm new file mode 100644 index 00000000..80717980 --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKResource.mm @@ -0,0 +1,79 @@ +/* + * MVKResource.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKResource.h" +#include "MVKCommandBuffer.h" + + +#pragma mark MVKResource + +VkResult MVKResource::bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset) { + if (_deviceMemory) { _deviceMemory->removeResource(this); } + + _deviceMemory = mvkMem; + _deviceMemoryOffset = memOffset; + + if (_deviceMemory) { _deviceMemory->addResource(this); } + + 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 + return false; +#endif +#if MVK_MACOS + return (mvkIsAnyFlagEnabled(dstStageMask, (VK_PIPELINE_STAGE_HOST_BIT)) && + mvkIsAnyFlagEnabled(pMemoryBarrier->dstAccessMask, (VK_ACCESS_HOST_READ_BIT)) && + _deviceMemory->isMemoryHostAccessible() && !_deviceMemory->isMemoryHostCoherent()); +#endif +} + +// Check if this resource overlaps the device memory offset and range +bool MVKResource::doesOverlap(VkDeviceSize offset, VkDeviceSize size) { + VkDeviceSize memStart = offset; + VkDeviceSize memEnd = memStart + size; + VkDeviceSize rezStart = _deviceMemoryOffset; + VkDeviceSize rezEnd = rezStart + _byteCount; + + return (memStart < rezEnd && memEnd > rezStart); +} + +// Check if this resource completely contains the device memory offset and range +bool MVKResource::doesContain(VkDeviceSize offset, VkDeviceSize size) { + VkDeviceSize memStart = offset; + VkDeviceSize memEnd = memStart + size; + VkDeviceSize rezStart = _deviceMemoryOffset; + VkDeviceSize rezEnd = rezStart + _byteCount; + + return (memStart >= rezStart && memEnd <= rezEnd); +} + + +#pragma mark Construction + +MVKResource::~MVKResource() { + if (_deviceMemory) { _deviceMemory->removeResource(this); } +}; + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h new file mode 100644 index 00000000..bd6e4ebf --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h @@ -0,0 +1,95 @@ +/* + * MVKShaderModule.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKDevice.h" +#include +#include +#include + +#import + +using namespace mvk; + + +#pragma mark - +#pragma mark MVKShaderLibrary + +/** Specifies the SPIRV LocalSize, which is the number of threads in a compute shader workgroup. */ +typedef struct { + id mtlFunction; + MTLSize threadGroupSize; +} MVKMTLFunction; + +/** A MVKMTLFunction indicating an invalid MTLFunction. The mtlFunction member is nil. */ +extern const MVKMTLFunction MVKMTLFunctionNull; + +/** Wraps a single MTLLibrary. */ +class MVKShaderLibrary : public MVKBaseDeviceObject { + +public: + /** Returns the Metal shader function used by the specified shader state. */ + MVKMTLFunction getMTLFunction(const VkPipelineShaderStageCreateInfo* pShaderStage); + + /** Constructs an instance from the MSL source code in the specified SPIRVToMSLConverter. */ + MVKShaderLibrary(MVKDevice* device, SPIRVToMSLConverter& mslConverter); + + /** Constructs an instance from the specified compiled MSL code data. */ + MVKShaderLibrary(MVKDevice* device, + const void* mslCompiledCodeData, + size_t mslCompiledCodeLength); + + ~MVKShaderLibrary() override; + +protected: + void handleCompilationError(NSError* err, const char* opDesc); + MTLFunctionConstant* getFunctionConstant(NSArray* mtlFCs, NSUInteger mtlFCID); + const std::string cleanMSLFunctionName(const std::string& name); + + id _mtlLibrary; + std::unordered_map _mtlFunctionNameMap; + SPIRVLocalSizesByEntryPointName _localSizes; +}; + + +#pragma mark - +#pragma mark MVKShaderModule + +/** Represents a Vulkan shader module. */ +class MVKShaderModule : public MVKBaseDeviceObject { + +public: + /** Returns the Metal shader function used by the specified shader state, or nil if it doesn't exist. */ + MVKMTLFunction getMTLFunction(const VkPipelineShaderStageCreateInfo* pShaderStage, + SPIRVToMSLConverterContext* pContext); + + MVKShaderModule(MVKDevice* device, const VkShaderModuleCreateInfo* pCreateInfo); + + ~MVKShaderModule() override; + +protected: + MVKShaderLibrary* getShaderLibrary(SPIRVToMSLConverterContext* pContext); + MVKShaderLibrary* findShaderLibrary(SPIRVToMSLConverterContext* pContext); + MVKShaderLibrary* addShaderLibrary(SPIRVToMSLConverterContext* pContext); + + SPIRVToMSLConverter _converter; + MVKShaderLibrary* _defaultLibrary; + std::vector> _shaderLibraries; + std::mutex _accessLock; +}; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm new file mode 100644 index 00000000..c281aa4c --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm @@ -0,0 +1,252 @@ +/* + * MVKShaderModule.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKShaderModule.h" +#include "MVKFoundation.h" +#include "vk_mvk_moltenvk.h" +#include + +using namespace std; + + +const MVKMTLFunction MVKMTLFunctionNull = { nil, MTLSizeMake(1, 1, 1) }; + +#pragma mark - +#pragma mark MVKShaderLibrary + +MVKMTLFunction MVKShaderLibrary::getMTLFunction(const VkPipelineShaderStageCreateInfo* pShaderStage) { + + if ( !_mtlLibrary ) { return MVKMTLFunctionNull; } + + // Ensure the function name is compatible with Metal (Metal does not allow main() + // as a function name), and retrieve the unspecialized Metal function with that name. + string funcName = cleanMSLFunctionName(pShaderStage->pName); + NSString* mtlFuncName = @(funcName.c_str()); + + NSTimeInterval startTime = _device->getPerformanceTimestamp(); + id mtlFunc = [[_mtlLibrary newFunctionWithName: mtlFuncName] autorelease]; + _device->addShaderCompilationEventPerformance(_device->_shaderCompilationPerformance.functionRetrieval, startTime); + + if (mtlFunc) { + // If the Metal device supports shader specialization, and the Metal function expects to be + // specialized, populate Metal function constant values from the Vulkan specialization info, + // and compiled a specialized Metal function, otherwise simply use the unspecialized Metal function. + if (_device->_pMetalFeatures->shaderSpecialization) { + NSArray* mtlFCs = mtlFunc.functionConstantsDictionary.allValues; + if (mtlFCs.count) { + NSTimeInterval startTimeSpec = _device->getPerformanceTimestamp(); + + // The Metal shader contains function constants and expects to be specialized + // Populate the Metal function constant values from the Vulkan specialization info. + MTLFunctionConstantValues* mtlFCVals = [[MTLFunctionConstantValues new] autorelease]; + const VkSpecializationInfo* pSpecInfo = pShaderStage->pSpecializationInfo; + if (pSpecInfo) { + // Iterate through the provided Vulkan specialization entries, and populate the + // Metal function constant value that matches the Vulkan specialization constantID. + for (uint32_t specIdx = 0; specIdx < pSpecInfo->mapEntryCount; specIdx++) { + const VkSpecializationMapEntry* pMapEntry = &pSpecInfo->pMapEntries[specIdx]; + NSUInteger mtlFCIndex = pMapEntry->constantID; + MTLFunctionConstant* mtlFC = getFunctionConstant(mtlFCs, mtlFCIndex); + if (mtlFC) { + [mtlFCVals setConstantValue: &(((char*)pSpecInfo->pData)[pMapEntry->offset]) + type: mtlFC.type + atIndex: mtlFCIndex]; + } + } + } + + // Compile the specialized Metal function, and use it instead of the unspecialized Metal function. + NSError* err = nil; + mtlFunc = [[_mtlLibrary newFunctionWithName: mtlFuncName constantValues: mtlFCVals error: &err] autorelease]; + handleCompilationError(err, "Shader function specialization"); + _device->addShaderCompilationEventPerformance(_device->_shaderCompilationPerformance.functionSpecialization, startTimeSpec); + } + } + } else { + mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "Shader module does not contain an entry point named '%s'.", funcName.c_str()); + } + + SPIRVLocalSize wgSize = _localSizes[funcName]; + return { mtlFunc, MTLSizeMake(wgSize.width, wgSize.height, wgSize.depth) }; +} + +// Returns the MTLFunctionConstant with the specified ID from the specified array of function constants. +// The specified ID is the index value contained within the function constant. +MTLFunctionConstant* MVKShaderLibrary::getFunctionConstant(NSArray* mtlFCs, NSUInteger mtlFCID) { + for (MTLFunctionConstant* mfc in mtlFCs) { if (mfc.index == mtlFCID) { return mfc; } } + return nil; +} + +// Cleans the specified shader function name so it can be used as as an MSL function name. +const std::string MVKShaderLibrary::cleanMSLFunctionName(const std::string& funcName) { + string cleanName = _mtlFunctionNameMap[funcName]; + return cleanName.empty() ? funcName : cleanName; +} + +MVKShaderLibrary::MVKShaderLibrary(MVKDevice* device, SPIRVToMSLConverter& mslConverter) : MVKBaseDeviceObject(device) { + NSTimeInterval startTime = _device->getPerformanceTimestamp(); + @autoreleasepool { + MTLCompileOptions* options = [[MTLCompileOptions new] autorelease]; // TODO: what compile options apply? + NSError* err = nil; + _mtlLibrary = [getMTLDevice() newLibraryWithSource: @(mslConverter.getMSL().data()) + options: options + error: &err]; // retained + handleCompilationError(err, "Shader module compilation"); + } + _device->addShaderCompilationEventPerformance(_device->_shaderCompilationPerformance.mslCompile, startTime); + + _mtlFunctionNameMap = mslConverter.getEntryPointNameMap(); + _localSizes = mslConverter.getLocalSizes(); +} + +MVKShaderLibrary::MVKShaderLibrary(MVKDevice* device, + const void* mslCompiledCodeData, + size_t mslCompiledCodeLength) : MVKBaseDeviceObject(device) { + NSTimeInterval startTime = _device->getPerformanceTimestamp(); + @autoreleasepool { + dispatch_data_t shdrData = dispatch_data_create(mslCompiledCodeData, + mslCompiledCodeLength, + NULL, + DISPATCH_DATA_DESTRUCTOR_DEFAULT); + NSError* err = nil; + _mtlLibrary = [getMTLDevice() newLibraryWithData: shdrData error: &err]; // retained + handleCompilationError(err, "Compiled shader module creation"); + [shdrData release]; + } + _device->addShaderCompilationEventPerformance(_device->_shaderCompilationPerformance.mslLoad, startTime); +} + +// If err object is nil, the compilation succeeded without any warnings. +// If err object exists, and the MTLLibrary was created, the compilation succeeded, but with warnings. +// If err object exists, and the MTLLibrary was not created, the compilation failed. +void MVKShaderLibrary::handleCompilationError(NSError* err, const char* opDesc) { + if ( !err ) return; + + if (_mtlLibrary) { + MVKLogInfo("%s succeeded with warnings (code %li):\n\n%s", opDesc, (long)err.code, + err.localizedDescription.UTF8String); + } else { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, + "%s failed (code %li):\n\n%s", + opDesc, (long)err.code, + err.localizedDescription.UTF8String)); + } +} + +MVKShaderLibrary::~MVKShaderLibrary() { + [_mtlLibrary release]; +} + + +#pragma mark - +#pragma mark MVKShaderModule + +MVKMTLFunction MVKShaderModule::getMTLFunction(const VkPipelineShaderStageCreateInfo* pShaderStage, + SPIRVToMSLConverterContext* pContext) { + lock_guard lock(_accessLock); + MVKShaderLibrary* mvkLib = getShaderLibrary(pContext); + return mvkLib ? mvkLib->getMTLFunction(pShaderStage) : MVKMTLFunctionNull; +} + +MVKShaderLibrary* MVKShaderModule::getShaderLibrary(SPIRVToMSLConverterContext* pContext) { + if (_defaultLibrary) { return _defaultLibrary; } + + MVKShaderLibrary* shLib = findShaderLibrary(pContext); + if ( !shLib ) { shLib = addShaderLibrary(pContext); } +// else { MVKLogDebug("Shader Module %p reusing library.", this); } + return shLib; +} + +// Finds and returns a shader library matching the specified context, or returns nullptr if it doesn't exist. +// If a match is found, the usage of the specified context is aligned with the context of the matching library. +MVKShaderLibrary* MVKShaderModule::findShaderLibrary(SPIRVToMSLConverterContext* pContext) { + for (auto& slPair : _shaderLibraries) { + if (slPair.first.matches(*pContext)) { + (*pContext).alignUsageWith(slPair.first); + return slPair.second; + } + } + return NULL; +} + +/** Adds and returns a new shader library configured from the specified context. */ +MVKShaderLibrary* MVKShaderModule::addShaderLibrary(SPIRVToMSLConverterContext* pContext) { + + MVKShaderLibrary* shLib = nullptr; + bool shouldLogCode = _device->_mvkConfig.debugMode; + + NSTimeInterval startTime = _device->getPerformanceTimestamp(); + bool wasConverted = _converter.convert(*pContext, shouldLogCode, shouldLogCode, shouldLogCode); + _device->addShaderCompilationEventPerformance(_device->_shaderCompilationPerformance.spirvToMSL, startTime); + + if (wasConverted) { + if (shouldLogCode) { MVKLogInfo("%s", _converter.getResultLog().data()); } + shLib = new MVKShaderLibrary(_device, _converter); + _shaderLibraries.push_back(pair(*pContext, shLib)); +// MVKLogDebug("Shader Module %p compiled %d libraries.", this, _shaderLibraries.size()); + } else { + mvkNotifyErrorWithText(VK_ERROR_FORMAT_NOT_SUPPORTED, "Unable to convert SPIR-V to MSL:\n%s", _converter.getResultLog().data()); + } + return shLib; +} + + +#pragma mark Construction + +MVKShaderModule::MVKShaderModule(MVKDevice* device, + const VkShaderModuleCreateInfo* pCreateInfo) : MVKBaseDeviceObject(device) { + _defaultLibrary = nullptr; + + // Ensure something is there. + if ( (pCreateInfo->pCode != VK_NULL_HANDLE) && (pCreateInfo->codeSize >= 4) ) { + + // Retrieve the magic number to determine what type of shader code has been loaded. + uint32_t magicNum = *pCreateInfo->pCode; + switch (magicNum) { + case kMVKMagicNumberSPIRVCode: { // SPIR-V code + size_t spvCount = (pCreateInfo->codeSize + 3) >> 2; // Round up if byte length not exactly on uint32_t boundary + _converter.setSPIRV(pCreateInfo->pCode, spvCount); + break; + } + case kMVKMagicNumberMSLSourceCode: { // MSL source code + uintptr_t pMSLCode = uintptr_t(pCreateInfo->pCode) + sizeof(MVKMSLSPIRVHeader); + unordered_map entryPointNameMap; + SPIRVLocalSizesByEntryPointName localSizes; + _converter.setMSL((char*)pMSLCode, entryPointNameMap, localSizes); + _defaultLibrary = new MVKShaderLibrary(_device, _converter); + break; + } + case kMVKMagicNumberMSLCompiledCode: { // MSL compiled binary code + uintptr_t pMSLCode = uintptr_t(pCreateInfo->pCode) + sizeof(MVKMSLSPIRVHeader); + _defaultLibrary = new MVKShaderLibrary(_device, (void*)(pMSLCode), (pCreateInfo->codeSize - sizeof(MVKMSLSPIRVHeader))); + break; + } + default: + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FORMAT_NOT_SUPPORTED, "SPIR-V contains invalid magic number %x.", magicNum)); + break; + } + } else { + setConfigurationResult(mvkNotifyErrorWithText(VK_INCOMPLETE, "Shader module contains no SPIR-V code.")); + } +} + +MVKShaderModule::~MVKShaderModule() { + if (_defaultLibrary) { delete _defaultLibrary; } + for (auto& slPair : _shaderLibraries) { delete slPair.second; } +} + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h b/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h new file mode 100644 index 00000000..ec6e6176 --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h @@ -0,0 +1,68 @@ +/* + * MVKSurface.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "mvk_vulkan.h" +#include "MVKBaseObject.h" + + +// Expose MoltenVK Apple surface extension functionality +#ifdef VK_USE_PLATFORM_IOS_MVK +# define vkCreate_PLATFORM_SurfaceMVK vkCreateIOSSurfaceMVK +# define Vk_PLATFORM_SurfaceCreateInfoMVK VkIOSSurfaceCreateInfoMVK +# define PLATFORM_VIEW_CLASS UIView +# include +#endif // MVK_IOS + +#ifdef VK_USE_PLATFORM_MACOS_MVK +# define vkCreate_PLATFORM_SurfaceMVK vkCreateMacOSSurfaceMVK +# define Vk_PLATFORM_SurfaceCreateInfoMVK VkMacOSSurfaceCreateInfoMVK +# define PLATFORM_VIEW_CLASS NSView +# include +#endif // MVK_MACOS + +#import +#import + +class MVKInstance; + + +#pragma mark MVKSurface + +/** Represents a Vulkan WSI surface. */ +class MVKSurface : public MVKConfigurableObject { + +public: + + /** Returns the CAMetalLayer underlying this surface. */ + inline CAMetalLayer* getCAMetalLayer() { return _mtlCAMetalLayer; } + + +#pragma mark Construction + + MVKSurface(MVKInstance* mvkInstance, + const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo, + const VkAllocationCallbacks* pAllocator); + + ~MVKSurface() override; + +protected: + CAMetalLayer* _mtlCAMetalLayer; +}; + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSurface.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSurface.mm new file mode 100644 index 00000000..fabff835 --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSurface.mm @@ -0,0 +1,44 @@ +/* + * MVKSurface.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKSurface.h" +#include "MVKInstance.h" +#include "MVKFoundation.h" + + +#pragma mark MVKSurface + +#pragma mark Construction + +MVKSurface::MVKSurface(MVKInstance* mvkInstance, + const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + + CALayer* viewLayer = ((PLATFORM_VIEW_CLASS*)pCreateInfo->pView).layer; + if ( [viewLayer isKindOfClass: [CAMetalLayer class]] ) { + _mtlCAMetalLayer = (CAMetalLayer*)[viewLayer retain]; // retained + } else { + setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "On-screen rendering requires a view that is backed by a layer of type CAMetalLayer.")); + _mtlCAMetalLayer = nil; + } +} + +MVKSurface::~MVKSurface() { + [_mtlCAMetalLayer release]; +} + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h new file mode 100644 index 00000000..91fcc4ff --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h @@ -0,0 +1,108 @@ +/* + * MVKSwapchain.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKDevice.h" +#include + +class MVKSwapchainImage; +class MVKWatermark; + + +#pragma mark MVKSwapchain + +/** Represents a Vulkan swapchain. */ +class MVKSwapchain : public MVKBaseDeviceObject { + +public: + + /** Returns the number of images in this swapchain. */ + uint32_t getImageCount(); + + /** Returns the image at the specified index. */ + MVKSwapchainImage* getImage(uint32_t index); + + /** + * Returns the array of presentable images associated with this swapchain. + * + * If pSwapchainImages is null, the value of pCount is updated with the number of + * presentable images associated with this swapchain. + * + * If pSwapchainImages is not null, then pCount images are copied into the array. + * If the number of available images is less than pCount, the value of pCount is + * updated to indicate the number of images actually returned in the array. + * + * Returns VK_SUCCESS if successful. Returns VK_INCOMPLETE if the number of supported + * images is larger than pCount. Returns other values if an error occurs. + */ + VkResult getImages(uint32_t* pCount, VkImage* pSwapchainImages); + + /** Returns the index of the next swapchain image. */ + VkResult acquireNextImageKHR(uint64_t timeout, + VkSemaphore semaphore, + VkFence fence, + uint32_t* pImageIndex); + + /** Returns whether the surface size has changed since the last time this function was called. */ + bool getHasSurfaceSizeChanged(); + + /** Populates the specified performance stats structure. */ + void getPerformanceStatistics(MVKSwapchainPerformance* pSwapchainPerf); + + +#pragma mark Metal + + /** + * Returns the next Metal drawable available to provide backing for + * an image in this swapchain. The returned object is autoreleased. + * + * This function may block until the next drawable is available, + * and may return nil if no drawable is available at all. + */ + id getNextCAMetalDrawable(); + + +#pragma mark Construction + + MVKSwapchain(MVKDevice* device, const VkSwapchainCreateInfoKHR* pCreateInfo); + + ~MVKSwapchain() override; + +protected: + friend class MVKSwapchainImage; + + void initSurfaceImages(const VkSwapchainCreateInfoKHR* pCreateInfo); + void initFrameIntervalTracking(); + void releaseUndisplayedSurfaces(); + uint64_t getNextAcquisitionID(); + void willPresentSurface(id mtlTexture, id mtlCmdBuff); + void renderWatermark(id mtlTexture, id mtlCmdBuff); + void markFrameInterval(); + + CAMetalLayer* _mtlLayer; + MVKWatermark* _licenseWatermark; + std::vector _surfaceImages; + std::atomic _currentAcquisitionID; + CGSize _mtlLayerOrigDrawSize; + MVKSwapchainPerformance _performanceStatistics; + double _lastFrameTime; + double _averageFrameIntervalFilterAlpha; + uint32_t _currentPerfLogFrameCount; +}; + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm new file mode 100644 index 00000000..eaf66a0d --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm @@ -0,0 +1,269 @@ +/* + * MVKSwapchain.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKSurface.h" +#include "MVKSwapchain.h" +#include "MVKImage.h" +#include "MVKFoundation.h" +#include "MVKOSExtensions.h" +#include "MVKWatermark.h" +#include "MVKWatermarkTextureContent.h" +#include "MVKWatermarkShaderSource.h" +#include "mvk_datatypes.h" +#include "MVKLogging.h" + +using namespace std; + + +#pragma mark MVKSwapchain + +uint32_t MVKSwapchain::getImageCount() { return (uint32_t)_surfaceImages.size(); } + +MVKSwapchainImage* MVKSwapchain::getImage(uint32_t index) { return _surfaceImages[index]; } + +VkResult MVKSwapchain::getImages(uint32_t* pCount, VkImage* pSwapchainImages) { + + // Get the number of surface images + uint32_t imgCnt = getImageCount(); + + // If images aren't actually being requested yet, simply update the returned count + if ( !pSwapchainImages ) { + *pCount = imgCnt; + return VK_SUCCESS; + } + + // Determine how many images we'll return, and return that number + VkResult result = (*pCount <= imgCnt) ? VK_SUCCESS : VK_INCOMPLETE; + *pCount = min(*pCount, imgCnt); + + // Now populate the images + for (uint32_t imgIdx = 0; imgIdx < *pCount; imgIdx++) { + pSwapchainImages[imgIdx] = (VkImage)_surfaceImages[imgIdx]; + } + + return result; +} + +VkResult MVKSwapchain::acquireNextImageKHR(uint64_t timeout, + VkSemaphore semaphore, + VkFence fence, + uint32_t* pImageIndex) { + // Find the image that has the smallest availability measure + uint32_t minWaitIndex = 0; + MVKSwapchainImageAvailability minAvailability = { .acquisitionID = numeric_limits::max(), .waitCount = numeric_limits::max(), .isAvailable = false }; + for (MVKSwapchainImage* mvkSCImg : _surfaceImages) { + const MVKSwapchainImageAvailability* currAvailability = mvkSCImg->getAvailability(); +// MVKLogDebug("Comparing availability (isAvailable: %d waitCount: %d, acquisitionID: %d) to (isAvailable: %d waitCount: %d, acquisitionID: %d)", +// currAvailability->isAvailable, currAvailability->waitCount, currAvailability->acquisitionID, +// minAvailability.isAvailable, minAvailability.waitCount, minAvailability.acquisitionID); + if (*currAvailability < minAvailability) { + minAvailability = *currAvailability; + minWaitIndex = mvkSCImg->getSwapchainIndex(); +// MVKLogDebug("Is smaller! Index: %d", minWaitIndex); + } + } +// MVKLogDebug("Selected MVKSwapchainImage %p, index: %d to trigger semaphore %p and fence %p", _surfaceImages[minWaitIndex], minWaitIndex, semaphore, fence); + + *pImageIndex = minWaitIndex; // Return the index of the image with the shortest wait + + if (semaphore || fence) { + MVKSemaphore* mvkSem4 = (MVKSemaphore*)semaphore; + MVKFence* mvkFence = (MVKFence*)fence; + _surfaceImages[minWaitIndex]->signalWhenAvailable(mvkSem4, mvkFence); + } else { + // Interpret a NULL semaphore to mean that this function itself should block + // until the image is available. Create temp semaphore and wait on it here. + VkSemaphoreCreateInfo semaphoreCreateInfo = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .pNext = NULL, + }; + MVKSemaphore mvkSem4(_device, &semaphoreCreateInfo); + _surfaceImages[minWaitIndex]->signalWhenAvailable(&mvkSem4, NULL); + if ( !mvkSem4.wait(timeout) ) { return VK_TIMEOUT; } + } + + return getHasSurfaceSizeChanged() ? VK_SUBOPTIMAL_KHR : VK_SUCCESS; +} + +bool MVKSwapchain::getHasSurfaceSizeChanged() { + return !CGSizeEqualToSize(_mtlLayer.drawableSize, _mtlLayerOrigDrawSize); +} + +uint64_t MVKSwapchain::getNextAcquisitionID() { return ++_currentAcquisitionID; } + +/** + * Releases any surfaces that are not currently being displayed, + * so they can be used by a different swapchain. + */ +void MVKSwapchain::releaseUndisplayedSurfaces() {} + + +#pragma mark Rendering + +// Called automatically when a swapchain image is about to be presented to the surface by the queue. +// Activities include marking the frame interval and rendering the watermark if needed. +void MVKSwapchain::willPresentSurface(id mtlTexture, id mtlCmdBuff) { + markFrameInterval(); + renderWatermark(mtlTexture, mtlCmdBuff); +} + +// If the product has not been fully licensed, renders the watermark image to the surface. +void MVKSwapchain::renderWatermark(id mtlTexture, id mtlCmdBuff) { + if (_device->_mvkConfig.displayWatermark) { + if ( !_licenseWatermark ) { + _licenseWatermark = new MVKWatermarkRandom(getMTLDevice(), + __watermarkTextureContent, + __watermarkTextureWidth, + __watermarkTextureHeight, + __watermarkTextureFormat, + mvkMTLPixelFormatBytesPerRow(__watermarkTextureFormat, __watermarkTextureWidth), + __watermarkShaderSource); + } + _licenseWatermark->render(mtlTexture, mtlCmdBuff, _performanceStatistics.lastFrameInterval); + } else { + if (_licenseWatermark) { + delete _licenseWatermark; + _licenseWatermark = NULL; + } + } +} + +// Calculates and remembers the time interval between frames. +void MVKSwapchain::markFrameInterval() { + if ( !(_device->_mvkConfig.performanceTracking || _licenseWatermark) ) { return; } + + NSTimeInterval prevFrameTime = _lastFrameTime; + _lastFrameTime = [NSDate timeIntervalSinceReferenceDate]; + _performanceStatistics.lastFrameInterval = _lastFrameTime - prevFrameTime; + + // Low pass filter. + // y[i] := α * x[i] + (1-α) * y[i-1] OR + // y[i] := y[i-1] + α * (x[i] - y[i-1]) + _performanceStatistics.averageFrameInterval += _averageFrameIntervalFilterAlpha * (_performanceStatistics.lastFrameInterval - _performanceStatistics.averageFrameInterval); + _performanceStatistics.averageFramesPerSecond = 1.0 / _performanceStatistics.averageFrameInterval; + + uint32_t perfLogCntLimit = _device->_mvkConfig.performanceLoggingFrameCount; + if (perfLogCntLimit > 0) { + _currentPerfLogFrameCount++; + if (_currentPerfLogFrameCount >= perfLogCntLimit) { + MVKLogInfo("Frame interval: %.3f. Avg frame interval: %.3f. FPS: %.3f.", + _performanceStatistics.lastFrameInterval, + _performanceStatistics.averageFrameInterval, + _performanceStatistics.averageFramesPerSecond); + _currentPerfLogFrameCount = 0; + } + } +} + +void MVKSwapchain::getPerformanceStatistics(MVKSwapchainPerformance* pSwapchainPerf) { + if (pSwapchainPerf) { *pSwapchainPerf = _performanceStatistics; } +} + + +#pragma mark Metal + +id MVKSwapchain::getNextCAMetalDrawable() { + id nextDrwbl = nil; + while ( !(nextDrwbl = [_mtlLayer nextDrawable]) ) { + MVKLogError("Drawable could not be retrieved! Elapsed time: %.6f ms.", mvkGetElapsedMilliseconds()); + } + return nextDrwbl; +} + + +#pragma mark Construction + +MVKSwapchain::MVKSwapchain(MVKDevice* device, + const VkSwapchainCreateInfoKHR* pCreateInfo) : MVKBaseDeviceObject(device) { + _currentAcquisitionID = 0; + + // If applicable, release any surfaces (not currently being displayed) from the old swapchain. + MVKSwapchain* oldSwapchain = (MVKSwapchain*)pCreateInfo->oldSwapchain; + if (oldSwapchain) { oldSwapchain->releaseUndisplayedSurfaces(); } + + // Get the layer underlying the surface view, which must be a CAMetalLayer. + MVKSurface* mvkSrfc = (MVKSurface*)pCreateInfo->surface; + _mtlLayer = mvkSrfc->getCAMetalLayer(); + _mtlLayer.device = getMTLDevice(); + _mtlLayer.pixelFormat = mtlPixelFormatFromVkFormat(pCreateInfo->imageFormat); + _mtlLayer.framebufferOnly = !mvkIsAnyFlagEnabled(pCreateInfo->imageUsage, (VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_STORAGE_BIT)); + + // TODO: set additional CAMetalLayer properties before extracting drawables: + // - presentsWithTransaction + // - drawsAsynchronously + // - colorspace (macOS only) Vulkan only supports sRGB colorspace for now. + // - wantsExtendedDynamicRangeContent (macOS only) + + initSurfaceImages(pCreateInfo); + initFrameIntervalTracking(); + + _licenseWatermark = NULL; +} + +/** Initializes the array of images used for the surfaces of this swapchain. */ +void MVKSwapchain::initSurfaceImages(const VkSwapchainCreateInfoKHR* pCreateInfo) { + + _mtlLayerOrigDrawSize = _mtlLayer.updatedDrawableSizeMVK; + VkExtent2D imgExtent = mvkVkExtent2DFromCGSize(_mtlLayerOrigDrawSize); + + const VkImageCreateInfo imgInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .pNext = VK_NULL_HANDLE, + .imageType = VK_IMAGE_TYPE_2D, + .format = mvkVkFormatFromMTLPixelFormat(_mtlLayer.pixelFormat), + .extent = { imgExtent.width, imgExtent.height, 1 }, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = pCreateInfo->imageUsage, + .flags = 0, + }; + + uint32_t imgCnt = MVK_MAX_SWAPCHAIN_SURFACE_IMAGE_COUNT; + _surfaceImages.reserve(imgCnt); + for (uint32_t imgIdx = 0; imgIdx < imgCnt; imgIdx++) { + _surfaceImages.push_back(_device->createSwapchainImage(&imgInfo, this, NULL)); + } + + MVKLogInfo("Created %d swapchain images with initial size (%d, %d).", imgCnt, imgExtent.width, imgExtent.height); +} + +// Initialize frame interval tracking, including start time and filtering parameters. +void MVKSwapchain::initFrameIntervalTracking() { + _performanceStatistics.lastFrameInterval = 0; + _performanceStatistics.averageFrameInterval = 0; + _performanceStatistics.averageFramesPerSecond = 0; + _currentPerfLogFrameCount = 0; + + _lastFrameTime = [NSDate timeIntervalSinceReferenceDate]; + + // Establish the alpha parameter of a low-pass filter for averaging frame intervals. + double RC_over_dt = 10; + _averageFrameIntervalFilterAlpha = 1.0 / (1 + RC_over_dt); +} + +MVKSwapchain::~MVKSwapchain() { + for (auto& img : _surfaceImages) { _device->destroySwapchainImage(img, NULL); } + + if (_licenseWatermark) { delete _licenseWatermark; } +} + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSync.h b/MoltenVK/MoltenVK/GPUObjects/MVKSync.h new file mode 100644 index 00000000..1e20caa8 --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSync.h @@ -0,0 +1,253 @@ +/* + * MVKSync.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKDevice.h" +#include +#include +#include + +class MVKFenceSitter; + + +#pragma mark - +#pragma mark MVKSemaphoreImpl + +/** + * A general utility semaphore object. Reservations can be made with an instance, + * and it will block waiting threads until reservations have been released. + * + * An instance can be configured so that each call to the reserve() function must be + * matched with a separate call to the release() function before waiting threads are + * unblocked, or it can be configured so that a single call to the release() function + * will release all outstanding reservations and unblock all threads immediately. + */ +class MVKSemaphoreImpl : public MVKBaseObject { + +public: + + /** + * Adds a reservation to this semaphore, incrementing the reservation count. + * Subsequent calls to a wait() function will block until a corresponding call + * is made to the release() function. + */ + void reserve(); + + /** + * Depending on configuration, releases one or all reservations. When all reservations + * have been released, unblocks all waiting threads to continue processing. + */ + void release(); + + /** + * Indefinitely blocks processing on the current thread until either any or all + * (depending on configuration) outstanding reservations have been released. + * + * If reserveAgain is set to true, a single reservation will be added to this + * instance once the wait is finished. + */ + void wait(bool reserveAgain = false); + + /** + * Blocks processing on the current thread until any or all (depending on configuration) outstanding + * reservations have been released, or until the specified timeout interval in nanoseconds expires. + * + * If reserveAgain is set to true, a single reservation will be added once this wait is finished. + * + * Returns true if all reservations were cleared, or false if the timeout interval expired. + */ + bool wait(uint64_t timeout, bool reserveAgain = false); + + +#pragma mark Construction + + /** + * Constructs an instance with the specified number of initial reservations. + * This value defaults to zero, starting the semaphore in an unblocking state. + * + * The waitAll parameter indicates whether a call to the release() function is required + * for each call to the reserve() function (waitAll = true), or whether a single call + * to the release() function will release all outstanding reservations (waitAll = true). + * This value defaults to true, indicating that each call to the reserve() function will + * require a separate call to the release() function to cause the semaphore to stop blocking. + */ + MVKSemaphoreImpl(bool waitAll = true, uint32_t reservationCount = 0) + : _shouldWaitAll(waitAll), _reservationCount(reservationCount) {} + + +private: + bool operator()(); + inline void reserveImpl() { _reservationCount++; } // Not thread-safe + inline bool isClear() { return _reservationCount == 0; } // Not thread-safe + + std::mutex _lock; + std::condition_variable _blocker; + uint32_t _reservationCount; + bool _shouldWaitAll; +}; + + +#pragma mark - +#pragma mark MVKSemaphore + +/** Represents a Vulkan semaphore. */ +class MVKSemaphore : public MVKBaseDeviceObject { + +public: + + /** Indefinitely blocks processing on the current thread until this semaphore is signaled. */ + void wait(); + + /** + * Blocks processing on the current thread until this semaphore is + * signaled, or until the specified timeout in nanoseconds expires. + * + * Returns true if this semaphore was signaled, or false if the timeout interval expired. + */ + bool wait(uint64_t timeout); + + /** Signals the semaphore. Unblocks all waiting threads to continue processing. */ + void signal(); + + +#pragma mark Construction + + MVKSemaphore(MVKDevice* device, const VkSemaphoreCreateInfo* pCreateInfo) + : MVKBaseDeviceObject(device), _blocker(false, 1) {} + +protected: + MVKSemaphoreImpl _blocker; +}; + + +#pragma mark - +#pragma mark MVKFence + +/** Represents a Vulkan fence. */ +class MVKFence : public MVKBaseDeviceObject { + +public: + + /** + * If this fence has not been signaled yet, adds the specified fence sitter to the + * internal list of fence sitters that will be notified when this fence is signaled, + * and then calls addUnsignaledFence() on the fence sitter so it is aware that it + * will be signaled. + * + * Does nothing if this fence has already been signaled, and does not call + * addUnsignaledFence() on the fence sitter. + * + * Each fence sitter should only listen once for each fence. Adding the same fence sitter + * more than once in between each fence reset and signal results in undefined behaviour. + */ + void addSitter(MVKFenceSitter* fenceSitter); + + /** Removes the specified fence sitter. */ + void removeSitter(MVKFenceSitter* fenceSitter); + + /** Signals this fence. Notifies all waiting fence sitters. */ + void signal(); + + /** Rremoves all fence sitters and resets this fence back to unsignaled state again. */ + void reset(); + + /** Returns whether this fence has been signaled and not reset. */ + bool getIsSignaled(); + + +#pragma mark Construction + + MVKFence(MVKDevice* device, const VkFenceCreateInfo* pCreateInfo) : MVKBaseDeviceObject(device), + _isSignaled(mvkAreFlagsEnabled(pCreateInfo->flags, VK_FENCE_CREATE_SIGNALED_BIT)) {} + + ~MVKFence() override; + +protected: + void notifySitters(); + + std::mutex _lock; + std::unordered_set _fenceSitters; + bool _isSignaled; +}; + + +#pragma mark - +#pragma mark MVKFenceSitter + +/** An object that responds to signals from MVKFences. */ +class MVKFenceSitter : public MVKBaseObject { + +public: + + /** + * If this instance has been configured to wait for fences, blocks processing on the + * current thread until any or all of the fences that this instance is waiting for are + * signaled. If this instance has not been configured to wait for fences, this function + * immediately returns true. + * + * Returns whether the lock timed out while waiting. + */ + void wait(); + + /** + * If this instance has been configured to wait for fences, blocks processing on the + * current thread until any or all of the fences that this instance is waiting for are + * signaled, or until the specified timeout in nanoseconds expires. If this instance + * has not been configured to wait for fences, this function immediately returns true. + * + * Returns true if the required fences were triggered, or false if the timeout interval expired. + */ + bool wait(uint64_t timeout); + + +#pragma mark Construction + + /** Constructs an instance with the specified type of waiting. */ + MVKFenceSitter(bool waitAll = true) : _blocker(waitAll, 0) {} + + ~MVKFenceSitter() override; + +private: + friend class MVKFence; + + void addUnsignaledFence(MVKFence* fence); + void fenceSignaled(MVKFence* fence); + + std::mutex _lock; + std::unordered_set _unsignaledFences; + MVKSemaphoreImpl _blocker; +}; + + +#pragma mark - +#pragma mark Support functions + +/** Resets the specified fences. */ +VkResult mvkResetFences(uint32_t fenceCount, const VkFence* pFences); + +/** + * Blocks the current thread until any or all of the specified + * fences have been signaled, or the specified timeout occurs. + */ +VkResult mvkWaitForFences(uint32_t fenceCount, + const VkFence* pFences, + VkBool32 waitAll, + uint64_t timeout); + + diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm new file mode 100644 index 00000000..6a0debff --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm @@ -0,0 +1,225 @@ +/* + * MVKSync.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKSync.h" +#include "MVKFoundation.h" + +using namespace std; + + +#pragma mark - +#pragma mark MVKSemaphoreImpl + +void MVKSemaphoreImpl::release() { + lock_guard lock(_lock); + if (isClear()) { return; } + + // Either decrement the reservation counter, or clear it altogether + if (_shouldWaitAll) { + _reservationCount--; + } else { + _reservationCount = 0; + } + // If all reservations have been released, unblock all waiting threads + if ( isClear() ) { _blocker.notify_all(); } +} + +void MVKSemaphoreImpl::reserve() { + lock_guard lock(_lock); + reserveImpl(); +} + +void MVKSemaphoreImpl::wait(bool reserveAgain) { + unique_lock lock(_lock); + _blocker.wait(lock, [this]{ return isClear(); }); + if (reserveAgain) { reserveImpl(); } +} + +bool MVKSemaphoreImpl::wait(uint64_t timeout, bool reserveAgain) { + unique_lock lock(_lock); + + bool isDone; + if (timeout > 0) { + // Limit timeout to avoid overflow since wait_for() uses wait_until() + uint64_t nanoTimeout = min(timeout, numeric_limits::max() >> 4); + chrono::nanoseconds nanos(nanoTimeout); + isDone = _blocker.wait_for(lock, nanos, [this]{ return isClear(); }); + } else { + isDone = isClear(); + } + + if (reserveAgain) { reserveImpl(); } + return isDone; +} + + +#pragma mark - +#pragma mark MVKSemaphore + +void MVKSemaphore::wait() { +// MVKLogDebug("Waiting on semaphore %p. Elapsed time: %.6f ms.", this, mvkGetElapsedMilliseconds()); + _blocker.wait(true); +// MVKLogDebug("Done waiting on semaphore %p. Elapsed time: %.6f ms.", this, mvkGetElapsedMilliseconds()); +} + +bool MVKSemaphore::wait(uint64_t timeout) { +// MVKLogDebug("Waiting on semaphore %p for max timeout %llu. Elapsed time: %.6f ms.", this, timeout, mvkGetElapsedMilliseconds()); + bool isDone = _blocker.wait(timeout, true); +// MVKLogDebug("Done waiting on semaphore %p. Elapsed time: %.6f ms.", this, mvkGetElapsedMilliseconds()); + if ( !isDone && timeout > 0 ) { mvkNotifyErrorWithText(VK_TIMEOUT, "Vulkan semaphore timeout after %d nanoseconds.", timeout); } + return isDone; +} + +void MVKSemaphore::signal() { + _blocker.release(); +// MVKLogDebug("Signalled semaphore %p. Elapsed time: %.6f ms.", this, mvkGetElapsedMilliseconds()); +} + + +#pragma mark - +#pragma mark MVKFence + +void MVKFence::addSitter(MVKFenceSitter* fenceSitter) { + lock_guard lock(_lock); + + // Sitters only care about unsignaled fences. If already signaled, + // don't add myself to the sitter and don't notify the sitter. + if (_isSignaled) { +// MVKLogDebug("Fence %p already signaled. Sitter not added. Elapsed time: %.6f ms.", this, mvkGetElapsedMilliseconds()); + return; + } + + // Ensure each fence only added once to each fence sitter + auto addRslt = _fenceSitters.insert(fenceSitter); // pair with second element true if was added + if (addRslt.second) { fenceSitter->addUnsignaledFence(this); } + +// MVKLogDebug("Fence %p adding sitter %d. Elapsed time: %.6f ms.", this, _fenceSitters.size(), mvkGetElapsedMilliseconds()); +} + +void MVKFence::removeSitter(MVKFenceSitter* fenceSitter) { + lock_guard lock(_lock); +// MVKLogDebug("Fence %p removing sitter %d. Elapsed time: %.6f ms.", this, _fenceSitters.size(), mvkGetElapsedMilliseconds()); + _fenceSitters.erase(fenceSitter); +} + +void MVKFence::signal() { + lock_guard lock(_lock); + + if (_isSignaled) { return; } // Only signal once + _isSignaled = true; + +// MVKLogDebug("Fence %p with %d sitters signaled. Elapsed time: %.6f ms.", this , _fenceSitters.size(), mvkGetElapsedMilliseconds()); + + // Notify all the fence sitters, and clear them from this instance. + for (auto& fs : _fenceSitters) { + fs->fenceSignaled(this); + } + _fenceSitters.clear(); +} + +void MVKFence::reset() { + lock_guard lock(_lock); + _isSignaled = false; + _fenceSitters.clear(); +// MVKLogDebug("Reset fence %p. Elapsed time: %.6f ms.", this, mvkGetElapsedMilliseconds()); +} + +bool MVKFence::getIsSignaled() { + lock_guard lock(_lock); +// MVKLogDebug("Checking fence %p status: %ssignaled. Elapsed time: %.6f ms.", this, (_isSignaled ? "" : "un"), mvkGetElapsedMilliseconds()); + return _isSignaled; +} + + +#pragma mark Construction + +MVKFence::~MVKFence() { + lock_guard lock(_lock); + for (auto& fs : _fenceSitters) { + fs->fenceSignaled(this); + } +} + + +#pragma mark - +#pragma mark MVKFenceSitter + +void MVKFenceSitter::addUnsignaledFence(MVKFence* fence) { + lock_guard lock(_lock); + // Only reserve semaphore once per fence + auto addRslt = _unsignaledFences.insert(fence); // pair with second element true if was added + if (addRslt.second) { _blocker.reserve(); } +} + +void MVKFenceSitter::fenceSignaled(MVKFence* fence) { + lock_guard lock(_lock); + // Only release semaphore if actually waiting for this fence + if (_unsignaledFences.erase(fence)) { _blocker.release(); } +} + +void MVKFenceSitter::wait() { _blocker.wait(); } + +bool MVKFenceSitter::wait(uint64_t timeout) { + bool isDone = _blocker.wait(timeout); + if ( !isDone && timeout > 0 ) { mvkNotifyErrorWithText(VK_TIMEOUT, "Vulkan fence timeout after %d nanoseconds.", timeout); } + return isDone; +} + + +#pragma mark Construction + +MVKFenceSitter::~MVKFenceSitter() { + lock_guard lock(_lock); + for (auto& uf : _unsignaledFences) { + uf->removeSitter(this); + } +} + + +#pragma mark - +#pragma mark Support functions + +VkResult mvkResetFences(uint32_t fenceCount, const VkFence* pFences) { + for (uint32_t i = 0; i < fenceCount; i++) { + ((MVKFence*)pFences[i])->reset(); + } + return VK_SUCCESS; +} + +VkResult mvkWaitForFences(uint32_t fenceCount, + const VkFence* pFences, + VkBool32 waitAll, + uint64_t timeout) { + +// MVKLogDebug("Waiting for fences. Elapsed time: %.6f ms.", mvkGetElapsedMilliseconds()); + + // Create a blocking fence sitter and add it to each fence + MVKFenceSitter fenceSitter(waitAll); + for (uint32_t i = 0; i < fenceCount; i++) { + MVKFence* mvkFence = (MVKFence*)pFences[i]; + mvkFence->addSitter(&fenceSitter); + } + VkResult rslt = fenceSitter.wait(timeout) ? VK_SUCCESS : VK_TIMEOUT; + +// MVKLogDebug("Fences done. Elapsed time: %.6f ms.", mvkGetElapsedMilliseconds()); + + return rslt; +} + + + diff --git a/MoltenVK/MoltenVK/Loader/MVKLayers.h b/MoltenVK/MoltenVK/Loader/MVKLayers.h new file mode 100644 index 00000000..2b54b49e --- /dev/null +++ b/MoltenVK/MoltenVK/Loader/MVKLayers.h @@ -0,0 +1,116 @@ +/* + * MVKLayers.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "mvk_vulkan.h" +#include "MVKBaseObject.h" +#include + + +#pragma mark MVKLayer + +/** Represents a single Vulkan layer. */ +class MVKLayer : public MVKConfigurableObject { + +public: + + /** Returns the name of this layer. */ + const char* getName(); + + /** Returns the properties associated with this layer. */ + VkLayerProperties* const getLayerProperties(); + + /** + * If pProperties is null, the value of pCount is updated with the number of extensions + * available in this layer. + * + * If pProperties is not null, then pCount extension properties are copied into the array. + * If the number of available layers is less than pCount, the value of pCount is updated + * to indicate the number of extension properties actually returned in the array. + * + * Returns VK_SUCCESS if successful. Returns VK_INCOMPLETE if the number of extensions + * available in this instance is larger than the specified pCount. Returns other values + * if an error occurs. + */ + VkResult getExtensionProperties(uint32_t* pCount, VkExtensionProperties* pProperties); + + /** Returns whether this layer supports the specified extension. */ + bool hasExtensionNamed(const char* extnName); + + /** Default constructor. This represents the driver implementation. */ + MVKLayer(); + +protected: + VkLayerProperties _layerProperties; + std::vector _extensions; + +}; + + +#pragma mark MVKLayerManager + +/** Manages a set of Vulkan layers. */ +class MVKLayerManager : public MVKConfigurableObject { + +public: + + /** Returns a pointer to the driver layer. */ + MVKLayer* getDriverLayer(); + + /** + * Returns a pointe to the layer with the specified name, + * or null if no layer was found with that name. + * + * If pLayerName is null, returns the driver layer, which is + * the same layer returned by the getDriverLayer() function. + */ + MVKLayer* getLayerNamed(const char* pLayerName); + + /** + * If pProperties is null, the value of pCount is updated with the number of layers + * available in this instance. + * + * If pProperties is not null, then pCount layer properties are copied into the array. + * If the number of available layers is less than pCount, the value of pCount is updated + * to indicate the number of layer properties actually returned in the array. + * + * Returns VK_SUCCESS if successful. Returns VK_INCOMPLETE if the number of layers + * available in this instance is larger than the specified pCount. Returns other + * values if an error occurs. + */ + VkResult getLayerProperties(uint32_t* pCount, VkLayerProperties* pProperties); + + +#pragma mark Object Creation + + /** Creates a default layer manager with a single layer representing the driver implementation. */ + MVKLayerManager(); + + /** + * Returns the singleton instance representing the global layers populated by the Loader. + * + * This function is thread-safe. + */ + static MVKLayerManager* globalManager(); + +protected: + std::vector _layers; + +}; + diff --git a/MoltenVK/MoltenVK/Loader/MVKLayers.mm b/MoltenVK/MoltenVK/Loader/MVKLayers.mm new file mode 100644 index 00000000..3a76740d --- /dev/null +++ b/MoltenVK/MoltenVK/Loader/MVKLayers.mm @@ -0,0 +1,172 @@ +/* + * MVKLayers.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKLayers.h" +#include "MVKEnvironment.h" +#include "vk_mvk_moltenvk.h" +#include + +using namespace std; + + +#pragma mark MVKLayer + +const char* MVKLayer::getName() { return (const char*)&_layerProperties.layerName; } + +VkLayerProperties* const MVKLayer::getLayerProperties() { return &_layerProperties; } + +VkResult MVKLayer::getExtensionProperties(uint32_t* pCount, VkExtensionProperties* pProperties) { + + // If properties aren't actually being requested yet, simply update the returned count + if ( !pProperties ) { + *pCount = (uint32_t)_extensions.size(); + return VK_SUCCESS; + } + + // Othewise, determine how many extensions we'll return, and return that count + uint32_t extCnt = (uint32_t)_extensions.size(); + VkResult result = (*pCount <= extCnt) ? VK_SUCCESS : VK_INCOMPLETE; + *pCount = min(extCnt, *pCount); + + // Now populate the layer properties + for (uint32_t extIdx = 0; extIdx < *pCount; extIdx++) { + pProperties[extIdx] = _extensions[extIdx]; + } + + return result; +} + +bool MVKLayer::hasExtensionNamed(const char* extnName) { + for (auto& extn : _extensions) { + if ( strcmp(extn.extensionName, extnName) == 0 ) { return true; } + } + return false; +} + + +#pragma mark Object Creation + +MVKLayer::MVKLayer() { + + // The core driver layer + strcpy(_layerProperties.layerName, "MoltenVK"); + strcpy(_layerProperties.description, "MoltenVK driver layer"); + _layerProperties.specVersion = MVK_VULKAN_API_VERSION; + _layerProperties.implementationVersion = MVK_VERSION; + + // Extensions + VkExtensionProperties extTmplt; + + memset(extTmplt.extensionName, 0, sizeof(extTmplt.extensionName)); + strcpy(extTmplt.extensionName, VK_MVK_MOLTENVK_EXTENSION_NAME); + extTmplt.specVersion = VK_MVK_MOLTENVK_SPEC_VERSION; + _extensions.push_back(extTmplt); + + memset(extTmplt.extensionName, 0, sizeof(extTmplt.extensionName)); + strcpy(extTmplt.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME); + extTmplt.specVersion = VK_KHR_SWAPCHAIN_SPEC_VERSION; + _extensions.push_back(extTmplt); + + memset(extTmplt.extensionName, 0, sizeof(extTmplt.extensionName)); + strcpy(extTmplt.extensionName, VK_KHR_SURFACE_EXTENSION_NAME); + extTmplt.specVersion = VK_KHR_SURFACE_SPEC_VERSION; + _extensions.push_back(extTmplt); + + memset(extTmplt.extensionName, 0, sizeof(extTmplt.extensionName)); + strcpy(extTmplt.extensionName, VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_EXTENSION_NAME); + extTmplt.specVersion = VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_SPEC_VERSION; + _extensions.push_back(extTmplt); + +#if MVK_IOS + memset(extTmplt.extensionName, 0, sizeof(extTmplt.extensionName)); + strcpy(extTmplt.extensionName, VK_MVK_IOS_SURFACE_EXTENSION_NAME); + extTmplt.specVersion = VK_MVK_IOS_SURFACE_SPEC_VERSION; + _extensions.push_back(extTmplt); + + memset(extTmplt.extensionName, 0, sizeof(extTmplt.extensionName)); + strcpy(extTmplt.extensionName, VK_IMG_FORMAT_PVRTC_EXTENSION_NAME); + extTmplt.specVersion = VK_IMG_FORMAT_PVRTC_SPEC_VERSION; + _extensions.push_back(extTmplt); +#endif +#if MVK_MACOS + memset(extTmplt.extensionName, 0, sizeof(extTmplt.extensionName)); + strcpy(extTmplt.extensionName, VK_MVK_MACOS_SURFACE_EXTENSION_NAME); + extTmplt.specVersion = VK_MVK_MACOS_SURFACE_SPEC_VERSION; + _extensions.push_back(extTmplt); +#endif +} + + +#pragma mark MVKLayerManager + +MVKLayer* MVKLayerManager::getDriverLayer() { return &(_layers[0]); } + +MVKLayer* MVKLayerManager::getLayerNamed(const char* pLayerName) { + + // If name is null, return the driver layer + if ( !pLayerName ) { return getDriverLayer(); } + + // Otherwise look for a layer with the specified name + uint32_t layCnt = (uint32_t)_layers.size(); + for (uint32_t layIdx = 0; layIdx < layCnt; layIdx++) { + MVKLayer* pLayer = &_layers[layIdx]; + if ( strcmp(pLayer->getName(), pLayerName) == 0 ) { return pLayer; } + } + return VK_NULL_HANDLE; +} + + +VkResult MVKLayerManager::getLayerProperties(uint32_t* pCount, VkLayerProperties* pProperties) { + + // If properties aren't actually being requested yet, simply update the returned count + if ( !pProperties ) { + *pCount = (uint32_t)_layers.size(); + return VK_SUCCESS; + } + + // Othewise, determine how many layers we'll return, and return that count + uint32_t layerCnt = (uint32_t)_layers.size(); + VkResult result = (*pCount <= layerCnt) ? VK_SUCCESS : VK_INCOMPLETE; + *pCount = min(layerCnt, *pCount); + + // Now populate the layer properties + for (uint32_t layIdx = 0; layIdx < *pCount; layIdx++) { + pProperties[layIdx] = *(&_layers[layIdx])->getLayerProperties(); + } + + return result; +} + + +#pragma mark Object Creation + +// Populate the layers +MVKLayerManager::MVKLayerManager() { + _layers.push_back(MVKLayer()); +} + +static mutex _lock; +static MVKLayerManager* _globalManager = VK_NULL_HANDLE; + +MVKLayerManager* MVKLayerManager::globalManager() { + lock_guard lock(_lock); + if ( !_globalManager ) { _globalManager = new MVKLayerManager(); } + return _globalManager; +} + + diff --git a/MoltenVK/MoltenVK/Utility/MVKBaseObject.cpp b/MoltenVK/MoltenVK/Utility/MVKBaseObject.cpp new file mode 100644 index 00000000..9e19373a --- /dev/null +++ b/MoltenVK/MoltenVK/Utility/MVKBaseObject.cpp @@ -0,0 +1,35 @@ +/* + * MVKBaseObject.cpp + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKBaseObject.h" +#include +#include + +using namespace std; + + +#pragma mark - +#pragma mark MVKBaseObject + +string MVKBaseObject::className() { + int status; + char* demangled = abi::__cxa_demangle(typeid(*this).name(), 0, 0, &status); + string clzName = demangled; + free(demangled); + return clzName; +} diff --git a/MoltenVK/MoltenVK/Utility/MVKBaseObject.h b/MoltenVK/MoltenVK/Utility/MVKBaseObject.h new file mode 100644 index 00000000..918aba29 --- /dev/null +++ b/MoltenVK/MoltenVK/Utility/MVKBaseObject.h @@ -0,0 +1,67 @@ +/* + * MVKBaseObject.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + + +#pragma mark - +#pragma mark MVKBaseObject + +/** + * An abstract base class for all MoltenVK C++ classes, to allow common object + * behaviour, and common custom allocation and deallocation behaviour. + */ +class MVKBaseObject { + +public: + + /** Returns the name of the class of which this object is an instance. */ + std::string className(); + + virtual ~MVKBaseObject() {} +}; + + +#pragma mark - +#pragma mark MVKConfigurableObject + +/** + * Abstract class that represents an object whose configuration can be validated and tracked + * as a queriable result. This is the base class of opaque Vulkan API objects, and commands. + */ +class MVKConfigurableObject : public MVKBaseObject { + +public: + + /** Returns a indication of the success of the configuration of this instance. */ + inline VkResult getConfigurationResult() { return _configurationResult; } + + /** If the existing configuration result is VK_SUCCESS, it is set to the specified value. */ + inline void setConfigurationResult(VkResult vkResult) { + if (_configurationResult == VK_SUCCESS) { _configurationResult = vkResult; } + } + + /** Resets the indication of the success of the configuration of this instance back to VK_SUCCESS. */ + inline void clearConfigurationResult() { _configurationResult = VK_SUCCESS; } + +protected: + VkResult _configurationResult = VK_SUCCESS; +}; diff --git a/MoltenVK/MoltenVK/Utility/MVKEnvironment.h b/MoltenVK/MoltenVK/Utility/MVKEnvironment.h new file mode 100644 index 00000000..a75bf16d --- /dev/null +++ b/MoltenVK/MoltenVK/Utility/MVKEnvironment.h @@ -0,0 +1,41 @@ +/* + * MVKEnvironment.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#pragma once + +#include "MVKCommonEnvironment.h" + + +/** Macro to determine the Vulkan version supported by MoltenVK. */ +#define MVK_VULKAN_API_VERSION VK_MAKE_VERSION(VK_VERSION_MAJOR(VK_API_VERSION_1_0), \ + VK_VERSION_MINOR(VK_API_VERSION_1_0), \ + VK_HEADER_VERSION) + +/** + * Macro to adjust the specified Vulkan version to a value that can be compared for conformance + * against another Vulkan version. This macro strips the patch value from the specified Vulkan + * version and replaces it with zero. A Vulkan version is comformant with another version if it + * has the same or higher major and minor values, regardless of the patch value of each version. + * In particular, by definition, a Vulkan version is conformant with another Vulkan version that + * has a larger patch number, as long as it has a same or greater major and minor value. + */ +#define MVK_VULKAN_API_VERSION_CONFORM(api_ver) VK_MAKE_VERSION(VK_VERSION_MAJOR(api_ver), \ + VK_VERSION_MINOR(api_ver), \ + 0) + diff --git a/MoltenVK/MoltenVK/Utility/MVKFoundation.h b/MoltenVK/MoltenVK/Utility/MVKFoundation.h new file mode 100644 index 00000000..a749e95d --- /dev/null +++ b/MoltenVK/MoltenVK/Utility/MVKFoundation.h @@ -0,0 +1,353 @@ +/* + * MVKFoundation.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#pragma once + + +#include +#include "MVKLogging.h" +#include +#include + + +#pragma mark Math + +/** Maximum value of any variable of unsigned integral type. */ +#define kMVKMaxUnsigned (~0U) + +// Common scaling multipliers +#define KIBI (1024) +#define MEBI (KIBI * KIBI) +#define GIBI (KIBI * MEBI) + +/** Represents a non-existent index. */ +static const int kMVKIndexNone = -1; + +/** A type definition for 16-bit half-float values. */ +typedef uint16_t MVKHalfFloat; + +/** A representation of the value of 1.0 as a 16-bit half-float. */ +#define kHalfFloat1 0x3C00 + +/** Common header for many standard Vulkan API structures. */ +typedef struct { + VkStructureType sType; + const void* pNext; +} MVKVkAPIStructHeader; + + +#pragma mark - +#pragma mark Vertex content structures + +/** 2D vertex position and texcoord content. */ +typedef struct { + simd::float2 position; + simd::float2 texCoord; +} MVKVertexPosTex; + + +#pragma mark - +#pragma mark Vulkan support + +/** Tracks the Vulkan command currently being used. */ +typedef enum { + kMVKCommandUseNone, /**< No use defined. */ + kMVKCommandUseQueueSubmit, /**< vkQueueSubmit. */ + kMVKCommandUseQueuePresent, /**< vkQueuePresentKHR. */ + kMVKCommandUseQueueWaitIdle, /**< vkQueueWaitIdle. */ + kMVKCommandUseDeviceWaitIdle, /**< vkDeviceWaitIdle. */ + kMVKCommandUseBeginRenderPass, /**< vkCmdBeginRenderPass. */ + kMVKCommandUseNextSubpass, /**< vkCmdNextSubpass. */ + kMVKCommandUsePipelineBarrier, /**< vkCmdPipelineBarrier. */ + kMVKCommandUseBlitImage, /**< vkCmdBlitImage. */ + kMVKCommandUseCopyImage, /**< vkCmdCopyImage. */ + kMVKCommandUseResolveImage, /**< vkCmdResolveImage - resolve stage. */ + kMVKCommandUseResolveExpandImage, /**< vkCmdResolveImage - expand stage. */ + kMVKCommandUseResolveCopyImage, /**< vkCmdResolveImage - expand stage. */ + kMVKCommandUseCopyBuffer, /**< vkCmdCopyBuffer. */ + kMVKCommandUseCopyBufferToImage, /**< vkCmdCopyBufferToImage. */ + kMVKCommandUseCopyImageToBuffer, /**< vkCmdCopyImageToBuffer. */ + kMVKCommandUseFillBuffer, /**< vkCmdFillBuffer. */ + kMVKCommandUseUpdateBuffer, /**< vkCmdUpdateBuffer. */ + kMVKCommandUseClearColorImage, /**< vkCmdClearColorImage. */ + kMVKCommandUseClearDepthStencilImage, /**< vkCmdClearDepthStencilImage. */ + kMVKCommandUseResetQueryPool, /**< vkCmdResetQueryPool. */ +} MVKCommandUse; + +/** + * Copies the name of the specified VkResult code to the specified string. + * + * Returns a pointer to that string. + */ +#define MVKResultNameMaxLen 64 +char* mvkResultName(VkResult vkResult, char* name); + +/** + * Notifies the app of an error code and error message, via the following methods: + * + * - Logs the error code and message to the console + */ +VkResult mvkNotifyErrorWithText(VkResult vkErr, const char* errFmt, ...); + + +#pragma mark - +#pragma mark Alignment functions + +/** Returns whether the specified value is a power-of-two. */ +static inline bool mvkIsPowerOfTwo(uintptr_t value) { + // Test POT: (x != 0) && ((x & (x - 1)) == 0) + return value && ((value & (value - 1)) == 0); +} + +/** + * Ensures the specified value is a power-of-two. Returns the specified value if it is a + * power-of-two value. If it is not, returns the next power-of-two value that is larger + * than the specified value is returned. + */ +static inline uintptr_t mvkEnsurePowerOfTwo(uintptr_t value) { + if (mvkIsPowerOfTwo(value)) { return value; } + + uintptr_t pot = 1; + while(pot <= value) { pot <<= 1; }; + return pot; +} + +/** + * Returns the power-of-two exponent of the next power-of-two + * number that is at least as big as the specified value. + * + * This implementation returns zero for both zero and one as inputs. + */ +static inline uint32_t mvkPowerOfTwoExponent(uintptr_t value) { + uintptr_t p2Value = mvkEnsurePowerOfTwo(value); + + // Count the trailing zeros + p2Value = (p2Value ^ (p2Value - 1)) >> 1; // Set trailing 0s to 1s and zero rest + uint32_t potExp = 0; + while (p2Value) { + p2Value >>= 1; + potExp++; + } + return potExp; +} + +/** + * Aligns the specified bytes reference to the specified alignment, and returns the + * aligned value, which will be equal to or greater than the specified reference. + * + * This is a low level utility method. Usually you will use the convenience methods + * mvkAlignAddress and mvkAlignByteOffset to align addresses and offsets respectively. + */ +static inline uintptr_t mvkAlignByteRef(uintptr_t byteRef, uintptr_t byteAlignment) { + if (byteAlignment == 0) { return byteRef; } + + MVKAssert(mvkIsPowerOfTwo(byteAlignment), "Byte alignment %lu is not a power-of-two value.", byteAlignment); + + uintptr_t mask = byteAlignment - 1; + return (byteRef + mask) & ~mask; +} + +/** + * Aligns the specified memory address to the specified byte alignment. The memory address + * returned by this function will be equal to or greater than the specified address. + */ +static inline void* mvkAlignAddress(void* address, uintptr_t byteAlignment) { + return (void*)mvkAlignByteRef((uintptr_t)address, byteAlignment); +} + +/** + * Aligns the specified byte offset to the specified byte alignment. The byte offset + * returned by this function will be equal to or greater than the specified byte offset. + */ +static inline uintptr_t mvkAlignByteOffset(uintptr_t byteOffset, uintptr_t byteAlignment) { + return mvkAlignByteRef(byteOffset, byteAlignment); +} + +/** + * Reverses the order of the rows in the specified data block. + * The transformation is performed in-place. + * + * This function may be used to reverse the order of the rows of any row-major memory + * structure, but is particularly useful for vertically flipping the contents of a texture + * or image, which is a common requirement when converting content data between a Vulkan + * texture orientation and a Metal texture orientation. + * + * The specified data block is assumed to be in row-major order, containing the specified + * number of rows, and with the specified number of bytes in each row. The total number of + * bytes in the data block must be at least (bytesPerRow * rowCount). + */ +void mvkFlipVertically(void* rowMajorData, uint32_t rowCount, size_t bytesPerRow); + + +#pragma mark Vulkan structure support functions + +/** Returns whether the two Vulkan extents are equal by comparing their respective components. */ +static inline bool mvkVkExtent2DsAreEqual(VkExtent2D e1, VkExtent2D e2) { + return (e1.width == e2.width) && (e1.height == e2.height); +} + +/** Returns whether the two Vulkan extents are equal by comparing their respective components. */ +static inline bool mvkVkExtent3DsAreEqual(VkExtent3D e1, VkExtent3D e2) { + return (e1.width == e2.width) && (e1.height == e2.height) && (e1.depth == e2.depth); +} + +/** Returns whether the two Vulkan offsets are equal by comparing their respective components. */ +static inline bool mvkVkOffset2DsAreEqual(VkOffset2D os1, VkOffset2D os2) { + return (os1.x == os2.x) && (os1.y == os2.y); +} + +/** Returns whether the two Vulkan offsets are equal by comparing their respective components. */ +static inline bool mvkVkOffset3DsAreEqual(VkOffset3D os1, VkOffset3D os2) { + return (os1.x == os2.x) && (os1.y == os2.y) && (os1.z == os2.z); +} + +/** + * Returns the difference between two offsets, by subtracting the subtrahend from the minuend, + * which is accomplished by subtracting each of the corresponding x,y,z components. + */ +static inline VkOffset3D mvkVkOffset3DDifference(VkOffset3D minuend, VkOffset3D subtrahend) { + VkOffset3D rslt; + rslt.x = minuend.x - subtrahend.x; + rslt.y = minuend.y - subtrahend.y; + rslt.z = minuend.z - subtrahend.z; + return rslt; +} + + +#pragma mark - +#pragma mark Template functions + +/** Clamps the value between the lower and upper bounds. */ +template +const T& mvkClamp(const T& val, const T& lower, const T& upper) { + return std::min(upper, std::max(val, lower)); +} + +/** + * Returns a hash value calculated from the specified array of numeric elements, + * using the DJB2a algorithm: hash = (hash * 33) ^ value. + * + * For a hash on a single array, leave the seed value unspecified, to use the default + * seed value. To accumulate a single hash value over several arrays, use the hash + * value returned by previous calls as the seed in subsequent calls. + */ +template +std::size_t mvkHash(const N* pVals, std::size_t count, std::size_t seed = 5381) { + std::size_t hash = seed; + for (std::size_t i = 0; i < count; i++) { hash = ((hash << 5) + hash) ^ pVals[i]; } + return hash; +} + +/** Ensures the size of the specified container is at least the specified size. */ +template +void mvkEnsureSize(C& container, S size) { + if (size > container.size()) { container.resize(size); } +} + +/** + * Iterates through the contents of the specified object pointer container and destroys + * each object, including freeing the object memory, and clearing the container. + */ +template +void mvkDestroyContainerContents(C& container) { + for (auto elem : container) { delete elem; } + container.clear(); +} + +/** + * Iterates through the contents of the specified Objective-C object pointer + * container and releases each object, and clears the container. + */ +template +void mvkReleaseContainerContents(C& container) { + for (auto elem : container) { [elem release]; } + container.clear(); +} + +/** Removes the first occurance of the specified value from the specified container. */ +template +void mvkRemoveFirstOccurance(C& container, T val) { + for (auto iter = container.begin(), end = container.end(); iter != end; iter++) { + if( *iter == val ) { + container.erase(iter); + return; + } + } +} + +/** Removes all occurances of the specified value from the specified container. */ +template +void mvkRemoveAllOccurances(C& container, T val) { + container.erase(remove(container.begin(), container.end(), val), container.end()); +} + +/** + * Sets the value referenced by the destination pointer with the value referenced by + * the source pointer, and returns whether the value was set. + * + * If both specified pointers are non-NULL, populates the value referenced by the + * destination pointer with the value referenced by the source pointer, and returns true. + * + * If the source pointer is NULL, the value referenced by the destination pointer + * is overwritten with zeros to clear it, and returns false. + * + * If the destination pointer is NULL, does nothing, and returns false. + */ +template +bool mvkSetOrClear(T* pDest, const T* pSrc) { + if (pDest && pSrc) { + *pDest = *pSrc; + return true; + } + if (pDest) { memset(pDest, 0, sizeof(T)); } + return false; +} + +/** + * Enables the flag (set the bit to 1) within the value parameter specified by the bitMask parameter. + * + * Typically, you call this function with only a single bit of the bitMask parameter set to 1. + * However, you may also call this function with more than one bit of the bitMask parameter set + * to 1, in which case, this function will set all corresponding bits in the value parameter to 1. + */ +template +void mvkEnableFlag(T1& value, const T2 bitMask) { value |= bitMask; } + +/** + * Disables the flag (set the bit to 0) within the value parameter specified by the bitMask parameter. + * + * Typically, you call this function with only a single bit of the bitMask parameter set to 1. + * However, you may also call this function with more than one bit of the bitMask parameter set + * to 1, in which case, this function will set all corresponding bits in the value parameter to 0. + */ +template +void mvkDisableFlag(T1& value, const T2 bitMask) { value &= ~bitMask; } + +/** Returns whether the specified value has ALL of the flags specified in bitMask enabled (set to 1). */ +template +bool mvkAreFlagsEnabled(T1 value, const T2 bitMask) { return ((value & bitMask) == bitMask); } + +/** Returns whether the specified value has ANY of the flags specified in bitMask enabled (set to 1). */ +template +bool mvkIsAnyFlagEnabled(T1 value, const T2 bitMask) { return !!(value & bitMask); } + +/** Returns whether the specified value has ONLY of the flags specified in bitMask enabled (set to 1). */ +template +bool mvkAreOnlyFlagsEnabled(T1 value, const T2 bitMask) { return (value == bitMask); } + diff --git a/MoltenVK/MoltenVK/Utility/MVKFoundation.mm b/MoltenVK/MoltenVK/Utility/MVKFoundation.mm new file mode 100644 index 00000000..27229035 --- /dev/null +++ b/MoltenVK/MoltenVK/Utility/MVKFoundation.mm @@ -0,0 +1,102 @@ +/* + * MVKFoundation.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKFoundation.h" +#include "MVKLogging.h" + +#define CASE_RESULT(R) case R: return strcpy(name, #R); + +char* mvkResultName(VkResult vkResult, char* name) { + switch (vkResult) { + CASE_RESULT(VK_SUCCESS) + CASE_RESULT(VK_NOT_READY) + CASE_RESULT(VK_TIMEOUT) + CASE_RESULT(VK_EVENT_SET) + CASE_RESULT(VK_EVENT_RESET) + CASE_RESULT(VK_INCOMPLETE) + + CASE_RESULT(VK_ERROR_OUT_OF_HOST_MEMORY) + CASE_RESULT(VK_ERROR_OUT_OF_DEVICE_MEMORY) + CASE_RESULT(VK_ERROR_INITIALIZATION_FAILED) + CASE_RESULT(VK_ERROR_DEVICE_LOST) + CASE_RESULT(VK_ERROR_MEMORY_MAP_FAILED) + CASE_RESULT(VK_ERROR_LAYER_NOT_PRESENT) + CASE_RESULT(VK_ERROR_EXTENSION_NOT_PRESENT) + CASE_RESULT(VK_ERROR_FEATURE_NOT_PRESENT) + CASE_RESULT(VK_ERROR_INCOMPATIBLE_DRIVER) + CASE_RESULT(VK_ERROR_TOO_MANY_OBJECTS) + CASE_RESULT(VK_ERROR_FORMAT_NOT_SUPPORTED) + CASE_RESULT(VK_ERROR_FRAGMENTED_POOL) + + CASE_RESULT(VK_ERROR_SURFACE_LOST_KHR) + CASE_RESULT(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR) + CASE_RESULT(VK_SUBOPTIMAL_KHR) + CASE_RESULT(VK_ERROR_OUT_OF_DATE_KHR) + CASE_RESULT(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR) + + CASE_RESULT(VK_ERROR_VALIDATION_FAILED_EXT) + CASE_RESULT(VK_ERROR_INVALID_SHADER_NV) + + CASE_RESULT(VK_ERROR_OUT_OF_POOL_MEMORY_KHR) + CASE_RESULT(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR) + + default: + sprintf(name, "UNKNOWN_VkResult(%d)", vkResult); + return name; + } +} + +VkResult mvkNotifyErrorWithText(VkResult vkErr, const char* errFmt, ...) { + va_list args; + va_start(args, errFmt); + + char vkRsltName[MVKResultNameMaxLen]; + mvkResultName(vkErr, vkRsltName); + + // Prepend the error code to the format string + char fmtStr[strlen(vkRsltName) + strlen(errFmt) + 4]; + sprintf(fmtStr, "%s: %s", vkRsltName, errFmt); + + // Log the error + MVKLogImplV(true, !(MVK_DEBUG), ASL_LEVEL_ERR, "***MoltenVK ERROR***", fmtStr, args); + + va_end(args); + + return vkErr; +} + + +#pragma mark - +#pragma mark Alignment functions + +void mvkFlipVertically(void* rowMajorData, uint32_t rowCount, size_t bytesPerRow) { + if ( !rowMajorData ) return; // If no data, nothing to flip! + + uint8_t tmpRow[bytesPerRow]; + uint32_t lastRowIdx = rowCount - 1; + uint32_t halfRowCnt = rowCount / 2; + for (uintptr_t rowIdx = 0; rowIdx < halfRowCnt; rowIdx++) { + uint8_t* lowerRow = (uint8_t*)((uintptr_t)rowMajorData + (bytesPerRow * rowIdx)); + uint8_t* upperRow = (uint8_t*)((uintptr_t)rowMajorData + (bytesPerRow * (lastRowIdx - rowIdx))); + memcpy(tmpRow, upperRow, bytesPerRow); + memcpy(upperRow, lowerRow, bytesPerRow); + memcpy(lowerRow, tmpRow, bytesPerRow); + } +} + + diff --git a/MoltenVK/MoltenVK/Utility/MVKOSExtensions.h b/MoltenVK/MoltenVK/Utility/MVKOSExtensions.h new file mode 100644 index 00000000..fa9cd1f7 --- /dev/null +++ b/MoltenVK/MoltenVK/Utility/MVKOSExtensions.h @@ -0,0 +1,101 @@ +/* + * MVKOSExtensions.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/* This file contains convenient functions for accessing Metal components during execution. */ + +#pragma once + +#include "MVKCommonEnvironment.h" + +#import +#import + + +typedef float MVKOSVersion; + +/** + * 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 + * the minor and patch versions, associating 2 decimal places to each. + * - (10.12.3) => 10.1203 + * - (8.0.2) => 8.0002 + */ +MVKOSVersion mvkOSVersion(void); + + +#pragma mark - +#pragma mark MTLTextureDescriptor + +/** Extensions to MTLTextureDescriptor to support MoltenVK. */ +@interface MTLTextureDescriptor (MoltenVK) + +/** + * Replacement for the usage property. + * + * This property allows support under iOS 8. Delegates to the usage property + * if it is available. otherwise, returns MTLTextureUsageUnknown when read + * and does nothing when set. + */ +@property(nonatomic, readwrite) MTLTextureUsage usageMVK; + +/** + * Replacement for the storageMode property. + * + * This property allows support under iOS 8. Delegates to the storageMode property + * if it is available. otherwise, returns MTLStorageModeShared when read and does + * nothing when set. + */ +@property(nonatomic, readwrite) MTLStorageMode storageModeMVK; + +@end + + +#pragma mark - +#pragma mark MTLSamplerDescriptor + +/** Extensions to MTLSamplerDescriptor to support MoltenVK. */ +@interface MTLSamplerDescriptor (MoltenVK) + +/** + * Replacement for the compareFunction property. + * + * This property allows support under iOS 8. Delegates to the compareFunction property + * if it is available. otherwise, returns MTLTextureUsageUnknown when read and does + * nothing when set. + */ +@property(nonatomic, readwrite) MTLCompareFunction compareFunctionMVK; + +@end + + +#pragma mark - +#pragma mark CAMetalLayer + +/** Extensions to CAMetalLayer to support MoltenVK. */ +@interface CAMetalLayer (MoltenVK) + +/** + * Ensures the drawableSize property of this layer is up to date, by combining the size + * of the bounds property and the contentScale property, and returns the updated value. + */ +-(CGSize) updatedDrawableSizeMVK; + +@end + + diff --git a/MoltenVK/MoltenVK/Utility/MVKOSExtensions.mm b/MoltenVK/MoltenVK/Utility/MVKOSExtensions.mm new file mode 100644 index 00000000..e9004234 --- /dev/null +++ b/MoltenVK/MoltenVK/Utility/MVKOSExtensions.mm @@ -0,0 +1,101 @@ +/* + * MVKOSExtensions.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "MVKOSExtensions.h" + + +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; +} + + +#pragma mark - +#pragma mark MTLTextureDescriptor + +@implementation MTLTextureDescriptor (MoltenVK) + +-(MTLTextureUsage) usageMVK { + if ( [self respondsToSelector: @selector(usage)]) { return self.usage; } + return MTLTextureUsageUnknown; +} + +-(void) setUsageMVK: (MTLTextureUsage) usage { + if ( [self respondsToSelector: @selector(setUsage:)]) { self.usage = usage; } +} + +-(MTLStorageMode) storageModeMVK { + if ( [self respondsToSelector: @selector(storageMode)]) { return self.storageMode; } + return MTLStorageModeShared; +} + +-(void) setStorageModeMVK: (MTLStorageMode) storageMode { + if ( [self respondsToSelector: @selector(setStorageMode:)]) { self.storageMode = storageMode; } +} + +@end + + +#pragma mark - +#pragma mark MTLSamplerDescriptor + +@implementation MTLSamplerDescriptor (MoltenVK) + +-(MTLCompareFunction) compareFunctionMVK { + if ( [self respondsToSelector: @selector(compareFunction)]) { return self.compareFunction; } + return MTLCompareFunctionNever; +} + +-(void) setCompareFunctionMVK: (MTLCompareFunction) cmpFunc { + if ( [self respondsToSelector: @selector(setCompareFunction:)]) { self.compareFunction = cmpFunc; } +} + +@end + + +#pragma mark - +#pragma mark CAMetalLayer + +@implementation CAMetalLayer (MoltenVK) + +-(CGSize) updatedDrawableSizeMVK { + CGSize drawSize = self.bounds.size; + CGFloat scaleFactor = self.contentsScale; + drawSize.width = trunc(drawSize.width * scaleFactor); + drawSize.height = trunc(drawSize.height * scaleFactor); + + // Only update property value if it needs to be, in case + // updating to same value causes internal reconfigurations. + if ( !CGSizeEqualToSize(drawSize, self.drawableSize) ) { + self.drawableSize = drawSize; + } + + return drawSize; +} + +@end + diff --git a/MoltenVK/MoltenVK/Utility/MVKObjectPool.h b/MoltenVK/MoltenVK/Utility/MVKObjectPool.h new file mode 100644 index 00000000..c787504b --- /dev/null +++ b/MoltenVK/MoltenVK/Utility/MVKObjectPool.h @@ -0,0 +1,140 @@ +/* + * MVKObjectPool.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#pragma once + +#include "MVKBaseObject.h" +#include + + +#pragma mark - +#pragma mark MVKObjectPool + +/** + * Manages a pool of instances of a particular object type. + * + * The objects managed by this pool must have public member variable named "_next", + * of the same object type, which is used by this pool to create a linked list of objects. + * + * When this pool is destroyed, any objects contained in the pool are also destroyed. + * + * This pool includes member functions for managing resources in either a thread-safe, + * or somewhat faster, but not-thread-safe manner. + * + * An instance of this pool can be configured to either manage a pool of objects, + * or simply allocate a new object instance on each request and delete the object + * when it is released back to the pool. + */ +template +class MVKObjectPool : public MVKBaseObject { + +public: + + /** + * Acquires and returns the next available object from the pool, creating it if necessary. + * + * If this instance was configured to use pooling, the object is removed from the pool + * until it is returned back to the pool. If this instance was configured NOT to use + * pooling, the object is created anew on each request, and will be deleted when + * returned back to the pool. + * + * This method is not thread-safe. For a particular pool instance, all calls to + * aquireObject() and returnObject() must be made from the same thread. + */ + T* acquireObject() { + T* obj = VK_NULL_HANDLE; + if (_isPooling) { obj = nextObject(); } + if ( !obj ) { obj = newObject(); } + + return obj; + } + + /** + * Returns the specified object back to the pool. + * + * If this instance was configured to use pooling, the returned object is added back + * into the pool. If this instance was configured NOT to use pooling, the returned + * object is simply deleted. + * + * This method is not thread-safe. For a particular pool instance, all calls to + * aquireObject() and returnObject() must be made from the same thread. + */ + void returnObject(T* obj) { + if (_isPooling) { + if (_tail) { _tail->_next = obj; } + obj->_next = VK_NULL_HANDLE; + _tail = obj; + if ( !_head ) { _head = obj; } + } else { + delete obj; + } + } + + /** A thread-safe version of the acquireObject() function. */ + T* acquireObjectSafely() { + std::lock_guard lock(_lock); + return acquireObject(); + } + + /** A thread-safe version of the returnObject() function. */ + void returnObjectSafely(T* obj) { + std::lock_guard lock(_lock); + returnObject(obj); + } + + /** Clears all the objects from this pool, deleting each one. This method is thread-safe. */ + void clear() { + std::lock_guard lock(_lock); + while ( T* obj = nextObject() ) { delete obj; } + } + + /** + * Configures this instance to either use pooling, or not, depending on the + * value of isPooling, which defaults to true if not indicated explicitly. + */ + MVKObjectPool(bool isPooling = true) : _isPooling(isPooling) {} + + ~MVKObjectPool() override { clear(); } + +protected: + + /** + * Removes and returns the first object in this pool, or returns null if this pool + * contains no objects. This differs from the acquireObject() function, which creates + * and return a new instance if this pool is empty. This method is not thread-safe. + */ + T* nextObject() { + T* obj = _head; + if (obj) { + _head = (T*)obj->_next; // Will be null for last object in pool + if ( !_head ) { _tail = VK_NULL_HANDLE; } // If last, also clear tail + obj->_next = VK_NULL_HANDLE; // Objects in the wild should never think they are still part of this pool + } + return obj; + } + + /** Returns a new instance of the type of object managed by this pool. */ + virtual T* newObject() = 0; + + std::mutex _lock; + T* _head = nullptr; + T* _tail = nullptr; + bool _isPooling; +}; + diff --git a/MoltenVK/MoltenVK/Utility/MVKWatermark.h b/MoltenVK/MoltenVK/Utility/MVKWatermark.h new file mode 100644 index 00000000..22b13570 --- /dev/null +++ b/MoltenVK/MoltenVK/Utility/MVKWatermark.h @@ -0,0 +1,171 @@ +/* + * MVKWatermark.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + + +#include + +#import + + +typedef struct MVKWatermarkPosition { + float x; + float y; + MVKWatermarkPosition(float xVal, float yVal) : x(xVal), y(yVal) {} +} MVKWatermarkPosition; + +typedef struct MVKWatermarkSize { + float width; + float height; + MVKWatermarkSize(float w, float h) : width(w), height(h) {} +} MVKWatermarkSize; + +typedef struct MVKWatermarkColor { + float r; + float g; + float b; + float a; + MVKWatermarkColor(float red, float green, float blue, float alpha) : r(red), g(green), b(blue), a(alpha) {} +} MVKWatermarkColor; + + +#pragma mark - +#pragma mark MVKWatermark + +/** + * A 2D watermark for display as an overlay on the rendered scene. + * + * This class uses Metal directly. + */ +class MVKWatermark { + +public: + + /** Sets the clip-space position (0.0 - 1.0) of this watermark. */ + void setPosition(MVKWatermarkPosition position); + + /** Sets the clip-space size (0.0 - 1.0) of this watermark. */ + void setSize(MVKWatermarkSize size); + + /** Sets the opacity (0.0 - 1.0) of this watermark. */ + void setOpacity(float opacity); + + /** Update the render state prior to rendering to the specified texture. */ + virtual void updateRenderState(id mtlTexture); + + /** Render to the specified Metal encoder. */ + virtual void render(id mtlEncoder, double frameInterval); + + /** + * Convenience function that calls updateRenderState() to update the render state to + * match the specified texture, creates a Metal encoder from the specified Metal + * command buffer, and calls render(encoder, interval) to render to the texture. + */ + void render(id mtlTexture, id mtlCommandBuffer, double frameInterval); + + MVKWatermark(id mtlDevice, + unsigned char* textureContent, + uint32_t textureWidth, + uint32_t textureHeight, + MTLPixelFormat textureFormat, + NSUInteger textureBytesPerRow, + const char* mtlShaderSource); + + virtual ~MVKWatermark(); + +protected: + void initTexture(unsigned char* textureContent, + uint32_t textureWidth, + uint32_t textureHeight, + MTLPixelFormat textureFormat, + NSUInteger textureBytesPerRow); + void initShaders(const char* mslSourceCode); + void initBuffers(); + void updateUniforms(); + void markUniformsDirty(); + void markRenderPipelineStateDirty(); + id mtlRenderPipelineState(); + id newRenderPipelineState(); + MTLRenderPassDescriptor* getMTLRenderPassDescriptor(); + + NSString* _mtlName; + NSString* _mtlRendEncName; + MVKWatermarkPosition _position; + MVKWatermarkSize _size; + MVKWatermarkColor _color; + id _mtlDevice; + id _mtlTexture; + id _mtlSamplerState; + id _mtlFunctionVertex; + id _mtlFunctionFragment; + id _mtlRenderPipelineState; + id _mtlVertexContentBuffer; + id _mtlVertexIndexBuffer; + id _mtlVertexUniformBuffer; + MTLRenderPassDescriptor* _mtlRenderPassDescriptor; + MTLPixelFormat _mtlColorFormat; + MTLPixelFormat _mtlDepthFormat; + MTLPixelFormat _mtlStencilFormat; + NSUInteger _sampleCount; + bool _isUniformsDirty; +}; + + +#pragma mark - +#pragma mark MVKWatermarkRandom + +typedef enum { + kMVKWatermarkPositionModeBounce, + kMVKWatermarkPositionModeTeleport, +} MVKWatermarkPositionMode; + +/** + * A 2D watermark displayed in a random location in the rendered scene, and then moves + * either by smoothly bouncing around the screen or by teleporting. The mode of movement + * is selected randomly during initialization. + */ +class MVKWatermarkRandom : public MVKWatermark { + +public: + + /** Update the render state prior to rendering to the specified texture. */ + void updateRenderState(id mtlTexture) override; + + /** Render to the specified Metal encoder. */ + void render(id mtlEncoder, double frameInterval) override; + + MVKWatermarkRandom(id mtlDevice, + unsigned char* textureContent, + uint32_t textureWidth, + uint32_t textureHeight, + MTLPixelFormat textureFormat, + NSUInteger textureBytesPerRow, + const char* mtlShaderSource); + +protected: + float _minOpacity; + float _maxOpacity; + float _opacityVelocity; + float _scale; + float _maxPosition; + MVKWatermarkPosition _positionVelocity; + MVKWatermarkPositionMode _positionMode; + NSTimeInterval _lastRenderTime; +}; + diff --git a/MoltenVK/MoltenVK/Utility/MVKWatermark.mm b/MoltenVK/MoltenVK/Utility/MVKWatermark.mm new file mode 100644 index 00000000..04f88117 --- /dev/null +++ b/MoltenVK/MoltenVK/Utility/MVKWatermark.mm @@ -0,0 +1,495 @@ +/* + * MVKWatermark.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "MVKWatermark.h" +#include "MVKOSExtensions.h" +#include "MVKLogging.h" + + +/** The structure to hold shader uniforms. */ +typedef struct { + float mvpMtx[16]; + MVKWatermarkColor color; +} MVKWatermarkUniforms; + + +#define kMVKWatermarkUniformBufferIndex 0 +#define kMVKWatermarkVertexUniformBufferLength (sizeof(MVKWatermarkUniforms)) + +#define kMVKWatermarkVertexContentBufferIndex 1 +#define kMVKWatermarkVertexContentBufferLength (sizeof(float) * 4 * 4) +#define kMVKWatermarkVertexIndexBufferLength (sizeof(uint16_t) * 6) + +#define kMVKWatermarkTextureIndex 0 + + +#pragma mark - +#pragma mark MVKWatermark + +void MVKWatermark::setPosition(MVKWatermarkPosition position) { + if ( (position.x == _position.x) && (position.y == _position.y) ) { return; } + _position = position; + markUniformsDirty(); +} + +void MVKWatermark::setSize(MVKWatermarkSize size) { + if ( (size.width == _size.width) && (size.height == _size.height) ) { return; } + _size = size; + markUniformsDirty(); +} + +void MVKWatermark::setOpacity(float opacity) { + if (opacity == _color.a) { return; } + _color.a = opacity; + markUniformsDirty(); +} + +void MVKWatermark::markUniformsDirty() { _isUniformsDirty = true; } + +void MVKWatermark::markRenderPipelineStateDirty() { + [_mtlRenderPipelineState release]; + _mtlRenderPipelineState = nil; +} + +id MVKWatermark::mtlRenderPipelineState() { + if ( !_mtlRenderPipelineState ) { _mtlRenderPipelineState = newRenderPipelineState(); } // retained + return _mtlRenderPipelineState; +} + +id MVKWatermark::newRenderPipelineState() { + MTLRenderPipelineDescriptor* plDesc = [[MTLRenderPipelineDescriptor new] autorelease]; + plDesc.label = _mtlName; + + plDesc.vertexFunction = _mtlFunctionVertex; + plDesc.fragmentFunction = _mtlFunctionFragment; + + plDesc.depthAttachmentPixelFormat = _mtlDepthFormat; + plDesc.stencilAttachmentPixelFormat = _mtlStencilFormat; + plDesc.sampleCount = _sampleCount; + plDesc.rasterizationEnabled = true; + + MTLRenderPipelineColorAttachmentDescriptor* colorDesc = plDesc.colorAttachments[0]; + colorDesc.pixelFormat = _mtlColorFormat; + colorDesc.writeMask = MTLColorWriteMaskAll; + colorDesc.blendingEnabled = true; + colorDesc.rgbBlendOperation = MTLBlendOperationAdd; + colorDesc.alphaBlendOperation = MTLBlendOperationMax; + colorDesc.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; + colorDesc.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + colorDesc.sourceAlphaBlendFactor = MTLBlendFactorOne; + colorDesc.destinationAlphaBlendFactor = MTLBlendFactorZero; + + MTLVertexDescriptor* vtxDesc = plDesc.vertexDescriptor; + + // Vertex attribute descriptors + MTLVertexAttributeDescriptorArray* vaDescArray = vtxDesc.attributes; + MTLVertexAttributeDescriptor* vaDesc; + NSUInteger vtxStride = 0; + + // Vertex location + vaDesc = vaDescArray[0]; + vaDesc.format = MTLVertexFormatFloat2; + vaDesc.bufferIndex = kMVKWatermarkVertexContentBufferIndex; + vaDesc.offset = vtxStride; + vtxStride += sizeof(float) * 2; + + // Vertex texture coords + vaDesc = vaDescArray[1]; + vaDesc.format = MTLVertexFormatFloat2; + vaDesc.bufferIndex = kMVKWatermarkVertexContentBufferIndex; + vaDesc.offset = vtxStride; + vtxStride += sizeof(float) * 2; + + // Vertex attribute buffer. + MTLVertexBufferLayoutDescriptorArray* vbDescArray = vtxDesc.layouts; + MTLVertexBufferLayoutDescriptor* vbDesc = vbDescArray[kMVKWatermarkVertexContentBufferIndex]; + vbDesc.stepFunction = MTLVertexStepFunctionPerVertex; + vbDesc.stepRate = 1; + vbDesc.stride = vtxStride; + + NSError* err = nil; + id rps = [_mtlDevice newRenderPipelineStateWithDescriptor: plDesc error: &err]; // retained + MVKAssert( !err, "Could not create watermark pipeline state %s (code %li) %s", + err.localizedDescription.UTF8String, (long)err.code, err.localizedFailureReason.UTF8String); + return rps; +} + + +#pragma mark Rendering + +void MVKWatermark::render(id mtlTexture, id mtlCommandBuffer, double frameInterval) { + + updateRenderState(mtlTexture); + + MTLRenderPassDescriptor* mtlRPDesc = getMTLRenderPassDescriptor(); + MTLRenderPassColorAttachmentDescriptor* mtlColorAttDesc = mtlRPDesc.colorAttachments[0]; + mtlColorAttDesc.texture = mtlTexture; + + id mtlRendEnc = [mtlCommandBuffer renderCommandEncoderWithDescriptor: mtlRPDesc]; + mtlRendEnc.label = _mtlRendEncName; + render(mtlRendEnc, frameInterval); + [mtlRendEnc endEncoding]; +} + +void MVKWatermark::updateRenderState(id mtlTexture) { + + MTLPixelFormat mtlColorFormat = mtlTexture.pixelFormat; + if (_mtlColorFormat != mtlColorFormat) { + _mtlColorFormat = mtlColorFormat; + markRenderPipelineStateDirty(); + } + + MTLPixelFormat mtlDepthFormat = MTLPixelFormatInvalid; + if (_mtlDepthFormat != mtlDepthFormat) { + _mtlDepthFormat = mtlDepthFormat; + markRenderPipelineStateDirty(); + } + + MTLPixelFormat mtlStencilFormat = MTLPixelFormatInvalid; + if (_mtlStencilFormat != mtlStencilFormat) { + _mtlStencilFormat = mtlStencilFormat; + markRenderPipelineStateDirty(); + } + + NSUInteger sampleCount = mtlTexture.sampleCount; + if (_sampleCount != sampleCount) { + _sampleCount = sampleCount; + markRenderPipelineStateDirty(); + } +} + +void MVKWatermark::render(id mtlEncoder, double frameInterval) { + + updateUniforms(); + + [mtlEncoder pushDebugGroup: _mtlName]; + + [mtlEncoder setRenderPipelineState: mtlRenderPipelineState()]; + [mtlEncoder setCullMode: MTLCullModeBack]; + [mtlEncoder setVertexBuffer: _mtlVertexContentBuffer offset: 0 atIndex: kMVKWatermarkVertexContentBufferIndex]; + [mtlEncoder setVertexBuffer: _mtlVertexUniformBuffer offset: 0 atIndex: kMVKWatermarkUniformBufferIndex]; + [mtlEncoder setFragmentTexture: _mtlTexture atIndex: kMVKWatermarkTextureIndex]; + [mtlEncoder setFragmentSamplerState: _mtlSamplerState atIndex: kMVKWatermarkTextureIndex]; + + [mtlEncoder drawIndexedPrimitives: MTLPrimitiveTypeTriangle + indexCount: 6 + indexType: MTLIndexTypeUInt16 + indexBuffer: _mtlVertexIndexBuffer + indexBufferOffset: 0]; + + [mtlEncoder popDebugGroup]; +} + +/** Updates the shader uniforms structure from the properties of this instance. */ +void MVKWatermark::updateUniforms() { + if ( !_isUniformsDirty ) { return; } + + MVKWatermarkUniforms* pUniforms = (MVKWatermarkUniforms*)_mtlVertexUniformBuffer.contents; + + // Populate the MVP matrix uniform + // The matrix is specified in clip-space coordinates (-1.0 < v < 1.0 for each axis). + float* mvpMtx = (float*)&(pUniforms->mvpMtx); + mvpMtx[0] = _size.width; + mvpMtx[1] = 0.0; + mvpMtx[2] = 0.0; + mvpMtx[3] = 0.0; + + mvpMtx[4] = 0.0; + mvpMtx[5] = _size.height; + mvpMtx[6] = 0.0; + mvpMtx[7] = 0.0; + + mvpMtx[8] = 0.0; + mvpMtx[9] = 0.0; + mvpMtx[10] = 1.0; + mvpMtx[11] = 0.0; + + mvpMtx[12] = _position.x; + mvpMtx[13] = _position.y; + mvpMtx[14] = 0.0; + mvpMtx[15] = 1.0; + + // Populate the opacity uniform + pUniforms->color = _color; + + _isUniformsDirty = false; +} + +// Returns a MTLRenderPassDescriptor that can be used to render this watermark. +MTLRenderPassDescriptor* MVKWatermark::getMTLRenderPassDescriptor() { + if ( !_mtlRenderPassDescriptor ) { + _mtlRenderPassDescriptor = [[MTLRenderPassDescriptor renderPassDescriptor] retain]; // retained + MTLRenderPassColorAttachmentDescriptor* mtlColorAttDesc = _mtlRenderPassDescriptor.colorAttachments[0]; + mtlColorAttDesc.loadAction = MTLLoadActionLoad; + mtlColorAttDesc.storeAction = MTLStoreActionStore; + } + return _mtlRenderPassDescriptor; +} + + +#pragma mark Instance creation + +MVKWatermark::MVKWatermark(id mtlDevice, + unsigned char* textureContent, + uint32_t textureWidth, + uint32_t textureHeight, + MTLPixelFormat textureFormat, + NSUInteger textureBytesPerRow, + const char* mslSourceCode) : _position(0, 0), _size(1, 1), _color(1, 1, 1, 0.25) { + _mtlColorFormat = MTLPixelFormatInvalid; + _mtlDepthFormat = MTLPixelFormatInvalid; + _mtlStencilFormat = MTLPixelFormatInvalid; + _sampleCount = 1; + _mtlName = [@"License Watermark" retain]; // retained + _mtlRendEncName = [@"License Watermark RenderEncoder" retain]; // retained + _isUniformsDirty = true; + + _mtlDevice = [mtlDevice retain]; // retained + initTexture(textureContent, textureWidth, textureHeight, textureFormat, textureBytesPerRow); + initShaders(mslSourceCode); + initBuffers(); + _mtlRenderPipelineState = nil; + _mtlRenderPassDescriptor = nil; +} + +// Initialize the texture to use for rendering the watermark +void MVKWatermark::initTexture(unsigned char* textureContent, + uint32_t textureWidth, + uint32_t textureHeight, + MTLPixelFormat textureFormat, + NSUInteger textureBytesPerRow) { + + MTLTextureDescriptor* texDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: textureFormat + width: textureWidth + height: textureHeight + mipmapped: NO]; + texDesc.usageMVK = MTLTextureUsageShaderRead; +#if MVK_IOS + texDesc.storageModeMVK = MTLStorageModeShared; +#endif +#if MVK_MACOS + texDesc.storageModeMVK = MTLStorageModeManaged; +#endif + + _mtlTexture = [_mtlDevice newTextureWithDescriptor: texDesc]; // retained + [_mtlTexture replaceRegion: MTLRegionMake2D(0, 0, textureWidth, textureHeight) + mipmapLevel: 0 + slice: 0 + withBytes: textureContent + bytesPerRow: textureBytesPerRow + bytesPerImage: 0]; + + MTLSamplerDescriptor* sampDesc = [[MTLSamplerDescriptor new] autorelease]; + sampDesc.minFilter = MTLSamplerMinMagFilterLinear; + _mtlSamplerState = [_mtlDevice newSamplerStateWithDescriptor: sampDesc]; // retained +} + +// Initialize the shader functions for rendering the watermark +void MVKWatermark::initShaders(const char* mslSourceCode) { + NSError* err = nil; + MTLCompileOptions* shdrOpts = [[MTLCompileOptions new] autorelease]; + id mtlLib = [[_mtlDevice newLibraryWithSource: @(mslSourceCode) + options: shdrOpts + error: &err] autorelease]; + MVKAssert( !err, "Could not compile watermark shaders %s (code %li) %s", + err.localizedDescription.UTF8String, (long)err.code, err.localizedFailureReason.UTF8String); + + _mtlFunctionVertex = [mtlLib newFunctionWithName: @"watermarkVertex"]; // retained + _mtlFunctionFragment = [mtlLib newFunctionWithName: @"watermarkFragment"]; // retained +} + +// Initialize the vertex buffers to use for rendering the watermark +void MVKWatermark::initBuffers() { + _mtlVertexUniformBuffer = [_mtlDevice newBufferWithLength: kMVKWatermarkVertexUniformBufferLength + options: MTLResourceOptionCPUCacheModeDefault]; // retained + + _mtlVertexContentBuffer = [_mtlDevice newBufferWithLength: kMVKWatermarkVertexContentBufferLength + options: MTLResourceOptionCPUCacheModeDefault]; // retained + + float* vtxContents = (float*)_mtlVertexContentBuffer.contents; + uint32_t idx = 0; + + // Bottom left + vtxContents[idx++] = -1.0; // Location X + vtxContents[idx++] = -1.0; // Location Y + vtxContents[idx++] = 0.0; // TexCoord X + vtxContents[idx++] = 1.0; // TexCoord Y + + // Bottom right + vtxContents[idx++] = 1.0; // Location X + vtxContents[idx++] = -1.0; // Location Y + vtxContents[idx++] = 1.0; // TexCoord X + vtxContents[idx++] = 1.0; // TexCoord Y + + // Top left + vtxContents[idx++] = -1.0; // Location X + vtxContents[idx++] = 1.0; // Location Y + vtxContents[idx++] = 0.0; // TexCoord X + vtxContents[idx++] = 0.0; // TexCoord Y + + // Top right + vtxContents[idx++] = 1.0; // Location X + vtxContents[idx++] = 1.0; // Location Y + vtxContents[idx++] = 1.0; // TexCoord X + vtxContents[idx++] = 0.0; // TexCoord Y + + _mtlVertexIndexBuffer = [_mtlDevice newBufferWithLength: kMVKWatermarkVertexIndexBufferLength + options: MTLResourceOptionCPUCacheModeDefault]; // retained + uint16_t* vtxIndices = (uint16_t*)_mtlVertexIndexBuffer.contents; + idx = 0; + vtxIndices[idx++] = 0; // First face + vtxIndices[idx++] = 2; + vtxIndices[idx++] = 3; + + vtxIndices[idx++] = 3; // Second face + vtxIndices[idx++] = 1; + vtxIndices[idx++] = 0; +} + +MVKWatermark::~MVKWatermark() { + [_mtlName release]; + [_mtlRendEncName release]; + [_mtlDevice release]; + [_mtlTexture release]; + [_mtlSamplerState release]; + [_mtlFunctionVertex release]; + [_mtlFunctionFragment release]; + [_mtlRenderPipelineState release]; + [_mtlVertexContentBuffer release]; + [_mtlVertexIndexBuffer release]; + [_mtlVertexUniformBuffer release]; + [_mtlRenderPassDescriptor release]; +} + + +#pragma mark - +#pragma mark MVKWatermarkRandom + +static inline uint32_t randomUInt() { return arc4random(); } +static inline uint32_t randomUIntBelow(uint32_t max) { return randomUInt() % max; } +static inline float randomFloat() { return (float)randomUInt() / (float)(1LL << 32); } +static inline double randomFloatBetween(float min, float max) { return min + (randomFloat() * (max - min)); } + + +void MVKWatermarkRandom::updateRenderState(id mtlTexture) { + + MVKWatermark::updateRenderState(mtlTexture); + + // Calculate the size of the watermark as a portion of the size of the framebuffer. + // The watermark is displayed in clip-space coordinates, with the coordinate origin + // at the center of the framebuffer, and positive coordinates to the right and up + // and negative coordinates to the left and down. + float sideLen = _scale; + double renderAspect = (double)mtlTexture.width / (double)mtlTexture.height; + sideLen = MIN(sideLen, sideLen * renderAspect); + setSize(MVKWatermarkSize(sideLen / renderAspect, sideLen)); +} + +void MVKWatermarkRandom::render(id mtlEncoder, double frameInterval) { + + // Determine the opacity + float opacity = _color.a + (_opacityVelocity * frameInterval); + BOOL isFadedOut = (opacity < _minOpacity); + if (opacity < _minOpacity) { + opacity = _minOpacity; + _opacityVelocity = ABS(_opacityVelocity); + } + if (opacity > _maxOpacity) { + opacity = _maxOpacity; + _opacityVelocity = -ABS(_opacityVelocity); + } + setOpacity(opacity); + + // Determine the position in clip-space coordinates. + MVKWatermarkPosition newPos = _position; + switch (_positionMode) { + case kMVKWatermarkPositionModeTeleport: { + if (isFadedOut) { + // Move to a new position somewhere on the screen before fading back in + newPos.x = randomFloatBetween(-_maxPosition, _maxPosition); + newPos.y = randomFloatBetween(-_maxPosition, _maxPosition); + } + break; + } + case kMVKWatermarkPositionModeBounce: { + // Bounce around the screen, always staying with the screen bounds + newPos.x = _position.x + (_positionVelocity.x * frameInterval); + newPos.y = _position.y + (_positionVelocity.y * frameInterval); + + if (newPos.x < -_maxPosition) { + newPos.x = -_maxPosition; + _positionVelocity.x = ABS(_positionVelocity.x); + } + if (newPos.x > _maxPosition) { + newPos.x = _maxPosition; + _positionVelocity.x = -ABS(_positionVelocity.x); + } + if (newPos.y < -_maxPosition) { + newPos.y = -_maxPosition; + _positionVelocity.y = ABS(_positionVelocity.y); + } + if (newPos.y > _maxPosition) { + newPos.y = _maxPosition; + _positionVelocity.y = -ABS(_positionVelocity.y); + } + break; + } + } + setPosition(newPos); + + MVKWatermark::render(mtlEncoder, frameInterval); +} + +MVKWatermarkRandom::MVKWatermarkRandom(id mtlDevice, + unsigned char* textureContent, + uint32_t textureWidth, + uint32_t textureHeight, + MTLPixelFormat textureFormat, + NSUInteger textureBytesPerRow, + const char* mslSourceCode) : MVKWatermark(mtlDevice, + textureContent, + textureWidth, + textureHeight, + textureFormat, + textureBytesPerRow, + mslSourceCode), _positionVelocity(0, 0) { + // Randomly select a position movement mode, but favour bounce mode + _positionMode = ( (randomUIntBelow(3) == kMVKWatermarkPositionModeTeleport) + ? kMVKWatermarkPositionModeTeleport + : kMVKWatermarkPositionModeBounce); + switch (_positionMode) { + case kMVKWatermarkPositionModeBounce: + _minOpacity = 0.25; + _maxOpacity = 0.75; + break; + case kMVKWatermarkPositionModeTeleport: + _minOpacity = 0.0; + _maxOpacity = 0.75; + break; + } + _opacityVelocity = (_maxOpacity - _minOpacity) / 2.5; + setOpacity(_minOpacity); + + _scale = 0.2; + _maxPosition = 1.0 - _scale; + _positionVelocity = MVKWatermarkPosition(_maxPosition / 3.0, _maxPosition / 4.0); + setPosition(MVKWatermarkPosition(randomFloatBetween(-_maxPosition, _maxPosition), + randomFloatBetween(-_maxPosition, _maxPosition))); +} + diff --git a/MoltenVK/MoltenVK/Utility/MVKWatermarkShaderSource.h b/MoltenVK/MoltenVK/Utility/MVKWatermarkShaderSource.h new file mode 100644 index 00000000..8b11d8c3 --- /dev/null +++ b/MoltenVK/MoltenVK/Utility/MVKWatermarkShaderSource.h @@ -0,0 +1,64 @@ +/* + * MVKWatermarkShaderSource.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + + +/** This file contains static source code for the Watermark shaders. */ + +static const char* __watermarkShaderSource = " \n\ +#include \n\ +using namespace metal; \n\ + \n\ +typedef struct { \n\ + float4x4 mvpMtx; \n\ + float4 color; \n\ +} Uniforms; \n\ + \n\ +typedef struct { \n\ + float2 a_position [[attribute(0)]]; \n\ + float2 a_texCoord [[attribute(1)]]; \n\ +} Attributes; \n\ + \n\ +typedef struct { \n\ + float4 gl_Position [[position]]; \n\ + float2 v_texCoord; \n\ + float4 v_fragColor; \n\ +} Varyings; \n\ + \n\ +vertex Varyings watermarkVertex(Attributes attributes [[stage_in]], \n\ + constant Uniforms& uniforms [[ buffer(0) ]]) { \n\ + Varyings varyings; \n\ + varyings.gl_Position = uniforms.mvpMtx * float4(attributes.a_position, 0.0, 1.0); \n\ + varyings.v_fragColor = uniforms.color; \n\ + varyings.v_texCoord = attributes.a_texCoord; \n\ + return varyings; \n\ +} \n\ + \n\ +fragment float4 watermarkFragment(Varyings varyings [[stage_in]], \n\ + texture2d texture [[ texture(0) ]], \n\ + sampler sampler [[ sampler(0) ]]) { \n\ + return varyings.v_fragColor * texture.sample(sampler, varyings.v_texCoord); \n\ +}; \n\ +"; + + + + + + diff --git a/MoltenVK/MoltenVK/Utility/MVKWatermarkTextureContent.h b/MoltenVK/MoltenVK/Utility/MVKWatermarkTextureContent.h new file mode 100644 index 00000000..d12f14b7 --- /dev/null +++ b/MoltenVK/MoltenVK/Utility/MVKWatermarkTextureContent.h @@ -0,0 +1,2097 @@ +/* + * MVKWatermarkTextureContent.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + + +#import + + +/** This file contains static content for the Watermark texture. */ + +static MTLPixelFormat __watermarkTextureFormat = MTLPixelFormatRGBA8Unorm; + +// Checkerboard texture pattern for testing +//static uint32_t __watermarkTextureWidth = 4; +//static uint32_t __watermarkTextureHeight = 4; +//static unsigned char __watermarkTextureContent[] = { +// 255, 0, 0, 255, 0, 255, 255, 255, 255, 0, 0, 255, 0, 255, 255, 255, +// 0, 255, 255, 255, 255, 0, 0, 255, 0, 255, 255, 255, 255, 0, 0, 255, +// 255, 0, 0, 255, 0, 255, 255, 255, 255, 0, 0, 255, 0, 255, 255, 255, +// 0, 255, 255, 255, 255, 0, 0, 255, 0, 255, 255, 255, 255, 0, 0, 255, +//}; + + +/** MoltenVK logo texture created using MGLLogByteContent() and pasted with preserve-formatting. */ + +static uint32_t __watermarkTextureWidth = 128; +static uint32_t __watermarkTextureHeight = 128; + +static unsigned char __watermarkTextureContent[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 4, 0, 0, 0, 0, 0, 0, 0, 0, + 12, 12, 12, 53, 30, 30, 31, 132, 45, 45, 47, 195, 55, 55, 57, 238, 59, 59, 62, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, + 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, + 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, + 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, + 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, + 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, + 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, + 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, + 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, + 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, + 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, + 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, + 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, + 59, 59, 61, 255, 59, 59, 61, 255, 59, 59, 61, 255, 60, 60, 62, 255, 56, 56, 58, 242, 47, 47, 48, 201, 33, 33, 34, 142, 15, 15, 15, 64, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 13, 13, 13, 61, 42, 42, 42, 189, + 58, 58, 59, 255, 61, 61, 62, 255, 59, 59, 61, 255, 57, 57, 59, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, + 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, + 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, + 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, + 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, + 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, + 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, + 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, + 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, + 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, + 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, + 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, + 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, + 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 56, 56, 58, 255, 57, 57, 58, 255, 59, 59, 60, 255, 61, 61, 62, 255, 59, 59, 60, 255, + 46, 46, 47, 207, 18, 18, 18, 82, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, 31, 145, 55, 55, 57, 255, 56, 56, 58, 255, + 52, 52, 54, 253, 52, 52, 53, 250, 52, 52, 54, 252, 52, 52, 54, 254, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, + 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, + 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, + 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, + 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, + 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, + 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, + 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, + 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, + 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, + 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, + + 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, + 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, + 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 53, 53, 55, 255, 52, 52, 54, 254, 52, 52, 54, 252, 52, 52, 53, 251, 52, 52, 54, 252, + 55, 55, 57, 255, 56, 56, 58, 255, 35, 35, 37, 172, 2, 2, 2, 11, 0, 0, 0, 0, 1, 1, 1, 4, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 1, 1, 7, 37, 37, 38, 191, 54, 54, 56, 255, 49, 49, 51, 252, 49, 49, 50, 251, + 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, + 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, + 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, + 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, + 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, + 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, + 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, + 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, + 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, + 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, + 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, + 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, + 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, + 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, 50, 50, 51, 255, + 49, 49, 51, 252, 49, 49, 50, 251, 54, 54, 55, 255, 42, 42, 44, 217, 4, 4, 5, 23, 0, 0, 0, 0, 1, 1, 1, 3, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 35, 35, 36, 191, 51, 51, 52, 255, 46, 46, 47, 248, 47, 47, 48, 255, 47, 47, 48, 255, + 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, + 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, + 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, + 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, + 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, + 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, + 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, + 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, + 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, + 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, + 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, + 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, + 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, + 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, 47, 47, 48, 255, + 47, 47, 48, 255, 47, 47, 48, 255, 46, 46, 47, 249, 50, 50, 51, 255, 41, 41, 41, 220, 2, 2, 2, 14, 0, 0, 0, 0, 0, 0, 0, 2, + 1, 1, 1, 4, 0, 0, 0, 0, 26, 26, 26, 145, 49, 49, 50, 255, 44, 44, 45, 248, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, + 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, + 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, + 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, + 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, + 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, + 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, + 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, + 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, + 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, + 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, + 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, + 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, + 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, + 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, + 45, 45, 46, 255, 45, 45, 46, 255, 45, 45, 46, 255, 44, 44, 45, 249, 48, 48, 49, 255, 32, 32, 33, 181, 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 10, 10, 10, 61, 43, 43, 45, 255, 41, 41, 43, 253, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, + 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, + 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, + 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, + 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, + 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, + 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, + 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, + 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, + 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, + 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, + 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, + 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, + 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, + 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, + 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 42, 42, 43, 255, 41, 41, 42, 250, 45, 45, 46, 255, 16, 16, 16, 96, 0, 0, 0, 0, + 0, 0, 0, 0, 29, 29, 30, 189, 41, 41, 42, 255, 39, 39, 40, 251, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, + 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, + 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, + 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, + 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, + 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, + 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, + 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, + 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, + 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, + 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, + 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, + 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, + 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, + 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, + 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 255, 39, 39, 40, 253, 41, 41, 42, 255, 34, 34, 35, 222, 1, 1, 1, 5, + 8, 8, 8, 53, 38, 38, 39, 255, 37, 37, 38, 253, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, + 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, + 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, + 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, + 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, + 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, + 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, + 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, + 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, + 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, + 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, + 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, + 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, + 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, + 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, + 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 37, 37, 38, 255, 36, 36, 37, 251, 39, 39, 40, 255, 13, 13, 13, 86, + 18, 18, 18, 132, 37, 37, 38, 255, 34, 34, 35, 250, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, + 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, + 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, + 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, + 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, + 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, + 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, + 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, + 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, + 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, + 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, + 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, + 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, + 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, + 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, + 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 35, 35, 36, 255, 34, 34, 35, 251, 37, 37, 38, 255, 23, 23, 24, 170, + 25, 25, 25, 195, 34, 34, 35, 255, 32, 32, 33, 252, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, + 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, + 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, + 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, + 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, + 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, + 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, + 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, + 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, + 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, + 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, + 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, + 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, + 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, + 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, + 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 255, 32, 32, 33, 254, 33, 33, 34, 255, 30, 30, 30, 234, + 28, 28, 29, 238, 31, 31, 32, 255, 30, 30, 31, 254, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, + 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, + 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, + 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, + 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, + 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, + 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, + 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, + 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, + 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, + 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, + 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, + 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, + 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, + 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, + 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 32, 255, 30, 30, 31, 255, 31, 31, 32, 255, + 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, + 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, + 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, + 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, + 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, + 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, + 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, + 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, + 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, + 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, + 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, + 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, + 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, + 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, + 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, + 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 29, 29, 29, 255, 28, 28, 29, 254, + 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, + 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, + 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, + 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, + 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, + 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, + 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, + 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, + 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, + 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, + 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, + 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, + 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, + 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, + 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, + 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, 27, 27, 27, 255, + 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, + 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, + 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, + 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, + 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, + 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, + 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, + 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, + 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, + 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, + 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, + 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, + 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, + 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, + 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, + 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, 26, 26, 26, 255, + 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, + 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, + 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, + 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, + 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, + 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, + 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, + 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, + 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, + 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, + 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, + 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, + 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, + 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, + 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, + 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, 24, 24, 24, 255, + 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, + 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, + 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, + 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, + 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, + 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, + 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, + 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, + 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, + 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, + 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, + 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, + 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, + 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, + 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, + 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, 22, 22, 23, 255, + 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, + 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, + 21, 21, 22, 255, 21, 21, 22, 255, 20, 20, 22, 255, 21, 21, 22, 255, 21, 21, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, + 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, + 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 21, 21, 22, 255, 21, 21, 22, 255, + 21, 21, 22, 255, 20, 20, 22, 255, 21, 21, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, + 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, + 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, + 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, + 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, + 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, + 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, + 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, + 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, + 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, + 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, 20, 20, 22, 255, + 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, + 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, + 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, + 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, + 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, + 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, + 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, + 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, + 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, + 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, + 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, + 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, + 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, + 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, + 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, + 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, 19, 19, 19, 255, + 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, + 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 17, 17, 17, 255, 23, 23, 23, 255, + 25, 25, 25, 255, 22, 22, 22, 255, 31, 32, 32, 255, 28, 28, 29, 255, 30, 30, 30, 255, 22, 22, 22, 255, 17, 17, 17, 255, 18, 18, 18, 255, + 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, + 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 17, 17, 17, 255, 20, 20, 20, 255, 29, 29, 29, 255, 28, 28, 28, 255, + 30, 30, 31, 255, 30, 31, 31, 255, 31, 31, 32, 255, 23, 23, 23, 255, 17, 17, 17, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, + 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, + 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, + 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, + 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, + 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, + 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, + 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, + 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, + 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, + 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, 18, 18, 18, 255, + 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, + 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 16, 16, 16, 255, 21, 21, 21, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 5, 5, 3, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 1, 1, 1, 255, 20, 20, 20, 255, 16, 16, 16, 255, + 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, + 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 16, 16, 16, 255, 18, 18, 18, 255, 12, 12, 12, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 21, 21, 21, 255, 16, 16, 16, 255, 17, 17, 17, 255, 17, 17, 17, 255, + 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, + 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, + 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, + 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, + 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, + 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, + 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, + 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, + 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, + 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 17, 255, + 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, + 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 26, 26, 26, 255, 0, 0, 0, 255, 94, 94, 95, 255, +163, 164, 165, 255, 134, 134, 136, 255, 156, 157, 159, 255, 165, 166, 167, 255, 169, 169, 171, 255, 59, 59, 58, 255, 0, 0, 0, 255, 21, 21, 21, 255, + 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, + 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 17, 17, 17, 255, 14, 14, 15, 255, 20, 20, 19, 255, 159, 160, 161, 255, 167, 168, 170, 255, +155, 156, 158, 255, 152, 153, 155, 255, 159, 160, 162, 255, 94, 94, 95, 255, 0, 0, 0, 255, 26, 26, 26, 255, 16, 16, 16, 255, 16, 16, 16, 255, + 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, + 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, + 16, 16, 16, 255, 16, 16, 16, 255, 17, 17, 17, 255, 17, 17, 17, 255, 17, 17, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, + 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, + 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, + 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, + 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, + 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, + 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, + 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, 16, 16, 16, 255, + 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, + 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 14, 14, 15, 255, 29, 29, 30, 255, 0, 0, 0, 255, 118, 119, 119, 255, +154, 154, 156, 255, 123, 124, 126, 255, 188, 189, 191, 255, 198, 199, 201, 255, 208, 209, 211, 255, 136, 137, 137, 255, 0, 0, 0, 255, 33, 33, 33, 255, + 14, 14, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, + 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 29, 29, 29, 255, 0, 0, 0, 255, 113, 113, 113, 255, 202, 203, 205, 255, 191, 192, 194, 255, +205, 206, 208, 255, 201, 202, 204, 255, 211, 212, 214, 255, 127, 128, 128, 255, 0, 0, 0, 255, 32, 32, 32, 255, 14, 14, 15, 255, 15, 15, 15, 255, + 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, + 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, + 14, 14, 14, 255, 18, 18, 18, 255, 21, 21, 22, 255, 5, 5, 6, 255, 13, 13, 13, 255, 15, 15, 16, 255, 14, 14, 15, 255, 15, 15, 15, 255, + 15, 15, 15, 255, 14, 14, 15, 255, 14, 14, 15, 255, 14, 14, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, + 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, + 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, + 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, + 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, + 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, + 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, 15, 15, 15, 255, + 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, + 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 28, 28, 29, 255, 0, 0, 0, 255, 107, 107, 108, 255, +181, 182, 183, 255, 207, 208, 210, 255, 195, 196, 198, 255, 175, 176, 178, 255, 208, 209, 211, 255, 188, 188, 189, 255, 0, 0, 0, 255, 28, 28, 29, 255, + 12, 12, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, + 13, 13, 14, 255, 13, 13, 14, 255, 12, 12, 13, 255, 34, 34, 34, 255, 0, 0, 0, 255, 173, 173, 174, 255, 207, 208, 209, 255, 190, 190, 192, 255, +213, 214, 216, 255, 206, 207, 209, 255, 215, 216, 218, 255, 127, 127, 128, 255, 0, 0, 0, 255, 31, 31, 32, 255, 13, 13, 14, 255, 13, 13, 14, 255, + 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, + 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 12, 12, 13, 255, + 16, 16, 17, 255, 0, 0, 0, 255, 0, 0, 0, 255, 32, 32, 32, 255, 22, 22, 22, 255, 9, 9, 10, 255, 14, 14, 15, 255, 13, 13, 14, 255, + 11, 11, 12, 255, 26, 26, 27, 255, 33, 33, 34, 255, 33, 33, 34, 255, 21, 21, 22, 255, 11, 11, 12, 255, 13, 13, 14, 255, 13, 13, 14, 255, + 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, + 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, + 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, + 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, + 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, + 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, 13, 13, 14, 255, + 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, + 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 28, 28, 29, 255, 0, 0, 0, 255, 111, 111, 112, 255, +217, 218, 219, 255, 208, 209, 211, 255, 185, 186, 188, 255, 198, 200, 202, 255, 211, 212, 214, 255, 213, 213, 215, 255, 91, 91, 91, 255, 0, 0, 0, 255, + 24, 24, 25, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, + 12, 12, 13, 255, 12, 12, 13, 255, 18, 18, 19, 255, 0, 0, 0, 255, 51, 51, 50, 255, 210, 211, 212, 255, 188, 189, 191, 255, 108, 108, 110, 255, +188, 189, 191, 255, 204, 205, 207, 255, 216, 217, 219, 255, 129, 129, 130, 255, 0, 0, 0, 255, 31, 31, 32, 255, 12, 12, 13, 255, 12, 12, 13, 255, + 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, + 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 28, 28, 28, 255, + 0, 0, 0, 255, 117, 118, 119, 255, 198, 198, 200, 255, 163, 163, 165, 255, 122, 122, 124, 255, 0, 0, 0, 255, 23, 23, 23, 255, 10, 10, 11, 255, + 19, 19, 19, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 19, 20, 20, 255, 10, 10, 11, 255, 12, 12, 13, 255, + 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, + 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, + 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, + 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, + 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, + 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, 12, 12, 13, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 27, 27, 28, 255, 0, 0, 0, 255, 110, 111, 111, 255, +215, 216, 218, 255, 202, 203, 205, 255, 207, 207, 209, 255, 216, 217, 219, 255, 207, 208, 210, 255, 219, 220, 221, 255, 158, 158, 159, 255, 0, 0, 0, 255, + 34, 34, 34, 255, 10, 10, 10, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 32, 32, 32, 255, 0, 0, 0, 255, 134, 134, 135, 255, 224, 225, 227, 255, 172, 173, 175, 255, 182, 183, 185, 255, +200, 201, 203, 255, 175, 176, 177, 255, 197, 198, 200, 255, 134, 135, 135, 255, 0, 0, 0, 255, 31, 31, 31, 255, 11, 11, 11, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 29, 29, 29, 255, + 0, 0, 0, 255, 130, 131, 132, 255, 194, 195, 197, 255, 174, 175, 177, 255, 160, 161, 163, 255, 0, 0, 0, 255, 29, 29, 30, 255, 19, 19, 19, 255, + 0, 0, 0, 255, 109, 109, 110, 255, 141, 141, 142, 255, 145, 145, 146, 255, 81, 81, 82, 255, 0, 0, 0, 255, 20, 20, 20, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 27, 28, 28, 255, 0, 0, 0, 255, 110, 111, 111, 255, +217, 218, 220, 255, 209, 210, 212, 255, 212, 213, 215, 255, 205, 205, 206, 255, 211, 212, 214, 255, 214, 215, 217, 255, 200, 201, 202, 255, 0, 0, 0, 255, + 13, 13, 14, 255, 13, 13, 13, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 10, 10, 10, 255, 28, 28, 29, 255, 0, 0, 0, 255, 187, 188, 189, 255, 210, 211, 213, 255, 199, 200, 202, 255, 199, 199, 201, 255, +203, 204, 206, 255, 196, 196, 198, 255, 212, 213, 215, 255, 128, 128, 129, 255, 0, 0, 0, 255, 31, 31, 31, 255, 11, 11, 11, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 29, 29, 29, 255, + 0, 0, 0, 255, 128, 128, 130, 255, 191, 191, 193, 255, 161, 162, 164, 255, 156, 157, 159, 255, 0, 0, 0, 255, 28, 28, 28, 255, 29, 29, 30, 255, + 0, 0, 0, 255, 175, 176, 177, 255, 219, 219, 222, 255, 219, 220, 222, 255, 137, 137, 138, 255, 0, 0, 0, 255, 33, 33, 33, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, + 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, 11, 11, 11, 255, + 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, + 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 27, 27, 27, 255, 0, 0, 0, 255, 112, 112, 112, 255, +213, 214, 215, 255, 201, 202, 203, 255, 207, 207, 209, 255, 157, 157, 158, 255, 207, 208, 209, 255, 211, 212, 213, 255, 218, 219, 220, 255, 117, 117, 117, 255, + 0, 0, 0, 255, 28, 28, 28, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, + 9, 9, 9, 255, 22, 22, 22, 255, 0, 0, 0, 255, 91, 91, 91, 255, 212, 213, 214, 255, 178, 179, 180, 255, 200, 201, 202, 255, 149, 149, 149, 255, +190, 191, 192, 255, 193, 194, 195, 255, 207, 208, 210, 255, 129, 129, 130, 255, 0, 0, 0, 255, 30, 30, 30, 255, 9, 9, 9, 255, 9, 9, 9, 255, + 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, + 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 29, 29, 29, 255, + 0, 0, 0, 255, 127, 127, 128, 255, 216, 217, 218, 255, 212, 213, 214, 255, 156, 156, 158, 255, 0, 0, 0, 255, 28, 28, 28, 255, 27, 27, 27, 255, + 0, 0, 0, 255, 169, 170, 171, 255, 191, 191, 193, 255, 203, 204, 205, 255, 135, 135, 136, 255, 0, 0, 0, 255, 31, 31, 31, 255, 9, 9, 9, 255, + 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, + 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, + 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, + 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, + 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, + 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, + 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, + 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 26, 26, 26, 255, 0, 0, 0, 255, 112, 112, 113, 255, +206, 207, 208, 255, 177, 178, 179, 255, 221, 222, 223, 255, 130, 130, 130, 255, 179, 180, 181, 255, 220, 221, 222, 255, 221, 222, 223, 255, 176, 177, 177, 255, + 0, 0, 0, 255, 33, 33, 33, 255, 8, 8, 8, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, + 8, 8, 8, 255, 33, 33, 33, 255, 0, 0, 0, 255, 159, 160, 160, 255, 212, 212, 214, 255, 202, 203, 204, 255, 202, 202, 203, 255, 93, 93, 94, 255, +214, 215, 217, 255, 193, 194, 195, 255, 209, 210, 211, 255, 133, 133, 134, 255, 0, 0, 0, 255, 30, 30, 30, 255, 9, 9, 9, 255, 9, 9, 9, 255, + 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 21, 21, 21, 255, 32, 32, 32, 255, 33, 33, 33, 255, 33, 33, 34, 255, 33, 33, 33, 255, + 28, 28, 28, 255, 14, 14, 14, 255, 8, 8, 8, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 29, 29, 30, 255, + 0, 0, 0, 255, 131, 132, 132, 255, 218, 219, 220, 255, 221, 222, 223, 255, 167, 167, 168, 255, 0, 0, 0, 255, 35, 35, 36, 255, 35, 35, 35, 255, + 0, 0, 0, 255, 175, 176, 177, 255, 205, 206, 207, 255, 221, 222, 223, 255, 134, 134, 134, 255, 0, 0, 0, 255, 36, 36, 36, 255, 17, 17, 17, 255, + 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 8, 8, 8, 255, 18, 18, 18, 255, 30, 30, 30, 255, + 32, 32, 33, 255, 32, 32, 33, 255, 31, 31, 31, 255, 23, 23, 23, 255, 10, 10, 10, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, + 9, 9, 9, 255, 9, 9, 9, 255, 8, 8, 8, 255, 16, 16, 16, 255, 21, 21, 21, 255, 20, 20, 20, 255, 11, 11, 11, 255, 9, 9, 9, 255, + 8, 8, 8, 255, 19, 19, 19, 255, 31, 32, 32, 255, 33, 33, 33, 255, 33, 33, 33, 255, 32, 32, 32, 255, 23, 23, 23, 255, 9, 9, 9, 255, + 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, + 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, + 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, + 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 24, 24, 25, 255, 0, 0, 0, 255, 98, 98, 99, 255, +222, 223, 224, 255, 219, 220, 221, 255, 229, 230, 231, 255, 131, 132, 132, 255, 120, 121, 121, 255, 228, 229, 230, 255, 221, 222, 223, 255, 215, 216, 217, 255, + 57, 56, 56, 255, 0, 0, 0, 255, 17, 17, 17, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, + 11, 11, 11, 255, 20, 20, 20, 255, 0, 0, 0, 255, 201, 202, 203, 255, 222, 223, 224, 255, 230, 231, 232, 255, 157, 158, 158, 255, 73, 73, 74, 255, +222, 223, 225, 255, 222, 223, 224, 255, 229, 230, 231, 255, 131, 131, 132, 255, 0, 0, 0, 255, 31, 31, 31, 255, 9, 9, 9, 255, 10, 10, 10, 255, + 6, 6, 6, 255, 27, 27, 28, 255, 33, 33, 33, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 4, 4, 4, 255, 36, 36, 36, 255, 16, 16, 16, 255, 7, 7, 7, 255, 10, 10, 10, 255, 9, 9, 9, 255, 30, 30, 31, 255, + 0, 0, 0, 255, 136, 136, 137, 255, 224, 225, 227, 255, 226, 227, 228, 255, 170, 171, 172, 255, 0, 0, 0, 255, 3, 4, 4, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 180, 181, 182, 255, 228, 229, 230, 255, 222, 223, 224, 255, 122, 123, 123, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 10, 10, 10, 255, 9, 9, 9, 255, 10, 10, 10, 255, 7, 7, 7, 255, 20, 20, 20, 255, 36, 36, 36, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 29, 29, 29, 255, 29, 29, 29, 255, 6, 6, 6, 255, 10, 10, 10, 255, + 9, 9, 9, 255, 8, 8, 8, 255, 13, 13, 13, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 18, 18, 18, 255, + 35, 35, 35, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 28, 25, 24, 255, + 22, 19, 18, 255, 6, 7, 7, 255, 10, 10, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, + 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, 9, 9, 9, 255, + 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, + 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, 24, 24, 25, 255, 0, 0, 0, 255, 101, 102, 102, 255, +227, 228, 229, 255, 226, 227, 228, 255, 235, 236, 236, 255, 143, 143, 143, 255, 0, 0, 0, 255, 216, 216, 217, 255, 229, 230, 230, 255, 234, 235, 236, 255, +142, 142, 142, 255, 0, 0, 0, 255, 32, 32, 32, 255, 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, + 25, 25, 25, 255, 0, 0, 0, 255, 104, 104, 105, 255, 228, 229, 230, 255, 228, 229, 229, 255, 229, 230, 230, 255, 81, 81, 81, 255, 91, 92, 92, 255, +228, 229, 230, 255, 226, 226, 227, 255, 233, 234, 235, 255, 134, 134, 134, 255, 0, 0, 0, 255, 31, 31, 31, 255, 7, 7, 7, 255, 6, 6, 6, 255, + 35, 35, 35, 255, 0, 0, 0, 255, 0, 0, 0, 255, 79, 79, 79, 255, 132, 132, 132, 255, 148, 149, 149, 255, 156, 156, 156, 255, 141, 141, 141, 255, +114, 114, 114, 255, 15, 14, 14, 255, 0, 0, 0, 255, 3, 3, 3, 255, 27, 27, 27, 255, 2, 2, 2, 255, 8, 8, 8, 255, 31, 31, 31, 255, + 0, 0, 0, 255, 139, 139, 139, 255, 232, 232, 233, 255, 233, 234, 235, 255, 176, 176, 177, 255, 0, 0, 0, 255, 72, 72, 72, 255, 80, 80, 80, 255, + 45, 44, 44, 255, 193, 194, 194, 255, 232, 232, 233, 255, 220, 221, 221, 255, 139, 140, 140, 255, 43, 44, 44, 255, 69, 69, 70, 255, 58, 58, 58, 255, + 5, 5, 5, 255, 9, 9, 9, 255, 2, 2, 2, 255, 31, 31, 31, 255, 0, 0, 0, 255, 0, 0, 0, 255, 58, 58, 58, 255, 125, 125, 125, 255, +145, 145, 146, 255, 148, 148, 149, 255, 130, 131, 131, 255, 89, 90, 90, 255, 0, 0, 0, 255, 0, 0, 0, 255, 34, 34, 34, 255, 6, 6, 6, 255, + 8, 8, 8, 255, 11, 11, 11, 255, 0, 0, 0, 255, 54, 54, 55, 255, 76, 76, 76, 255, 72, 72, 72, 255, 38, 38, 38, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 68, 68, 67, 255, 132, 132, 132, 255, 151, 152, 152, 255, 152, 152, 153, 255, 137, 137, 137, 255, 94, 94, 94, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 25, 20, 19, 255, 3, 5, 5, 255, 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, + 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, 8, 8, 8, 255, + 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, + 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 26, 26, 26, 255, 0, 0, 0, 255, 108, 108, 109, 255, +236, 237, 237, 255, 234, 235, 235, 255, 242, 243, 244, 255, 149, 149, 150, 255, 0, 0, 0, 255, 180, 181, 181, 255, 242, 243, 243, 255, 240, 241, 242, 255, +202, 202, 203, 255, 0, 0, 0, 255, 31, 31, 31, 255, 6, 6, 6, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 6, 6, 6, 255, + 35, 35, 35, 255, 0, 0, 0, 255, 178, 179, 180, 255, 242, 243, 243, 255, 240, 241, 241, 255, 205, 205, 206, 255, 0, 0, 0, 255, 108, 109, 109, 255, +235, 236, 237, 255, 234, 235, 236, 255, 242, 242, 243, 255, 136, 136, 136, 255, 0, 0, 0, 255, 31, 31, 31, 255, 7, 7, 7, 255, 31, 31, 31, 255, + 0, 0, 0, 255, 108, 108, 108, 255, 202, 202, 202, 255, 232, 233, 233, 255, 240, 241, 241, 255, 241, 242, 242, 255, 241, 242, 242, 255, 240, 241, 242, 255, +237, 238, 238, 255, 223, 223, 224, 255, 170, 170, 170, 255, 0, 0, 0, 255, 0, 0, 0, 255, 31, 31, 31, 255, 0, 0, 0, 255, 32, 32, 32, 255, + 0, 0, 0, 255, 142, 143, 143, 255, 240, 240, 241, 255, 242, 242, 243, 255, 183, 184, 184, 255, 0, 0, 0, 255, 209, 210, 210, 255, 236, 237, 237, 255, +229, 230, 230, 255, 235, 235, 236, 255, 234, 235, 236, 255, 236, 236, 237, 255, 205, 206, 207, 255, 218, 219, 219, 255, 233, 234, 235, 255, 207, 208, 208, 255, + 0, 0, 0, 255, 8, 8, 8, 255, 35, 35, 35, 255, 0, 0, 0, 255, 56, 56, 56, 255, 183, 183, 183, 255, 227, 228, 228, 255, 238, 239, 240, 255, +240, 241, 242, 255, 240, 241, 241, 255, 238, 239, 240, 255, 232, 233, 234, 255, 206, 207, 207, 255, 116, 116, 116, 255, 0, 0, 0, 255, 31, 31, 31, 255, + 5, 5, 5, 255, 33, 33, 33, 255, 0, 0, 0, 255, 179, 179, 180, 255, 239, 240, 241, 255, 233, 233, 234, 255, 105, 105, 105, 255, 0, 0, 0, 255, +181, 181, 181, 255, 229, 230, 230, 255, 240, 241, 241, 255, 241, 241, 242, 255, 241, 242, 242, 255, 240, 241, 242, 255, 229, 228, 229, 255, 191, 181, 177, 255, + 71, 60, 56, 255, 0, 0, 0, 255, 24, 19, 18, 255, 5, 6, 6, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, + 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, + 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, + 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 27, 27, 27, 255, 0, 0, 0, 255, 111, 111, 111, 255, +243, 244, 244, 255, 243, 243, 243, 255, 249, 249, 250, 255, 150, 150, 150, 255, 0, 0, 0, 255, 116, 116, 116, 255, 244, 244, 245, 255, 244, 244, 245, 255, +240, 240, 241, 255, 88, 88, 88, 255, 0, 0, 0, 255, 22, 22, 22, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 15, 15, 15, 255, + 0, 0, 0, 255, 41, 41, 41, 255, 230, 230, 230, 255, 245, 245, 245, 255, 250, 250, 250, 255, 151, 151, 151, 255, 0, 0, 0, 255, 114, 114, 114, 255, +243, 243, 244, 255, 242, 242, 243, 255, 249, 249, 249, 255, 139, 139, 139, 255, 0, 0, 0, 255, 31, 31, 31, 255, 32, 32, 32, 255, 0, 0, 0, 255, +160, 160, 160, 255, 246, 246, 246, 255, 249, 249, 249, 255, 246, 246, 247, 255, 250, 250, 250, 255, 248, 248, 249, 255, 248, 248, 248, 255, 249, 249, 250, 255, +249, 249, 250, 255, 245, 246, 246, 255, 251, 252, 252, 255, 221, 221, 221, 255, 82, 82, 82, 255, 0, 0, 0, 255, 29, 29, 29, 255, 32, 32, 32, 255, + 0, 0, 0, 255, 147, 147, 147, 255, 248, 248, 248, 255, 250, 250, 250, 255, 189, 189, 190, 255, 0, 0, 0, 255, 226, 226, 226, 255, 255, 255, 255, 255, +250, 251, 251, 255, 247, 247, 247, 255, 240, 240, 241, 255, 236, 236, 237, 255, 224, 224, 224, 255, 247, 247, 247, 255, 255, 255, 255, 255, 229, 229, 229, 255, + 0, 0, 0, 255, 38, 38, 38, 255, 0, 0, 0, 255, 111, 111, 111, 255, 233, 233, 233, 255, 250, 250, 251, 255, 249, 249, 250, 255, 250, 250, 250, 255, +247, 247, 248, 255, 248, 248, 248, 255, 250, 250, 250, 255, 248, 249, 249, 255, 247, 247, 247, 255, 244, 244, 245, 255, 160, 159, 160, 255, 0, 0, 0, 255, + 33, 33, 33, 255, 34, 34, 34, 255, 0, 0, 0, 255, 188, 188, 189, 255, 252, 252, 253, 255, 242, 242, 243, 255, 113, 113, 113, 255, 208, 208, 208, 255, +255, 255, 255, 255, 252, 252, 253, 255, 248, 249, 249, 255, 248, 248, 248, 255, 247, 247, 248, 255, 239, 239, 239, 255, 227, 219, 216, 255, 235, 205, 195, 255, +211, 165, 151, 255, 55, 36, 31, 255, 0, 0, 0, 255, 18, 13, 12, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, + 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, + 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, + 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 26, 26, 27, 255, 0, 0, 0, 255, 111, 111, 111, 255, +248, 248, 248, 255, 228, 228, 228, 255, 240, 240, 241, 255, 154, 154, 154, 255, 0, 0, 0, 255, 0, 0, 0, 255, 218, 218, 218, 255, 249, 248, 248, 255, +252, 252, 252, 255, 169, 169, 169, 255, 0, 0, 0, 255, 36, 36, 36, 255, 6, 6, 6, 255, 7, 7, 7, 255, 7, 7, 7, 255, 32, 32, 32, 255, + 0, 0, 0, 255, 142, 142, 142, 255, 250, 250, 250, 255, 246, 246, 246, 255, 234, 234, 234, 255, 59, 59, 59, 255, 0, 0, 0, 255, 113, 113, 113, 255, +245, 245, 245, 255, 244, 244, 244, 255, 251, 251, 251, 255, 139, 139, 139, 255, 0, 0, 0, 255, 47, 47, 47, 255, 0, 0, 0, 255, 153, 153, 153, 255, +253, 253, 253, 255, 244, 244, 244, 255, 248, 248, 248, 255, 238, 238, 238, 255, 183, 183, 183, 255, 140, 140, 140, 255, 136, 136, 136, 255, 153, 153, 153, 255, +210, 210, 210, 255, 249, 249, 249, 255, 244, 244, 244, 255, 247, 247, 247, 255, 222, 222, 222, 255, 56, 56, 56, 255, 0, 0, 0, 255, 38, 38, 38, 255, + 0, 0, 0, 255, 148, 148, 148, 255, 249, 249, 249, 255, 251, 251, 251, 255, 190, 190, 190, 255, 0, 0, 0, 255, 179, 179, 179, 255, 205, 205, 205, 255, +195, 195, 195, 255, 231, 231, 230, 255, 247, 247, 247, 255, 243, 243, 243, 255, 218, 218, 218, 255, 198, 198, 198, 255, 207, 207, 207, 255, 183, 183, 183, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 94, 94, 94, 255, 241, 241, 241, 255, 249, 249, 249, 255, 249, 249, 249, 255, 226, 226, 225, 255, 165, 165, 165, 255, +133, 133, 133, 255, 135, 135, 135, 255, 165, 165, 165, 255, 226, 226, 226, 255, 244, 244, 244, 255, 238, 238, 238, 255, 252, 252, 252, 255, 137, 137, 138, 255, + 0, 0, 0, 255, 48, 48, 48, 255, 0, 0, 0, 255, 188, 188, 188, 255, 255, 255, 254, 255, 240, 240, 240, 255, 214, 214, 214, 255, 255, 255, 255, 255, +241, 241, 241, 255, 191, 191, 191, 255, 144, 144, 144, 255, 137, 136, 136, 255, 141, 141, 142, 255, 191, 185, 183, 255, 230, 200, 190, 255, 223, 172, 157, 255, +234, 166, 148, 255, 171, 114, 100, 255, 0, 0, 0, 255, 32, 20, 18, 255, 6, 6, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, + 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, 7, 7, 7, 255, + 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, + 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 25, 25, 26, 255, 0, 0, 0, 255, 112, 112, 112, 255, +246, 246, 246, 255, 242, 242, 242, 255, 244, 244, 244, 255, 145, 145, 145, 255, 0, 0, 0, 255, 0, 0, 0, 255, 164, 164, 164, 255, 252, 252, 252, 255, +249, 249, 249, 255, 222, 222, 222, 255, 0, 0, 0, 255, 20, 20, 20, 255, 6, 6, 7, 255, 4, 4, 5, 255, 3, 3, 4, 255, 32, 32, 32, 255, + 0, 0, 0, 255, 205, 205, 205, 255, 251, 251, 251, 255, 252, 252, 252, 255, 191, 191, 191, 255, 0, 0, 0, 255, 0, 0, 0, 255, 111, 111, 111, 255, +246, 245, 246, 255, 244, 244, 244, 255, 251, 251, 251, 255, 139, 139, 139, 255, 0, 0, 0, 255, 0, 0, 0, 255, 80, 80, 80, 255, 241, 241, 241, 255, +246, 246, 246, 255, 250, 250, 250, 255, 220, 220, 220, 255, 72, 72, 72, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 158, 158, 158, 255, 235, 235, 235, 255, 221, 221, 221, 255, 254, 254, 254, 255, 195, 195, 195, 255, 0, 0, 0, 255, 47, 47, 47, 255, + 0, 0, 0, 255, 147, 147, 147, 255, 250, 250, 250, 255, 252, 252, 252, 255, 188, 188, 189, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 198, 198, 198, 255, 252, 252, 252, 255, 250, 250, 250, 255, 125, 125, 125, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 22, 22, 22, 255, 0, 0, 0, 255, 218, 218, 218, 255, 251, 251, 251, 255, 250, 250, 250, 255, 201, 201, 201, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 191, 191, 191, 255, 248, 248, 248, 255, 248, 248, 248, 255, 232, 232, 232, 255, + 43, 42, 43, 255, 24, 24, 24, 255, 0, 0, 0, 255, 191, 191, 192, 255, 249, 249, 249, 255, 227, 227, 227, 255, 254, 254, 254, 255, 230, 230, 230, 255, +101, 101, 101, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 162, 124, 113, 255, 221, 158, 141, 255, +217, 140, 122, 255, 211, 127, 108, 255, 27, 8, 7, 255, 0, 2, 4, 255, 10, 6, 6, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, + 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, + 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, + 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 25, 25, 26, 255, 0, 0, 0, 255, 112, 112, 112, 255, +246, 246, 246, 255, 247, 247, 247, 255, 231, 231, 232, 255, 143, 143, 143, 255, 0, 0, 0, 255, 0, 0, 0, 255, 79, 79, 79, 255, 240, 240, 240, 255, +247, 247, 247, 255, 248, 248, 248, 255, 118, 118, 118, 255, 0, 0, 0, 255, 27, 27, 27, 255, 4, 4, 5, 255, 19, 19, 20, 255, 0, 0, 0, 255, + 82, 82, 82, 255, 241, 241, 241, 255, 247, 247, 247, 255, 249, 249, 249, 255, 121, 121, 121, 255, 0, 0, 0, 255, 0, 0, 0, 255, 111, 111, 111, 255, +246, 246, 246, 255, 245, 245, 245, 255, 252, 252, 252, 255, 139, 139, 139, 255, 0, 0, 0, 255, 0, 0, 0, 255, 186, 187, 187, 255, 253, 254, 254, 255, +247, 247, 247, 255, 235, 235, 235, 255, 54, 54, 55, 255, 0, 0, 0, 255, 44, 44, 44, 255, 30, 30, 31, 255, 30, 30, 31, 255, 34, 34, 34, 255, + 39, 39, 40, 255, 0, 0, 0, 255, 152, 152, 153, 255, 231, 232, 232, 255, 239, 239, 240, 255, 234, 234, 234, 255, 104, 105, 105, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 149, 149, 149, 255, 247, 248, 248, 255, 251, 252, 252, 255, 192, 192, 193, 255, 0, 0, 0, 255, 45, 45, 45, 255, 46, 46, 46, 255, + 0, 0, 0, 255, 202, 203, 203, 255, 252, 252, 252, 255, 249, 250, 250, 255, 139, 139, 139, 255, 0, 0, 0, 255, 45, 45, 46, 255, 43, 43, 43, 255, + 0, 0, 0, 255, 145, 145, 145, 255, 252, 252, 252, 255, 249, 249, 249, 255, 220, 220, 220, 255, 0, 0, 0, 255, 0, 0, 0, 255, 40, 40, 41, 255, + 29, 29, 30, 255, 30, 30, 30, 255, 39, 39, 39, 255, 0, 0, 0, 255, 0, 0, 0, 255, 207, 207, 207, 255, 238, 238, 238, 255, 255, 255, 255, 255, +164, 164, 164, 255, 0, 0, 0, 255, 0, 0, 0, 255, 191, 191, 191, 255, 248, 248, 248, 255, 234, 234, 234, 255, 246, 246, 246, 255, 117, 117, 118, 255, + 0, 0, 0, 255, 47, 47, 47, 255, 31, 31, 32, 255, 30, 30, 31, 255, 36, 34, 34, 255, 3, 16, 19, 255, 11, 1, 1, 255, 212, 137, 119, 255, +233, 137, 116, 255, 225, 120, 99, 255, 85, 41, 33, 255, 0, 0, 0, 255, 19, 8, 8, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, + 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, + 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, + 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 26, 26, 26, 255, 0, 0, 0, 255, 111, 111, 111, 255, +255, 255, 255, 255, 222, 222, 222, 255, 236, 237, 237, 255, 157, 158, 158, 255, 0, 0, 0, 255, 48, 48, 48, 255, 0, 0, 0, 255, 207, 207, 207, 255, +255, 255, 255, 255, 255, 255, 255, 255, 193, 193, 194, 255, 0, 0, 0, 255, 37, 37, 37, 255, 2, 2, 3, 255, 36, 36, 36, 255, 0, 0, 0, 255, +173, 173, 173, 255, 255, 255, 255, 255, 255, 255, 255, 255, 226, 226, 226, 255, 0, 0, 0, 255, 33, 33, 34, 255, 0, 0, 0, 255, 113, 113, 113, 255, +251, 252, 252, 255, 251, 251, 251, 255, 255, 255, 255, 255, 141, 142, 142, 255, 0, 0, 0, 255, 50, 49, 50, 255, 237, 237, 237, 255, 253, 254, 254, 255, +255, 255, 255, 255, 162, 162, 162, 255, 0, 0, 0, 255, 45, 45, 45, 255, 0, 0, 0, 255, 5, 5, 6, 255, 4, 4, 5, 255, 3, 3, 4, 255, + 12, 12, 13, 255, 25, 25, 25, 255, 0, 0, 0, 255, 203, 203, 203, 255, 245, 245, 245, 255, 255, 255, 255, 255, 180, 180, 180, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 152, 152, 152, 255, 253, 254, 254, 255, 250, 250, 251, 255, 185, 186, 186, 255, 0, 0, 0, 255, 32, 32, 33, 255, 32, 32, 33, 255, + 0, 0, 0, 255, 205, 206, 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 141, 141, 141, 255, 0, 0, 0, 255, 31, 31, 32, 255, 32, 32, 33, 255, + 0, 0, 0, 255, 212, 213, 213, 255, 255, 255, 255, 255, 255, 255, 255, 255, 147, 147, 147, 255, 0, 0, 0, 255, 44, 45, 45, 255, 17, 17, 18, 255, + 21, 21, 22, 255, 20, 21, 21, 255, 18, 18, 19, 255, 42, 42, 42, 255, 0, 0, 0, 255, 124, 125, 125, 255, 225, 226, 226, 255, 238, 238, 239, 255, +226, 226, 226, 255, 0, 0, 0, 255, 0, 0, 0, 255, 183, 184, 184, 255, 247, 247, 247, 255, 247, 248, 248, 255, 201, 201, 201, 255, 0, 0, 0, 255, + 38, 38, 38, 255, 0, 0, 1, 255, 5, 5, 6, 255, 4, 4, 5, 255, 3, 3, 4, 255, 21, 11, 10, 255, 0, 0, 3, 255, 185, 107, 90, 255, +162, 87, 71, 255, 219, 104, 84, 255, 118, 49, 38, 255, 0, 0, 0, 255, 26, 9, 8, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, + 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, + 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, + 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 24, 24, 25, 255, 0, 0, 0, 255, 108, 108, 109, 255, +237, 238, 239, 255, 209, 209, 211, 255, 232, 233, 234, 255, 148, 149, 149, 255, 0, 0, 0, 255, 44, 44, 44, 255, 0, 0, 0, 255, 133, 134, 134, 255, +238, 238, 239, 255, 235, 235, 237, 255, 223, 223, 224, 255, 49, 49, 49, 255, 0, 0, 0, 255, 15, 15, 15, 255, 19, 19, 19, 255, 0, 0, 0, 255, +208, 209, 210, 255, 236, 237, 238, 255, 240, 241, 242, 255, 167, 167, 168, 255, 0, 0, 0, 255, 43, 43, 44, 255, 0, 0, 0, 255, 110, 110, 110, 255, +234, 235, 236, 255, 232, 232, 234, 255, 239, 240, 241, 255, 137, 137, 137, 255, 0, 0, 0, 255, 113, 114, 114, 255, 234, 235, 236, 255, 234, 235, 236, 255, +229, 230, 231, 255, 79, 78, 79, 255, 0, 0, 0, 255, 17, 17, 18, 255, 5, 5, 6, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, + 2, 2, 3, 255, 34, 34, 34, 255, 0, 0, 0, 255, 158, 158, 159, 255, 233, 234, 235, 255, 221, 222, 223, 255, 201, 202, 203, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 142, 142, 143, 255, 225, 226, 227, 255, 220, 221, 222, 255, 158, 159, 160, 255, 0, 0, 0, 255, 25, 25, 26, 255, 29, 29, 30, 255, + 0, 0, 0, 255, 193, 194, 195, 255, 238, 239, 240, 255, 239, 239, 240, 255, 137, 137, 137, 255, 0, 0, 0, 255, 35, 35, 35, 255, 0, 0, 0, 255, + 58, 58, 58, 255, 225, 225, 226, 255, 235, 236, 237, 255, 229, 230, 231, 255, 74, 73, 73, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 218, 218, 219, 255, 234, 234, 236, 255, +229, 230, 231, 255, 82, 82, 83, 255, 0, 0, 0, 255, 182, 182, 183, 255, 235, 236, 237, 255, 221, 221, 222, 255, 163, 163, 164, 255, 0, 0, 0, 255, + 32, 32, 33, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 0, 1, 3, 255, 29, 16, 14, 255, 136, 68, 55, 255, +159, 68, 52, 255, 209, 82, 65, 255, 127, 44, 34, 255, 0, 0, 0, 255, 28, 8, 7, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, + 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, + 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, + 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 23, 23, 24, 255, 0, 0, 0, 255, 105, 106, 106, 255, +206, 207, 209, 255, 206, 207, 209, 255, 214, 215, 217, 255, 135, 135, 136, 255, 0, 0, 0, 255, 30, 30, 31, 255, 0, 0, 0, 255, 16, 17, 18, 255, +190, 191, 193, 255, 204, 205, 208, 255, 212, 213, 215, 255, 134, 134, 135, 255, 0, 0, 0, 255, 36, 36, 37, 255, 0, 0, 0, 255, 97, 97, 99, 255, +206, 207, 209, 255, 203, 204, 206, 255, 207, 208, 210, 255, 88, 88, 88, 255, 0, 0, 0, 255, 31, 31, 32, 255, 0, 0, 0, 255, 105, 105, 106, 255, +207, 208, 210, 255, 201, 202, 204, 255, 211, 212, 214, 255, 127, 127, 128, 255, 0, 0, 0, 255, 123, 123, 125, 255, 208, 209, 211, 255, 206, 207, 209, 255, +187, 188, 189, 255, 0, 0, 0, 255, 12, 12, 13, 255, 6, 6, 7, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, + 4, 4, 5, 255, 24, 24, 25, 255, 0, 0, 0, 255, 112, 112, 114, 255, 207, 208, 210, 255, 204, 205, 207, 255, 188, 189, 191, 255, 27, 27, 28, 255, + 0, 0, 0, 255, 124, 125, 125, 255, 194, 195, 197, 255, 206, 208, 210, 255, 158, 159, 160, 255, 0, 0, 0, 255, 27, 28, 28, 255, 22, 23, 24, 255, + 0, 0, 0, 255, 176, 176, 178, 255, 207, 208, 210, 255, 211, 212, 214, 255, 130, 130, 131, 255, 0, 0, 0, 255, 37, 37, 37, 255, 0, 0, 0, 255, +101, 101, 102, 255, 193, 194, 196, 255, 194, 195, 198, 255, 201, 202, 203, 255, 192, 192, 193, 255, 188, 188, 189, 255, 185, 186, 187, 255, 179, 180, 181, 255, +177, 178, 179, 255, 177, 178, 180, 255, 177, 178, 180, 255, 177, 178, 180, 255, 177, 178, 180, 255, 176, 177, 179, 255, 199, 200, 202, 255, 203, 205, 207, 255, +211, 212, 213, 255, 124, 124, 124, 255, 0, 0, 0, 255, 163, 164, 165, 255, 193, 195, 197, 255, 188, 189, 191, 255, 136, 137, 137, 255, 0, 0, 0, 255, + 27, 27, 27, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 20, 7, 7, 255, 0, 0, 0, 255, 177, 80, 64, 255, +232, 97, 78, 255, 241, 82, 66, 255, 128, 34, 28, 255, 0, 0, 0, 255, 29, 7, 7, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, + 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, 4, 4, 5, 255, + 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, + 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, 21, 21, 22, 255, 0, 0, 0, 255, 103, 103, 103, 255, +196, 197, 199, 255, 187, 188, 191, 255, 200, 201, 203, 255, 132, 133, 133, 255, 0, 0, 0, 255, 26, 26, 26, 255, 26, 26, 27, 255, 0, 0, 0, 255, +150, 150, 152, 255, 197, 198, 200, 255, 195, 196, 198, 255, 174, 175, 176, 255, 0, 0, 0, 255, 34, 34, 35, 255, 0, 0, 0, 255, 146, 147, 148, 255, +196, 197, 200, 255, 195, 196, 198, 255, 176, 177, 178, 255, 0, 0, 0, 255, 19, 19, 20, 255, 21, 21, 21, 255, 0, 0, 0, 255, 103, 103, 103, 255, +195, 197, 199, 255, 188, 190, 192, 255, 199, 200, 202, 255, 123, 123, 124, 255, 0, 0, 0, 255, 126, 127, 128, 255, 195, 197, 199, 255, 194, 195, 198, 255, +177, 177, 179, 255, 0, 0, 0, 255, 16, 17, 17, 255, 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, + 3, 3, 3, 255, 17, 18, 18, 255, 0, 0, 0, 255, 87, 87, 88, 255, 191, 192, 194, 255, 193, 195, 197, 255, 185, 186, 188, 255, 57, 58, 58, 255, + 0, 0, 0, 255, 131, 131, 132, 255, 176, 177, 179, 255, 173, 175, 177, 255, 150, 151, 153, 255, 0, 0, 0, 255, 24, 24, 25, 255, 19, 19, 20, 255, + 0, 0, 0, 255, 168, 169, 170, 255, 193, 195, 197, 255, 200, 201, 203, 255, 127, 127, 128, 255, 0, 0, 0, 255, 38, 38, 39, 255, 0, 0, 0, 255, +119, 119, 119, 255, 197, 198, 200, 255, 190, 191, 193, 255, 193, 194, 196, 255, 198, 200, 202, 255, 199, 200, 203, 255, 199, 200, 203, 255, 199, 201, 203, 255, +199, 201, 203, 255, 199, 201, 203, 255, 199, 201, 203, 255, 199, 201, 203, 255, 199, 200, 203, 255, 199, 201, 203, 255, 195, 196, 199, 255, 191, 192, 195, 255, +204, 205, 207, 255, 131, 132, 133, 255, 0, 0, 0, 255, 156, 157, 158, 255, 196, 197, 199, 255, 196, 197, 199, 255, 120, 121, 122, 255, 0, 0, 0, 255, + 24, 24, 24, 255, 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, 23, 5, 5, 255, 0, 0, 0, 255, 193, 77, 61, 255, +233, 77, 63, 255, 242, 73, 55, 255, 130, 41, 25, 255, 0, 0, 0, 255, 29, 6, 4, 255, 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, + 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, 3, 3, 3, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 21, 21, 21, 255, 0, 0, 0, 255, 102, 103, 103, 255, +191, 192, 195, 255, 183, 185, 187, 255, 194, 195, 198, 255, 127, 127, 128, 255, 0, 0, 0, 255, 24, 25, 25, 255, 20, 20, 20, 255, 0, 0, 0, 255, +102, 103, 103, 255, 193, 194, 196, 255, 181, 182, 185, 255, 191, 192, 194, 255, 85, 85, 86, 255, 0, 0, 0, 255, 37, 38, 38, 255, 176, 178, 180, 255, +186, 188, 190, 255, 195, 196, 198, 255, 130, 131, 131, 255, 0, 0, 0, 255, 26, 26, 26, 255, 20, 20, 21, 255, 0, 0, 0, 255, 102, 102, 102, 255, +191, 192, 194, 255, 184, 185, 187, 255, 194, 196, 198, 255, 121, 121, 122, 255, 0, 0, 0, 255, 123, 124, 125, 255, 190, 192, 194, 255, 190, 191, 193, 255, +164, 165, 167, 255, 0, 0, 0, 255, 18, 18, 18, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 20, 20, 20, 255, 0, 0, 0, 255, 99, 99, 100, 255, 190, 191, 193, 255, 187, 188, 191, 255, 180, 182, 184, 255, 56, 56, 57, 255, + 0, 0, 0, 255, 125, 126, 127, 255, 190, 191, 193, 255, 189, 190, 193, 255, 143, 145, 146, 255, 0, 0, 0, 255, 23, 23, 23, 255, 17, 18, 18, 255, + 0, 0, 0, 255, 165, 166, 167, 255, 188, 190, 192, 255, 196, 197, 199, 255, 126, 126, 127, 255, 0, 0, 0, 255, 33, 33, 34, 255, 0, 0, 0, 255, + 88, 89, 90, 255, 188, 189, 191, 255, 187, 188, 190, 255, 184, 185, 187, 255, 170, 171, 173, 255, 171, 172, 173, 255, 170, 171, 173, 255, 172, 173, 175, 255, +175, 176, 178, 255, 175, 176, 178, 255, 175, 176, 178, 255, 175, 176, 178, 255, 175, 176, 178, 255, 175, 176, 178, 255, 176, 178, 179, 255, 175, 176, 178, 255, +184, 185, 187, 255, 120, 120, 121, 255, 0, 0, 0, 255, 157, 158, 159, 255, 188, 189, 192, 255, 182, 184, 186, 255, 115, 115, 116, 255, 0, 0, 0, 255, + 22, 22, 22, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 24, 3, 3, 255, 0, 0, 0, 255, 200, 61, 50, 255, +233, 67, 54, 255, 245, 89, 54, 255, 130, 50, 23, 255, 0, 0, 0, 255, 29, 7, 3, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 21, 21, 22, 255, 0, 0, 0, 255, 106, 106, 106, 255, +190, 191, 194, 255, 179, 181, 183, 255, 190, 191, 194, 255, 121, 122, 123, 255, 0, 0, 0, 255, 23, 23, 24, 255, 4, 4, 4, 255, 0, 1, 2, 255, + 0, 0, 0, 255, 176, 177, 178, 255, 182, 184, 187, 255, 186, 188, 190, 255, 144, 144, 145, 255, 0, 0, 0, 255, 110, 111, 112, 255, 187, 188, 191, 255, +181, 183, 185, 255, 185, 187, 189, 255, 62, 61, 61, 255, 0, 0, 0, 255, 11, 11, 11, 255, 20, 20, 21, 255, 0, 0, 0, 255, 101, 102, 102, 255, +187, 188, 191, 255, 177, 178, 181, 255, 191, 192, 195, 255, 119, 119, 120, 255, 0, 0, 0, 255, 109, 110, 111, 255, 186, 187, 190, 255, 185, 186, 189, 255, +163, 164, 167, 255, 0, 0, 0, 255, 12, 12, 12, 255, 3, 3, 3, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 23, 23, 24, 255, 0, 0, 0, 255, 115, 116, 117, 255, 190, 191, 194, 255, 183, 185, 188, 255, 167, 169, 171, 255, 22, 22, 22, 255, + 0, 0, 0, 255, 122, 123, 124, 255, 188, 190, 192, 255, 187, 188, 191, 255, 140, 141, 143, 255, 0, 0, 0, 255, 22, 22, 23, 255, 16, 17, 17, 255, + 0, 0, 0, 255, 163, 163, 165, 255, 184, 185, 188, 255, 192, 193, 196, 255, 125, 125, 126, 255, 0, 0, 0, 255, 30, 30, 31, 255, 0, 0, 0, 255, + 62, 62, 63, 255, 178, 180, 182, 255, 186, 187, 190, 255, 170, 172, 174, 255, 27, 27, 26, 255, 0, 0, 0, 255, 8, 8, 6, 255, 8, 8, 6, 255, + 15, 15, 13, 255, 15, 15, 12, 255, 15, 15, 12, 255, 15, 15, 13, 255, 14, 14, 12, 255, 18, 18, 16, 255, 23, 23, 22, 255, 20, 20, 19, 255, + 19, 19, 17, 255, 16, 17, 17, 255, 0, 0, 0, 255, 151, 151, 153, 255, 163, 166, 169, 255, 174, 171, 171, 255, 115, 107, 104, 255, 0, 0, 0, 255, + 21, 19, 19, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 23, 4, 3, 255, 0, 0, 0, 255, 199, 55, 44, 255, +224, 76, 48, 255, 247, 105, 52, 255, 131, 57, 22, 255, 0, 0, 0, 255, 29, 9, 3, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 21, 21, 21, 255, 0, 0, 0, 255, 105, 106, 106, 255, +188, 190, 192, 255, 173, 175, 178, 255, 171, 173, 176, 255, 120, 121, 122, 255, 0, 0, 0, 255, 22, 23, 23, 255, 1, 1, 1, 255, 26, 27, 27, 255, + 0, 0, 0, 255, 141, 142, 143, 255, 182, 184, 187, 255, 173, 175, 178, 255, 173, 175, 177, 255, 0, 0, 0, 255, 157, 159, 161, 255, 182, 184, 187, 255, +184, 186, 189, 255, 157, 158, 159, 255, 0, 0, 0, 255, 24, 24, 25, 255, 1, 1, 1, 255, 20, 20, 20, 255, 0, 0, 0, 255, 101, 101, 101, 255, +185, 186, 189, 255, 172, 174, 177, 255, 186, 188, 191, 255, 118, 119, 120, 255, 0, 0, 0, 255, 85, 86, 87, 255, 179, 181, 184, 255, 179, 181, 184, 255, +170, 172, 175, 255, 54, 55, 56, 255, 0, 0, 0, 255, 9, 9, 10, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 0, 0, 0, 255, 27, 27, 27, 255, 0, 0, 0, 255, 145, 146, 148, 255, 184, 187, 189, 255, 181, 184, 187, 255, 153, 155, 157, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 121, 122, 123, 255, 184, 186, 189, 255, 183, 185, 188, 255, 137, 138, 141, 255, 0, 0, 0, 255, 22, 22, 22, 255, 16, 16, 17, 255, + 0, 0, 0, 255, 161, 162, 164, 255, 180, 182, 185, 255, 189, 191, 194, 255, 124, 125, 125, 255, 0, 0, 0, 255, 27, 28, 28, 255, 0, 0, 0, 255, + 24, 25, 25, 255, 167, 169, 171, 255, 183, 185, 188, 255, 176, 178, 181, 255, 57, 58, 59, 255, 0, 0, 0, 255, 8, 8, 9, 255, 0, 0, 1, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 3, 3, 4, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 18, 19, 19, 255, 0, 0, 0, 255, 143, 144, 146, 255, 174, 173, 175, 255, 180, 160, 154, 255, 115, 93, 86, 255, 0, 0, 0, 255, + 22, 16, 15, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 20, 5, 3, 255, 0, 0, 0, 255, 194, 67, 40, 255, +244, 100, 51, 255, 248, 118, 48, 255, 132, 65, 20, 255, 0, 0, 0, 255, 29, 10, 3, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 21, 21, 21, 255, 0, 0, 0, 255, 105, 105, 106, 255, +187, 188, 191, 255, 172, 173, 176, 255, 181, 182, 185, 255, 121, 122, 123, 255, 0, 0, 0, 255, 23, 23, 23, 255, 2, 2, 2, 255, 16, 16, 16, 255, + 0, 0, 0, 255, 84, 84, 84, 255, 183, 184, 186, 255, 174, 175, 178, 255, 180, 181, 184, 255, 132, 132, 133, 255, 177, 178, 181, 255, 174, 175, 178, 255, +186, 187, 190, 255, 108, 109, 109, 255, 0, 0, 0, 255, 22, 22, 22, 255, 2, 2, 2, 255, 20, 20, 20, 255, 0, 0, 0, 255, 101, 101, 102, 255, +183, 184, 187, 255, 173, 174, 177, 255, 182, 184, 186, 255, 112, 113, 114, 255, 0, 0, 0, 255, 31, 31, 32, 255, 166, 168, 171, 255, 179, 180, 184, 255, +184, 185, 188, 255, 115, 116, 118, 255, 0, 0, 0, 255, 29, 30, 30, 255, 0, 0, 0, 255, 2, 2, 2, 255, 2, 2, 2, 255, 1, 1, 1, 255, + 14, 14, 14, 255, 0, 0, 0, 255, 29, 28, 26, 255, 177, 178, 180, 255, 178, 180, 183, 255, 186, 187, 190, 255, 142, 143, 144, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 120, 121, 122, 255, 184, 185, 188, 255, 182, 184, 187, 255, 151, 151, 153, 255, 0, 0, 0, 255, 22, 22, 22, 255, 15, 16, 16, 255, + 0, 0, 0, 255, 160, 161, 163, 255, 179, 180, 184, 255, 188, 189, 192, 255, 125, 125, 126, 255, 0, 0, 0, 255, 25, 25, 25, 255, 20, 21, 21, 255, + 0, 0, 0, 255, 149, 150, 153, 255, 179, 180, 183, 255, 182, 184, 187, 255, 127, 127, 128, 255, 0, 0, 0, 255, 35, 35, 36, 255, 1, 1, 1, 255, + 5, 5, 5, 255, 5, 5, 5, 255, 5, 5, 5, 255, 13, 13, 13, 255, 0, 0, 0, 255, 59, 59, 59, 255, 174, 175, 176, 255, 175, 176, 178, 255, +177, 178, 179, 255, 66, 65, 65, 255, 0, 0, 0, 255, 140, 138, 138, 255, 177, 164, 160, 255, 185, 142, 130, 255, 115, 77, 68, 255, 0, 0, 0, 255, + 22, 13, 11, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 27, 8, 3, 255, 0, 0, 0, 255, 215, 89, 44, 255, +248, 115, 49, 255, 247, 130, 44, 255, 133, 72, 17, 255, 0, 0, 0, 255, 29, 12, 3, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 21, 21, 21, 255, 0, 0, 0, 255, 105, 105, 105, 255, +188, 188, 191, 255, 170, 171, 174, 255, 179, 180, 183, 255, 122, 122, 123, 255, 0, 0, 0, 255, 23, 23, 23, 255, 2, 2, 2, 255, 1, 1, 1, 255, + 18, 18, 19, 255, 0, 0, 0, 255, 169, 170, 171, 255, 182, 183, 186, 255, 175, 176, 179, 255, 188, 188, 190, 255, 178, 179, 182, 255, 176, 177, 180, 255, +172, 172, 174, 255, 26, 25, 24, 255, 0, 0, 0, 255, 5, 5, 5, 255, 2, 2, 2, 255, 20, 20, 20, 255, 0, 0, 0, 255, 102, 102, 102, 255, +182, 182, 185, 255, 159, 160, 163, 255, 172, 172, 175, 255, 105, 105, 107, 255, 0, 0, 0, 255, 0, 0, 0, 255, 133, 134, 136, 255, 183, 184, 187, 255, +172, 172, 175, 255, 170, 171, 174, 255, 45, 45, 46, 255, 0, 0, 0, 255, 28, 28, 29, 255, 18, 18, 19, 255, 20, 20, 21, 255, 27, 27, 27, 255, + 11, 12, 13, 255, 0, 0, 0, 255, 150, 150, 152, 255, 178, 178, 181, 255, 153, 154, 157, 255, 183, 184, 186, 255, 100, 100, 100, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 121, 122, 123, 255, 181, 181, 184, 255, 182, 183, 186, 255, 158, 158, 160, 255, 0, 0, 0, 255, 22, 22, 23, 255, 15, 16, 16, 255, + 0, 0, 0, 255, 160, 160, 162, 255, 179, 180, 183, 255, 189, 189, 192, 255, 120, 120, 120, 255, 0, 0, 0, 255, 31, 32, 32, 255, 29, 29, 30, 255, + 0, 0, 0, 255, 122, 122, 123, 255, 180, 181, 184, 255, 170, 170, 174, 255, 183, 184, 185, 255, 56, 56, 55, 255, 0, 0, 0, 255, 29, 29, 30, 255, + 21, 21, 21, 255, 16, 16, 16, 255, 17, 18, 18, 255, 32, 32, 32, 255, 0, 0, 0, 255, 122, 123, 123, 255, 186, 187, 189, 255, 181, 182, 186, 255, +177, 177, 179, 255, 0, 0, 0, 255, 0, 0, 0, 255, 144, 128, 123, 255, 187, 147, 137, 255, 190, 124, 109, 255, 114, 69, 60, 255, 0, 0, 0, 255, + 22, 11, 8, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 24, 8, 3, 255, 0, 0, 0, 255, 207, 95, 39, 255, +248, 129, 45, 255, 255, 145, 41, 255, 134, 76, 16, 255, 0, 0, 0, 255, 30, 14, 3, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 21, 21, 21, 255, 0, 0, 0, 255, 105, 106, 106, 255, +187, 188, 190, 255, 175, 176, 180, 255, 183, 184, 187, 255, 119, 120, 121, 255, 0, 0, 0, 255, 22, 23, 23, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 25, 25, 26, 255, 0, 0, 0, 255, 128, 128, 129, 255, 185, 186, 188, 255, 169, 170, 173, 255, 179, 180, 183, 255, 174, 175, 178, 255, 172, 173, 175, 255, +136, 137, 138, 255, 0, 0, 0, 255, 24, 24, 25, 255, 1, 1, 1, 255, 2, 2, 2, 255, 20, 20, 20, 255, 0, 0, 0, 255, 100, 100, 101, 255, +185, 186, 188, 255, 161, 162, 165, 255, 166, 167, 170, 255, 108, 108, 109, 255, 0, 0, 0, 255, 0, 0, 0, 255, 59, 59, 60, 255, 178, 179, 182, 255, +157, 158, 161, 255, 167, 168, 171, 255, 158, 159, 162, 255, 58, 59, 60, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 146, 147, 147, 255, 190, 191, 193, 255, 168, 169, 172, 255, 172, 173, 175, 255, 159, 159, 160, 255, 0, 0, 0, 255, 32, 32, 33, 255, + 0, 0, 0, 255, 121, 121, 122, 255, 184, 185, 188, 255, 179, 180, 183, 255, 157, 158, 159, 255, 0, 0, 0, 255, 22, 22, 23, 255, 15, 16, 16, 255, + 0, 0, 0, 255, 162, 163, 164, 255, 178, 179, 182, 255, 185, 186, 189, 255, 141, 141, 142, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 26, 25, 23, 255, 176, 176, 178, 255, 173, 174, 177, 255, 180, 181, 184, 255, 178, 179, 180, 255, 73, 73, 73, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 86, 87, 88, 255, 166, 167, 170, 255, 173, 175, 178, 255, 187, 188, 191, 255, +126, 126, 126, 255, 0, 0, 0, 255, 0, 0, 0, 255, 132, 100, 91, 255, 172, 116, 103, 255, 185, 109, 93, 255, 118, 65, 55, 255, 0, 0, 0, 255, + 22, 9, 7, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 22, 9, 2, 255, 0, 0, 0, 255, 210, 108, 36, 255, +178, 95, 25, 255, 226, 132, 32, 255, 135, 77, 15, 255, 0, 0, 0, 255, 28, 13, 3, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 21, 21, 21, 255, 0, 0, 0, 255, 104, 104, 105, 255, +185, 186, 189, 255, 167, 169, 172, 255, 177, 179, 182, 255, 119, 119, 120, 255, 0, 0, 0, 255, 22, 22, 22, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 10, 10, 10, 255, 0, 0, 0, 255, 64, 64, 63, 255, 173, 174, 175, 255, 168, 170, 174, 255, 174, 176, 179, 255, 171, 173, 176, 255, 178, 180, 183, 255, + 83, 83, 84, 255, 0, 0, 0, 255, 16, 16, 16, 255, 2, 2, 2, 255, 2, 2, 2, 255, 20, 20, 20, 255, 0, 0, 0, 255, 101, 102, 102, 255, +177, 178, 181, 255, 162, 164, 167, 255, 168, 170, 173, 255, 101, 101, 102, 255, 0, 0, 0, 255, 32, 32, 32, 255, 0, 0, 0, 255, 122, 123, 124, 255, +184, 185, 188, 255, 156, 158, 161, 255, 163, 165, 168, 255, 170, 172, 175, 255, 137, 139, 141, 255, 102, 103, 105, 255, 108, 108, 110, 255, 136, 136, 137, 255, +176, 177, 179, 255, 183, 185, 187, 255, 146, 148, 151, 255, 170, 172, 175, 255, 188, 189, 190, 255, 60, 59, 59, 255, 0, 0, 0, 255, 29, 29, 29, 255, + 0, 0, 0, 255, 118, 119, 120, 255, 173, 175, 177, 255, 173, 175, 178, 255, 152, 153, 154, 255, 0, 0, 0, 255, 20, 21, 21, 255, 23, 23, 24, 255, + 0, 0, 0, 255, 159, 159, 161, 255, 175, 176, 179, 255, 169, 171, 174, 255, 178, 179, 182, 255, 171, 172, 173, 255, 161, 162, 163, 255, 148, 149, 150, 255, + 23, 22, 21, 255, 0, 0, 0, 255, 101, 101, 101, 255, 187, 189, 191, 255, 168, 170, 173, 255, 172, 174, 177, 255, 176, 177, 179, 255, 145, 146, 147, 255, +119, 120, 121, 255, 88, 89, 90, 255, 100, 102, 103, 255, 128, 130, 132, 255, 163, 164, 167, 255, 172, 174, 177, 255, 167, 168, 171, 255, 158, 159, 160, 255, + 0, 0, 0, 255, 11, 5, 4, 255, 0, 0, 0, 255, 103, 61, 52, 255, 160, 93, 78, 255, 166, 87, 72, 255, 78, 34, 26, 255, 0, 0, 0, 255, + 13, 4, 3, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 6, 3, 2, 255, 0, 0, 2, 255, 188, 104, 27, 255, +159, 90, 17, 255, 207, 122, 26, 255, 122, 71, 12, 255, 0, 0, 0, 255, 25, 11, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 21, 21, 21, 255, 0, 0, 0, 255, 107, 107, 108, 255, +176, 178, 180, 255, 142, 143, 146, 255, 161, 163, 166, 255, 111, 112, 113, 255, 0, 0, 0, 255, 20, 20, 20, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 1, 1, 1, 255, 25, 25, 25, 255, 0, 0, 0, 255, 157, 158, 159, 255, 177, 179, 182, 255, 166, 168, 171, 255, 172, 174, 177, 255, 155, 157, 159, 255, + 0, 0, 0, 255, 15, 16, 16, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 20, 20, 20, 255, 0, 0, 0, 255, 103, 104, 104, 255, +168, 169, 172, 255, 158, 160, 163, 255, 176, 178, 180, 255, 105, 106, 108, 255, 0, 0, 0, 255, 20, 20, 21, 255, 18, 18, 19, 255, 0, 0, 0, 255, +132, 133, 133, 255, 171, 173, 175, 255, 132, 133, 135, 255, 136, 138, 140, 255, 147, 149, 152, 255, 152, 154, 157, 255, 160, 162, 165, 255, 160, 162, 165, 255, +162, 164, 168, 255, 153, 155, 158, 255, 169, 170, 173, 255, 179, 180, 182, 255, 75, 75, 75, 255, 0, 0, 0, 255, 23, 23, 23, 255, 20, 21, 21, 255, + 0, 0, 0, 255, 120, 121, 122, 255, 166, 168, 171, 255, 157, 159, 162, 255, 148, 148, 150, 255, 0, 0, 0, 255, 18, 18, 19, 255, 23, 23, 23, 255, + 0, 0, 0, 255, 122, 123, 123, 255, 178, 180, 182, 255, 147, 149, 152, 255, 154, 156, 159, 255, 143, 144, 147, 255, 147, 148, 151, 255, 155, 156, 158, 255, + 40, 40, 39, 255, 8, 9, 10, 255, 0, 0, 0, 255, 109, 110, 110, 255, 153, 154, 156, 255, 168, 170, 173, 255, 154, 156, 159, 255, 141, 143, 145, 255, +137, 139, 141, 255, 106, 107, 109, 255, 123, 124, 126, 255, 151, 153, 156, 255, 165, 167, 170, 255, 161, 163, 165, 255, 162, 163, 164, 255, 29, 28, 27, 255, + 0, 0, 0, 255, 28, 18, 17, 255, 0, 0, 0, 255, 151, 87, 74, 255, 181, 97, 81, 255, 78, 20, 9, 255, 52, 13, 9, 255, 0, 3, 3, 255, + 6, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 11, 5, 2, 255, 0, 0, 2, 255, 189, 109, 25, 255, +243, 143, 34, 255, 255, 167, 38, 255, 136, 91, 16, 255, 0, 0, 0, 255, 31, 18, 3, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 19, 19, 19, 255, 0, 0, 0, 255, 101, 101, 101, 255, +163, 164, 165, 255, 120, 120, 122, 255, 135, 137, 139, 255, 101, 102, 103, 255, 0, 0, 0, 255, 17, 17, 17, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 20, 20, 20, 255, 0, 0, 0, 255, 100, 100, 100, 255, 174, 175, 177, 255, 156, 157, 160, 255, 153, 155, 157, 255, 101, 102, 103, 255, + 0, 0, 0, 255, 18, 18, 19, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 18, 18, 18, 255, 0, 0, 0, 255, 93, 93, 93, 255, +169, 171, 173, 255, 158, 160, 162, 255, 152, 154, 157, 255, 88, 89, 90, 255, 0, 0, 0, 255, 16, 16, 16, 255, 3, 3, 3, 255, 18, 18, 19, 255, + 0, 0, 0, 255, 98, 98, 99, 255, 146, 146, 148, 255, 146, 147, 148, 255, 150, 151, 154, 255, 157, 159, 161, 255, 166, 167, 170, 255, 155, 157, 159, 255, +172, 173, 176, 255, 170, 171, 173, 255, 143, 144, 144, 255, 34, 33, 32, 255, 0, 0, 0, 255, 24, 24, 25, 255, 0, 0, 0, 255, 18, 18, 19, 255, + 0, 0, 0, 255, 103, 104, 105, 255, 152, 154, 156, 255, 155, 157, 159, 255, 138, 139, 141, 255, 0, 0, 0, 255, 18, 19, 19, 255, 2, 2, 2, 255, + 10, 10, 11, 255, 0, 0, 0, 255, 131, 132, 132, 255, 162, 163, 165, 255, 161, 163, 165, 255, 131, 132, 134, 255, 137, 139, 142, 255, 131, 133, 135, 255, + 29, 29, 29, 255, 0, 0, 0, 255, 24, 25, 25, 255, 0, 0, 0, 255, 77, 77, 77, 255, 146, 147, 148, 255, 157, 158, 160, 255, 167, 169, 172, 255, +147, 149, 151, 255, 141, 142, 145, 255, 150, 151, 153, 255, 168, 170, 172, 255, 173, 174, 176, 255, 140, 141, 141, 255, 18, 17, 15, 255, 0, 0, 0, 255, + 17, 17, 17, 255, 19, 5, 3, 255, 0, 0, 0, 255, 140, 72, 60, 255, 173, 81, 67, 255, 150, 53, 42, 255, 107, 37, 29, 255, 0, 0, 0, 255, + 19, 4, 4, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 6, 3, 2, 255, 6, 6, 2, 255, 160, 92, 19, 255, +209, 127, 28, 255, 236, 167, 36, 255, 123, 94, 16, 255, 0, 0, 0, 255, 27, 18, 3, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, 2, 2, 2, 255, + 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, + 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 0, 0, 0, 255, 7, 7, 6, 255, + 60, 60, 59, 255, 59, 59, 59, 255, 29, 29, 30, 255, 5, 6, 6, 255, 0, 0, 0, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, + 1, 1, 1, 255, 0, 0, 0, 255, 6, 6, 6, 255, 0, 0, 0, 255, 53, 53, 52, 255, 45, 45, 45, 255, 3, 4, 4, 255, 0, 0, 0, 255, + 4, 4, 5, 255, 0, 0, 0, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 0, 0, 0, 255, 12, 12, 12, 255, + 58, 57, 57, 255, 52, 51, 51, 255, 17, 18, 19, 255, 0, 0, 0, 255, 1, 1, 1, 255, 1, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 23, 24, 24, 255, 0, 0, 0, 255, 0, 0, 0, 255, 79, 79, 79, 255, 117, 118, 118, 255, 123, 124, 125, 255, 122, 122, 123, 255, 121, 122, 123, 255, +100, 101, 101, 255, 41, 41, 40, 255, 0, 0, 0, 255, 0, 0, 0, 255, 20, 20, 20, 255, 0, 0, 0, 255, 1, 1, 1, 255, 1, 1, 1, 255, + 1, 1, 1, 255, 0, 0, 0, 255, 17, 18, 19, 255, 49, 50, 50, 255, 31, 31, 30, 255, 0, 0, 0, 255, 2, 2, 2, 255, 1, 1, 1, 255, + 1, 1, 1, 255, 5, 5, 6, 255, 0, 0, 0, 255, 0, 0, 0, 255, 59, 58, 58, 255, 53, 54, 54, 255, 28, 29, 30, 255, 23, 24, 24, 255, + 0, 0, 0, 255, 1, 1, 1, 255, 0, 0, 0, 255, 24, 24, 24, 255, 0, 0, 0, 255, 0, 0, 0, 255, 70, 70, 70, 255, 109, 110, 110, 255, +122, 122, 123, 255, 113, 114, 114, 255, 118, 118, 119, 255, 104, 104, 104, 255, 36, 36, 34, 255, 0, 0, 0, 255, 0, 0, 0, 255, 14, 14, 14, 255, + 0, 0, 0, 255, 2, 1, 1, 255, 0, 0, 0, 255, 26, 6, 5, 255, 47, 15, 11, 255, 60, 19, 15, 255, 28, 4, 3, 255, 0, 0, 0, 255, + 3, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 3, 2, 1, 255, 0, 0, 0, 255, 49, 26, 3, 255, + 41, 27, 3, 255, 25, 28, 3, 255, 11, 11, 1, 255, 0, 0, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, + 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, 1, 1, 1, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 6, 6, 6, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 5, 5, 5, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 1, 1, 1, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 17, 17, 18, 255, 12, 12, 13, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 25, 25, 25, 255, 6, 6, 6, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 2, 2, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 1, 1, 1, 255, 21, 22, 22, 255, 2, 2, 3, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 14, 14, 14, 255, 17, 17, 18, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 24, 24, 24, 255, 4, 4, 4, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 1, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 2, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 3, 1, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 1, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 1, 1, 1, 255, + 8, 8, 8, 255, 7, 7, 7, 255, 3, 3, 3, 255, 1, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 8, 8, 8, 255, 6, 6, 6, 255, 3, 3, 3, 255, 1, 1, 1, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 1, 1, 1, 255, + 8, 8, 8, 255, 7, 7, 7, 255, 3, 3, 3, 255, 1, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 10, 10, 10, 255, 21, 21, 22, 255, 22, 22, 23, 255, 21, 21, 22, 255, 23, 23, 23, 255, + 18, 18, 18, 255, 4, 4, 4, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 1, 1, 1, 255, 3, 3, 3, 255, 6, 6, 6, 255, 4, 4, 4, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 1, 1, 1, 255, 7, 7, 7, 255, 5, 5, 6, 255, 3, 3, 3, 255, 3, 3, 3, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 9, 9, 9, 255, 21, 21, 21, 255, + 22, 22, 22, 255, 18, 19, 19, 255, 21, 22, 22, 255, 19, 19, 19, 255, 4, 4, 4, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 3, 1, 0, 255, 6, 1, 1, 255, 5, 1, 0, 255, 2, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 6, 2, 0, 255, + 8, 3, 0, 255, 8, 4, 0, 255, 2, 1, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 2, 2, 2, 255, 11, 11, 11, 255, 11, 11, 11, 255, 8, 8, 8, 255, 4, 4, 4, 255, 14, 14, 14, 255, 14, 14, 15, 255, 14, 14, 15, 255, + 14, 14, 15, 255, 15, 15, 15, 255, 13, 13, 13, 255, 12, 12, 12, 255, 1, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 5, 5, 5, 255, 15, 15, 15, 255, 14, 14, 14, 255, 14, 14, 15, 255, 14, 14, 15, 255, 14, 14, 15, 255, 14, 14, 15, 255, + 14, 14, 15, 255, 14, 14, 14, 255, 14, 14, 14, 255, 15, 15, 15, 255, 10, 9, 9, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 2, 1, 1, 255, 16, 13, 12, 255, 14, 10, 10, 255, 15, 12, 11, 255, 17, 12, 11, 255, 13, 9, 8, 255, 16, 11, 10, 255, 16, 10, 9, 255, + 13, 8, 7, 255, 9, 5, 5, 255, 16, 10, 9, 255, 10, 5, 5, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 1, 0, 0, 255, 9, 2, 1, 255, 0, 0, 0, 255, + 13, 3, 2, 255, 23, 5, 4, 255, 11, 2, 1, 255, 0, 0, 0, 255, 5, 1, 0, 255, 5, 0, 0, 255, 6, 1, 0, 255, 13, 1, 1, 255, + 18, 2, 2, 255, 23, 3, 2, 255, 24, 2, 2, 255, 12, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 2, 2, 2, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 1, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 4, 4, 4, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 4, 4, 4, 255, 0, 0, 0, 255, 2, 1, 1, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 6, 4, 3, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 1, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 22, 4, 3, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 17, 2, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 9, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 2, 2, 3, 255, 0, 0, 0, 255, + 36, 36, 37, 255, 89, 89, 90, 255, 89, 89, 90, 255, 73, 73, 74, 255, 61, 60, 61, 255, 102, 102, 103, 255, 103, 103, 105, 255, 103, 103, 104, 255, +103, 103, 104, 255, 104, 104, 105, 255, 99, 99, 100, 255, 93, 93, 94, 255, 23, 23, 23, 255, 0, 0, 0, 255, 1, 1, 2, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 6, 6, 7, 255, + 0, 0, 0, 255, 58, 57, 58, 255, 105, 105, 107, 255, 102, 103, 104, 255, 107, 107, 108, 255, 103, 104, 106, 255, 102, 103, 105, 255, 102, 103, 105, 255, +102, 103, 105, 255, 102, 103, 105, 255, 102, 102, 103, 255, 106, 105, 106, 255, 87, 86, 86, 255, 0, 0, 0, 255, 9, 8, 8, 255, 0, 0, 0, 255, + 33, 30, 29, 255, 108, 98, 95, 255, 101, 90, 88, 255, 106, 94, 90, 255, 112, 96, 93, 255, 92, 76, 72, 255, 106, 88, 83, 255, 104, 85, 80, 255, + 93, 76, 71, 255, 84, 68, 63, 255, 113, 90, 83, 255, 84, 64, 60, 255, 0, 0, 0, 255, 10, 6, 5, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 20, 6, 5, 255, 57, 27, 22, 255, 0, 0, 0, 255, +106, 49, 41, 255, 140, 67, 57, 255, 88, 39, 32, 255, 0, 0, 0, 255, 35, 14, 8, 255, 39, 19, 12, 255, 49, 21, 14, 255, 93, 35, 29, 255, +120, 44, 38, 255, 137, 47, 43, 255, 145, 45, 43, 255, 105, 31, 29, 255, 0, 0, 0, 255, 12, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 1, 1, 1, 255, 1, 1, 1, 255, + 0, 0, 0, 255, 128, 128, 130, 255, 136, 135, 137, 255, 116, 115, 117, 255, 122, 122, 123, 255, 144, 144, 146, 255, 145, 145, 147, 255, 144, 144, 147, 255, +144, 144, 146, 255, 146, 146, 148, 255, 136, 136, 137, 255, 137, 137, 138, 255, 70, 70, 71, 255, 0, 0, 0, 255, 9, 9, 10, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 15, 15, 15, 255, + 0, 0, 0, 255, 106, 107, 108, 255, 151, 151, 153, 255, 134, 133, 134, 255, 105, 104, 105, 255, 144, 141, 142, 255, 148, 143, 144, 255, 148, 142, 142, 255, +149, 142, 140, 255, 150, 141, 139, 255, 150, 139, 137, 255, 157, 143, 140, 255, 106, 96, 94, 255, 0, 0, 0, 255, 21, 17, 16, 255, 0, 0, 0, 255, + 53, 44, 41, 255, 156, 130, 123, 255, 154, 126, 118, 255, 159, 127, 119, 255, 157, 125, 117, 255, 149, 116, 108, 255, 147, 114, 105, 255, 165, 124, 114, 255, +148, 109, 99, 255, 150, 108, 98, 255, 167, 118, 107, 255, 124, 87, 78, 255, 0, 0, 0, 255, 19, 10, 8, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 22, 3, 3, 255, 0, 0, 0, 255, 93, 39, 33, 255, 177, 76, 66, 255, 141, 51, 45, 255, +138, 58, 49, 255, 182, 73, 64, 255, 139, 48, 43, 255, 116, 33, 30, 255, 157, 46, 45, 255, 172, 48, 48, 255, 169, 42, 45, 255, 186, 48, 51, 255, +161, 39, 42, 255, 168, 41, 45, 255, 171, 40, 45, 255, 61, 13, 13, 255, 0, 0, 0, 255, 8, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 15, 15, 16, 255, + 0, 0, 0, 255, 99, 99, 100, 255, 144, 144, 146, 255, 140, 140, 142, 255, 143, 143, 145, 255, 139, 139, 141, 255, 138, 138, 140, 255, 139, 139, 140, 255, +139, 139, 141, 255, 139, 139, 141, 255, 138, 138, 140, 255, 144, 144, 146, 255, 101, 101, 102, 255, 0, 0, 0, 255, 14, 14, 15, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 1, 1, 1, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 133, 123, 121, 255, 150, 137, 134, 255, 139, 124, 121, 255, 81, 67, 64, 255, 147, 127, 122, 255, 151, 130, 124, 255, 151, 127, 121, 255, +152, 127, 120, 255, 153, 125, 118, 255, 154, 124, 116, 255, 154, 122, 114, 255, 61, 45, 40, 255, 0, 0, 0, 255, 16, 9, 8, 255, 0, 0, 0, 255, + 55, 38, 34, 255, 158, 112, 101, 255, 164, 115, 103, 255, 155, 107, 95, 255, 159, 105, 94, 255, 139, 90, 79, 255, 151, 95, 83, 255, 172, 109, 96, 255, +161, 100, 87, 255, 161, 99, 86, 255, 176, 106, 92, 255, 124, 71, 61, 255, 0, 0, 0, 255, 20, 7, 5, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 6, 0, 0, 255, 0, 0, 0, 255, 14, 2, 1, 255, 144, 39, 39, 255, 156, 42, 43, 255, 152, 43, 43, 255, +141, 23, 32, 255, 126, 18, 27, 255, 124, 22, 28, 255, 106, 20, 24, 255, 165, 30, 40, 255, 171, 29, 41, 255, 167, 27, 40, 255, 183, 29, 44, 255, +171, 25, 40, 255, 152, 20, 34, 255, 6, 0, 0, 255, 0, 0, 0, 255, 14, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 9, 9, 9, 255, + 0, 0, 0, 255, 64, 64, 65, 255, 141, 141, 143, 255, 139, 139, 141, 255, 140, 140, 142, 255, 140, 140, 141, 255, 143, 143, 145, 255, 141, 141, 143, 255, +139, 139, 141, 255, 140, 140, 142, 255, 139, 139, 141, 255, 143, 143, 145, 255, 124, 125, 127, 255, 0, 0, 0, 255, 6, 5, 5, 255, 0, 1, 1, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 10, 7, 6, 255, 0, 0, 0, 255, + 68, 55, 52, 255, 155, 127, 119, 255, 154, 124, 116, 255, 159, 126, 118, 255, 133, 104, 96, 255, 135, 104, 96, 255, 162, 122, 112, 255, 159, 118, 108, 255, +161, 117, 106, 255, 161, 115, 104, 255, 166, 116, 105, 255, 147, 101, 90, 255, 0, 0, 0, 255, 6, 3, 2, 255, 9, 3, 3, 255, 0, 0, 0, 255, + 58, 33, 28, 255, 165, 101, 88, 255, 172, 102, 89, 255, 153, 87, 75, 255, 168, 96, 83, 255, 169, 94, 81, 255, 160, 87, 75, 255, 162, 86, 74, 255, +176, 90, 78, 255, 163, 80, 68, 255, 184, 88, 76, 255, 133, 61, 52, 255, 0, 0, 0, 255, 22, 5, 4, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 10, 0, 1, 255, 13, 0, 1, 255, 61, 5, 9, 255, 153, 25, 36, 255, 167, 28, 40, 255, 82, 2, 13, 255, 90, 1, 14, 255, +178, 30, 43, 255, 77, 6, 13, 255, 139, 20, 31, 255, 145, 21, 32, 255, 163, 26, 38, 255, 154, 24, 36, 255, 153, 25, 36, 255, 191, 34, 47, 255, +171, 30, 41, 255, 33, 4, 5, 255, 0, 0, 0, 255, 19, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 1, 1, 2, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 128, 128, 130, 255, 144, 144, 146, 255, 121, 121, 123, 255, 67, 67, 67, 255, 78, 78, 80, 255, 126, 127, 129, 255, +143, 143, 145, 255, 141, 141, 142, 255, 141, 140, 141, 255, 141, 139, 141, 255, 140, 138, 138, 255, 53, 52, 52, 255, 0, 0, 0, 255, 7, 6, 6, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 18, 10, 8, 255, 0, 0, 0, 255, +113, 80, 73, 255, 167, 119, 108, 255, 162, 113, 101, 255, 164, 113, 101, 255, 164, 110, 98, 255, 155, 101, 89, 255, 167, 108, 96, 255, 167, 106, 93, 255, +168, 104, 91, 255, 168, 102, 89, 255, 175, 104, 91, 255, 123, 71, 61, 255, 0, 0, 0, 255, 20, 7, 5, 255, 9, 2, 1, 255, 0, 0, 0, 255, + 61, 27, 23, 255, 173, 86, 74, 255, 178, 86, 74, 255, 180, 85, 73, 255, 179, 80, 70, 255, 180, 79, 68, 255, 176, 73, 64, 255, 178, 71, 62, 255, +178, 69, 61, 255, 166, 61, 54, 255, 183, 64, 59, 255, 129, 42, 38, 255, 0, 0, 0, 255, 20, 2, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 12, 0, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 96, 11, 19, 255, 138, 20, 31, 255, 109, 13, 22, 255, 87, 10, 17, 255, 51, 3, 6, 255, +121, 17, 26, 255, 82, 8, 14, 255, 117, 17, 25, 255, 105, 14, 21, 255, 84, 9, 15, 255, 120, 17, 26, 255, 102, 13, 20, 255, 94, 11, 18, 255, + 76, 8, 14, 255, 0, 0, 0, 255, 20, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 9, 8, 9, 255, 0, 0, 0, 255, 90, 89, 90, 255, 137, 134, 135, 255, 99, 96, 96, 255, 96, 90, 89, 255, 114, 105, 103, 255, 138, 129, 127, 255, +144, 134, 132, 255, 139, 128, 126, 255, 144, 131, 128, 255, 148, 132, 128, 255, 155, 135, 130, 255, 98, 84, 80, 255, 0, 0, 0, 255, 15, 12, 11, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 11, 3, 3, 255, 0, 0, 0, 255, +148, 91, 79, 255, 173, 105, 91, 255, 169, 100, 87, 255, 171, 99, 85, 255, 172, 97, 84, 255, 174, 97, 84, 255, 173, 93, 80, 255, 174, 92, 79, 255, +175, 90, 77, 255, 176, 87, 75, 255, 178, 86, 74, 255, 83, 36, 30, 255, 0, 0, 0, 255, 14, 3, 2, 255, 10, 1, 1, 255, 0, 0, 0, 255, + 64, 20, 17, 255, 180, 65, 59, 255, 185, 63, 58, 255, 183, 58, 55, 255, 186, 55, 54, 255, 187, 51, 53, 255, 189, 49, 52, 255, 191, 46, 51, 255, +189, 40, 49, 255, 129, 20, 29, 255, 143, 23, 33, 255, 131, 19, 29, 255, 0, 0, 0, 255, 19, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 13, 0, 1, 255, + 0, 0, 0, 255, 31, 2, 3, 255, 127, 19, 28, 255, 87, 9, 16, 255, 34, 1, 3, 255, 12, 0, 0, 255, 60, 3, 8, 255, 55, 2, 6, 255, + 0, 0, 0, 255, 92, 10, 17, 255, 152, 26, 35, 255, 162, 28, 38, 255, 201, 37, 49, 255, 191, 35, 46, 255, 190, 35, 46, 255, 66, 7, 10, 255, + 0, 0, 0, 255, 23, 1, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 3, 2, 2, 255, 0, 0, 0, 255, 27, 19, 17, 255, 134, 119, 115, 255, 144, 126, 122, 255, 141, 122, 117, 255, 159, 137, 131, 255, 132, 111, 105, 255, + 83, 65, 61, 255, 99, 76, 70, 255, 155, 122, 113, 255, 156, 122, 113, 255, 162, 124, 115, 255, 134, 101, 93, 255, 0, 0, 0, 255, 14, 8, 7, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 10, 2, 2, 255, 0, 0, 0, 255, 62, 29, 24, 255, +173, 88, 75, 255, 177, 87, 75, 255, 177, 85, 73, 255, 178, 82, 71, 255, 179, 80, 69, 255, 179, 77, 67, 255, 181, 75, 66, 255, 183, 72, 64, 255, +181, 69, 61, 255, 186, 67, 61, 255, 170, 57, 53, 255, 0, 0, 0, 255, 0, 0, 0, 255, 2, 0, 0, 255, 11, 0, 1, 255, 0, 0, 0, 255, + 66, 9, 12, 255, 187, 36, 47, 255, 193, 35, 48, 255, 188, 32, 46, 255, 183, 30, 44, 255, 195, 32, 47, 255, 187, 30, 45, 255, 187, 30, 45, 255, +196, 32, 48, 255, 188, 32, 46, 255, 201, 35, 49, 255, 148, 24, 35, 255, 0, 0, 0, 255, 25, 1, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 10, 0, 1, 255, 0, 0, 0, 255, + 48, 4, 6, 255, 144, 24, 33, 255, 179, 32, 43, 255, 167, 29, 39, 255, 87, 9, 16, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 72, 7, 11, 255, 142, 24, 32, 255, 158, 28, 36, 255, 195, 36, 47, 255, 179, 33, 42, 255, 166, 30, 39, 255, 98, 14, 20, 255, 0, 0, 0, 255, + 26, 1, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 3, 2, 2, 255, 0, 0, 0, 255, 28, 22, 20, 255, 142, 109, 100, 255, 164, 124, 114, 255, 163, 121, 111, 255, 158, 115, 104, 255, 132, 94, 85, 255, + 72, 48, 44, 255, 99, 70, 64, 255, 148, 102, 92, 255, 165, 111, 99, 255, 166, 110, 97, 255, 161, 104, 92, 255, 44, 25, 21, 255, 0, 0, 0, 255, + 6, 2, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 21, 3, 2, 255, 0, 0, 0, 255, 116, 42, 37, 255, +188, 70, 63, 255, 182, 64, 59, 255, 185, 61, 57, 255, 185, 58, 56, 255, 186, 54, 54, 255, 187, 51, 53, 255, 186, 47, 51, 255, 181, 42, 48, 255, +190, 40, 49, 255, 197, 39, 50, 255, 145, 24, 34, 255, 0, 0, 0, 255, 23, 1, 2, 255, 0, 0, 0, 255, 11, 0, 1, 255, 0, 0, 0, 255, + 68, 6, 12, 255, 190, 32, 46, 255, 196, 34, 48, 255, 155, 25, 36, 255, 189, 33, 46, 255, 185, 33, 45, 255, 185, 33, 45, 255, 163, 28, 39, 255, +197, 35, 49, 255, 195, 35, 48, 255, 195, 35, 48, 255, 136, 22, 31, 255, 0, 0, 0, 255, 22, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 8, 0, 0, 255, 1, 0, 0, 255, 0, 0, 0, 255, +101, 14, 20, 255, 118, 18, 25, 255, 111, 17, 23, 255, 179, 33, 43, 255, 81, 9, 14, 255, 149, 26, 34, 255, 160, 29, 37, 255, 97, 13, 18, 255, + 53, 3, 6, 255, 99, 14, 19, 255, 194, 37, 46, 255, 205, 40, 49, 255, 146, 26, 33, 255, 103, 16, 21, 255, 0, 0, 0, 255, 23, 1, 1, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 9, 4, 3, 255, 0, 0, 0, 255, 110, 75, 67, 255, 123, 83, 74, 255, 106, 70, 63, 255, 131, 83, 73, 255, 167, 103, 91, 255, +126, 76, 66, 255, 112, 62, 54, 255, 167, 96, 83, 255, 173, 98, 85, 255, 172, 95, 82, 255, 177, 96, 82, 255, 99, 50, 43, 255, 0, 0, 0, 255, + 17, 5, 4, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 20, 1, 1, 255, 0, 0, 0, 255, 158, 34, 40, 255, +195, 41, 50, 255, 189, 36, 47, 255, 191, 34, 47, 255, 191, 32, 47, 255, 192, 31, 47, 255, 192, 31, 47, 255, 191, 31, 46, 255, 190, 31, 46, 255, +185, 30, 45, 255, 190, 32, 46, 255, 104, 14, 21, 255, 0, 0, 0, 255, 19, 0, 1, 255, 0, 0, 0, 255, 12, 0, 1, 255, 0, 0, 0, 255, + 68, 7, 12, 255, 192, 35, 47, 255, 196, 35, 48, 255, 171, 29, 41, 255, 193, 35, 47, 255, 202, 37, 50, 255, 162, 27, 38, 255, 167, 29, 39, 255, +201, 37, 49, 255, 200, 37, 49, 255, 178, 32, 42, 255, 111, 17, 23, 255, 0, 0, 0, 255, 16, 0, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 7, 0, 0, 255, 10, 0, 0, 255, 0, 0, 0, 255, 148, 26, 34, 255, +152, 27, 35, 255, 136, 23, 30, 255, 136, 23, 30, 255, 147, 25, 33, 255, 176, 33, 41, 255, 155, 28, 35, 255, 167, 31, 39, 255, 183, 35, 43, 255, +171, 32, 39, 255, 169, 32, 39, 255, 174, 33, 40, 255, 181, 35, 42, 255, 121, 20, 25, 255, 0, 0, 0, 255, 24, 1, 1, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 4, 1, 1, 255, 0, 0, 0, 255, 28, 2, 2, 255, 110, 54, 47, 255, 151, 77, 66, 255, 154, 80, 68, 255, 160, 81, 69, 255, +158, 78, 67, 255, 175, 85, 73, 255, 181, 85, 73, 255, 175, 78, 68, 255, 173, 75, 65, 255, 187, 78, 68, 255, 143, 56, 49, 255, 0, 0, 0, 255, + 21, 3, 3, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 7, 0, 0, 255, 0, 0, 0, 255, 48, 4, 6, 255, 186, 31, 45, 255, +195, 32, 47, 255, 193, 33, 47, 255, 194, 33, 47, 255, 194, 34, 48, 255, 194, 34, 48, 255, 196, 35, 48, 255, 196, 35, 48, 255, 197, 35, 48, 255, +187, 33, 46, 255, 175, 31, 43, 255, 44, 3, 5, 255, 0, 0, 0, 255, 5, 0, 0, 255, 0, 0, 0, 255, 12, 0, 1, 255, 0, 0, 0, 255, + 69, 8, 12, 255, 194, 36, 47, 255, 199, 37, 48, 255, 202, 37, 49, 255, 201, 37, 49, 255, 192, 35, 46, 255, 184, 34, 44, 255, 202, 38, 49, 255, +201, 38, 48, 255, 196, 36, 47, 255, 191, 36, 46, 255, 152, 27, 35, 255, 0, 0, 0, 255, 26, 1, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 2, 0, 0, 255, 9, 0, 0, 255, 0, 0, 0, 255, 116, 19, 24, 255, 160, 30, 36, 255, +203, 40, 49, 255, 201, 40, 48, 255, 191, 38, 45, 255, 176, 34, 41, 255, 199, 40, 47, 255, 160, 31, 36, 255, 145, 28, 32, 255, 207, 43, 50, 255, +189, 38, 44, 255, 186, 37, 44, 255, 158, 31, 36, 255, 58, 7, 8, 255, 0, 0, 0, 255, 23, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 8, 1, 1, 255, 0, 0, 0, 255, 54, 22, 19, 255, 178, 78, 68, 255, 187, 80, 69, 255, 179, 70, 62, 255, 181, 67, 60, 255, +179, 63, 58, 255, 181, 60, 56, 255, 188, 59, 57, 255, 114, 34, 33, 255, 149, 39, 41, 255, 196, 50, 54, 255, 175, 40, 46, 255, 17, 1, 2, 255, + 0, 0, 0, 255, 4, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 21, 1, 1, 255, 0, 0, 0, 255, 111, 16, 24, 255, 199, 36, 49, 255, +194, 35, 48, 255, 196, 35, 48, 255, 196, 35, 48, 255, 196, 35, 48, 255, 199, 36, 49, 255, 187, 33, 46, 255, 186, 33, 45, 255, 198, 36, 48, 255, +202, 37, 49, 255, 147, 25, 34, 255, 0, 0, 0, 255, 22, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 12, 0, 1, 255, 0, 0, 0, 255, + 70, 8, 12, 255, 197, 37, 48, 255, 201, 38, 49, 255, 200, 38, 48, 255, 201, 38, 49, 255, 201, 38, 48, 255, 203, 39, 49, 255, 202, 39, 49, 255, +202, 39, 49, 255, 199, 38, 48, 255, 197, 38, 47, 255, 150, 27, 34, 255, 0, 0, 0, 255, 26, 1, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 27, 1, 2, 255, 0, 0, 0, 255, 142, 27, 32, 255, 210, 43, 51, 255, 204, 42, 49, 255, +208, 43, 50, 255, 209, 43, 50, 255, 205, 42, 49, 255, 197, 40, 47, 255, 173, 34, 40, 255, 173, 35, 40, 255, 217, 46, 52, 255, 206, 43, 49, 255, +176, 35, 41, 255, 211, 45, 50, 255, 132, 25, 29, 255, 0, 0, 0, 255, 25, 1, 1, 255, 1, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 17, 1, 1, 255, 0, 0, 0, 255, 160, 41, 43, 255, 192, 46, 51, 255, 188, 43, 49, 255, 190, 40, 49, 255, +191, 37, 48, 255, 191, 35, 47, 255, 193, 33, 47, 255, 172, 28, 41, 255, 175, 29, 42, 255, 195, 31, 47, 255, 194, 32, 47, 255, 94, 12, 19, 255, + 0, 0, 0, 255, 17, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 24, 1, 2, 255, 0, 0, 0, 255, 155, 26, 36, 255, 203, 37, 50, 255, +196, 36, 48, 255, 198, 36, 48, 255, 198, 36, 48, 255, 198, 37, 48, 255, 200, 37, 49, 255, 194, 36, 47, 255, 194, 36, 47, 255, 200, 37, 48, 255, +203, 38, 49, 255, 111, 17, 23, 255, 0, 0, 0, 255, 21, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 12, 0, 1, 255, 0, 0, 0, 255, + 71, 9, 12, 255, 199, 38, 48, 255, 204, 39, 49, 255, 203, 39, 49, 255, 203, 40, 49, 255, 204, 40, 49, 255, 200, 39, 48, 255, 203, 40, 49, 255, +205, 41, 49, 255, 203, 41, 49, 255, 214, 44, 52, 255, 152, 29, 34, 255, 0, 0, 0, 255, 26, 1, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 30, 2, 2, 255, 0, 0, 0, 255, 122, 22, 26, 255, 215, 45, 52, 255, 208, 44, 50, 255, 209, 44, 50, 255, +209, 44, 50, 255, 206, 44, 49, 255, 210, 45, 50, 255, 211, 45, 50, 255, 176, 36, 40, 255, 200, 43, 47, 255, 213, 46, 50, 255, 207, 45, 49, 255, +167, 35, 38, 255, 129, 25, 27, 255, 0, 0, 0, 255, 0, 0, 0, 255, 4, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 23, 1, 1, 255, 0, 0, 0, 255, 127, 16, 28, 255, 198, 32, 48, 255, 193, 31, 47, 255, 193, 32, 47, 255, +193, 33, 47, 255, 194, 33, 47, 255, 194, 34, 47, 255, 198, 35, 48, 255, 197, 35, 48, 255, 193, 34, 47, 255, 201, 36, 50, 255, 142, 23, 33, 255, + 0, 0, 0, 255, 24, 1, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 4, 0, 0, 255, 0, 0, 0, 255, 17, 1, 2, 255, 186, 34, 45, 255, 203, 38, 49, 255, +199, 37, 48, 255, 200, 38, 48, 255, 201, 38, 49, 255, 200, 38, 48, 255, 199, 38, 48, 255, 202, 39, 49, 255, 202, 39, 49, 255, 203, 39, 49, 255, +194, 37, 47, 255, 52, 5, 7, 255, 0, 0, 0, 255, 8, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 13, 0, 1, 255, 0, 0, 0, 255, + 72, 9, 12, 255, 201, 40, 48, 255, 206, 42, 49, 255, 205, 42, 49, 255, 205, 42, 49, 255, 208, 42, 50, 255, 194, 39, 46, 255, 204, 42, 49, 255, +208, 43, 50, 255, 206, 42, 49, 255, 214, 45, 52, 255, 154, 30, 35, 255, 0, 0, 0, 255, 27, 1, 2, 255, 0, 0, 0, 255, 1, 0, 0, 255, + 0, 0, 0, 255, 29, 2, 2, 255, 0, 0, 0, 255, 101, 18, 20, 255, 167, 34, 38, 255, 204, 44, 48, 255, 212, 46, 50, 255, 212, 46, 50, 255, +211, 46, 50, 255, 181, 38, 42, 255, 217, 48, 52, 255, 213, 47, 51, 255, 214, 47, 51, 255, 217, 48, 52, 255, 211, 47, 50, 255, 223, 50, 53, 255, +172, 37, 39, 255, 0, 0, 0, 255, 15, 0, 1, 255, 6, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 13, 0, 1, 255, 0, 0, 0, 255, 76, 9, 14, 255, 195, 35, 48, 255, 188, 33, 46, 255, 195, 35, 48, 255, +196, 35, 48, 255, 196, 35, 48, 255, 196, 35, 48, 255, 196, 35, 48, 255, 196, 35, 48, 255, 196, 35, 48, 255, 201, 37, 49, 255, 176, 31, 42, 255, + 0, 0, 0, 255, 12, 0, 0, 255, 1, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 18, 1, 1, 255, 0, 0, 0, 255, 98, 14, 19, 255, 204, 39, 50, 255, 201, 39, 49, 255, +202, 39, 49, 255, 202, 39, 49, 255, 203, 39, 49, 255, 203, 40, 49, 255, 193, 38, 46, 255, 204, 40, 49, 255, 202, 40, 48, 255, 210, 42, 50, 255, +169, 32, 39, 255, 0, 0, 0, 255, 23, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 13, 1, 1, 255, 0, 0, 0, 255, + 73, 10, 13, 255, 204, 42, 49, 255, 209, 43, 50, 255, 208, 43, 50, 255, 208, 44, 50, 255, 208, 44, 50, 255, 210, 44, 50, 255, 210, 44, 50, 255, +209, 44, 50, 255, 208, 44, 49, 255, 217, 47, 52, 255, 156, 32, 35, 255, 0, 0, 0, 255, 27, 1, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 30, 2, 2, 255, 0, 0, 0, 255, 98, 17, 19, 255, 205, 45, 48, 255, 185, 40, 43, 255, 202, 44, 48, 255, 216, 48, 51, 255, 214, 48, 51, 255, +215, 48, 51, 255, 220, 50, 52, 255, 191, 42, 44, 255, 183, 40, 42, 255, 214, 49, 50, 255, 211, 48, 49, 255, 225, 52, 53, 255, 174, 39, 39, 255, + 0, 0, 0, 255, 14, 0, 0, 255, 10, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 1, 0, 0, 255, 0, 0, 0, 255, 24, 1, 2, 255, 145, 25, 34, 255, 158, 28, 38, 255, 199, 36, 49, 255, +197, 36, 48, 255, 198, 36, 48, 255, 198, 37, 48, 255, 199, 37, 48, 255, 199, 37, 48, 255, 199, 37, 48, 255, 200, 37, 48, 255, 198, 37, 48, 255, + 78, 9, 14, 255, 0, 0, 0, 255, 14, 0, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 26, 1, 2, 255, 0, 0, 0, 255, 149, 27, 33, 255, 210, 42, 51, 255, 203, 40, 48, 255, +205, 41, 49, 255, 205, 41, 49, 255, 205, 41, 49, 255, 196, 40, 47, 255, 207, 42, 49, 255, 206, 42, 49, 255, 205, 42, 49, 255, 212, 44, 51, 255, +128, 23, 28, 255, 0, 0, 0, 255, 25, 1, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 13, 1, 1, 255, 0, 0, 0, 255, + 74, 11, 13, 255, 206, 44, 49, 255, 211, 45, 50, 255, 210, 45, 50, 255, 211, 46, 50, 255, 211, 46, 50, 255, 211, 46, 50, 255, 212, 46, 50, 255, +212, 47, 50, 255, 211, 46, 50, 255, 220, 49, 52, 255, 158, 33, 36, 255, 0, 0, 0, 255, 28, 2, 2, 255, 0, 0, 0, 255, 27, 2, 2, 255, + 0, 0, 0, 255, 72, 11, 12, 255, 206, 47, 48, 255, 218, 50, 52, 255, 221, 51, 52, 255, 218, 50, 51, 255, 216, 50, 51, 255, 218, 50, 51, 255, +213, 49, 50, 255, 211, 48, 49, 255, 191, 44, 44, 255, 208, 48, 48, 255, 188, 43, 43, 255, 217, 51, 51, 255, 185, 42, 42, 255, 0, 0, 0, 255, + 2, 0, 0, 255, 14, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 21, 1, 1, 255, 0, 0, 0, 255, 125, 21, 28, 255, 204, 38, 49, 255, 199, 37, 48, 255, +200, 38, 48, 255, 201, 38, 48, 255, 201, 38, 48, 255, 202, 38, 49, 255, 202, 39, 49, 255, 201, 38, 49, 255, 200, 38, 48, 255, 208, 40, 51, 255, +132, 22, 29, 255, 0, 0, 0, 255, 25, 1, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 2, 0, 0, 255, 11, 0, 0, 255, 0, 0, 0, 255, 185, 37, 44, 255, 211, 43, 51, 255, 206, 42, 49, 255, +207, 43, 50, 255, 208, 43, 50, 255, 208, 43, 50, 255, 205, 43, 49, 255, 208, 44, 50, 255, 208, 44, 50, 255, 210, 44, 50, 255, 205, 43, 49, 255, + 68, 9, 11, 255, 0, 0, 0, 255, 12, 0, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 14, 1, 1, 255, 0, 0, 0, 255, + 75, 11, 13, 255, 209, 46, 50, 255, 214, 47, 51, 255, 213, 47, 51, 255, 214, 47, 51, 255, 214, 48, 51, 255, 214, 48, 51, 255, 215, 48, 51, 255, +215, 48, 51, 255, 214, 48, 51, 255, 223, 51, 53, 255, 160, 34, 36, 255, 0, 0, 0, 255, 27, 1, 2, 255, 22, 1, 1, 255, 0, 0, 0, 255, + 41, 4, 5, 255, 203, 47, 47, 255, 220, 51, 52, 255, 201, 46, 46, 255, 218, 51, 51, 255, 219, 51, 51, 255, 219, 52, 52, 255, 221, 52, 52, 255, +218, 52, 51, 255, 213, 50, 50, 255, 226, 54, 53, 255, 221, 53, 52, 255, 231, 56, 54, 255, 192, 45, 44, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 17, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 18, 1, 1, 255, 0, 0, 0, 255, 97, 14, 19, 255, 203, 39, 49, 255, 203, 39, 49, 255, +202, 39, 49, 255, 202, 39, 49, 255, 204, 40, 49, 255, 197, 38, 48, 255, 199, 39, 48, 255, 205, 40, 49, 255, 203, 40, 48, 255, 211, 42, 51, 255, +173, 33, 40, 255, 0, 0, 0, 255, 22, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 16, 1, 1, 255, 0, 0, 0, 255, 84, 12, 15, 255, 208, 44, 50, 255, 210, 44, 50, 255, 209, 44, 50, 255, +210, 45, 50, 255, 210, 45, 50, 255, 210, 45, 50, 255, 212, 46, 50, 255, 211, 46, 50, 255, 210, 46, 50, 255, 217, 48, 52, 255, 182, 39, 42, 255, + 0, 0, 0, 255, 21, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 14, 1, 1, 255, 0, 0, 0, 255, + 76, 12, 13, 255, 212, 48, 50, 255, 217, 49, 51, 255, 216, 49, 51, 255, 216, 50, 51, 255, 217, 50, 51, 255, 217, 50, 51, 255, 217, 50, 51, 255, +218, 50, 51, 255, 216, 50, 51, 255, 225, 53, 53, 255, 162, 36, 36, 255, 0, 0, 0, 255, 36, 3, 3, 255, 0, 0, 0, 255, 0, 0, 0, 255, +195, 46, 45, 255, 226, 54, 53, 255, 221, 53, 52, 255, 213, 51, 50, 255, 213, 51, 50, 255, 223, 54, 52, 255, 222, 54, 52, 255, 222, 54, 52, 255, +208, 50, 48, 255, 224, 55, 52, 255, 220, 54, 51, 255, 230, 57, 54, 255, 199, 48, 46, 255, 0, 1, 0, 255, 0, 0, 0, 255, 20, 1, 1, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 3, 0, 0, 255, 1, 0, 0, 255, 0, 0, 0, 255, 189, 37, 45, 255, 203, 40, 49, 255, +203, 41, 49, 255, 204, 41, 49, 255, 210, 43, 50, 255, 179, 36, 43, 255, 187, 38, 45, 255, 210, 43, 50, 255, 206, 42, 49, 255, 204, 42, 49, 255, +200, 41, 48, 255, 57, 6, 8, 255, 0, 0, 0, 255, 9, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 26, 1, 2, 255, 0, 0, 0, 255, 140, 28, 31, 255, 218, 48, 52, 255, 210, 46, 50, 255, 212, 46, 50, 255, +212, 47, 50, 255, 213, 47, 50, 255, 213, 47, 51, 255, 213, 47, 51, 255, 214, 48, 51, 255, 212, 47, 50, 255, 221, 50, 53, 255, 142, 29, 31, 255, + 0, 0, 0, 255, 27, 2, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 14, 1, 1, 255, 0, 0, 0, 255, + 77, 13, 13, 255, 214, 50, 50, 255, 219, 51, 51, 255, 218, 51, 51, 255, 219, 51, 51, 255, 219, 52, 52, 255, 219, 52, 52, 255, 220, 52, 52, 255, +220, 52, 52, 255, 219, 52, 51, 255, 228, 55, 54, 255, 164, 37, 37, 255, 0, 0, 0, 255, 34, 2, 2, 255, 0, 0, 0, 255, 186, 44, 42, 255, +230, 56, 54, 255, 221, 54, 51, 255, 223, 55, 52, 255, 224, 55, 52, 255, 225, 55, 52, 255, 224, 55, 52, 255, 225, 55, 52, 255, 225, 56, 52, 255, +221, 55, 51, 255, 224, 56, 52, 255, 233, 58, 54, 255, 207, 51, 48, 255, 38, 5, 4, 255, 0, 0, 0, 255, 24, 2, 1, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 26, 1, 2, 255, 0, 0, 0, 255, 159, 31, 36, 255, 210, 44, 51, 255, +206, 42, 49, 255, 207, 43, 50, 255, 210, 44, 50, 255, 184, 38, 44, 255, 205, 43, 49, 255, 212, 45, 51, 255, 197, 41, 47, 255, 183, 39, 44, 255, +217, 47, 52, 255, 120, 22, 25, 255, 0, 0, 0, 255, 23, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 22, 1, 1, 255, 0, 0, 0, 255, 183, 40, 42, 255, 220, 49, 52, 255, 213, 48, 51, 255, 215, 48, 51, 255, +215, 49, 51, 255, 215, 49, 51, 255, 216, 49, 51, 255, 216, 49, 51, 255, 216, 50, 51, 255, 217, 50, 51, 255, 215, 50, 50, 255, 85, 14, 15, 255, + 0, 0, 0, 255, 16, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 15, 1, 1, 255, 0, 0, 0, 255, + 78, 14, 13, 255, 217, 52, 51, 255, 222, 53, 52, 255, 221, 53, 52, 255, 222, 53, 52, 255, 222, 54, 52, 255, 222, 54, 52, 255, 223, 54, 52, 255, +223, 54, 52, 255, 221, 54, 51, 255, 231, 57, 54, 255, 166, 39, 37, 255, 0, 0, 0, 255, 0, 0, 0, 255, 176, 42, 39, 255, 232, 58, 54, 255, +224, 55, 52, 255, 226, 56, 53, 255, 226, 56, 53, 255, 226, 57, 53, 255, 227, 57, 53, 255, 227, 57, 53, 255, 227, 57, 53, 255, 228, 58, 53, 255, +228, 58, 53, 255, 231, 59, 54, 255, 210, 53, 48, 255, 60, 10, 8, 255, 0, 0, 0, 255, 27, 2, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 22, 1, 1, 255, 0, 0, 0, 255, 113, 20, 23, 255, 214, 46, 51, 255, +209, 45, 50, 255, 210, 45, 50, 255, 211, 45, 50, 255, 208, 45, 49, 255, 211, 46, 50, 255, 212, 46, 50, 255, 212, 46, 50, 255, 212, 46, 50, 255, +219, 48, 52, 255, 168, 35, 38, 255, 0, 0, 0, 255, 27, 1, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 12, 1, 1, 255, 0, 0, 0, 255, 65, 9, 9, 255, 211, 48, 49, 255, 218, 50, 51, 255, 217, 50, 51, 255, 218, 50, 51, 255, +218, 50, 51, 255, 218, 51, 51, 255, 218, 51, 51, 255, 219, 51, 51, 255, 218, 51, 51, 255, 224, 53, 53, 255, 194, 45, 45, 255, 0, 0, 0, 255, + 17, 1, 1, 255, 1, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 15, 1, 1, 255, 0, 0, 0, 255, + 79, 14, 13, 255, 219, 54, 51, 255, 225, 55, 52, 255, 224, 55, 52, 255, 224, 55, 52, 255, 225, 55, 52, 255, 225, 56, 52, 255, 225, 56, 52, 255, +225, 56, 52, 255, 224, 56, 52, 255, 233, 59, 55, 255, 171, 41, 38, 255, 0, 0, 0, 255, 165, 40, 36, 255, 235, 60, 55, 255, 226, 57, 53, 255, +228, 58, 53, 255, 228, 58, 53, 255, 229, 58, 53, 255, 229, 59, 54, 255, 229, 59, 54, 255, 230, 59, 54, 255, 230, 59, 54, 255, 229, 59, 54, 255, +234, 61, 55, 255, 197, 50, 45, 255, 31, 4, 3, 255, 0, 0, 0, 255, 26, 2, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 7, 0, 0, 255, 0, 0, 0, 255, 42, 4, 5, 255, 202, 44, 48, 255, +215, 47, 51, 255, 212, 47, 50, 255, 213, 47, 51, 255, 214, 48, 51, 255, 214, 48, 51, 255, 214, 48, 51, 255, 215, 48, 51, 255, 214, 48, 51, 255, +219, 50, 52, 255, 201, 46, 47, 255, 20, 2, 2, 255, 0, 0, 0, 255, 5, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 26, 2, 2, 255, 0, 0, 0, 255, 132, 27, 28, 255, 225, 53, 53, 255, 218, 51, 51, 255, 220, 52, 52, 255, 220, 52, 52, 255, +220, 53, 52, 255, 221, 53, 52, 255, 221, 53, 52, 255, 221, 53, 52, 255, 220, 53, 51, 255, 229, 56, 54, 255, 157, 36, 34, 255, 0, 0, 0, 255, + 29, 2, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 15, 1, 1, 255, 0, 0, 0, 255, + 80, 15, 14, 255, 222, 55, 52, 255, 227, 57, 53, 255, 226, 57, 53, 255, 227, 57, 53, 255, 227, 57, 53, 255, 228, 58, 53, 255, 228, 58, 53, 255, +228, 58, 53, 255, 227, 58, 53, 255, 238, 61, 56, 255, 159, 38, 35, 255, 134, 31, 28, 255, 237, 61, 56, 255, 229, 59, 53, 255, 230, 59, 54, 255, +231, 60, 54, 255, 231, 60, 54, 255, 231, 60, 54, 255, 231, 60, 54, 255, 232, 60, 54, 255, 232, 60, 54, 255, 232, 60, 54, 255, 233, 61, 54, 255, +231, 61, 54, 255, 101, 22, 19, 255, 0, 0, 0, 255, 30, 2, 2, 255, 0, 0, 0, 255, 1, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 26, 1, 2, 255, 0, 0, 0, 255, 175, 38, 40, 255, +222, 51, 53, 255, 214, 49, 50, 255, 216, 49, 51, 255, 216, 50, 51, 255, 217, 50, 51, 255, 217, 50, 51, 255, 217, 50, 51, 255, 218, 50, 51, 255, +218, 50, 51, 255, 220, 51, 52, 255, 106, 20, 21, 255, 0, 0, 0, 255, 21, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 28, 2, 2, 255, 0, 0, 0, 255, 179, 42, 41, 255, 228, 55, 54, 255, 221, 53, 51, 255, 222, 54, 52, 255, 223, 54, 52, 255, +223, 54, 52, 255, 223, 55, 52, 255, 224, 55, 52, 255, 224, 55, 52, 255, 224, 55, 52, 255, 225, 56, 52, 255, 101, 20, 19, 255, 0, 0, 0, 255, + 20, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 16, 1, 1, 255, 0, 0, 0, 255, + 81, 16, 14, 255, 224, 57, 52, 255, 230, 59, 54, 255, 229, 59, 53, 255, 229, 59, 54, 255, 230, 59, 54, 255, 230, 59, 54, 255, 230, 59, 54, 255, +231, 60, 54, 255, 230, 60, 54, 255, 234, 61, 55, 255, 207, 53, 48, 255, 227, 59, 53, 255, 234, 61, 54, 255, 232, 60, 54, 255, 233, 61, 54, 255, +233, 61, 54, 255, 233, 61, 54, 255, 234, 62, 54, 255, 234, 62, 54, 255, 234, 62, 54, 255, 235, 62, 55, 255, 235, 62, 55, 255, 234, 62, 54, 255, +222, 59, 51, 255, 152, 38, 33, 255, 0, 0, 0, 255, 33, 3, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 26, 2, 2, 255, 0, 0, 0, 255, 132, 27, 28, 255, +224, 52, 53, 255, 217, 50, 51, 255, 219, 51, 51, 255, 219, 51, 51, 255, 219, 52, 52, 255, 220, 52, 52, 255, 220, 52, 52, 255, 220, 52, 52, 255, +219, 52, 51, 255, 228, 55, 54, 255, 161, 37, 36, 255, 0, 0, 0, 255, 29, 2, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 7, 0, 0, 255, 0, 0, 0, 255, 38, 4, 4, 255, 212, 52, 49, 255, 227, 56, 53, 255, 224, 55, 52, 255, 225, 56, 52, 255, 225, 56, 52, 255, +226, 56, 53, 255, 226, 56, 53, 255, 226, 57, 53, 255, 226, 57, 53, 255, 231, 58, 54, 255, 207, 52, 48, 255, 0, 0, 0, 255, 7, 0, 0, 255, + 3, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 16, 1, 1, 255, 0, 0, 0, 255, + 82, 16, 14, 255, 227, 59, 53, 255, 232, 60, 54, 255, 231, 60, 54, 255, 232, 60, 54, 255, 232, 61, 54, 255, 233, 61, 54, 255, 233, 61, 54, 255, +233, 61, 54, 255, 234, 61, 54, 255, 233, 61, 54, 255, 237, 63, 55, 255, 235, 62, 55, 255, 234, 62, 54, 255, 235, 62, 55, 255, 235, 63, 55, 255, +236, 63, 55, 255, 236, 63, 55, 255, 236, 63, 55, 255, 237, 63, 55, 255, 237, 64, 55, 255, 237, 64, 55, 255, 238, 64, 55, 255, 237, 64, 55, 255, +240, 66, 55, 255, 236, 65, 54, 255, 89, 20, 15, 255, 0, 0, 0, 255, 23, 2, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 12, 1, 1, 255, 0, 0, 0, 255, 68, 11, 10, 255, +215, 51, 51, 255, 222, 53, 52, 255, 221, 53, 52, 255, 222, 54, 52, 255, 222, 54, 52, 255, 222, 54, 52, 255, 223, 54, 52, 255, 223, 54, 52, 255, +222, 54, 52, 255, 228, 56, 53, 255, 200, 48, 46, 255, 0, 0, 0, 255, 15, 1, 1, 255, 1, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 24, 2, 1, 255, 0, 0, 0, 255, 119, 26, 24, 255, 230, 58, 54, 255, 226, 57, 53, 255, 228, 58, 53, 255, 228, 58, 53, 255, 228, 58, 53, 255, +228, 58, 53, 255, 229, 58, 53, 255, 229, 59, 54, 255, 228, 58, 53, 255, 237, 61, 56, 255, 171, 42, 38, 255, 0, 0, 0, 255, 31, 2, 2, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 16, 1, 1, 255, 0, 0, 0, 255, + 83, 17, 14, 255, 229, 60, 53, 255, 235, 62, 54, 255, 234, 62, 54, 255, 235, 62, 54, 255, 235, 62, 55, 255, 235, 62, 55, 255, 235, 63, 55, 255, +236, 63, 55, 255, 236, 63, 55, 255, 237, 63, 55, 255, 236, 63, 55, 255, 237, 64, 55, 255, 238, 64, 55, 255, 238, 64, 55, 255, 238, 65, 55, 255, +238, 66, 55, 255, 239, 66, 55, 255, 239, 67, 55, 255, 239, 68, 55, 255, 239, 69, 55, 255, 239, 70, 55, 255, 239, 71, 55, 255, 239, 72, 54, 255, +237, 73, 54, 255, 246, 77, 56, 255, 209, 65, 47, 255, 0, 0, 0, 255, 18, 2, 1, 255, 4, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 23, 1, 1, 255, 0, 0, 0, 255, +191, 46, 43, 255, 229, 56, 54, 255, 222, 54, 51, 255, 224, 55, 52, 255, 225, 55, 52, 255, 225, 56, 52, 255, 225, 56, 52, 255, 226, 56, 53, 255, +226, 56, 53, 255, 227, 57, 53, 255, 225, 56, 52, 255, 89, 17, 16, 255, 0, 0, 0, 255, 17, 1, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 31, 2, 2, 255, 0, 0, 0, 255, 173, 43, 39, 255, 237, 61, 56, 255, 228, 59, 53, 255, 228, 58, 53, 255, 230, 59, 54, 255, 231, 60, 54, 255, +231, 60, 54, 255, 231, 60, 54, 255, 231, 60, 54, 255, 231, 60, 54, 255, 235, 61, 55, 255, 117, 26, 23, 255, 0, 0, 0, 255, 24, 2, 1, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 16, 1, 1, 255, 0, 0, 0, 255, + 84, 18, 15, 255, 232, 62, 54, 255, 238, 63, 55, 255, 237, 63, 55, 255, 237, 64, 55, 255, 238, 64, 55, 255, 238, 64, 55, 255, 238, 65, 55, 255, +239, 66, 55, 255, 239, 66, 55, 255, 239, 68, 55, 255, 239, 68, 55, 255, 239, 69, 55, 255, 239, 70, 55, 255, 239, 72, 54, 255, 239, 73, 54, 255, +239, 74, 54, 255, 239, 75, 54, 255, 239, 76, 54, 255, 239, 76, 54, 255, 239, 77, 54, 255, 239, 78, 54, 255, 239, 79, 54, 255, 239, 80, 54, 255, +242, 81, 54, 255, 226, 76, 50, 255, 240, 82, 54, 255, 169, 57, 35, 255, 0, 0, 0, 255, 34, 6, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 29, 2, 2, 255, 0, 0, 0, 255, +150, 35, 32, 255, 234, 59, 55, 255, 222, 55, 52, 255, 221, 56, 52, 255, 228, 58, 53, 255, 227, 58, 53, 255, 228, 58, 53, 255, 228, 58, 53, 255, +229, 58, 53, 255, 227, 58, 53, 255, 236, 61, 55, 255, 150, 36, 32, 255, 0, 0, 0, 255, 29, 2, 2, 255, 0, 0, 0, 255, 3, 0, 0, 255, + 7, 0, 0, 255, 0, 0, 0, 255, 211, 54, 49, 255, 236, 61, 55, 255, 232, 60, 54, 255, 224, 58, 52, 255, 231, 60, 54, 255, 233, 61, 54, 255, +233, 61, 54, 255, 233, 61, 54, 255, 233, 61, 54, 255, 238, 63, 55, 255, 218, 57, 50, 255, 20, 2, 2, 255, 0, 0, 0, 255, 6, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 17, 1, 1, 255, 0, 0, 0, 255, + 85, 19, 15, 255, 234, 66, 54, 255, 240, 68, 55, 255, 238, 69, 55, 255, 239, 70, 55, 255, 239, 71, 55, 255, 239, 72, 54, 255, 239, 73, 54, 255, +239, 74, 54, 255, 239, 75, 54, 255, 239, 76, 54, 255, 239, 77, 54, 255, 239, 78, 54, 255, 239, 78, 54, 255, 239, 79, 54, 255, 240, 80, 54, 255, +240, 81, 54, 255, 240, 81, 53, 255, 240, 82, 53, 255, 240, 83, 53, 255, 240, 84, 53, 255, 240, 85, 53, 255, 240, 85, 53, 255, 240, 86, 53, 255, +241, 87, 52, 255, 238, 87, 52, 255, 244, 90, 53, 255, 230, 85, 50, 255, 83, 26, 13, 255, 0, 0, 0, 255, 22, 3, 1, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 18, 1, 1, 255, 0, 0, 0, 255, + 91, 18, 16, 255, 227, 58, 53, 255, 230, 59, 54, 255, 230, 59, 54, 255, 230, 59, 54, 255, 230, 59, 54, 255, 230, 59, 54, 255, 231, 60, 54, 255, +231, 60, 54, 255, 230, 60, 53, 255, 238, 62, 55, 255, 196, 50, 44, 255, 0, 0, 0, 255, 26, 2, 1, 255, 0, 0, 0, 255, 21, 1, 1, 255, + 0, 0, 0, 255, 103, 23, 19, 255, 234, 62, 55, 255, 234, 62, 54, 255, 234, 62, 55, 255, 236, 63, 55, 255, 236, 63, 55, 255, 236, 63, 55, 255, +236, 63, 55, 255, 236, 63, 55, 255, 235, 63, 55, 255, 244, 66, 57, 255, 185, 48, 41, 255, 0, 0, 0, 255, 32, 3, 2, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 17, 2, 1, 255, 0, 0, 0, 255, + 85, 22, 14, 255, 235, 74, 53, 255, 240, 77, 54, 255, 239, 77, 54, 255, 239, 78, 54, 255, 239, 79, 54, 255, 239, 80, 54, 255, 240, 80, 54, 255, +240, 81, 54, 255, 240, 82, 53, 255, 240, 82, 53, 255, 240, 83, 53, 255, 240, 84, 53, 255, 240, 85, 53, 255, 240, 86, 53, 255, 240, 86, 53, 255, +241, 87, 52, 255, 240, 88, 52, 255, 241, 89, 52, 255, 241, 90, 52, 255, 241, 90, 52, 255, 241, 91, 52, 255, 241, 92, 52, 255, 241, 93, 52, 255, +241, 94, 52, 255, 240, 94, 51, 255, 231, 91, 49, 255, 239, 95, 51, 255, 207, 83, 43, 255, 0, 0, 0, 255, 19, 3, 0, 255, 3, 0, 0, 255, + 1, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 2, 0, 0, 255, 16, 1, 1, 255, + 0, 0, 0, 255, 207, 53, 47, 255, 236, 61, 55, 255, 231, 60, 53, 255, 232, 60, 54, 255, 235, 61, 54, 255, 233, 61, 54, 255, 233, 61, 54, 255, +234, 61, 54, 255, 233, 61, 54, 255, 236, 62, 55, 255, 227, 60, 53, 255, 65, 12, 9, 255, 0, 0, 0, 255, 12, 1, 1, 255, 32, 3, 2, 255, + 0, 0, 0, 255, 164, 41, 36, 255, 244, 66, 57, 255, 236, 63, 55, 255, 238, 64, 55, 255, 238, 64, 55, 255, 238, 65, 55, 255, 238, 65, 55, 255, +239, 66, 55, 255, 239, 67, 55, 255, 238, 67, 54, 255, 243, 71, 56, 255, 132, 35, 26, 255, 0, 0, 0, 255, 27, 3, 2, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 17, 2, 1, 255, 0, 0, 0, 255, + 85, 25, 14, 255, 236, 80, 53, 255, 241, 83, 54, 255, 240, 83, 53, 255, 240, 84, 53, 255, 240, 85, 53, 255, 240, 86, 53, 255, 240, 87, 52, 255, +241, 87, 52, 255, 241, 88, 52, 255, 241, 89, 52, 255, 241, 90, 52, 255, 241, 91, 52, 255, 241, 92, 52, 255, 241, 92, 52, 255, 241, 93, 52, 255, +240, 94, 51, 255, 243, 96, 52, 255, 241, 96, 51, 255, 241, 96, 51, 255, 242, 97, 51, 255, 242, 98, 51, 255, 242, 99, 51, 255, 242, 100, 51, 255, +242, 101, 51, 255, 240, 101, 50, 255, 232, 98, 48, 255, 248, 106, 51, 255, 251, 108, 52, 255, 164, 69, 31, 255, 0, 0, 0, 255, 36, 10, 2, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 32, 3, 2, 255, + 0, 0, 0, 255, 168, 42, 37, 255, 243, 64, 57, 255, 233, 62, 54, 255, 234, 62, 55, 255, 225, 60, 52, 255, 236, 63, 55, 255, 236, 63, 55, 255, +236, 63, 55, 255, 237, 63, 55, 255, 236, 63, 55, 255, 243, 65, 56, 255, 137, 33, 28, 255, 0, 0, 0, 255, 28, 2, 2, 255, 23, 2, 1, 255, + 0, 0, 0, 255, 209, 58, 47, 255, 244, 70, 56, 255, 238, 69, 54, 255, 239, 70, 55, 255, 239, 71, 55, 255, 239, 72, 54, 255, 239, 74, 54, 255, +239, 75, 54, 255, 238, 75, 54, 255, 242, 77, 55, 255, 228, 73, 51, 255, 49, 11, 5, 255, 0, 0, 0, 255, 9, 1, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 17, 2, 1, 255, 0, 0, 0, 255, + 86, 27, 14, 255, 236, 87, 51, 255, 242, 90, 52, 255, 240, 90, 52, 255, 241, 91, 52, 255, 241, 92, 52, 255, 241, 93, 52, 255, 241, 93, 52, 255, +241, 94, 52, 255, 241, 95, 51, 255, 241, 96, 51, 255, 241, 97, 51, 255, 242, 97, 51, 255, 242, 98, 51, 255, 242, 99, 51, 255, 240, 99, 50, 255, +245, 102, 51, 255, 234, 98, 49, 255, 242, 102, 50, 255, 242, 103, 50, 255, 242, 104, 50, 255, 242, 104, 50, 255, 242, 105, 49, 255, 242, 106, 49, 255, +242, 106, 49, 255, 244, 108, 49, 255, 237, 105, 48, 255, 180, 78, 35, 255, 233, 104, 46, 255, 243, 110, 49, 255, 97, 41, 14, 255, 0, 0, 0, 255, + 25, 6, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 23, 2, 1, 255, + 0, 0, 0, 255, 114, 27, 22, 255, 234, 63, 55, 255, 234, 63, 54, 255, 239, 64, 55, 255, 191, 51, 45, 255, 234, 64, 54, 255, 239, 66, 55, 255, +238, 67, 55, 255, 239, 68, 55, 255, 237, 68, 54, 255, 246, 72, 57, 255, 189, 54, 42, 255, 0, 0, 0, 255, 38, 5, 3, 255, 0, 0, 0, 255, + 84, 21, 13, 255, 235, 75, 53, 255, 240, 77, 54, 255, 239, 77, 54, 255, 239, 78, 54, 255, 239, 79, 54, 255, 240, 80, 54, 255, 240, 81, 54, 255, +240, 81, 53, 255, 238, 81, 53, 255, 247, 86, 55, 255, 195, 67, 42, 255, 0, 0, 0, 255, 31, 4, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 17, 2, 1, 255, 0, 0, 0, 255, + 86, 30, 13, 255, 237, 94, 51, 255, 242, 97, 52, 255, 241, 97, 51, 255, 242, 98, 51, 255, 242, 99, 51, 255, 242, 99, 51, 255, 242, 100, 50, 255, +242, 101, 50, 255, 242, 102, 50, 255, 242, 102, 50, 255, 242, 103, 50, 255, 242, 104, 50, 255, 242, 105, 50, 255, 240, 104, 49, 255, 248, 109, 51, 255, +223, 97, 45, 255, 57, 21, 7, 255, 215, 95, 43, 255, 248, 111, 50, 255, 241, 109, 48, 255, 243, 110, 49, 255, 243, 111, 48, 255, 243, 111, 48, 255, +244, 113, 49, 255, 238, 110, 47, 255, 228, 106, 45, 255, 227, 107, 44, 255, 236, 112, 46, 255, 250, 119, 49, 255, 216, 103, 41, 255, 0, 0, 0, 255, + 16, 4, 0, 255, 4, 1, 0, 255, 1, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 5, 0, 0, 255, + 0, 0, 0, 255, 26, 2, 2, 255, 207, 59, 47, 255, 238, 70, 54, 255, 239, 71, 55, 255, 232, 70, 53, 255, 238, 73, 54, 255, 239, 74, 54, 255, +239, 75, 54, 255, 239, 76, 54, 255, 238, 77, 54, 255, 243, 79, 55, 255, 224, 73, 50, 255, 23, 4, 2, 255, 26, 3, 1, 255, 0, 0, 0, 255, +151, 49, 31, 255, 246, 85, 55, 255, 238, 82, 53, 255, 240, 84, 53, 255, 240, 85, 53, 255, 240, 85, 53, 255, 240, 86, 53, 255, 240, 87, 52, 255, +241, 88, 52, 255, 239, 88, 52, 255, 247, 92, 54, 255, 145, 51, 28, 255, 0, 0, 0, 255, 30, 5, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 17, 3, 1, 255, 0, 0, 0, 255, + 86, 32, 13, 255, 238, 100, 49, 255, 243, 103, 50, 255, 242, 103, 50, 255, 242, 104, 50, 255, 242, 105, 50, 255, 242, 105, 49, 255, 242, 106, 49, 255, +242, 107, 49, 255, 242, 107, 49, 255, 243, 108, 49, 255, 243, 109, 49, 255, 243, 110, 49, 255, 241, 109, 48, 255, 248, 114, 50, 255, 228, 105, 45, 255, + 74, 30, 9, 255, 0, 0, 0, 255, 116, 51, 18, 255, 244, 115, 48, 255, 243, 115, 48, 255, 243, 116, 48, 255, 243, 117, 47, 255, 243, 117, 47, 255, +243, 118, 47, 255, 244, 119, 47, 255, 244, 120, 47, 255, 247, 122, 47, 255, 245, 121, 46, 255, 241, 120, 45, 255, 252, 126, 47, 255, 171, 85, 29, 255, + 0, 0, 0, 255, 36, 13, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 33, 4, 2, 255, 0, 0, 0, 255, 184, 58, 40, 255, 246, 80, 56, 255, 238, 79, 54, 255, 243, 81, 55, 255, 239, 80, 54, 255, 240, 81, 54, 255, +240, 82, 53, 255, 240, 83, 53, 255, 240, 83, 53, 255, 240, 84, 53, 255, 242, 86, 53, 255, 118, 38, 21, 255, 0, 0, 0, 255, 0, 0, 0, 255, +199, 72, 42, 255, 247, 92, 54, 255, 239, 89, 52, 255, 241, 91, 52, 255, 241, 91, 52, 255, 241, 92, 52, 255, 241, 93, 52, 255, 241, 94, 52, 255, +241, 95, 51, 255, 243, 96, 52, 255, 234, 93, 50, 255, 70, 24, 9, 255, 0, 0, 0, 255, 14, 2, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 17, 3, 1, 255, 0, 0, 0, 255, + 86, 35, 12, 255, 238, 106, 48, 255, 244, 109, 49, 255, 242, 109, 49, 255, 243, 110, 49, 255, 243, 111, 49, 255, 243, 111, 48, 255, 243, 112, 48, 255, +243, 113, 48, 255, 243, 113, 48, 255, 243, 114, 48, 255, 243, 115, 48, 255, 241, 114, 48, 255, 247, 118, 48, 255, 233, 112, 45, 255, 86, 38, 11, 255, + 0, 0, 0, 255, 51, 18, 3, 255, 0, 0, 0, 255, 183, 89, 33, 255, 252, 125, 47, 255, 242, 121, 45, 255, 244, 123, 45, 255, 244, 123, 45, 255, +244, 124, 45, 255, 244, 125, 44, 255, 244, 125, 44, 255, 244, 126, 44, 255, 244, 127, 44, 255, 244, 127, 44, 255, 245, 128, 43, 255, 244, 128, 43, 255, +103, 51, 13, 255, 0, 0, 0, 255, 26, 9, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 26, 4, 1, 255, 0, 0, 0, 255, 131, 42, 25, 255, 233, 82, 52, 255, 230, 82, 51, 255, 231, 82, 51, 255, 242, 87, 53, 255, 240, 87, 52, 255, +241, 88, 52, 255, 241, 89, 52, 255, 241, 90, 52, 255, 239, 90, 51, 255, 249, 95, 54, 255, 176, 66, 36, 255, 0, 0, 0, 255, 67, 21, 8, 255, +231, 91, 49, 255, 244, 97, 52, 255, 241, 96, 51, 255, 242, 97, 51, 255, 242, 98, 51, 255, 242, 99, 51, 255, 242, 100, 51, 255, 242, 100, 50, 255, +240, 101, 50, 255, 248, 105, 52, 255, 204, 86, 41, 255, 0, 0, 0, 255, 28, 6, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 17, 4, 1, 255, 0, 0, 0, 255, + 87, 37, 12, 255, 239, 112, 47, 255, 244, 115, 48, 255, 243, 115, 48, 255, 243, 116, 48, 255, 243, 116, 48, 255, 243, 117, 47, 255, 243, 118, 47, 255, +243, 118, 47, 255, 243, 119, 46, 255, 244, 120, 46, 255, 242, 120, 46, 255, 247, 123, 46, 255, 239, 119, 44, 255, 102, 48, 14, 255, 0, 0, 0, 255, + 35, 11, 2, 255, 4, 1, 0, 255, 0, 0, 0, 255, 24, 9, 1, 255, 228, 118, 40, 255, 248, 129, 44, 255, 244, 127, 43, 255, 245, 129, 43, 255, +245, 129, 43, 255, 245, 130, 43, 255, 245, 130, 43, 255, 245, 131, 42, 255, 245, 132, 42, 255, 245, 132, 42, 255, 244, 132, 41, 255, 251, 137, 43, 255, +218, 119, 36, 255, 0, 0, 0, 255, 16, 4, 0, 255, 5, 1, 0, 255, 1, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 9, 1, 0, 255, 0, 0, 0, 255, 53, 15, 5, 255, 224, 84, 48, 255, 220, 84, 48, 255, 218, 84, 47, 255, 244, 95, 52, 255, 240, 94, 51, 255, +241, 95, 51, 255, 241, 96, 51, 255, 241, 97, 51, 255, 240, 97, 51, 255, 247, 101, 52, 255, 218, 89, 45, 255, 0, 0, 0, 255, 136, 54, 25, 255, +247, 104, 52, 255, 241, 102, 50, 255, 242, 103, 50, 255, 242, 104, 50, 255, 242, 104, 50, 255, 242, 105, 49, 255, 242, 106, 49, 255, 242, 106, 49, 255, +241, 106, 49, 255, 249, 111, 51, 255, 157, 68, 29, 255, 0, 0, 0, 255, 31, 8, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 17, 4, 1, 255, 0, 0, 0, 255, + 87, 39, 11, 255, 239, 117, 46, 255, 245, 121, 46, 255, 243, 121, 46, 255, 244, 121, 46, 255, 244, 122, 45, 255, 244, 123, 45, 255, 244, 124, 45, 255, +244, 124, 44, 255, 244, 125, 44, 255, 243, 125, 44, 255, 248, 128, 44, 255, 242, 126, 43, 255, 115, 57, 16, 255, 0, 0, 0, 255, 37, 13, 2, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 32, 11, 1, 255, 0, 0, 0, 255, 135, 71, 19, 255, 250, 135, 43, 255, 244, 132, 42, 255, 246, 134, 41, 255, +246, 134, 41, 255, 246, 135, 41, 255, 246, 136, 41, 255, 246, 136, 41, 255, 246, 137, 40, 255, 246, 138, 40, 255, 246, 138, 40, 255, 246, 139, 39, 255, +251, 142, 40, 255, 150, 84, 21, 255, 0, 0, 0, 255, 33, 14, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 29, 5, 1, 255, 0, 0, 0, 255, 190, 76, 39, 255, 234, 96, 50, 255, 245, 100, 51, 255, 241, 100, 50, 255, 242, 101, 50, 255, +242, 102, 50, 255, 242, 102, 50, 255, 242, 103, 50, 255, 242, 104, 50, 255, 243, 105, 50, 255, 239, 104, 49, 255, 48, 16, 5, 255, 185, 80, 35, 255, +250, 111, 51, 255, 241, 107, 48, 255, 243, 109, 49, 255, 243, 110, 49, 255, 243, 110, 49, 255, 243, 111, 48, 255, 243, 112, 48, 255, 243, 112, 48, 255, +244, 113, 48, 255, 239, 112, 47, 255, 86, 37, 11, 255, 0, 0, 0, 255, 17, 4, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 17, 4, 1, 255, 0, 0, 0, 255, + 87, 41, 10, 255, 240, 123, 43, 255, 245, 127, 44, 255, 244, 126, 44, 255, 245, 127, 44, 255, 245, 128, 43, 255, 245, 128, 43, 255, 245, 129, 43, 255, +245, 129, 43, 255, 245, 130, 43, 255, 246, 131, 43, 255, 242, 129, 42, 255, 129, 67, 18, 255, 0, 0, 0, 255, 38, 14, 2, 255, 0, 0, 0, 255, + 1, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 33, 13, 1, 255, 0, 0, 0, 255, 199, 110, 31, 255, 253, 143, 41, 255, 244, 138, 39, 255, +246, 140, 39, 255, 246, 140, 39, 255, 246, 140, 38, 255, 246, 141, 38, 255, 246, 141, 38, 255, 246, 142, 37, 255, 248, 143, 38, 255, 237, 137, 36, 255, +239, 138, 36, 255, 248, 143, 38, 255, 107, 60, 11, 255, 0, 0, 0, 255, 27, 11, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 30, 7, 2, 255, 0, 0, 0, 255, 149, 62, 27, 255, 250, 108, 51, 255, 239, 104, 49, 255, 242, 106, 49, 255, 242, 107, 49, 255, +244, 108, 49, 255, 243, 108, 49, 255, 243, 109, 49, 255, 242, 109, 48, 255, 244, 111, 49, 255, 232, 107, 47, 255, 132, 60, 25, 255, 220, 102, 43, 255, +248, 116, 49, 255, 242, 114, 48, 255, 243, 115, 48, 255, 243, 115, 48, 255, 243, 116, 48, 255, 243, 117, 47, 255, 243, 117, 47, 255, 242, 117, 47, 255, +249, 122, 48, 255, 213, 104, 40, 255, 0, 0, 0, 255, 24, 6, 1, 255, 1, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 17, 5, 1, 255, 0, 0, 0, 255, + 88, 43, 10, 255, 241, 128, 42, 255, 246, 132, 43, 255, 245, 131, 42, 255, 245, 132, 42, 255, 245, 133, 42, 255, 246, 133, 42, 255, 246, 134, 41, 255, +246, 135, 41, 255, 244, 134, 40, 255, 254, 141, 42, 255, 186, 102, 29, 255, 0, 0, 0, 255, 44, 19, 2, 255, 0, 0, 0, 255, 1, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 17, 6, 0, 255, 0, 0, 0, 255, 66, 35, 4, 255, 237, 137, 36, 255, 249, 144, 38, 255, +246, 142, 37, 255, 247, 143, 37, 255, 247, 143, 37, 255, 247, 143, 37, 255, 247, 143, 37, 255, 247, 143, 37, 255, 248, 144, 37, 255, 244, 142, 36, 255, +242, 141, 36, 255, 253, 148, 38, 255, 221, 128, 32, 255, 0, 0, 0, 255, 13, 4, 0, 255, 5, 2, 0, 255, 1, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 15, 3, 1, 255, 0, 0, 0, 255, 75, 30, 9, 255, 235, 107, 47, 255, 236, 108, 47, 255, 243, 112, 48, 255, 243, 113, 48, 255, +235, 110, 47, 255, 242, 114, 48, 255, 243, 115, 48, 255, 242, 115, 48, 255, 245, 117, 48, 255, 230, 110, 45, 255, 202, 97, 40, 255, 242, 118, 47, 255, +243, 119, 47, 255, 243, 120, 46, 255, 244, 120, 46, 255, 244, 121, 46, 255, 244, 122, 45, 255, 244, 123, 45, 255, 244, 123, 45, 255, 242, 123, 44, 255, +252, 129, 46, 255, 168, 84, 27, 255, 0, 0, 0, 255, 33, 11, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 18, 5, 0, 255, 0, 0, 0, 255, + 88, 45, 9, 255, 241, 133, 40, 255, 247, 137, 41, 255, 245, 137, 40, 255, 246, 137, 40, 255, 246, 138, 40, 255, 246, 139, 39, 255, 246, 139, 39, 255, +246, 140, 39, 255, 244, 139, 38, 255, 254, 145, 40, 255, 182, 103, 26, 255, 0, 0, 0, 255, 33, 14, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 35, 15, 1, 255, 0, 0, 0, 255, 155, 89, 20, 255, 254, 148, 38, 255, +245, 143, 36, 255, 247, 144, 37, 255, 247, 144, 37, 255, 247, 144, 36, 255, 247, 144, 36, 255, 247, 144, 36, 255, 247, 144, 36, 255, 248, 145, 36, 255, +248, 145, 36, 255, 245, 144, 36, 255, 255, 150, 37, 255, 175, 102, 23, 255, 0, 0, 0, 255, 37, 17, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 27, 7, 1, 255, 0, 0, 0, 255, 208, 99, 40, 255, 249, 120, 49, 255, 241, 117, 47, 255, 243, 118, 47, 255, +238, 117, 46, 255, 242, 119, 46, 255, 243, 120, 46, 255, 249, 124, 46, 255, 236, 118, 44, 255, 191, 96, 37, 255, 240, 122, 44, 255, 248, 127, 45, 255, +246, 126, 44, 255, 244, 125, 44, 255, 244, 126, 44, 255, 245, 127, 44, 255, 245, 127, 43, 255, 245, 128, 43, 255, 244, 129, 43, 255, 245, 129, 43, 255, +244, 129, 43, 255, 102, 51, 12, 255, 0, 0, 0, 255, 21, 6, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 18, 6, 0, 255, 0, 0, 0, 255, + 88, 47, 8, 255, 241, 138, 38, 255, 247, 142, 38, 255, 246, 141, 38, 255, 246, 142, 38, 255, 246, 142, 37, 255, 246, 142, 37, 255, 246, 143, 37, 255, +247, 143, 37, 255, 245, 142, 37, 255, 255, 148, 39, 255, 184, 106, 25, 255, 0, 0, 0, 255, 34, 15, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 1, 0, 0, 255, 0, 0, 0, 255, 26, 10, 1, 255, 0, 0, 0, 255, 212, 124, 30, 255, +253, 149, 37, 255, 245, 144, 36, 255, 247, 145, 36, 255, 247, 145, 36, 255, 247, 146, 36, 255, 247, 146, 36, 255, 247, 146, 36, 255, 247, 146, 36, 255, +247, 146, 35, 255, 246, 146, 35, 255, 247, 147, 35, 255, 247, 146, 35, 255, 108, 62, 10, 255, 0, 0, 0, 255, 28, 12, 1, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 32, 11, 1, 255, 0, 0, 0, 255, 163, 80, 27, 255, 251, 127, 47, 255, 243, 123, 44, 255, 247, 126, 45, 255, +248, 127, 45, 255, 249, 128, 45, 255, 244, 127, 44, 255, 236, 122, 42, 255, 181, 94, 34, 255, 223, 117, 40, 255, 233, 122, 42, 255, 230, 121, 41, 255, +237, 125, 42, 255, 246, 131, 43, 255, 245, 131, 42, 255, 245, 132, 42, 255, 245, 133, 42, 255, 245, 133, 42, 255, 244, 133, 41, 255, 251, 137, 42, 255, +221, 121, 36, 255, 0, 0, 0, 255, 16, 4, 0, 255, 2, 1, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 18, 6, 0, 255, 0, 0, 0, 255, + 88, 48, 8, 255, 242, 140, 36, 255, 248, 144, 37, 255, 246, 143, 37, 255, 247, 143, 37, 255, 247, 144, 37, 255, 247, 144, 37, 255, 247, 144, 37, 255, +247, 144, 37, 255, 245, 143, 36, 255, 255, 149, 38, 255, 183, 106, 25, 255, 0, 0, 0, 255, 34, 15, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 24, 9, 1, 255, 0, 0, 0, 255, 97, 55, 8, 255, +245, 145, 35, 255, 248, 147, 36, 255, 246, 146, 35, 255, 247, 147, 35, 255, 247, 147, 35, 255, 247, 147, 35, 255, 247, 148, 35, 255, 244, 146, 35, 255, +246, 147, 35, 255, 249, 149, 35, 255, 244, 146, 34, 255, 252, 151, 36, 255, 221, 132, 30, 255, 0, 0, 0, 255, 11, 5, 0, 255, 6, 2, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 20, 6, 1, 255, 0, 0, 0, 255, 96, 47, 12, 255, 245, 128, 43, 255, 243, 128, 43, 255, 231, 122, 41, 255, +232, 123, 41, 255, 206, 110, 37, 255, 238, 127, 41, 255, 203, 109, 36, 255, 145, 79, 29, 255, 239, 130, 41, 255, 215, 118, 37, 255, 188, 103, 33, 255, +203, 112, 35, 255, 248, 137, 41, 255, 245, 136, 40, 255, 246, 137, 40, 255, 246, 138, 40, 255, 246, 138, 39, 255, 244, 138, 39, 255, 254, 144, 40, 255, +178, 100, 26, 255, 0, 0, 0, 255, 34, 14, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 18, 6, 0, 255, 0, 0, 0, 255, + 88, 49, 8, 255, 242, 142, 36, 255, 248, 145, 37, 255, 246, 144, 36, 255, 247, 144, 36, 255, 247, 145, 36, 255, 247, 145, 36, 255, 247, 145, 36, 255, +247, 145, 36, 255, 245, 144, 36, 255, 255, 150, 37, 255, 183, 107, 25, 255, 0, 0, 0, 255, 34, 15, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 37, 17, 1, 255, 0, 0, 0, 255, +173, 102, 22, 255, 255, 152, 36, 255, 245, 146, 34, 255, 247, 148, 35, 255, 247, 148, 35, 255, 246, 148, 34, 255, 249, 149, 35, 255, 236, 141, 32, 255, +163, 96, 21, 255, 240, 144, 33, 255, 251, 151, 35, 255, 245, 148, 34, 255, 255, 155, 36, 255, 180, 108, 23, 255, 0, 0, 0, 255, 35, 17, 1, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 2, 0, 0, 255, 17, 5, 0, 255, 0, 0, 0, 255, 201, 109, 34, 255, 231, 126, 40, 255, 216, 119, 37, 255, +196, 108, 34, 255, 161, 90, 30, 255, 219, 122, 37, 255, 237, 132, 39, 255, 228, 128, 38, 255, 236, 133, 38, 255, 227, 128, 37, 255, 215, 121, 35, 255, +210, 119, 35, 255, 251, 144, 39, 255, 245, 140, 38, 255, 246, 141, 38, 255, 246, 142, 37, 255, 246, 142, 37, 255, 246, 142, 37, 255, 248, 144, 38, 255, +116, 65, 13, 255, 0, 0, 0, 255, 24, 9, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 18, 6, 0, 255, 0, 0, 0, 255, + 88, 49, 7, 255, 242, 143, 35, 255, 248, 146, 36, 255, 247, 145, 36, 255, 247, 146, 36, 255, 247, 146, 36, 255, 247, 146, 36, 255, 247, 146, 35, 255, +247, 146, 35, 255, 245, 145, 35, 255, 255, 151, 37, 255, 183, 108, 24, 255, 0, 0, 0, 255, 34, 16, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 1, 0, 0, 255, 5, 2, 0, 255, 12, 4, 0, 255, + 0, 0, 0, 255, 223, 134, 30, 255, 252, 152, 35, 255, 245, 148, 34, 255, 247, 150, 34, 255, 247, 150, 34, 255, 249, 153, 35, 255, 239, 147, 33, 255, +209, 129, 29, 255, 243, 151, 34, 255, 233, 146, 33, 255, 235, 148, 33, 255, 249, 159, 36, 255, 248, 159, 36, 255, 125, 80, 13, 255, 0, 0, 0, 255, + 25, 13, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 32, 13, 1, 255, 0, 0, 0, 255, 171, 96, 25, 255, 251, 142, 40, 255, 248, 141, 39, 255, +237, 135, 37, 255, 237, 135, 37, 255, 236, 135, 37, 255, 239, 137, 37, 255, 239, 138, 37, 255, 239, 138, 37, 255, 208, 121, 33, 255, 204, 118, 33, 255, +219, 127, 34, 255, 251, 146, 37, 255, 246, 143, 37, 255, 247, 143, 37, 255, 247, 144, 37, 255, 246, 143, 37, 255, 251, 146, 38, 255, 227, 132, 33, 255, + 0, 0, 0, 255, 1, 0, 0, 255, 5, 1, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 18, 6, 0, 255, 0, 0, 0, 255, + 88, 50, 7, 255, 242, 144, 35, 255, 248, 147, 35, 255, 247, 147, 35, 255, 247, 147, 35, 255, 247, 147, 35, 255, 247, 147, 35, 255, 247, 147, 35, 255, +247, 148, 35, 255, 245, 146, 34, 255, 255, 153, 36, 255, 183, 109, 24, 255, 0, 0, 0, 255, 34, 16, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 29, 13, 1, 255, + 0, 0, 0, 255, 120, 73, 12, 255, 250, 157, 36, 255, 247, 157, 35, 255, 248, 158, 35, 255, 248, 159, 36, 255, 248, 161, 36, 255, 247, 162, 36, 255, +250, 164, 36, 255, 242, 161, 35, 255, 242, 162, 36, 255, 249, 168, 37, 255, 249, 169, 37, 255, 250, 170, 37, 255, 146, 96, 20, 255, 0, 0, 0, 255, + 28, 17, 1, 255, 1, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 16, 5, 0, 255, 0, 0, 0, 255, 81, 45, 9, 255, 202, 117, 33, 255, 201, 116, 32, 255, +210, 122, 33, 255, 174, 101, 30, 255, 209, 121, 33, 255, 213, 124, 33, 255, 219, 127, 34, 255, 249, 145, 37, 255, 254, 148, 38, 255, 253, 148, 37, 255, +252, 147, 37, 255, 246, 144, 36, 255, 247, 144, 36, 255, 247, 144, 36, 255, 247, 144, 36, 255, 245, 144, 36, 255, 255, 150, 38, 255, 188, 109, 25, 255, + 0, 0, 0, 255, 34, 15, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 18, 6, 0, 255, 0, 0, 0, 255, + 88, 50, 7, 255, 242, 145, 34, 255, 248, 149, 35, 255, 246, 148, 35, 255, 247, 148, 34, 255, 247, 148, 34, 255, 247, 148, 34, 255, 247, 148, 34, 255, +247, 148, 34, 255, 245, 147, 34, 255, 255, 154, 35, 255, 184, 110, 23, 255, 0, 0, 0, 255, 34, 16, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 36, 19, 1, 255, 0, 0, 0, 255, 189, 126, 26, 255, 255, 172, 38, 255, 246, 167, 36, 255, 248, 169, 37, 255, 248, 170, 37, 255, 248, 172, 38, 255, +247, 173, 38, 255, 248, 174, 38, 255, 241, 170, 37, 255, 242, 172, 38, 255, 209, 148, 32, 255, 249, 179, 39, 255, 236, 172, 37, 255, 174, 127, 25, 255, + 0, 0, 0, 255, 34, 23, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 4, 1, 0, 255, 5, 1, 0, 255, 0, 0, 0, 255, 222, 129, 33, 255, 231, 135, 35, 255, +242, 141, 36, 255, 246, 143, 36, 255, 242, 142, 36, 255, 243, 143, 36, 255, 244, 143, 36, 255, 241, 141, 36, 255, 228, 134, 34, 255, 220, 130, 33, 255, +238, 140, 35, 255, 249, 147, 36, 255, 246, 145, 36, 255, 247, 146, 36, 255, 247, 146, 36, 255, 246, 145, 35, 255, 251, 148, 36, 255, 130, 75, 14, 255, + 0, 0, 0, 255, 27, 11, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 18, 6, 0, 255, 0, 0, 0, 255, + 88, 50, 7, 255, 242, 146, 34, 255, 248, 150, 34, 255, 247, 149, 34, 255, 247, 150, 34, 255, 247, 151, 34, 255, 247, 152, 35, 255, 247, 153, 35, 255, +247, 154, 35, 255, 246, 154, 35, 255, 255, 162, 37, 255, 184, 116, 24, 255, 0, 0, 0, 255, 34, 17, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 13, 6, 0, 255, 0, 0, 0, 255, 39, 25, 2, 255, 234, 167, 36, 255, 252, 181, 40, 255, 247, 179, 39, 255, 248, 181, 39, 255, 247, 181, 39, 255, +253, 187, 40, 255, 237, 176, 37, 255, 135, 100, 20, 255, 148, 110, 23, 255, 194, 147, 30, 255, 236, 180, 38, 255, 251, 192, 40, 255, 250, 193, 41, 255, +107, 82, 13, 255, 0, 0, 0, 255, 26, 18, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 33, 15, 1, 255, 0, 0, 0, 255, 192, 112, 26, 255, 255, 152, 38, 255, +245, 144, 36, 255, 247, 146, 36, 255, 248, 146, 36, 255, 247, 146, 36, 255, 239, 141, 35, 255, 222, 131, 33, 255, 200, 119, 31, 255, 165, 98, 28, 255, +236, 140, 34, 255, 249, 148, 35, 255, 246, 146, 35, 255, 247, 147, 35, 255, 246, 146, 35, 255, 250, 149, 35, 255, 233, 139, 33, 255, 40, 20, 2, 255, + 0, 0, 0, 255, 8, 3, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 18, 7, 0, 255, 0, 0, 0, 255, + 88, 53, 7, 255, 243, 153, 35, 255, 249, 158, 35, 255, 247, 158, 35, 255, 248, 159, 36, 255, 248, 161, 36, 255, 248, 162, 36, 255, 248, 163, 36, 255, +248, 164, 36, 255, 246, 164, 36, 255, 255, 172, 38, 255, 184, 123, 25, 255, 0, 0, 0, 255, 34, 19, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 33, 22, 1, 255, 0, 0, 0, 255, 142, 106, 19, 255, 254, 193, 41, 255, 247, 189, 39, 255, 248, 191, 40, 255, 249, 192, 40, 255, +239, 186, 39, 255, 246, 193, 41, 255, 243, 192, 41, 255, 239, 190, 40, 255, 249, 199, 43, 255, 229, 184, 39, 255, 212, 171, 36, 255, 254, 208, 45, 255, +164, 134, 28, 255, 0, 0, 0, 255, 29, 23, 1, 255, 2, 1, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 28, 12, 1, 255, 0, 0, 0, 255, 134, 77, 15, 255, 251, 148, 36, 255, +246, 146, 35, 255, 247, 146, 35, 255, 247, 147, 35, 255, 246, 146, 35, 255, 247, 147, 35, 255, 249, 149, 35, 255, 244, 146, 35, 255, 244, 146, 35, 255, +248, 148, 35, 255, 247, 148, 35, 255, 247, 148, 35, 255, 247, 148, 35, 255, 245, 147, 34, 255, 254, 152, 36, 255, 197, 117, 26, 255, 0, 0, 0, 255, + 33, 15, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 18, 8, 0, 255, 0, 0, 0, 255, + 89, 57, 8, 255, 243, 163, 36, 255, 249, 168, 37, 255, 248, 168, 37, 255, 248, 169, 37, 255, 248, 170, 37, 255, 248, 172, 38, 255, 248, 173, 38, 255, +248, 174, 38, 255, 246, 174, 38, 255, 255, 182, 40, 255, 184, 131, 27, 255, 0, 0, 0, 255, 34, 21, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 1, 0, 0, 255, 0, 0, 0, 255, 32, 23, 1, 255, 0, 0, 0, 255, 203, 162, 33, 255, 255, 205, 44, 255, 245, 199, 43, 255, 247, 202, 44, 255, +201, 166, 36, 255, 242, 200, 44, 255, 249, 207, 45, 255, 249, 208, 45, 255, 248, 208, 46, 255, 232, 197, 43, 255, 235, 201, 44, 255, 249, 213, 47, 255, +224, 193, 43, 255, 138, 119, 24, 255, 0, 0, 0, 255, 30, 27, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 10, 3, 0, 255, 0, 0, 0, 255, 51, 27, 3, 255, 235, 140, 33, 255, +250, 149, 35, 255, 246, 147, 35, 255, 247, 148, 35, 255, 247, 148, 34, 255, 247, 148, 34, 255, 246, 148, 34, 255, 248, 149, 34, 255, 248, 149, 34, 255, +247, 149, 34, 255, 247, 149, 34, 255, 247, 149, 34, 255, 247, 150, 34, 255, 246, 150, 34, 255, 253, 155, 36, 255, 142, 85, 16, 255, 0, 0, 0, 255, + 30, 14, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 18, 9, 0, 255, 0, 0, 0, 255, + 89, 60, 8, 255, 244, 172, 38, 255, 249, 178, 39, 255, 248, 178, 39, 255, 248, 179, 39, 255, 248, 181, 39, 255, 248, 182, 39, 255, 248, 183, 39, 255, +248, 184, 39, 255, 247, 184, 39, 255, 255, 193, 41, 255, 184, 139, 27, 255, 0, 0, 0, 255, 34, 23, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 19, 14, 1, 255, 0, 0, 0, 255, 74, 62, 8, 255, 239, 203, 45, 255, 249, 212, 47, 255, 245, 211, 47, 255, +248, 214, 47, 255, 246, 214, 47, 255, 246, 215, 48, 255, 245, 216, 48, 255, 245, 218, 48, 255, 249, 221, 49, 255, 248, 223, 49, 255, 244, 221, 49, 255, +251, 229, 51, 255, 244, 225, 50, 255, 114, 105, 19, 255, 0, 0, 0, 255, 29, 26, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 32, 14, 1, 255, 0, 0, 0, 255, 201, 120, 26, 255, +254, 153, 35, 255, 245, 148, 34, 255, 246, 149, 34, 255, 247, 150, 34, 255, 247, 151, 34, 255, 247, 152, 35, 255, 247, 153, 35, 255, 247, 154, 35, 255, +247, 155, 35, 255, 248, 156, 35, 255, 248, 158, 35, 255, 247, 158, 35, 255, 250, 161, 36, 255, 238, 155, 34, 255, 64, 39, 4, 255, 0, 0, 0, 255, + 12, 5, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 18, 10, 0, 255, 0, 0, 0, 255, + 89, 64, 9, 255, 244, 182, 39, 255, 249, 188, 40, 255, 248, 188, 39, 255, 248, 189, 40, 255, 248, 191, 40, 255, 248, 192, 40, 255, 248, 193, 41, 255, +248, 194, 41, 255, 246, 194, 41, 255, 255, 203, 44, 255, 184, 146, 29, 255, 0, 0, 0, 255, 34, 25, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 35, 30, 2, 255, 0, 0, 0, 255, 159, 142, 28, 255, 253, 228, 51, 255, 243, 220, 49, 255, +246, 225, 49, 255, 246, 226, 50, 255, 244, 226, 49, 255, 246, 229, 50, 255, 245, 230, 50, 255, 244, 230, 50, 255, 243, 231, 50, 255, 248, 237, 51, 255, +215, 206, 44, 255, 236, 226, 49, 255, 227, 219, 47, 255, 0, 0, 0, 255, 0, 0, 0, 255, 8, 7, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 31, 15, 1, 255, 0, 0, 0, 255, 150, 92, 18, 255, +254, 159, 36, 255, 249, 158, 35, 255, 252, 161, 36, 255, 250, 161, 36, 255, 247, 160, 36, 255, 248, 162, 36, 255, 248, 163, 36, 255, 248, 164, 36, 255, +248, 165, 36, 255, 248, 166, 36, 255, 248, 167, 37, 255, 246, 167, 37, 255, 255, 175, 39, 255, 205, 141, 30, 255, 0, 0, 0, 255, 31, 17, 1, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 18, 12, 0, 255, 0, 0, 0, 255, + 88, 68, 9, 255, 243, 192, 41, 255, 249, 198, 42, 255, 247, 198, 42, 255, 247, 199, 43, 255, 247, 201, 43, 255, 247, 202, 44, 255, 247, 203, 44, 255, +247, 204, 44, 255, 245, 204, 44, 255, 255, 213, 47, 255, 183, 154, 32, 255, 0, 0, 0, 255, 34, 27, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 1, 0, 0, 255, 1, 2, 0, 255, 24, 20, 1, 255, 0, 0, 0, 255, 211, 201, 43, 255, 255, 244, 53, 255, +209, 200, 43, 255, 216, 208, 45, 255, 234, 226, 49, 255, 243, 235, 51, 255, 241, 234, 50, 255, 245, 238, 51, 255, 246, 239, 51, 255, 239, 232, 50, 255, +129, 126, 26, 255, 228, 222, 47, 255, 255, 249, 54, 255, 178, 173, 35, 255, 0, 0, 0, 255, 34, 32, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 15, 7, 0, 255, 0, 0, 0, 255, 75, 47, 5, 255, +245, 164, 36, 255, 213, 142, 33, 255, 181, 121, 31, 255, 241, 163, 36, 255, 247, 169, 37, 255, 246, 169, 37, 255, 246, 171, 38, 255, 246, 172, 38, 255, +246, 173, 38, 255, 246, 175, 38, 255, 246, 176, 39, 255, 245, 176, 38, 255, 254, 184, 40, 255, 154, 112, 21, 255, 0, 0, 0, 255, 32, 20, 1, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 17, 12, 1, 255, 0, 0, 0, 255, + 88, 71, 11, 255, 241, 200, 44, 255, 246, 206, 45, 255, 245, 206, 45, 255, 245, 207, 46, 255, 245, 208, 46, 255, 245, 209, 46, 255, 245, 211, 47, 255, +245, 212, 47, 255, 243, 212, 47, 255, 252, 222, 49, 255, 181, 160, 33, 255, 0, 0, 0, 255, 34, 28, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 25, 24, 1, 255, 0, 0, 0, 255, 100, 98, 16, 255, 245, 238, 51, 255, +239, 233, 50, 255, 237, 231, 49, 255, 244, 237, 51, 255, 206, 201, 43, 255, 234, 227, 48, 255, 245, 238, 51, 255, 245, 238, 51, 255, 236, 229, 49, 255, +232, 225, 48, 255, 248, 241, 52, 255, 245, 238, 51, 255, 235, 228, 49, 255, 54, 53, 7, 255, 0, 0, 0, 255, 18, 17, 1, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 29, 17, 1, 255, 0, 0, 0, 255, +217, 154, 33, 255, 247, 178, 40, 255, 217, 158, 37, 255, 248, 180, 40, 255, 255, 187, 41, 255, 255, 187, 41, 255, 255, 189, 41, 255, 255, 190, 41, 255, +255, 191, 41, 255, 255, 193, 41, 255, 255, 193, 41, 255, 255, 197, 42, 255, 247, 190, 40, 255, 79, 59, 7, 255, 0, 0, 0, 255, 16, 10, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 18, 15, 1, 255, 0, 0, 0, 255, + 90, 78, 12, 255, 248, 219, 49, 255, 254, 225, 50, 255, 252, 225, 50, 255, 252, 227, 50, 255, 252, 228, 51, 255, 252, 229, 51, 255, 252, 231, 51, 255, +252, 232, 51, 255, 250, 232, 51, 255, 255, 243, 54, 255, 187, 175, 36, 255, 0, 0, 0, 255, 35, 32, 2, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 36, 35, 2, 255, 0, 0, 0, 255, 174, 168, 34, 255, +255, 250, 54, 255, 255, 248, 53, 255, 232, 225, 48, 255, 210, 204, 43, 255, 249, 242, 52, 255, 253, 245, 53, 255, 251, 244, 52, 255, 254, 247, 53, 255, +247, 240, 52, 255, 246, 239, 51, 255, 239, 232, 50, 255, 203, 197, 42, 255, 181, 176, 37, 255, 0, 0, 0, 255, 20, 19, 1, 255, 3, 3, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 26, 16, 1, 255, 0, 0, 0, 255, +141, 103, 18, 255, 212, 157, 32, 255, 202, 151, 30, 255, 205, 154, 31, 255, 202, 153, 31, 255, 202, 154, 31, 255, 202, 155, 31, 255, 202, 156, 32, 255, +202, 157, 32, 255, 201, 158, 32, 255, 200, 158, 32, 255, 206, 164, 34, 255, 177, 142, 28, 255, 0, 0, 0, 255, 17, 11, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 12, 10, 0, 255, 0, 0, 0, 255, + 70, 63, 8, 255, 196, 180, 38, 255, 200, 185, 39, 255, 199, 185, 39, 255, 199, 186, 39, 255, 199, 187, 39, 255, 199, 188, 40, 255, 199, 190, 40, 255, +199, 190, 40, 255, 197, 190, 40, 255, 206, 199, 42, 255, 147, 143, 28, 255, 0, 0, 0, 255, 25, 24, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 7, 6, 0, 255, 0, 0, 0, 255, 32, 31, 2, 255, +190, 184, 38, 255, 201, 195, 40, 255, 200, 194, 40, 255, 199, 193, 40, 255, 198, 193, 40, 255, 199, 194, 40, 255, 199, 193, 40, 255, 200, 194, 40, 255, +192, 187, 39, 255, 187, 182, 37, 255, 177, 172, 35, 255, 182, 177, 36, 255, 188, 183, 38, 255, 113, 110, 20, 255, 0, 0, 0, 255, 18, 17, 1, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 15, 8, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 9, 4, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 5, 4, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 13, 12, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 7, 7, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 4, 4, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 1, 0, 0, 255, 0, 0, 0, 255, + 21, 12, 0, 255, 32, 20, 1, 255, 28, 17, 1, 255, 31, 20, 1, 255, 31, 20, 1, 255, 31, 20, 1, 255, 31, 20, 1, 255, 31, 20, 1, 255, + 31, 21, 1, 255, 31, 21, 1, 255, 30, 21, 1, 255, 31, 22, 1, 255, 27, 19, 1, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 4, 3, 0, 255, 29, 25, 1, 255, 30, 26, 1, 255, 30, 26, 1, 255, 30, 27, 1, 255, 30, 27, 1, 255, 30, 27, 1, 255, 30, 27, 1, 255, + 30, 27, 1, 255, 30, 27, 1, 255, 31, 29, 2, 255, 19, 18, 1, 255, 0, 0, 0, 255, 1, 1, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 7, 7, 0, 255, + 30, 29, 1, 255, 30, 29, 1, 255, 30, 29, 1, 255, 30, 29, 1, 255, 30, 29, 1, 255, 30, 29, 1, 255, 30, 29, 1, 255, 31, 29, 1, 255, + 27, 26, 1, 255, 28, 27, 1, 255, 28, 27, 1, 255, 21, 20, 1, 255, 29, 28, 1, 255, 3, 2, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 254, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 230, 0, 0, 0, 255, 0, 0, 0, 254, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 184, 0, 0, 0, 255, 0, 0, 0, 252, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 253, 0, 0, 0, 255, 0, 0, 0, 223, + 0, 0, 0, 118, 0, 0, 0, 255, 0, 0, 0, 250, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 251, 0, 0, 0, 255, 0, 0, 0, 155, + 0, 0, 0, 37, 0, 0, 0, 252, 0, 0, 0, 255, 0, 0, 0, 254, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 252, 0, 0, 0, 255, 0, 0, 0, 68, + 0, 0, 0, 0, 0, 0, 0, 167, 0, 0, 0, 255, 0, 0, 0, 250, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 251, 0, 0, 0, 255, 0, 0, 0, 203, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 253, 0, 0, 0, 255, 0, 0, 0, 253, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 252, 0, 0, 0, 255, 0, 0, 0, 68, 0, 0, 0, 0, + 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 108, 0, 0, 0, 255, 0, 0, 0, 250, 0, 0, 0, 254, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 248, 0, 0, 0, 255, 0, 0, 0, 145, 0, 0, 0, 0, 0, 0, 0, 4, + 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 149, 0, 0, 0, 255, 0, 0, 0, 250, 0, 0, 0, 253, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 254, 0, 0, 0, 249, 0, 0, 0, 255, 0, 0, 0, 183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 145, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 250, + 0, 0, 0, 254, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 254, + 0, 0, 0, 250, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 175, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 0, 0, 0, 241, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 251, 0, 0, 0, 251, 0, 0, 0, 252, 0, 0, 0, 252, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, + 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, + 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, + 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, + 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, + 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, + 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, + 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, + 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, + 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, + 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, + 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, + 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, + 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 253, 0, 0, 0, 252, 0, 0, 0, 251, 0, 0, 0, 251, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 253, 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 143, + 0, 0, 0, 235, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 243, + 0, 0, 0, 158, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 15, 0, 0, 0, 82, 0, 0, 0, 142, 0, 0, 0, 184, 0, 0, 0, 205, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, + 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, + 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, + 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, + 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, + 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, + 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, + 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, + 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, + 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, + 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, + 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, + 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, + 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 206, 0, 0, 0, 188, 0, 0, 0, 150, 0, 0, 0, 92, 0, 0, 0, 23, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + + diff --git a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm new file mode 100644 index 00000000..6a88aed8 --- /dev/null +++ b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm @@ -0,0 +1,1168 @@ +/* + * mvk_datatypes.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKEnvironment.h" +#include "mvk_datatypes.h" +#include "MVKFoundation.h" +#include "MVKOSExtensions.h" +#include "MVKLogging.h" +#include +#include +#include + +using namespace std; + + +// Pixel normalization divisors +#define kMax1 0x1 // = (2^1 - 1) = 1 +#define kMax2 0x3 // = (2^2 - 1) = 3 +#define kMax4 0xF // = (2^4 - 1) = 15 +#define kMax5 0x1F // = (2^5 - 1) = 31 +#define kMax6 0x3F // = (2^6 - 1) = 63 +#define kMax8 0xFF // = (2^8 - 1) = 255 +#define kMax10 0x3FF // = (2^10 - 1) = 1023 +#define kMax16 0xFFFF // = (2^16 - 1) = 32767 +#define kMax24 0xFFFFFF // = (2^24 - 1) = 16777215 +#define kMax32 0xFFFFFFFF // = (2^32 - 1) = 4294967295 + + +#pragma mark - +#pragma mark Image properties + +#define MVK_FMT_DFLT_IMG_FEATS (VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT \ + | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT \ + | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT \ + | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR \ + | VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR) + +#define MVK_FMT_DFLT_CLR_FEATS (MVK_FMT_DFLT_IMG_FEATS \ + | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT \ + | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT \ + | VK_FORMAT_FEATURE_BLIT_SRC_BIT \ + | VK_FORMAT_FEATURE_BLIT_DST_BIT) + +#define MVK_FMT_DFLT_DS_FEATS (MVK_FMT_DFLT_IMG_FEATS \ + | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) + +#define MVK_FMT_DFLT_BUFF_FEATS (VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT \ + | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT \ + | VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT) + +#define MVK_FMT_ALL_CLR_FEATS { MVK_FMT_DFLT_CLR_FEATS, MVK_FMT_DFLT_CLR_FEATS, MVK_FMT_DFLT_BUFF_FEATS } + +#define MVK_FMT_ALL_CLR_VTX_FEATS { MVK_FMT_DFLT_CLR_FEATS, MVK_FMT_DFLT_CLR_FEATS, (MVK_FMT_DFLT_BUFF_FEATS | VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT) } + +#define MVK_FMT_ALL_DS_FEATS { MVK_FMT_DFLT_DS_FEATS, MVK_FMT_DFLT_DS_FEATS, MVK_FMT_DFLT_BUFF_FEATS } + +#define MVK_FMT_NO_FEATS { 0, 0, 0 } + +#define MVK_MAKE_FMT_STRUCT(VK_FMT, MTL_FMT, MTL_FMT_ALT, IOS_SINCE, MACOS_SINCE, BLK_W, BLK_H, BLK_BYTE_CNT, MTL_VTX_FMT, CLR_TYPE, PROPS) { VK_FMT, MTL_FMT, MTL_FMT_ALT, IOS_SINCE, MACOS_SINCE, { BLK_W, BLK_H }, BLK_BYTE_CNT, MTL_VTX_FMT, CLR_TYPE, PROPS, #VK_FMT, #MTL_FMT } + + +#pragma mark Texture formats + +static const MVKOSVersion kMTLFmtNA = numeric_limits::max(); + +/** Describes the properties of each VkFormat, including the corresponding Metal pixel format. */ +typedef struct { + VkFormat vk; + MTLPixelFormat mtl; + MTLPixelFormat mtlSubstitute; + MVKOSVersion sinceIOSVersion; + MVKOSVersion sinceMacOSVersion; + VkExtent2D blockTexelSize; + uint32_t bytesPerBlock; + MTLVertexFormat mtlVertexFormat; + MVKFormatType formatType; + VkFormatProperties properties; + const char* vkName; + const char* mtlName; + + inline double bytesPerTexel() const { return (double)bytesPerBlock / (double)(blockTexelSize.width * blockTexelSize.height); }; + + inline MVKOSVersion sinceOSVersion() const { +#if MVK_IOS + return sinceIOSVersion; +#endif +#if MVK_MACOS + return sinceMacOSVersion; +#endif + } + inline bool isSupported() const { return (mtl != MTLPixelFormatInvalid) && (mvkOSVersion() >= sinceOSVersion()); }; + inline bool isSupportedOrSubstitutable() const { return isSupported() || (mtlSubstitute != MTLPixelFormatInvalid); }; +} MVKFormatDesc; + +/** Mapping between Vulkan and Metal pixel formats. */ +#if MVK_MACOS +# define MTLPixelFormatABGR4Unorm MTLPixelFormatInvalid +# define MTLPixelFormatB5G6R5Unorm MTLPixelFormatInvalid +# define MTLPixelFormatA1BGR5Unorm MTLPixelFormatInvalid +# define MTLPixelFormatBGR5A1Unorm MTLPixelFormatInvalid +# define MTLPixelFormatR8Unorm_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatRG8Unorm_sRGB MTLPixelFormatInvalid + +# define MTLPixelFormatETC2_RGB8 MTLPixelFormatInvalid +# define MTLPixelFormatETC2_RGB8_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatETC2_RGB8A1 MTLPixelFormatInvalid +# define MTLPixelFormatETC2_RGB8A1_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatEAC_RGBA8 MTLPixelFormatInvalid +# define MTLPixelFormatEAC_RGBA8_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatEAC_R11Unorm MTLPixelFormatInvalid +# define MTLPixelFormatEAC_R11Snorm MTLPixelFormatInvalid +# define MTLPixelFormatEAC_RG11Unorm MTLPixelFormatInvalid +# define MTLPixelFormatEAC_RG11Snorm MTLPixelFormatInvalid + +# define MTLPixelFormatASTC_4x4_LDR MTLPixelFormatInvalid +# define MTLPixelFormatASTC_4x4_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatASTC_5x4_LDR MTLPixelFormatInvalid +# define MTLPixelFormatASTC_5x4_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatASTC_5x5_LDR MTLPixelFormatInvalid +# define MTLPixelFormatASTC_5x5_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatASTC_6x5_LDR MTLPixelFormatInvalid +# define MTLPixelFormatASTC_6x5_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatASTC_6x6_LDR MTLPixelFormatInvalid +# define MTLPixelFormatASTC_6x6_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatASTC_8x5_LDR MTLPixelFormatInvalid +# define MTLPixelFormatASTC_8x5_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatASTC_8x6_LDR MTLPixelFormatInvalid +# define MTLPixelFormatASTC_8x6_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatASTC_8x8_LDR MTLPixelFormatInvalid +# define MTLPixelFormatASTC_8x8_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatASTC_10x5_LDR MTLPixelFormatInvalid +# define MTLPixelFormatASTC_10x5_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatASTC_10x6_LDR MTLPixelFormatInvalid +# define MTLPixelFormatASTC_10x6_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatASTC_10x8_LDR MTLPixelFormatInvalid +# define MTLPixelFormatASTC_10x8_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatASTC_10x10_LDR MTLPixelFormatInvalid +# define MTLPixelFormatASTC_10x10_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatASTC_12x10_LDR MTLPixelFormatInvalid +# define MTLPixelFormatASTC_12x10_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatASTC_12x12_LDR MTLPixelFormatInvalid +# define MTLPixelFormatASTC_12x12_sRGB MTLPixelFormatInvalid + +# define MTLPixelFormatPVRTC_RGB_2BPP MTLPixelFormatInvalid +# define MTLPixelFormatPVRTC_RGB_2BPP_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatPVRTC_RGB_4BPP MTLPixelFormatInvalid +# define MTLPixelFormatPVRTC_RGB_4BPP_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatPVRTC_RGBA_2BPP MTLPixelFormatInvalid +# define MTLPixelFormatPVRTC_RGBA_2BPP_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatPVRTC_RGBA_4BPP MTLPixelFormatInvalid +# define MTLPixelFormatPVRTC_RGBA_4BPP_sRGB MTLPixelFormatInvalid + +# define MTLPixelFormatDepth16Unorm_Stencil8 MTLPixelFormatDepth24Unorm_Stencil8 +#endif + +#if MVK_IOS +# define MTLPixelFormatDepth16Unorm MTLPixelFormatInvalid +# define MTLPixelFormatDepth24Unorm_Stencil8 MTLPixelFormatInvalid +# define MTLPixelFormatBC1_RGBA MTLPixelFormatInvalid +# define MTLPixelFormatBC1_RGBA_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatBC2_RGBA MTLPixelFormatInvalid +# define MTLPixelFormatBC2_RGBA_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatBC3_RGBA MTLPixelFormatInvalid +# define MTLPixelFormatBC3_RGBA_sRGB MTLPixelFormatInvalid +# define MTLPixelFormatBC4_RUnorm MTLPixelFormatInvalid +# define MTLPixelFormatBC4_RSnorm MTLPixelFormatInvalid +# define MTLPixelFormatBC5_RGUnorm MTLPixelFormatInvalid +# define MTLPixelFormatBC5_RGSnorm MTLPixelFormatInvalid +# define MTLPixelFormatBC6H_RGBUfloat MTLPixelFormatInvalid +# define MTLPixelFormatBC6H_RGBFloat MTLPixelFormatInvalid +# define MTLPixelFormatBC7_RGBAUnorm MTLPixelFormatInvalid +# define MTLPixelFormatBC7_RGBAUnorm_sRGB MTLPixelFormatInvalid + +# define MTLPixelFormatDepth16Unorm_Stencil8 MTLPixelFormatDepth32Float_Stencil8 +#endif + + +static const MVKFormatDesc _formatDescriptions[] { + MVK_MAKE_FMT_STRUCT( VK_FORMAT_UNDEFINED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 0, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_NO_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R4G4_UNORM_PACK8, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 1, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R4G4B4A4_UNORM_PACK16, MTLPixelFormatABGR4Unorm, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 1, 1, 2, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), // Vulkan packed is reversed + MVK_MAKE_FMT_STRUCT( VK_FORMAT_B4G4R4A4_UNORM_PACK16, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 2, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R5G6B5_UNORM_PACK16, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 2, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_B5G6R5_UNORM_PACK16, MTLPixelFormatB5G6R5Unorm, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 1, 1, 2, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R5G5B5A1_UNORM_PACK16, MTLPixelFormatA1BGR5Unorm, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 1, 1, 2, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_B5G5R5A1_UNORM_PACK16, MTLPixelFormatBGR5A1Unorm, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 1, 1, 2, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_A1R5G5B5_UNORM_PACK16, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 2, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8_UNORM, MTLPixelFormatR8Unorm, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 1, MTLVertexFormatUChar2Normalized, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8_SNORM, MTLPixelFormatR8Snorm, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 1, MTLVertexFormatChar2Normalized, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8_USCALED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 1, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8_SSCALED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 1, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8_UINT, MTLPixelFormatR8Uint, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 1, MTLVertexFormatUChar2, kMVKFormatColorUInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8_SINT, MTLPixelFormatR8Sint, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 1, MTLVertexFormatChar2, kMVKFormatColorInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8_SRGB, MTLPixelFormatR8Unorm_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 1, 1, 1, MTLVertexFormatUChar2, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8_UNORM, MTLPixelFormatRG8Unorm, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 2, MTLVertexFormatUChar2Normalized, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8_SNORM, MTLPixelFormatRG8Snorm, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 2, MTLVertexFormatChar2Normalized, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8_USCALED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 2, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8_SSCALED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 2, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8_UINT, MTLPixelFormatRG8Uint, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 2, MTLVertexFormatUChar2, kMVKFormatColorUInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8_SINT, MTLPixelFormatRG8Sint, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 2, MTLVertexFormatChar2, kMVKFormatColorInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8_SRGB, MTLPixelFormatRG8Unorm_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 1, 1, 2, MTLVertexFormatUChar2, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8B8_UNORM, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 3, MTLVertexFormatUChar3Normalized, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8B8_SNORM, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 3, MTLVertexFormatChar3Normalized, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8B8_USCALED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 3, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8B8_SSCALED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 3, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8B8_UINT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 3, MTLVertexFormatUChar3, kMVKFormatColorUInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8B8_SINT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 3, MTLVertexFormatChar3, kMVKFormatColorInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8B8_SRGB, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 3, MTLVertexFormatUChar3, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_B8G8R8_UNORM, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 3, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_B8G8R8_SNORM, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 3, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_B8G8R8_USCALED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 3, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_B8G8R8_SSCALED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 3, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_B8G8R8_UINT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 3, MTLVertexFormatInvalid, kMVKFormatColorUInt, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_B8G8R8_SINT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 3, MTLVertexFormatInvalid, kMVKFormatColorInt, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_B8G8R8_SRGB, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 3, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8B8A8_UNORM, MTLPixelFormatRGBA8Unorm, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 4, MTLVertexFormatUChar4Normalized, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8B8A8_SNORM, MTLPixelFormatRGBA8Snorm, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 4, MTLVertexFormatChar4Normalized, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8B8A8_USCALED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8B8A8_SSCALED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8B8A8_UINT, MTLPixelFormatRGBA8Uint, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 4, MTLVertexFormatUChar4, kMVKFormatColorUInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8B8A8_SINT, MTLPixelFormatRGBA8Sint, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 4, MTLVertexFormatChar4, kMVKFormatColorInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R8G8B8A8_SRGB, MTLPixelFormatRGBA8Unorm_sRGB, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 4, MTLVertexFormatUChar4, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_B8G8R8A8_UNORM, MTLPixelFormatBGRA8Unorm, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_B8G8R8A8_SNORM, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_B8G8R8A8_USCALED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_B8G8R8A8_SSCALED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_B8G8R8A8_UINT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorUInt, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_B8G8R8A8_SINT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorInt, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_B8G8R8A8_SRGB, MTLPixelFormatBGRA8Unorm_sRGB, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_A8B8G8R8_UNORM_PACK32, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorUInt, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_A8B8G8R8_SNORM_PACK32, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorInt, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_A8B8G8R8_USCALED_PACK32, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorUInt, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_A8B8G8R8_SSCALED_PACK32, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorInt, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_A8B8G8R8_UINT_PACK32, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorUInt, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_A8B8G8R8_SINT_PACK32, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorInt, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_A8B8G8R8_SRGB_PACK32, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorUInt, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_A2R10G10B10_UNORM_PACK32, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_A2R10G10B10_SNORM_PACK32, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_A2R10G10B10_USCALED_PACK32, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_A2R10G10B10_SSCALED_PACK32, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_A2R10G10B10_UINT_PACK32, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorUInt, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_A2R10G10B10_SINT_PACK32, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorInt, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_A2B10G10R10_UNORM_PACK32, MTLPixelFormatRGB10A2Unorm, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), // Vulkan packed is reversed + MVK_MAKE_FMT_STRUCT( VK_FORMAT_A2B10G10R10_SNORM_PACK32, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_A2B10G10R10_USCALED_PACK32, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_A2B10G10R10_SSCALED_PACK32, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_A2B10G10R10_UINT_PACK32, MTLPixelFormatRGB10A2Uint, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorUInt, MVK_FMT_ALL_CLR_FEATS ), // Vulkan packed is reversed + MVK_MAKE_FMT_STRUCT( VK_FORMAT_A2B10G10R10_SINT_PACK32, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorInt, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16_UNORM, MTLPixelFormatR16Unorm, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 2, MTLVertexFormatUShort2Normalized, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16_SNORM, MTLPixelFormatR16Snorm, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 2, MTLVertexFormatShort2Normalized, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16_USCALED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 2, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16_SSCALED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 2, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16_UINT, MTLPixelFormatR16Uint, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 2, MTLVertexFormatUShort2, kMVKFormatColorUInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16_SINT, MTLPixelFormatR16Sint, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 2, MTLVertexFormatShort2, kMVKFormatColorInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16_SFLOAT, MTLPixelFormatR16Float, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 2, MTLVertexFormatHalf2, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16_UNORM, MTLPixelFormatRG16Unorm, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 4, MTLVertexFormatUShort2Normalized, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16_SNORM, MTLPixelFormatRG16Snorm, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 4, MTLVertexFormatShort2Normalized, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16_USCALED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16_SSCALED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16_UINT, MTLPixelFormatRG16Uint, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 4, MTLVertexFormatUShort2, kMVKFormatColorUInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16_SINT, MTLPixelFormatRG16Sint, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 4, MTLVertexFormatShort2, kMVKFormatColorInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16_SFLOAT, MTLPixelFormatRG16Float, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 4, MTLVertexFormatHalf2, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16B16_UNORM, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 6, MTLVertexFormatUShort3Normalized, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16B16_SNORM, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 6, MTLVertexFormatShort3Normalized, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16B16_USCALED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 6, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16B16_SSCALED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 6, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16B16_UINT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 6, MTLVertexFormatUShort3, kMVKFormatColorUInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16B16_SINT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 6, MTLVertexFormatShort3, kMVKFormatColorInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16B16_SFLOAT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 6, MTLVertexFormatHalf3, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16B16A16_UNORM, MTLPixelFormatRGBA16Unorm, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 8, MTLVertexFormatUShort4Normalized, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16B16A16_SNORM, MTLPixelFormatRGBA16Snorm, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 8, MTLVertexFormatShort4Normalized, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16B16A16_USCALED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 8, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16B16A16_SSCALED, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 8, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16B16A16_UINT, MTLPixelFormatRGBA16Uint, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 8, MTLVertexFormatUShort4, kMVKFormatColorUInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16B16A16_SINT, MTLPixelFormatRGBA16Sint, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 8, MTLVertexFormatShort4, kMVKFormatColorInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R16G16B16A16_SFLOAT, MTLPixelFormatRGBA16Float, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 8, MTLVertexFormatHalf4, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R32_UINT, MTLPixelFormatR32Uint, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 4, MTLVertexFormatUInt, kMVKFormatColorUInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R32_SINT, MTLPixelFormatR32Sint, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 4, MTLVertexFormatInt, kMVKFormatColorInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R32_SFLOAT, MTLPixelFormatR32Float, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 4, MTLVertexFormatFloat, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R32G32_UINT, MTLPixelFormatRG32Uint, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 8, MTLVertexFormatUInt2, kMVKFormatColorUInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R32G32_SINT, MTLPixelFormatRG32Sint, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 8, MTLVertexFormatInt2, kMVKFormatColorInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R32G32_SFLOAT, MTLPixelFormatRG32Float, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 8, MTLVertexFormatFloat2, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R32G32B32_UINT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 12, MTLVertexFormatUInt3, kMVKFormatColorUInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R32G32B32_SINT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 12, MTLVertexFormatInt3, kMVKFormatColorInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R32G32B32_SFLOAT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 12, MTLVertexFormatFloat3, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R32G32B32A32_UINT, MTLPixelFormatRGBA32Uint, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 16, MTLVertexFormatUInt4, kMVKFormatColorUInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R32G32B32A32_SINT, MTLPixelFormatRGBA32Sint, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 16, MTLVertexFormatInt4, kMVKFormatColorInt, MVK_FMT_ALL_CLR_VTX_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R32G32B32A32_SFLOAT, MTLPixelFormatRGBA32Float, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 16, MTLVertexFormatFloat4, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_VTX_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R64_UINT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 8, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R64_SINT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 8, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R64_SFLOAT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 8, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R64G64_UINT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 16, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R64G64_SINT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 16, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R64G64_SFLOAT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 16, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R64G64B64_UINT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 24, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R64G64B64_SINT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 24, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R64G64B64_SFLOAT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 24, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R64G64B64A64_UINT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 32, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R64G64B64A64_SINT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 32, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_R64G64B64A64_SFLOAT, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 1, 1, 32, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_B10G11R11_UFLOAT_PACK32, MTLPixelFormatRG11B10Float, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), // Vulkan packed is reversed + MVK_MAKE_FMT_STRUCT( VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, MTLPixelFormatRGB9E5Float, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), // Vulkan packed is reversed + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_D32_SFLOAT, MTLPixelFormatDepth32Float, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatDepthStencil, MVK_FMT_ALL_DS_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_D32_SFLOAT_S8_UINT, MTLPixelFormatDepth32Float_Stencil8, MTLPixelFormatInvalid, 9.0, 10.11, 1, 1, 5, MTLVertexFormatInvalid, kMVKFormatDepthStencil, MVK_FMT_ALL_DS_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_S8_UINT, MTLPixelFormatStencil8, MTLPixelFormatInvalid, 8.0, 10.11, 1, 1, 1, MTLVertexFormatInvalid, kMVKFormatDepthStencil, MVK_FMT_ALL_DS_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_D16_UNORM, MTLPixelFormatDepth16Unorm, MTLPixelFormatDepth32Float, kMTLFmtNA, 10.12, 1, 1, 2, MTLVertexFormatInvalid, kMVKFormatDepthStencil, MVK_FMT_ALL_DS_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_D16_UNORM_S8_UINT, MTLPixelFormatInvalid, MTLPixelFormatDepth16Unorm_Stencil8, kMTLFmtNA, kMTLFmtNA, 1, 1, 3, MTLVertexFormatInvalid, kMVKFormatDepthStencil, MVK_FMT_ALL_DS_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_D24_UNORM_S8_UINT, MTLPixelFormatDepth24Unorm_Stencil8, MTLPixelFormatDepth32Float_Stencil8, kMTLFmtNA, 10.11, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatDepthStencil, MVK_FMT_ALL_DS_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_X8_D24_UNORM_PACK32, MTLPixelFormatInvalid, MTLPixelFormatDepth24Unorm_Stencil8, kMTLFmtNA, kMTLFmtNA, 1, 1, 4, MTLVertexFormatInvalid, kMVKFormatDepthStencil, MVK_FMT_ALL_DS_FEATS ), // Vulkan packed is reversed + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_BC1_RGB_UNORM_BLOCK, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 4, 4, 8, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_BC1_RGB_SRGB_BLOCK, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 4, 4, 8, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_BC1_RGBA_UNORM_BLOCK, MTLPixelFormatBC1_RGBA, MTLPixelFormatInvalid, kMTLFmtNA, 10.11, 4, 4, 8, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_BC1_RGBA_SRGB_BLOCK, MTLPixelFormatBC1_RGBA_sRGB, MTLPixelFormatInvalid, kMTLFmtNA, 10.11, 4, 4, 8, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_BC2_UNORM_BLOCK, MTLPixelFormatBC2_RGBA, MTLPixelFormatInvalid, kMTLFmtNA, 10.11, 4, 4, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_BC2_SRGB_BLOCK, MTLPixelFormatBC2_RGBA_sRGB, MTLPixelFormatInvalid, kMTLFmtNA, 10.11, 4, 4, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_BC3_UNORM_BLOCK, MTLPixelFormatBC3_RGBA, MTLPixelFormatInvalid, kMTLFmtNA, 10.11, 4, 4, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_BC3_SRGB_BLOCK, MTLPixelFormatBC3_RGBA_sRGB, MTLPixelFormatInvalid, kMTLFmtNA, 10.11, 4, 4, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_BC4_UNORM_BLOCK, MTLPixelFormatBC4_RUnorm, MTLPixelFormatInvalid, kMTLFmtNA, 10.11, 4, 4, 8, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_BC4_SNORM_BLOCK, MTLPixelFormatBC4_RSnorm, MTLPixelFormatInvalid, kMTLFmtNA, 10.11, 4, 4, 8, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_BC5_UNORM_BLOCK, MTLPixelFormatBC5_RGUnorm, MTLPixelFormatInvalid, kMTLFmtNA, 10.11, 4, 4, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_BC5_SNORM_BLOCK, MTLPixelFormatBC5_RGSnorm, MTLPixelFormatInvalid, kMTLFmtNA, 10.11, 4, 4, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_BC6H_UFLOAT_BLOCK, MTLPixelFormatBC6H_RGBUfloat, MTLPixelFormatInvalid, kMTLFmtNA, 10.11, 4, 4, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_BC6H_SFLOAT_BLOCK, MTLPixelFormatBC6H_RGBFloat, MTLPixelFormatInvalid, kMTLFmtNA, 10.11, 4, 4, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_BC7_UNORM_BLOCK, MTLPixelFormatBC7_RGBAUnorm, MTLPixelFormatInvalid, kMTLFmtNA, 10.11, 4, 4, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_BC7_SRGB_BLOCK, MTLPixelFormatBC7_RGBAUnorm_sRGB, MTLPixelFormatInvalid, kMTLFmtNA, 10.11, 4, 4, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, MTLPixelFormatETC2_RGB8, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 4, 4, 8, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, MTLPixelFormatETC2_RGB8_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 4, 4, 8, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, MTLPixelFormatETC2_RGB8A1, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 4, 4, 8, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, MTLPixelFormatETC2_RGB8A1_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 4, 4, 8, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, MTLPixelFormatEAC_RGBA8, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 4, 4, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, MTLPixelFormatEAC_RGBA8_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 4, 4, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_EAC_R11_UNORM_BLOCK, MTLPixelFormatEAC_R11Unorm, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 4, 4, 8, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_EAC_R11_SNORM_BLOCK, MTLPixelFormatEAC_R11Snorm, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 4, 4, 8, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_EAC_R11G11_UNORM_BLOCK, MTLPixelFormatEAC_RG11Unorm, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 4, 4, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_EAC_R11G11_SNORM_BLOCK, MTLPixelFormatEAC_RG11Snorm, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 4, 4, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_4x4_UNORM_BLOCK, MTLPixelFormatASTC_4x4_LDR, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 4, 4, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_4x4_SRGB_BLOCK, MTLPixelFormatASTC_4x4_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 4, 4, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_5x4_UNORM_BLOCK, MTLPixelFormatASTC_5x4_LDR, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 5, 4, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_5x4_SRGB_BLOCK, MTLPixelFormatASTC_5x4_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 5, 4, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_5x5_UNORM_BLOCK, MTLPixelFormatASTC_5x5_LDR, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 5, 5, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_5x5_SRGB_BLOCK, MTLPixelFormatASTC_5x5_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 5, 5, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_6x5_UNORM_BLOCK, MTLPixelFormatASTC_6x5_LDR, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 6, 5, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_6x5_SRGB_BLOCK, MTLPixelFormatASTC_6x5_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 6, 5, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_6x6_UNORM_BLOCK, MTLPixelFormatASTC_6x6_LDR, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 6, 6, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_6x6_SRGB_BLOCK, MTLPixelFormatASTC_6x6_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 6, 6, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_8x5_UNORM_BLOCK, MTLPixelFormatASTC_8x5_LDR, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 8, 5, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_8x5_SRGB_BLOCK, MTLPixelFormatASTC_8x5_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 8, 5, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_8x6_UNORM_BLOCK, MTLPixelFormatASTC_8x6_LDR, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 8, 6, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_8x6_SRGB_BLOCK, MTLPixelFormatASTC_8x6_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 8, 6, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_8x8_UNORM_BLOCK, MTLPixelFormatASTC_8x8_LDR, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 8, 8, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_8x8_SRGB_BLOCK, MTLPixelFormatASTC_8x8_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 8, 8, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_10x5_UNORM_BLOCK, MTLPixelFormatASTC_10x5_LDR, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 10, 5, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_10x5_SRGB_BLOCK, MTLPixelFormatASTC_10x5_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 10, 5, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_10x6_UNORM_BLOCK, MTLPixelFormatASTC_10x6_LDR, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 10, 6, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_10x6_SRGB_BLOCK, MTLPixelFormatASTC_10x6_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 10, 6, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_10x8_UNORM_BLOCK, MTLPixelFormatASTC_10x8_LDR, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 10, 8, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_10x8_SRGB_BLOCK, MTLPixelFormatASTC_10x8_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 10, 8, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_10x10_UNORM_BLOCK, MTLPixelFormatASTC_10x10_LDR, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 10, 10, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_10x10_SRGB_BLOCK, MTLPixelFormatASTC_10x10_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 10, 10, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_12x10_UNORM_BLOCK, MTLPixelFormatASTC_12x10_LDR, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 12, 10, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_12x10_SRGB_BLOCK, MTLPixelFormatASTC_12x10_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 12, 10, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_12x12_UNORM_BLOCK, MTLPixelFormatASTC_12x12_LDR, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 12, 12, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_ASTC_12x12_SRGB_BLOCK, MTLPixelFormatASTC_12x12_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 12, 12, 16, MTLVertexFormatInvalid, kMVKFormatNone, MVK_FMT_ALL_CLR_FEATS ), + + // Extension VK_IMG_format_pvrtc + MVK_MAKE_FMT_STRUCT( VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG, MTLPixelFormatPVRTC_RGBA_2BPP, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 8, 4, 8, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG, MTLPixelFormatPVRTC_RGBA_4BPP, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 8, 4, 8, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 4, 4, 8, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 4, 4, 8, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG, MTLPixelFormatPVRTC_RGBA_2BPP_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 8, 4, 8, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG, MTLPixelFormatPVRTC_RGBA_4BPP_sRGB, MTLPixelFormatInvalid, 8.0, kMTLFmtNA, 8, 4, 8, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 4, 4, 8, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG, MTLPixelFormatInvalid, MTLPixelFormatInvalid, kMTLFmtNA, kMTLFmtNA, 4, 4, 8, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + + // Future extension VK_KHX_color_conversion and Vulkan 1.1. + MVK_MAKE_FMT_STRUCT( VK_FORMAT_UNDEFINED, MTLPixelFormatGBGR422, MTLPixelFormatInvalid, 8.0, 10.11, 2, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), + MVK_MAKE_FMT_STRUCT( VK_FORMAT_UNDEFINED, MTLPixelFormatBGRG422, MTLPixelFormatInvalid, 8.0, 10.11, 2, 1, 4, MTLVertexFormatInvalid, kMVKFormatColorFloat, MVK_FMT_ALL_CLR_FEATS ), +}; + +static const uint32_t _vkFormatCoreCount = VK_FORMAT_ASTC_12x12_SRGB_BLOCK + 1; +static const uint32_t _mtlFormatCount = MTLPixelFormatX32_Stencil8 + 2; // The actual last enum value is not available on iOS + +// Map for mapping large VkFormat values to an index. +typedef unordered_map MVKFormatIndexByVkFormatMap; + +// Vulkan core formats have small values and are mapped by simple lookup array. +// Vulkan extension formats have larger values and are mapped by a map. +static uint16_t _fmtDescIndicesByVkFormatsCore[_vkFormatCoreCount]; +static MVKFormatIndexByVkFormatMap _fmtDescIndicesByVkFormatsExt; + +// Metal formats have small values and are mapped by simple lookup array. +static uint16_t _fmtDescIndicesByMTLPixelFormats[_mtlFormatCount]; + +/** + * Populates the lookup maps that map Vulkan and Metal pixel formats to one-another. + * + * Because both Metal and core Vulkan format value are enumerations that start at zero and are + * more or less consecutively enumerated, we can use a simple lookup array in each direction + * to map the value in one architecture (as an array index) to the corresponding value in the + * other architecture. Values that exist in one API but not the other are given a default value. + * + * Vulkan extension formats have very large values, and are tracked in a separate map. + */ +static void MVKInitFormatMaps() { + + // Set all VkFormats and MTLPixelFormats to undefined/invalid + memset(_fmtDescIndicesByVkFormatsCore, 0, sizeof(_fmtDescIndicesByVkFormatsCore)); + memset(_fmtDescIndicesByMTLPixelFormats, 0, sizeof(_fmtDescIndicesByMTLPixelFormats)); + + _fmtDescIndicesByVkFormatsExt = MVKFormatIndexByVkFormatMap(); + + // Iterate through the format descriptions and populate the lookup maps. + uint32_t fmtCnt = sizeof(_formatDescriptions) / sizeof(MVKFormatDesc); + for (uint32_t fmtIdx = 0; fmtIdx < fmtCnt; fmtIdx++) { + + // Access the mapping + const MVKFormatDesc& tfm = _formatDescriptions[fmtIdx]; + + // If the Vulkan format is defined, create a lookup between the Vulkan format + // and an index to the format info. For core Vulkan formats, which are small + // and consecutive, use a simple lookup array. For extension formats, use a map. + if (tfm.vk != VK_FORMAT_UNDEFINED) { + if (tfm.vk < _vkFormatCoreCount) { + _fmtDescIndicesByVkFormatsCore[tfm.vk] = fmtIdx; + } else { + _fmtDescIndicesByVkFormatsExt[tfm.vk] = fmtIdx; + } + } + + // If the Metal format is defined, create a lookup between the Metal format and an + // index to the format info. Metal formats are small, so use a simple lookup array. + if (tfm.mtl != MTLPixelFormatInvalid) { _fmtDescIndicesByMTLPixelFormats[tfm.mtl] = fmtIdx; } + } +} + +// Return a reference to the format description corresponding to the VkFormat. +inline const MVKFormatDesc& formatDescForVkFormat(VkFormat vkFormat) { + uint16_t fmtIdx = (vkFormat < _vkFormatCoreCount) ? _fmtDescIndicesByVkFormatsCore[vkFormat] : _fmtDescIndicesByVkFormatsExt[vkFormat]; + return _formatDescriptions[fmtIdx]; +} + +// Return a reference to the format description corresponding to the MTLPixelFormat. +inline const MVKFormatDesc& formatDescForMTLPixelFormat(MTLPixelFormat mtlFormat) { + uint16_t fmtIdx = _fmtDescIndicesByMTLPixelFormats[mtlFormat]; + return _formatDescriptions[fmtIdx]; +} + +MVK_PUBLIC_SYMBOL MTLPixelFormat mvkMTLPixelFormatFromVkFormat(VkFormat vkFormat) { + MTLPixelFormat mtlPixFmt = MTLPixelFormatInvalid; + + const MVKFormatDesc& fmtDesc = formatDescForVkFormat(vkFormat); + if (fmtDesc.isSupported()) { + mtlPixFmt = fmtDesc.mtl; + } else if (vkFormat != VK_FORMAT_UNDEFINED) { + // If the MTLPixelFormat is not supported but VkFormat is valid, + // report an error, and possibly substitute a different MTLPixelFormat. + string errMsg; + errMsg += "VkFormat "; + errMsg += (fmtDesc.vkName) ? fmtDesc.vkName : to_string(fmtDesc.vk); + errMsg += " is not supported on this platform."; + + if (fmtDesc.isSupportedOrSubstitutable()) { + mtlPixFmt = fmtDesc.mtlSubstitute; + + const MVKFormatDesc& fmtDescSubs = formatDescForMTLPixelFormat(mtlPixFmt); + errMsg += " Using VkFormat "; + errMsg += (fmtDescSubs.vkName) ? fmtDescSubs.vkName : to_string(fmtDescSubs.vk); + errMsg += " instead."; + } + mvkNotifyErrorWithText(VK_ERROR_FORMAT_NOT_SUPPORTED, errMsg.c_str()); + } + + return mtlPixFmt; +} + +MVK_PUBLIC_SYMBOL MVKFormatType mvkFormatTypeFromVkFormat(VkFormat vkFormat) { + return formatDescForVkFormat(vkFormat).formatType; +} + +MVK_PUBLIC_SYMBOL MVKFormatType mvkFormatTypeFromMTLPixelFormat(MTLPixelFormat mtlFormat) { + return formatDescForMTLPixelFormat(mtlFormat).formatType; +} + +MVK_PUBLIC_SYMBOL VkFormat mvkVkFormatFromMTLPixelFormat(MTLPixelFormat mtlFormat) { + return formatDescForMTLPixelFormat(mtlFormat).vk; +} + +MVK_PUBLIC_SYMBOL uint32_t mvkVkFormatBytesPerBlock(VkFormat vkFormat) { + return formatDescForVkFormat(vkFormat).bytesPerBlock; +} + +MVK_PUBLIC_SYMBOL uint32_t mvkMTLPixelFormatBytesPerBlock(MTLPixelFormat mtlFormat) { + return formatDescForMTLPixelFormat(mtlFormat).bytesPerBlock; +} + +MVK_PUBLIC_SYMBOL VkExtent2D mvkVkFormatBlockTexelSize(VkFormat vkFormat) { + return formatDescForVkFormat(vkFormat).blockTexelSize; +} + +MVK_PUBLIC_SYMBOL VkExtent2D mvkMTLPixelFormatBlockTexelSize(MTLPixelFormat mtlFormat) { + return formatDescForMTLPixelFormat(mtlFormat).blockTexelSize; +} + +MVK_PUBLIC_SYMBOL float mvkVkFormatBytesPerTexel(VkFormat vkFormat) { + return formatDescForVkFormat(vkFormat).bytesPerTexel(); +} + +MVK_PUBLIC_SYMBOL float mvkMTLPixelFormatBytesPerTexel(MTLPixelFormat mtlFormat) { + return formatDescForMTLPixelFormat(mtlFormat).bytesPerTexel(); +} + +MVK_PUBLIC_SYMBOL size_t mvkVkFormatBytesPerRow(VkFormat vkFormat, uint32_t texelsPerRow) { + const MVKFormatDesc& fmtDesc = formatDescForVkFormat(vkFormat); + return (texelsPerRow / fmtDesc.blockTexelSize.width) * fmtDesc.bytesPerBlock; +} + +MVK_PUBLIC_SYMBOL size_t mvkMTLPixelFormatBytesPerRow(MTLPixelFormat mtlFormat, uint32_t texelsPerRow) { + const MVKFormatDesc& fmtDesc = formatDescForMTLPixelFormat(mtlFormat); + return (texelsPerRow / fmtDesc.blockTexelSize.width) * fmtDesc.bytesPerBlock; +} + +MVK_PUBLIC_SYMBOL size_t mvkVkFormatBytesPerLayer(VkFormat vkFormat, size_t bytesPerRow, uint32_t texelRowsPerLayer) { + return (texelRowsPerLayer / formatDescForVkFormat(vkFormat).blockTexelSize.height) * bytesPerRow; +} + +MVK_PUBLIC_SYMBOL size_t mvkMTLPixelFormatBytesPerLayer(MTLPixelFormat mtlFormat, size_t bytesPerRow, uint32_t texelRowsPerLayer) { + return (texelRowsPerLayer / formatDescForMTLPixelFormat(mtlFormat).blockTexelSize.height) * bytesPerRow; +} + +MVK_PUBLIC_SYMBOL VkFormatProperties mvkVkFormatProperties(VkFormat vkFormat) { + const MVKFormatDesc& fmtDesc = formatDescForVkFormat(vkFormat); + return fmtDesc.isSupported() ? fmtDesc.properties : (VkFormatProperties)MVK_FMT_NO_FEATS; +} + +MVK_PUBLIC_SYMBOL const char* mvkVkFormatName(VkFormat vkFormat) { + return formatDescForVkFormat(vkFormat).vkName; +} + +MVK_PUBLIC_SYMBOL const char* mvkMTLPixelFormatName(MTLPixelFormat mtlFormat) { + return formatDescForMTLPixelFormat(mtlFormat).mtlName; +} + +MVK_PUBLIC_SYMBOL MTLVertexFormat mvkMTLVertexFormatFromVkFormat(VkFormat vkFormat) { + return formatDescForVkFormat(vkFormat).mtlVertexFormat; +} + +MVK_PUBLIC_SYMBOL MTLClearColor mvkMTLClearColorFromVkClearValue(VkClearValue vkClearValue, + VkFormat vkFormat) { + MTLClearColor mtlClr; + switch (mvkFormatTypeFromVkFormat(vkFormat)) { + case kMVKFormatColorFloat: + mtlClr.red = vkClearValue.color.float32[0]; + mtlClr.green = vkClearValue.color.float32[1]; + mtlClr.blue = vkClearValue.color.float32[2]; + mtlClr.alpha = vkClearValue.color.float32[3]; + break; + case kMVKFormatColorUInt: + mtlClr.red = vkClearValue.color.uint32[0]; + mtlClr.green = vkClearValue.color.uint32[1]; + mtlClr.blue = vkClearValue.color.uint32[2]; + mtlClr.alpha = vkClearValue.color.uint32[3]; + break; + case kMVKFormatColorInt: + mtlClr.red = vkClearValue.color.int32[0]; + mtlClr.green = vkClearValue.color.int32[1]; + mtlClr.blue = vkClearValue.color.int32[2]; + mtlClr.alpha = vkClearValue.color.int32[3]; + break; + default: + mtlClr.red = 0.0; + mtlClr.green = 0.0; + mtlClr.blue = 0.0; + mtlClr.alpha = 1.0; + break; + } + return mtlClr; +} + +MVK_PUBLIC_SYMBOL double mvkMTLClearDepthFromVkClearValue(VkClearValue vkClearValue) { + return vkClearValue.depthStencil.depth; +} + +MVK_PUBLIC_SYMBOL uint32_t mvkMTLClearStencilFromVkClearValue(VkClearValue vkClearValue) { + return vkClearValue.depthStencil.stencil; +} + +MVK_PUBLIC_SYMBOL bool mvkMTLPixelFormatIsDepthFormat(MTLPixelFormat mtlFormat) { + switch (mtlFormat) { + case MTLPixelFormatDepth32Float: +#if MVK_MACOS + case MTLPixelFormatDepth16Unorm: + case MTLPixelFormatDepth24Unorm_Stencil8: +#endif + case MTLPixelFormatDepth32Float_Stencil8: + return true; + default: + return false; + } +} + +MVK_PUBLIC_SYMBOL bool mvkMTLPixelFormatIsStencilFormat(MTLPixelFormat mtlFormat) { + switch (mtlFormat) { + case MTLPixelFormatStencil8: +#if MVK_MACOS + case MTLPixelFormatDepth24Unorm_Stencil8: + case MTLPixelFormatX24_Stencil8: +#endif + case MTLPixelFormatDepth32Float_Stencil8: + case MTLPixelFormatX32_Stencil8: + return true; + default: + return false; + } +} + +MVK_PUBLIC_SYMBOL MTLTextureType mvkMTLTextureTypeFromVkImageType(VkImageType vkImageType, + uint32_t arraySize, + bool isMultisample) { + switch (vkImageType) { + case VK_IMAGE_TYPE_1D: return (arraySize > 1 ? MTLTextureType1DArray : MTLTextureType1D); + case VK_IMAGE_TYPE_3D: return MTLTextureType3D; + case VK_IMAGE_TYPE_2D: + default: { + if (arraySize > 1) { return MTLTextureType2DArray; } + if (isMultisample) { return MTLTextureType2DMultisample; } + return MTLTextureType2D; + } + } +} + +MVK_PUBLIC_SYMBOL VkImageType mvkVkImageTypeFromMTLTextureType(MTLTextureType mtlTextureType) { + switch (mtlTextureType) { + case MTLTextureType1D: return VK_IMAGE_TYPE_1D; + case MTLTextureType1DArray: return VK_IMAGE_TYPE_1D; + case MTLTextureType3D: return VK_IMAGE_TYPE_3D; + default: return VK_IMAGE_TYPE_2D; + } +} + +MVK_PUBLIC_SYMBOL MTLTextureType mvkMTLTextureTypeFromVkImageViewType(VkImageViewType vkImageViewType, + bool isMultisample) { + switch (vkImageViewType) { + case VK_IMAGE_VIEW_TYPE_1D: return MTLTextureType1D; + case VK_IMAGE_VIEW_TYPE_1D_ARRAY: return MTLTextureType1DArray; + case VK_IMAGE_VIEW_TYPE_2D: return (isMultisample ? MTLTextureType2DMultisample : MTLTextureType2D); + case VK_IMAGE_VIEW_TYPE_2D_ARRAY: return MTLTextureType2DArray; + case VK_IMAGE_VIEW_TYPE_3D: return MTLTextureType3D; + case VK_IMAGE_VIEW_TYPE_CUBE: return MTLTextureTypeCube; +#if MVK_MACOS + case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY: return MTLTextureTypeCubeArray; +#endif + default: return MTLTextureType2D; + } +} + +MVK_PUBLIC_SYMBOL MTLTextureUsage mvkMTLTextureUsageFromVkImageUsageFlags(VkImageUsageFlags vkImageUsageFlags) { + MTLTextureUsage mtlUsage = MTLTextureUsageUnknown; + + if ( mvkAreFlagsEnabled(vkImageUsageFlags, VK_IMAGE_USAGE_TRANSFER_SRC_BIT) ) { + mvkEnableFlag(mtlUsage, MTLTextureUsageShaderRead); + } + if ( mvkAreFlagsEnabled(vkImageUsageFlags, VK_IMAGE_USAGE_TRANSFER_DST_BIT) ) { + mvkEnableFlag(mtlUsage, MTLTextureUsageRenderTarget); + } + if ( mvkAreFlagsEnabled(vkImageUsageFlags, VK_IMAGE_USAGE_SAMPLED_BIT) ) { + mvkEnableFlag(mtlUsage, MTLTextureUsageShaderRead); + mvkEnableFlag(mtlUsage, MTLTextureUsagePixelFormatView); + } + if ( mvkAreFlagsEnabled(vkImageUsageFlags, VK_IMAGE_USAGE_STORAGE_BIT) ) { + mvkEnableFlag(mtlUsage, MTLTextureUsageShaderWrite); + mvkEnableFlag(mtlUsage, MTLTextureUsagePixelFormatView); + } + if ( mvkAreFlagsEnabled(vkImageUsageFlags, VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT) ) { + mvkEnableFlag(mtlUsage, MTLTextureUsageShaderRead); + mvkEnableFlag(mtlUsage, MTLTextureUsagePixelFormatView); + } + if ( mvkAreFlagsEnabled(vkImageUsageFlags, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) ) { + mvkEnableFlag(mtlUsage, MTLTextureUsageRenderTarget); + mvkEnableFlag(mtlUsage, MTLTextureUsagePixelFormatView); + } + if ( mvkAreFlagsEnabled(vkImageUsageFlags, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ) { + mvkEnableFlag(mtlUsage, MTLTextureUsageRenderTarget); + mvkDisableFlag(mtlUsage, MTLTextureUsagePixelFormatView); // Clears bit. Do this last. + } + + return mtlUsage; +} + +MVK_PUBLIC_SYMBOL VkImageUsageFlags mvkVkImageUsageFlagsFromMTLTextureUsage(MTLTextureUsage mtlUsage, MTLPixelFormat mtlFormat) { + VkImageUsageFlags vkImageUsageFlags = 0; + + if ( mvkAreFlagsEnabled(mtlUsage, MTLTextureUsageShaderRead) ) { + mvkEnableFlag(vkImageUsageFlags, VK_IMAGE_USAGE_TRANSFER_SRC_BIT); + mvkEnableFlag(vkImageUsageFlags, VK_IMAGE_USAGE_SAMPLED_BIT); + mvkEnableFlag(vkImageUsageFlags, VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT); + } + if ( mvkAreFlagsEnabled(mtlUsage, MTLTextureUsageRenderTarget) ) { + mvkEnableFlag(vkImageUsageFlags, VK_IMAGE_USAGE_TRANSFER_DST_BIT); + if (mvkMTLPixelFormatIsDepthFormat(mtlFormat) || mvkMTLPixelFormatIsStencilFormat(mtlFormat)) { + mvkEnableFlag(vkImageUsageFlags, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT); + } else { + mvkEnableFlag(vkImageUsageFlags, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); + } + } + if ( mvkAreFlagsEnabled(mtlUsage, MTLTextureUsageShaderWrite) ) { + mvkEnableFlag(vkImageUsageFlags, VK_IMAGE_USAGE_STORAGE_BIT); + } + + return vkImageUsageFlags; +} + +MVK_PUBLIC_SYMBOL uint32_t mvkSampleCountFromVkSampleCountFlagBits(VkSampleCountFlagBits vkSampleCountFlag) { + // The bits are already in the correct mathematical sequence (assuming only one bit is set) + return uint32_t(vkSampleCountFlag); +} + +MVK_PUBLIC_SYMBOL VkSampleCountFlagBits mvkVkSampleCountFlagBitsFromSampleCount(NSUInteger sampleCount) { + // The bits are already in the correct mathematical sequence (assuming only POT sample counts) + return VkSampleCountFlagBits(sampleCount); +} + + +#pragma mark Mipmaps + +MVK_PUBLIC_SYMBOL uint32_t mvkMipmapLevels(uint32_t dim) { + if ( !mvkIsPowerOfTwo(dim) ) { return 0; } + + uint32_t exp = 0; + while (dim) { + exp++; + dim >>= 1; + } + return exp; +} + +MVK_PUBLIC_SYMBOL uint32_t mvkMipmapLevels2D(VkExtent2D extent) { + return mvkMipmapLevels3D( {extent.width, extent.height, 1} ); +} + +MVK_PUBLIC_SYMBOL uint32_t mvkMipmapLevels3D(VkExtent3D extent) { + uint32_t maxDim = max({extent.width, extent.height, extent.depth}); + return max(mvkMipmapLevels(maxDim), 1U); +} + +MVK_PUBLIC_SYMBOL VkExtent2D mvkMipmapLevelSizeFromBaseSize(VkExtent2D baseSize, uint32_t level) { + // Before shifting, ensure dims are treated as unsigned + uint32_t width = baseSize.width; + uint32_t height = baseSize.height; + + VkExtent2D lvlSize; + lvlSize.width = MAX(width >> level, 1); + lvlSize.height = MAX(height >> level, 1); + return lvlSize; +} + +MVK_PUBLIC_SYMBOL VkExtent2D mvkMipmapBaseSizeFromLevelSize(VkExtent2D levelSize, uint32_t level) { + VkExtent2D baseSize; + baseSize.width = levelSize.width << level; + baseSize.height = levelSize.height << level; + return baseSize; +} + + +#pragma mark Samplers + +MVK_PUBLIC_SYMBOL MTLSamplerAddressMode mvkMTLSamplerAddressModeFromVkSamplerAddressMode(VkSamplerAddressMode vkMode) { + switch (vkMode) { + case VK_SAMPLER_ADDRESS_MODE_REPEAT: return MTLSamplerAddressModeRepeat; + case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE: return MTLSamplerAddressModeClampToEdge; + case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToZero; + case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat; +#if MVK_MACOS + case VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE: return MTLSamplerAddressModeMirrorClampToEdge; +#endif + default: return MTLSamplerAddressModeClampToZero; + } +} + +MVK_PUBLIC_SYMBOL MTLSamplerMinMagFilter mvkMTLSamplerMinMagFilterFromVkFilter(VkFilter vkFilter) { + switch (vkFilter) { + case VK_FILTER_NEAREST: return MTLSamplerMinMagFilterNearest; + case VK_FILTER_LINEAR: return MTLSamplerMinMagFilterLinear; + default: return MTLSamplerMinMagFilterNearest; + } +} + +MVK_PUBLIC_SYMBOL MTLSamplerMipFilter mvkMTLSamplerMipFilterFromVkSamplerMipmapMode(VkSamplerMipmapMode vkMode) { + switch (vkMode) { + case VK_SAMPLER_MIPMAP_MODE_NEAREST: return MTLSamplerMipFilterNearest; + case VK_SAMPLER_MIPMAP_MODE_LINEAR: return MTLSamplerMipFilterLinear; + default: return MTLSamplerMipFilterNotMipmapped; + } +} + + +#pragma mark - +#pragma mark Render pipeline + +MVK_PUBLIC_SYMBOL MTLColorWriteMask mvkMTLColorWriteMaskFromVkChannelFlags(VkColorComponentFlags vkWriteFlags) { + MTLColorWriteMask mtlWriteMask = MTLColorWriteMaskNone; + if (mvkAreFlagsEnabled(vkWriteFlags, VK_COLOR_COMPONENT_R_BIT)) { mvkEnableFlag(mtlWriteMask, MTLColorWriteMaskRed); } + if (mvkAreFlagsEnabled(vkWriteFlags, VK_COLOR_COMPONENT_G_BIT)) { mvkEnableFlag(mtlWriteMask, MTLColorWriteMaskGreen); } + if (mvkAreFlagsEnabled(vkWriteFlags, VK_COLOR_COMPONENT_B_BIT)) { mvkEnableFlag(mtlWriteMask, MTLColorWriteMaskBlue); } + if (mvkAreFlagsEnabled(vkWriteFlags, VK_COLOR_COMPONENT_A_BIT)) { mvkEnableFlag(mtlWriteMask, MTLColorWriteMaskAlpha); } + return mtlWriteMask; +} + +MVK_PUBLIC_SYMBOL MTLBlendOperation mvkMTLBlendOperationFromVkBlendOp(VkBlendOp vkBlendOp) { + switch (vkBlendOp) { + case VK_BLEND_OP_ADD: return MTLBlendOperationAdd; + case VK_BLEND_OP_SUBTRACT: return MTLBlendOperationSubtract; + case VK_BLEND_OP_REVERSE_SUBTRACT: return MTLBlendOperationReverseSubtract; + case VK_BLEND_OP_MIN: return MTLBlendOperationMin; + case VK_BLEND_OP_MAX: return MTLBlendOperationMax; + default: return MTLBlendOperationAdd; + } +} + +MVK_PUBLIC_SYMBOL MTLBlendFactor mvkMTLBlendFactorFromVkBlendFactor(VkBlendFactor vkBlendFactor) { + switch (vkBlendFactor) { + case VK_BLEND_FACTOR_ZERO: return MTLBlendFactorZero; + case VK_BLEND_FACTOR_ONE: return MTLBlendFactorOne; + case VK_BLEND_FACTOR_SRC_COLOR: return MTLBlendFactorSourceColor; + case VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR: return MTLBlendFactorOneMinusSourceColor; + case VK_BLEND_FACTOR_DST_COLOR: return MTLBlendFactorDestinationColor; + case VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR: return MTLBlendFactorOneMinusDestinationColor; + case VK_BLEND_FACTOR_SRC_ALPHA: return MTLBlendFactorSourceAlpha; + case VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA: return MTLBlendFactorOneMinusSourceAlpha; + case VK_BLEND_FACTOR_DST_ALPHA: return MTLBlendFactorDestinationAlpha; + case VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA: return MTLBlendFactorOneMinusDestinationAlpha; + case VK_BLEND_FACTOR_CONSTANT_COLOR: return MTLBlendFactorBlendColor; + case VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR: return MTLBlendFactorOneMinusBlendColor; + case VK_BLEND_FACTOR_CONSTANT_ALPHA: return MTLBlendFactorBlendAlpha; + case VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA: return MTLBlendFactorOneMinusBlendAlpha; + case VK_BLEND_FACTOR_SRC_ALPHA_SATURATE: return MTLBlendFactorSourceAlphaSaturated; + +#if MVK_IOS + case VK_BLEND_FACTOR_SRC1_COLOR: return MTLBlendFactorSourceColor; + case VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR: return MTLBlendFactorOneMinusSourceColor; + case VK_BLEND_FACTOR_SRC1_ALPHA: return MTLBlendFactorSourceAlpha; + case VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA: return MTLBlendFactorOneMinusSourceAlpha; +#endif + +#if MVK_MACOS + case VK_BLEND_FACTOR_SRC1_COLOR: return MTLBlendFactorSource1Color; + case VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR: return MTLBlendFactorOneMinusSource1Color; + case VK_BLEND_FACTOR_SRC1_ALPHA: return MTLBlendFactorSource1Alpha; + case VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA: return MTLBlendFactorOneMinusSource1Alpha; +#endif + + default: return MTLBlendFactorZero; + } +} + +MVK_PUBLIC_SYMBOL MTLVertexStepFunction mvkMTLVertexStepFunctionFromVkVertexInputRate(VkVertexInputRate vkVtxStep) { + switch (vkVtxStep) { + case VK_VERTEX_INPUT_RATE_VERTEX: return MTLVertexStepFunctionPerVertex; + case VK_VERTEX_INPUT_RATE_INSTANCE: return MTLVertexStepFunctionPerInstance; + default: return MTLVertexStepFunctionPerVertex; + } +} + +MVK_PUBLIC_SYMBOL MTLPrimitiveType mvkMTLPrimitiveTypeFromVkPrimitiveTopology(VkPrimitiveTopology vkTopology) { + switch (vkTopology) { + case VK_PRIMITIVE_TOPOLOGY_POINT_LIST: + return MTLPrimitiveTypePoint; + + case VK_PRIMITIVE_TOPOLOGY_LINE_LIST: + case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY: + return MTLPrimitiveTypeLine; + + case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP: + case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY: + return MTLPrimitiveTypeLineStrip; + + case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST: + case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY: + return MTLPrimitiveTypeTriangle; + + case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP: + case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY: + return MTLPrimitiveTypeTriangleStrip; + + case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN: + case VK_PRIMITIVE_TOPOLOGY_PATCH_LIST: + default: + mvkNotifyErrorWithText(VK_ERROR_FORMAT_NOT_SUPPORTED, "VkPrimitiveTopology value %d is not supported for rendering.", vkTopology); + return MTLPrimitiveTypePoint; + } +} + +#if MVK_MACOS +MVK_PUBLIC_SYMBOL MTLPrimitiveTopologyClass mvkMTLPrimitiveTopologyClassFromVkPrimitiveTopology(VkPrimitiveTopology vkTopology) { + switch (vkTopology) { + case VK_PRIMITIVE_TOPOLOGY_POINT_LIST: + return MTLPrimitiveTopologyClassPoint; + + case VK_PRIMITIVE_TOPOLOGY_LINE_LIST: + case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP: + case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY: + case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY: + return MTLPrimitiveTopologyClassLine; + + case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST: + case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP: + case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN: + case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY: + case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY: + return MTLPrimitiveTopologyClassTriangle; + + case VK_PRIMITIVE_TOPOLOGY_PATCH_LIST: + default: + mvkNotifyErrorWithText(VK_ERROR_FORMAT_NOT_SUPPORTED, "VkPrimitiveTopology value %d is not supported for render pipelines.", vkTopology); + return MTLPrimitiveTopologyClassUnspecified; + } +} +#endif + +MVK_PUBLIC_SYMBOL MTLLoadAction mvkMTLLoadActionFromVkAttachmentLoadOp(VkAttachmentLoadOp vkLoadOp) { + switch (vkLoadOp) { + case VK_ATTACHMENT_LOAD_OP_LOAD: return MTLLoadActionLoad; + case VK_ATTACHMENT_LOAD_OP_CLEAR: return MTLLoadActionClear; + case VK_ATTACHMENT_LOAD_OP_DONT_CARE: return MTLLoadActionDontCare; + + default: + mvkNotifyErrorWithText(VK_ERROR_FORMAT_NOT_SUPPORTED, "VkAttachmentLoadOp value %d is not supported.", vkLoadOp); + return MTLLoadActionLoad; + } +} + +MVK_PUBLIC_SYMBOL MTLStoreAction mvkMTLStoreActionFromVkAttachmentStoreOp(VkAttachmentStoreOp vkStoreOp) { + switch (vkStoreOp) { + case VK_ATTACHMENT_STORE_OP_STORE: return MTLStoreActionStore; + case VK_ATTACHMENT_STORE_OP_DONT_CARE: return MTLStoreActionDontCare; + + default: + mvkNotifyErrorWithText(VK_ERROR_FORMAT_NOT_SUPPORTED, "VkAttachmentStoreOp value %d is not supported.", vkStoreOp); + return MTLStoreActionStore; + } +} + +MVK_PUBLIC_SYMBOL MTLViewport mvkMTLViewportFromVkViewport(VkViewport vkViewport) { + MTLViewport mtlViewport; + mtlViewport.originX = vkViewport.x; + mtlViewport.originY = vkViewport.y; + mtlViewport.width = vkViewport.width; + mtlViewport.height = vkViewport.height; + mtlViewport.znear = vkViewport.minDepth; + mtlViewport.zfar = vkViewport.maxDepth; + return mtlViewport; +} + +MVK_PUBLIC_SYMBOL MTLScissorRect mvkMTLScissorRectFromVkRect2D(VkRect2D vkRect) { + MTLScissorRect mtlScissor; + mtlScissor.x = vkRect.offset.x; + mtlScissor.y = vkRect.offset.y; + mtlScissor.width = vkRect.extent.width; + mtlScissor.height = vkRect.extent.height; + return mtlScissor; +} + +MVK_PUBLIC_SYMBOL MTLCompareFunction mvkMTLCompareFunctionFromVkCompareOp(VkCompareOp vkOp) { + switch (vkOp) { + case VK_COMPARE_OP_NEVER: return MTLCompareFunctionNever; + case VK_COMPARE_OP_LESS: return MTLCompareFunctionLess; + case VK_COMPARE_OP_EQUAL: return MTLCompareFunctionEqual; + case VK_COMPARE_OP_LESS_OR_EQUAL: return MTLCompareFunctionLessEqual; + case VK_COMPARE_OP_GREATER: return MTLCompareFunctionGreater; + case VK_COMPARE_OP_NOT_EQUAL: return MTLCompareFunctionNotEqual; + case VK_COMPARE_OP_GREATER_OR_EQUAL: return MTLCompareFunctionGreaterEqual; + case VK_COMPARE_OP_ALWAYS: return MTLCompareFunctionAlways; + default: return MTLCompareFunctionNever; + } +} + +MVK_PUBLIC_SYMBOL MTLStencilOperation mvkMTLStencilOperationFromVkStencilOp(VkStencilOp vkOp) { + switch (vkOp) { + case VK_STENCIL_OP_KEEP: return MTLStencilOperationKeep; + case VK_STENCIL_OP_ZERO: return MTLStencilOperationZero; + case VK_STENCIL_OP_REPLACE: return MTLStencilOperationReplace; + case VK_STENCIL_OP_INCREMENT_AND_CLAMP: return MTLStencilOperationIncrementClamp; + case VK_STENCIL_OP_DECREMENT_AND_CLAMP: return MTLStencilOperationDecrementClamp; + case VK_STENCIL_OP_INVERT: return MTLStencilOperationInvert; + case VK_STENCIL_OP_INCREMENT_AND_WRAP: return MTLStencilOperationIncrementWrap; + case VK_STENCIL_OP_DECREMENT_AND_WRAP: return MTLStencilOperationDecrementWrap; + default: return MTLStencilOperationKeep; + } +} + +MVK_PUBLIC_SYMBOL MTLCullMode mvkMTLCullModeFromVkCullModeFlags(VkCullModeFlags vkCull) { + switch (vkCull) { + case VK_CULL_MODE_NONE: return MTLCullModeNone; + case VK_CULL_MODE_FRONT_BIT: return MTLCullModeFront; + case VK_CULL_MODE_BACK_BIT: return MTLCullModeBack; + default: return MTLCullModeNone; + } +} + +MVK_PUBLIC_SYMBOL MTLWinding mvkMTLWindingFromVkFrontFace(VkFrontFace vkWinding) { + switch (vkWinding) { + case VK_FRONT_FACE_COUNTER_CLOCKWISE: return MTLWindingCounterClockwise; + case VK_FRONT_FACE_CLOCKWISE: return MTLWindingClockwise; + default: return MTLWindingCounterClockwise; + } +} + +MVK_PUBLIC_SYMBOL MTLTriangleFillMode mvkMTLTriangleFillModeFromVkPolygonMode(VkPolygonMode vkFillMode) { + switch (vkFillMode) { + case VK_POLYGON_MODE_FILL: return MTLTriangleFillModeFill; + case VK_POLYGON_MODE_LINE: return MTLTriangleFillModeLines; + case VK_POLYGON_MODE_POINT: return MTLTriangleFillModeLines; + default: return MTLTriangleFillModeLines; + } +} + +MVK_PUBLIC_SYMBOL MTLIndexType mvkMTLIndexTypeFromVkIndexType(VkIndexType vkIdxType) { + switch (vkIdxType) { + case VK_INDEX_TYPE_UINT32: return MTLIndexTypeUInt32; + case VK_INDEX_TYPE_UINT16: return MTLIndexTypeUInt16; + default: return MTLIndexTypeUInt16; + } +} + +MVK_PUBLIC_SYMBOL size_t mvkMTLIndexTypeSizeInBytes(MTLIndexType mtlIdxType) { + switch (mtlIdxType) { + case MTLIndexTypeUInt16: return 2; + case MTLIndexTypeUInt32: return 4; + } +} + + +#pragma mark - +#pragma mark Memory options + +MVK_PUBLIC_SYMBOL MTLStorageMode mvkMTLStorageModeFromVkMemoryPropertyFlags(VkMemoryPropertyFlags vkFlags) { + + // If not visible to the host: Private + if ( !mvkAreFlagsEnabled(vkFlags, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) ) { + return MTLStorageModePrivate; + } + + // If visible to the host and coherent: Shared + if (mvkAreFlagsEnabled(vkFlags, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { + return MTLStorageModeShared; + } + + // If visible to the host, and not coherent: Managed on macOS, Shared on iOS +#if MVK_MACOS + return MTLStorageModeManaged; +#else + return MTLStorageModeShared; +#endif +} + +MVK_PUBLIC_SYMBOL MTLCPUCacheMode mvkMTLCPUCacheModeFromVkMemoryPropertyFlags(VkMemoryPropertyFlags vkFlags) { + return MTLCPUCacheModeDefaultCache; +} + +MVK_PUBLIC_SYMBOL MTLResourceOptions mvkMTLResourceOptionsFromVkMemoryPropertyFlags(VkMemoryPropertyFlags vkFlags) { + MTLResourceOptions mtlFlags = 0; + + // First set the resource CPU cache mode + MTLCPUCacheMode mtlCPUMode = mvkMTLCPUCacheModeFromVkMemoryPropertyFlags(vkFlags); + switch (mtlCPUMode) { + case MTLCPUCacheModeDefaultCache: + mvkEnableFlag(mtlFlags, MTLResourceCPUCacheModeDefaultCache); + break; + case MTLCPUCacheModeWriteCombined: + mvkEnableFlag(mtlFlags, MTLResourceCPUCacheModeWriteCombined); + break; + } + + // Then set the resource storage mode + MTLStorageMode mtlStgMode = mvkMTLStorageModeFromVkMemoryPropertyFlags(vkFlags); + switch (mtlStgMode) { + case MTLStorageModePrivate: + mvkEnableFlag(mtlFlags, MTLResourceStorageModePrivate); + break; + case MTLStorageModeShared: + mvkEnableFlag(mtlFlags, MTLResourceStorageModeShared); + break; +#if MVK_MACOS + case MTLStorageModeManaged: + mvkEnableFlag(mtlFlags, MTLResourceStorageModeManaged); + break; +#endif + default: // Silence erroneous -Wswitch-enum warning on MTLResourceStorageModeManaged under iOS + break; + } + + return mtlFlags; +} + + +#pragma mark - +#pragma mark Library initialization + +/** + * Called automatically when the framework is loaded and initialized. + * + * Initialize various data type lookups. + */ +static bool _mvkDataTypesInitialized = false; +__attribute__((constructor)) static void MVKInitDataTypes() { + if (_mvkDataTypesInitialized ) { return; } + + MVKInitFormatMaps(); + + _mvkDataTypesInitialized = true; +} + + + diff --git a/MoltenVK/MoltenVK/Vulkan/vk_mvk_moltenvk.mm b/MoltenVK/MoltenVK/Vulkan/vk_mvk_moltenvk.mm new file mode 100644 index 00000000..0ce6eb52 --- /dev/null +++ b/MoltenVK/MoltenVK/Vulkan/vk_mvk_moltenvk.mm @@ -0,0 +1,140 @@ +/* + * vk_mvk_moltenvk.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "vk_mvk_moltenvk.h" +#include "MVKEnvironment.h" +#include "MVKSwapchain.h" +#include "MVKImage.h" +#include + +using namespace std; + + +MVK_PUBLIC_SYMBOL void vkGetMoltenVKDeviceConfigurationMVK( + VkDevice device, + MVKDeviceConfiguration* pConfiguration) { + + MVKDevice* mvkDev = (MVKDevice*)device; + if (pConfiguration) { *pConfiguration = mvkDev->_mvkConfig; } +} + +MVK_PUBLIC_SYMBOL VkResult vkSetMoltenVKDeviceConfigurationMVK( + VkDevice device, + MVKDeviceConfiguration* pConfiguration) { + + MVKDevice* mvkDev = (MVKDevice*)device; + if (pConfiguration) { *(MVKDeviceConfiguration*)&mvkDev->_mvkConfig = *pConfiguration; } + return VK_SUCCESS; +} + +MVK_PUBLIC_SYMBOL void vkGetPhysicalDeviceMetalFeaturesMVK( + VkPhysicalDevice physicalDevice, + MVKPhysicalDeviceMetalFeatures* pMetalFeatures) { + + MVKPhysicalDevice* mvkPD = (MVKPhysicalDevice*)physicalDevice; + mvkPD->getMetalFeatures(pMetalFeatures); +} + +MVK_PUBLIC_SYMBOL void vkGetSwapchainPerformanceMVK( + VkDevice device, + VkSwapchainKHR swapchain, + MVKSwapchainPerformance* pSwapchainPerf) { + + MVKSwapchain* mvkSwapchain = (MVKSwapchain*)swapchain; + mvkSwapchain->getPerformanceStatistics(pSwapchainPerf); +} + +MVK_PUBLIC_SYMBOL void vkGetShaderCompilationPerformanceMVK( + VkDevice device, + MVKShaderCompilationPerformance* pShaderCompPerf) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->getShaderCompilationPerformanceStatistics(pShaderCompPerf); +} + +MVK_PUBLIC_SYMBOL void vkGetVersionStringsMVK( + char* pMoltenVersionStringBuffer, + uint32_t moltenVersionStringBufferLength, + char* pVulkanVersionStringBuffer, + uint32_t vulkanVersionStringBufferLength) { + + size_t len; + + string mvkVer; + mvkVer += to_string((MVK_VERSION >> 24) & 0xFF); + mvkVer += "."; + mvkVer += to_string((MVK_VERSION >> 16) & 0xFF); + mvkVer += "."; + mvkVer += to_string((MVK_VERSION >> 8) & 0xFF); + len = mvkVer.copy(pMoltenVersionStringBuffer, moltenVersionStringBufferLength - 1); + pMoltenVersionStringBuffer[len] = 0; // terminator + + string vkVer; + vkVer += to_string(VK_VERSION_MAJOR(MVK_VULKAN_API_VERSION)); + vkVer += "."; + vkVer += to_string(VK_VERSION_MINOR(MVK_VULKAN_API_VERSION)); + vkVer += "."; + vkVer += to_string(VK_VERSION_PATCH(MVK_VULKAN_API_VERSION)); + len = vkVer.copy(pVulkanVersionStringBuffer, vulkanVersionStringBufferLength - 1); + pVulkanVersionStringBuffer[len] = 0; // terminator +} + +MVK_PUBLIC_SYMBOL void vkGetMTLDeviceMVK( + VkPhysicalDevice physicalDevice, + id* pMTLDevice) { + + MVKPhysicalDevice* mvkPD = (MVKPhysicalDevice*)physicalDevice; + *pMTLDevice = mvkPD->getMTLDevice(); +} + +MVK_PUBLIC_SYMBOL VkResult vkSetMTLTextureMVK( + VkImage image, + id mtlTexture) { + + MVKImage* mvkImg = (MVKImage*)image; + return mvkImg->setMTLTexture(mtlTexture); +} + +MVK_PUBLIC_SYMBOL void vkGetMTLTextureMVK( + VkImage image, + id* pMTLTexture) { + + MVKImage* mvkImg = (MVKImage*)image; + *pMTLTexture = mvkImg->getMTLTexture(); +} + +MVK_PUBLIC_SYMBOL VkResult vkUseIOSurfaceMVK( + VkImage image, + IOSurfaceRef ioSurface) { + + MVKImage* mvkImg = (MVKImage*)image; + return mvkImg->useIOSurface(ioSurface); +} + +MVK_PUBLIC_SYMBOL void vkGetIOSurfaceMVK( + VkImage image, + IOSurfaceRef* pIOSurface) { + + MVKImage* mvkImg = (MVKImage*)image; + *pIOSurface = mvkImg->getIOSurface(); +} + +// Deprecated license function +MVK_PUBLIC_SYMBOL VkResult vkActivateMoltenVKLicenseMVK(const char* licenseID, const char* licenseKey, VkBool32 acceptLicenseTermsAndConditions) { return VK_SUCCESS; } + diff --git a/MoltenVK/MoltenVK/Vulkan/vulkan.mm b/MoltenVK/MoltenVK/Vulkan/vulkan.mm new file mode 100644 index 00000000..a8e274bd --- /dev/null +++ b/MoltenVK/MoltenVK/Vulkan/vulkan.mm @@ -0,0 +1,1537 @@ +/* + * vulkan.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "MVKLayers.h" +#include "MVKInstance.h" +#include "MVKDevice.h" +#include "MVKCommandPool.h" +#include "MVKCommandBuffer.h" +#include "MVKCmdPipeline.h" +#include "MVKCmdDraw.h" +#include "MVKCmdTransfer.h" +#include "MVKCmdQueries.h" +#include "MVKImage.h" +#include "MVKBuffer.h" +#include "MVKDeviceMemory.h" +#include "MVKDescriptorSet.h" +#include "MVKRenderpass.h" +#include "MVKShaderModule.h" +#include "MVKPipeline.h" +#include "MVKFramebuffer.h" +#include "MVKSync.h" +#include "MVKQueue.h" +#include "MVKQueryPool.h" +#include "MVKSwapchain.h" +#include "MVKFoundation.h" +#include "MVKLogging.h" + +/** Log that the specified Vulkan function is not yet supported, and raise an assertion. */ +static VkResult MVKLogUnimplemented(const char* funcName) { + return mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "%s() is not implemented!", funcName); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreateInstance( + const VkInstanceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkInstance* pInstance) { + + MVKInstance* mvkInst = new MVKInstance(pCreateInfo); + *pInstance = (VkInstance)(mvkInst); + return mvkInst->getConfigurationResult(); +} + +MVK_PUBLIC_SYMBOL void vkDestroyInstance( + VkInstance instance, + const VkAllocationCallbacks* pAllocator) { + + MVKInstance* mvkInst = (MVKInstance*)instance; + delete mvkInst; +} + +MVK_PUBLIC_SYMBOL VkResult vkEnumeratePhysicalDevices( + VkInstance instance, + uint32_t* pPhysicalDeviceCount, + VkPhysicalDevice* pPhysicalDevices) { + + MVKInstance* mvkInst = (MVKInstance*)instance; + return mvkInst->getPhysicalDevices(pPhysicalDeviceCount, pPhysicalDevices); +} + +MVK_PUBLIC_SYMBOL void vkGetPhysicalDeviceFeatures( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceFeatures* pFeatures) { + + MVKPhysicalDevice* mvkPD = (MVKPhysicalDevice*)physicalDevice; + mvkPD->getFeatures(pFeatures); +} + +MVK_PUBLIC_SYMBOL void vkGetPhysicalDeviceFormatProperties( + VkPhysicalDevice physicalDevice, + VkFormat format, + VkFormatProperties* pFormatProperties) { + + MVKPhysicalDevice* mvkPD = (MVKPhysicalDevice*)physicalDevice; + mvkPD->getFormatProperties(format, pFormatProperties); +} + +MVK_PUBLIC_SYMBOL VkResult vkGetPhysicalDeviceImageFormatProperties( + VkPhysicalDevice physicalDevice, + VkFormat format, + VkImageType type, + VkImageTiling tiling, + VkImageUsageFlags usage, + VkImageCreateFlags flags, + VkImageFormatProperties* pImageFormatProperties) { + + MVKPhysicalDevice* mvkPD = (MVKPhysicalDevice*)physicalDevice; + return mvkPD->getImageFormatProperties(format, type, tiling, usage, flags, pImageFormatProperties); +} + +MVK_PUBLIC_SYMBOL void vkGetPhysicalDeviceProperties( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceProperties* pProperties) { + + MVKPhysicalDevice* mvkPD = (MVKPhysicalDevice*)physicalDevice; + mvkPD->getProperties(pProperties); +} + +MVK_PUBLIC_SYMBOL void vkGetPhysicalDeviceQueueFamilyProperties( + VkPhysicalDevice physicalDevice, + uint32_t* pCount, + VkQueueFamilyProperties* pQueueFamilyProperties) { + + MVKPhysicalDevice* mvkPD = (MVKPhysicalDevice*)physicalDevice; + mvkPD->getQueueFamilyProperties(pCount, pQueueFamilyProperties); +} + +MVK_PUBLIC_SYMBOL void vkGetPhysicalDeviceMemoryProperties( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceMemoryProperties* pMemoryProperties) { + + MVKPhysicalDevice* mvkPD = (MVKPhysicalDevice*)physicalDevice; + mvkPD->getPhysicalDeviceMemoryProperties(pMemoryProperties); +} + +MVK_PUBLIC_SYMBOL PFN_vkVoidFunction vkGetInstanceProcAddr( + VkInstance instance, + const char* pName) { + + // Handle the function that creates an instance specially. + // In this case, the instance parameter is ignored and may be NULL. + if (strcmp(pName, "vkCreateInstance") == 0) { return (PFN_vkVoidFunction)vkCreateInstance; } + if (strcmp(pName, "vkEnumerateInstanceExtensionProperties") == 0) { return (PFN_vkVoidFunction)vkEnumerateInstanceExtensionProperties; } + if (strcmp(pName, "vkEnumerateInstanceLayerProperties") == 0) { return (PFN_vkVoidFunction)vkEnumerateInstanceLayerProperties; } + + MVKInstance* mvkInst = (MVKInstance*)instance; + return mvkInst->getProcAddr(pName); +} + +MVK_PUBLIC_SYMBOL PFN_vkVoidFunction vkGetDeviceProcAddr( + VkDevice device, + const char* pName) { + + MVKDevice* mvkDev = (MVKDevice*)device; + return mvkDev->getProcAddr(pName); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreateDevice( + VkPhysicalDevice physicalDevice, + const VkDeviceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDevice* pDevice) { + + MVKPhysicalDevice* mvkPD = (MVKPhysicalDevice*)physicalDevice; + MVKDevice* mvkDev = new MVKDevice(mvkPD, pCreateInfo); + *pDevice = (VkDevice)(mvkDev); + return VK_SUCCESS; +} + +MVK_PUBLIC_SYMBOL void vkDestroyDevice( + VkDevice device, + const VkAllocationCallbacks* pAllocator) { + + MVKDevice* mvkDev = (MVKDevice*)device; + delete mvkDev; +} + +MVK_PUBLIC_SYMBOL VkResult vkEnumerateInstanceExtensionProperties( + const char* pLayerName, + uint32_t* pCount, + VkExtensionProperties* pProperties) { + + return MVKLayerManager::globalManager()->getLayerNamed(pLayerName)->getExtensionProperties(pCount, pProperties); +} + +MVK_PUBLIC_SYMBOL VkResult vkEnumerateDeviceExtensionProperties( + VkPhysicalDevice physicalDevice, + const char* pLayerName, + uint32_t* pCount, + VkExtensionProperties* pProperties) { + + MVKPhysicalDevice* mvkPD = (MVKPhysicalDevice*)physicalDevice; + return mvkPD->getLayerManager()->getLayerNamed(pLayerName)->getExtensionProperties(pCount, pProperties); +} + +MVK_PUBLIC_SYMBOL VkResult vkEnumerateInstanceLayerProperties( + uint32_t* pCount, + VkLayerProperties* pProperties) { + + return MVKLayerManager::globalManager()->getLayerProperties(pCount, pProperties); +} + +MVK_PUBLIC_SYMBOL VkResult vkEnumerateDeviceLayerProperties( + VkPhysicalDevice physicalDevice, + uint32_t* pCount, + VkLayerProperties* pProperties) { + + MVKPhysicalDevice* mvkPD = (MVKPhysicalDevice*)physicalDevice; + return mvkPD->getLayerManager()->getLayerProperties(pCount, pProperties); +} + +MVK_PUBLIC_SYMBOL void vkGetDeviceQueue( + VkDevice device, + uint32_t queueFamilyIndex, + uint32_t queueIndex, + VkQueue* pQueue) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->getDeviceQueue(queueFamilyIndex, queueIndex, pQueue); +} + +MVK_PUBLIC_SYMBOL VkResult vkQueueSubmit( + VkQueue queue, + uint32_t submitCount, + const VkSubmitInfo* pSubmits, + VkFence fence) { + + MVKQueue* mvkQ = (MVKQueue*)queue; + return mvkQ->submit(submitCount, pSubmits, fence, kMVKCommandUseQueueSubmit); +} + +MVK_PUBLIC_SYMBOL VkResult vkQueueWaitIdle( + VkQueue queue) { + + MVKQueue* mvkQ = (MVKQueue*)queue; + return mvkQ->waitIdle(kMVKCommandUseQueueWaitIdle); +} + +MVK_PUBLIC_SYMBOL VkResult vkDeviceWaitIdle( + VkDevice device) { + + MVKDevice* mvkDev = (MVKDevice*)device; + return mvkDev->waitIdle(); +} + +MVK_PUBLIC_SYMBOL VkResult vkAllocateMemory( + VkDevice device, + const VkMemoryAllocateInfo* pAllocateInfo, + const VkAllocationCallbacks* pAllocator, + VkDeviceMemory* pMem) { + + MVKDevice* mvkDev = (MVKDevice*)device; + MVKDeviceMemory* mvkMem = mvkDev->allocateMemory(pAllocateInfo, pAllocator); + VkResult rslt = mvkMem->getConfigurationResult(); + *pMem = (VkDeviceMemory)((rslt == VK_SUCCESS) ? mvkMem : VK_NULL_HANDLE); + return rslt; +} + +MVK_PUBLIC_SYMBOL void vkFreeMemory( + VkDevice device, + VkDeviceMemory mem, + const VkAllocationCallbacks* pAllocator) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->freeMemory((MVKDeviceMemory*)mem, pAllocator); +} + +MVK_PUBLIC_SYMBOL VkResult vkMapMemory( + VkDevice device, + VkDeviceMemory mem, + VkDeviceSize offset, + VkDeviceSize size, + VkMemoryMapFlags flags, + void** ppData) { + + MVKDeviceMemory* mvkMem = (MVKDeviceMemory*)mem; + return mvkMem->map(offset, size, flags, ppData); +} + +MVK_PUBLIC_SYMBOL void vkUnmapMemory( + VkDevice device, + VkDeviceMemory mem) { + + MVKDeviceMemory* mvkMem = (MVKDeviceMemory*)mem; + return mvkMem->unmap(); +} + +MVK_PUBLIC_SYMBOL VkResult vkFlushMappedMemoryRanges( + VkDevice device, + uint32_t memRangeCount, + const VkMappedMemoryRange* pMemRanges) { + + for (uint32_t i = 0; i < memRangeCount; i++) { + const VkMappedMemoryRange* pMem = &pMemRanges[i]; + MVKDeviceMemory* mvkMem = (MVKDeviceMemory*)pMem->memory; + mvkMem->flushToDevice(pMem->offset, pMem->size); + } + return VK_SUCCESS; +} + +MVK_PUBLIC_SYMBOL VkResult vkInvalidateMappedMemoryRanges( + VkDevice device, + uint32_t memRangeCount, + const VkMappedMemoryRange* pMemRanges) { + + for (uint32_t i = 0; i < memRangeCount; i++) { + const VkMappedMemoryRange* pMem = &pMemRanges[i]; + MVKDeviceMemory* mvkMem = (MVKDeviceMemory*)pMem->memory; + mvkMem->pullFromDevice(pMem->offset, pMem->size); + } + return VK_SUCCESS; +} + +MVK_PUBLIC_SYMBOL void vkGetDeviceMemoryCommitment( + VkDevice device, + VkDeviceMemory memory, + VkDeviceSize* pCommittedMemoryInBytes) { + + if ( !pCommittedMemoryInBytes ) { return; } + + MVKDeviceMemory* mvkMem = (MVKDeviceMemory*)memory; + *pCommittedMemoryInBytes = mvkMem->getDeviceMemoryCommitment(); +} + +MVK_PUBLIC_SYMBOL VkResult vkBindBufferMemory( + VkDevice device, + VkBuffer buffer, + VkDeviceMemory mem, + VkDeviceSize memOffset) { + + MVKBuffer* mvkBuff = (MVKBuffer*)buffer; + MVKDeviceMemory* mvkMem = (MVKDeviceMemory*)mem; + return mvkBuff->bindDeviceMemory(mvkMem, memOffset); +} + +MVK_PUBLIC_SYMBOL VkResult vkBindImageMemory( + VkDevice device, + VkImage image, + VkDeviceMemory mem, + VkDeviceSize memOffset) { + + MVKImage* mvkImg = (MVKImage*)image; + MVKDeviceMemory* mvkMem = (MVKDeviceMemory*)mem; + return mvkImg->bindDeviceMemory(mvkMem, memOffset); +} + +MVK_PUBLIC_SYMBOL void vkGetBufferMemoryRequirements( + VkDevice device, + VkBuffer buffer, + VkMemoryRequirements* pMemoryRequirements) { + + MVKBuffer* mvkBuff = (MVKBuffer*)buffer; + mvkBuff->getMemoryRequirements(pMemoryRequirements); +} + +MVK_PUBLIC_SYMBOL void vkGetImageMemoryRequirements( + VkDevice device, + VkImage image, + VkMemoryRequirements* pMemoryRequirements) { + + MVKImage* mvkImg = (MVKImage*)image; + mvkImg->getMemoryRequirements(pMemoryRequirements); +} + +MVK_PUBLIC_SYMBOL void vkGetImageSparseMemoryRequirements( + VkDevice device, + VkImage image, + uint32_t* pNumRequirements, + VkSparseImageMemoryRequirements* pSparseMemoryRequirements) { + + MVKLogUnimplemented("vkGetImageSparseMemoryRequirements"); +} + +MVK_PUBLIC_SYMBOL void vkGetPhysicalDeviceSparseImageFormatProperties( + VkPhysicalDevice physicalDevice, + VkFormat format, + VkImageType type, + VkSampleCountFlagBits samples, + VkImageUsageFlags usage, + VkImageTiling tiling, + uint32_t* pPropertyCount, + VkSparseImageFormatProperties* pProperties) { + + MVKLogUnimplemented("vkGetPhysicalDeviceSparseImageFormatProperties"); +} + +MVK_PUBLIC_SYMBOL VkResult vkQueueBindSparse( + VkQueue queue, + uint32_t bindInfoCount, + const VkBindSparseInfo* pBindInfo, + VkFence fence) { + + return MVKLogUnimplemented("vkQueueBindSparse"); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreateFence( + VkDevice device, + const VkFenceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkFence* pFence) { + + MVKDevice* mvkDev = (MVKDevice*)device; + MVKFence* mvkFence = mvkDev->createFence(pCreateInfo, pAllocator); + *pFence = (VkFence)mvkFence; + return mvkFence->getConfigurationResult(); +} + +MVK_PUBLIC_SYMBOL void vkDestroyFence( + VkDevice device, + VkFence fence, + const VkAllocationCallbacks* pAllocator) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->destroyFence((MVKFence*)fence, pAllocator); +} + +MVK_PUBLIC_SYMBOL VkResult vkResetFences( + VkDevice device, + uint32_t fenceCount, + const VkFence* pFences) { + + return mvkResetFences(fenceCount, pFences); +} + +MVK_PUBLIC_SYMBOL VkResult vkGetFenceStatus( + VkDevice device, + VkFence fence) { + + MVKFence* mvkFence = (MVKFence*)fence; + return mvkFence->getIsSignaled() ? VK_SUCCESS : VK_NOT_READY; +} + +MVK_PUBLIC_SYMBOL VkResult vkWaitForFences( + VkDevice device, + uint32_t fenceCount, + const VkFence* pFences, + VkBool32 waitAll, + uint64_t timeout) { + + return mvkWaitForFences(fenceCount, pFences, waitAll, timeout); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreateSemaphore( + VkDevice device, + const VkSemaphoreCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSemaphore* pSemaphore) { + + MVKDevice* mvkDev = (MVKDevice*)device; + MVKSemaphore* mvkSem4 = mvkDev->createSemaphore(pCreateInfo, pAllocator); + *pSemaphore = (VkSemaphore)mvkSem4; + return mvkSem4->getConfigurationResult(); +} + +MVK_PUBLIC_SYMBOL void vkDestroySemaphore( + VkDevice device, + VkSemaphore semaphore, + const VkAllocationCallbacks* pAllocator) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->destroySemaphore((MVKSemaphore*)semaphore, pAllocator); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreateEvent( + VkDevice device, + const VkEventCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkEvent* pEvent) { + + return MVKLogUnimplemented("vkCreateEvent"); +} + +MVK_PUBLIC_SYMBOL void vkDestroyEvent( + VkDevice device, + VkEvent event, + const VkAllocationCallbacks* pAllocator) { + + MVKLogUnimplemented("vkDestroyEvent"); +} + +MVK_PUBLIC_SYMBOL VkResult vkGetEventStatus( + VkDevice device, + VkEvent event) { + + return MVKLogUnimplemented("vkGetEventStatus"); +} + +MVK_PUBLIC_SYMBOL VkResult vkSetEvent( + VkDevice device, + VkEvent event) { + + return MVKLogUnimplemented("vkSetEvent"); +} + +MVK_PUBLIC_SYMBOL VkResult vkResetEvent( + VkDevice device, + VkEvent event) { + + return MVKLogUnimplemented("vkResetEvent"); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreateQueryPool( + VkDevice device, + const VkQueryPoolCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkQueryPool* pQueryPool) { + + MVKDevice* mvkDev = (MVKDevice*)device; + MVKQueryPool* mvkQP = mvkDev->createQueryPool(pCreateInfo, pAllocator); + *pQueryPool = (VkQueryPool)mvkQP; + return mvkQP->getConfigurationResult(); +} + +MVK_PUBLIC_SYMBOL void vkDestroyQueryPool( + VkDevice device, + VkQueryPool queryPool, + const VkAllocationCallbacks* pAllocator) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->destroyQueryPool((MVKQueryPool*)queryPool, pAllocator); +} + +MVK_PUBLIC_SYMBOL VkResult vkGetQueryPoolResults( + VkDevice device, + VkQueryPool queryPool, + uint32_t firstQuery, + uint32_t queryCount, + size_t dataSize, + void* pData, + VkDeviceSize stride, + VkQueryResultFlags flags) { + + MVKQueryPool* mvkQP = (MVKQueryPool*)queryPool; + return mvkQP->getResults(firstQuery, queryCount, dataSize, pData, stride, flags); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreateBuffer( + VkDevice device, + const VkBufferCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkBuffer* pBuffer) { + + MVKDevice* mvkDev = (MVKDevice*)device; + MVKBuffer* mvkBuff = mvkDev->createBuffer(pCreateInfo, pAllocator); + *pBuffer = (VkBuffer)mvkBuff; + return mvkBuff->getConfigurationResult(); +} + +MVK_PUBLIC_SYMBOL void vkDestroyBuffer( + VkDevice device, + VkBuffer buffer, + const VkAllocationCallbacks* pAllocator) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->destroyBuffer((MVKBuffer*)buffer, pAllocator); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreateBufferView( + VkDevice device, + const VkBufferViewCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkBufferView* pView) { + + MVKDevice* mvkDev = (MVKDevice*)device; + MVKBufferView* mvkBuffView = mvkDev->createBufferView(pCreateInfo, pAllocator); + *pView = (VkBufferView)mvkBuffView; + return mvkBuffView->getConfigurationResult(); +} + +MVK_PUBLIC_SYMBOL void vkDestroyBufferView( + VkDevice device, + VkBufferView bufferView, + const VkAllocationCallbacks* pAllocator) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->destroyBufferView((MVKBufferView*)bufferView, pAllocator); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreateImage( + VkDevice device, + const VkImageCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkImage* pImage) { + + MVKDevice* mvkDev = (MVKDevice*)device; + MVKImage* mvkImg = mvkDev->createImage(pCreateInfo, pAllocator); + *pImage = (VkImage)mvkImg; + return mvkImg->getConfigurationResult(); +} + +MVK_PUBLIC_SYMBOL void vkDestroyImage( + VkDevice device, + VkImage image, + const VkAllocationCallbacks* pAllocator) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->destroyImage((MVKImage*)image, pAllocator); +} + +MVK_PUBLIC_SYMBOL void vkGetImageSubresourceLayout( + VkDevice device, + VkImage image, + const VkImageSubresource* pSubresource, + VkSubresourceLayout* pLayout) { + + MVKImage* mvkImg = (MVKImage*)image; + mvkImg->getSubresourceLayout(pSubresource, pLayout); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreateImageView( + VkDevice device, + const VkImageViewCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkImageView* pView) { + + MVKDevice* mvkDev = (MVKDevice*)device; + MVKImageView* mvkImgView = mvkDev->createImageView(pCreateInfo, pAllocator); + *pView = (VkImageView)mvkImgView; + return mvkImgView->getConfigurationResult(); +} + +MVK_PUBLIC_SYMBOL void vkDestroyImageView( + VkDevice device, + VkImageView imageView, + const VkAllocationCallbacks* pAllocator) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->destroyImageView((MVKImageView*)imageView, pAllocator); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreateShaderModule( + VkDevice device, + const VkShaderModuleCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkShaderModule* pShaderModule) { + + MVKDevice* mvkDev = (MVKDevice*)device; + MVKShaderModule* mvkShdrMod = mvkDev->createShaderModule(pCreateInfo, pAllocator); + *pShaderModule = (VkShaderModule)mvkShdrMod; + return mvkShdrMod->getConfigurationResult(); +} + +MVK_PUBLIC_SYMBOL void vkDestroyShaderModule( + VkDevice device, + VkShaderModule shaderModule, + const VkAllocationCallbacks* pAllocator) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->destroyShaderModule((MVKShaderModule*)shaderModule, pAllocator); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreatePipelineCache( + VkDevice device, + const VkPipelineCacheCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkPipelineCache* pPipelineCache) { + + MVKDevice* mvkDev = (MVKDevice*)device; + MVKPipelineCache* mvkPLC = mvkDev->createPipelineCache(pCreateInfo, pAllocator); + *pPipelineCache = (VkPipelineCache)mvkPLC; + return mvkPLC->getConfigurationResult(); +} + +MVK_PUBLIC_SYMBOL void vkDestroyPipelineCache( + VkDevice device, + VkPipelineCache pipelineCache, + const VkAllocationCallbacks* pAllocator) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->destroyPipelineCache((MVKPipelineCache*)pipelineCache, pAllocator); +} + +MVK_PUBLIC_SYMBOL VkResult vkGetPipelineCacheData( + VkDevice device, + VkPipelineCache pipelineCache, + size_t* pDataSize, + void* pData) { + + MVKPipelineCache* mvkPLC = (MVKPipelineCache*)pipelineCache; + return mvkPLC->getData(pDataSize, pData); +} + +MVK_PUBLIC_SYMBOL VkResult vkMergePipelineCaches( + VkDevice device, + VkPipelineCache destCache, + uint32_t srcCacheCount, + const VkPipelineCache* pSrcCaches) { + + MVKPipelineCache* mvkPLC = (MVKPipelineCache*)destCache; + return mvkPLC->mergePipelineCaches(srcCacheCount, pSrcCaches); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreateGraphicsPipelines( + VkDevice device, + VkPipelineCache pipelineCache, + uint32_t count, + const VkGraphicsPipelineCreateInfo* pCreateInfos, + const VkAllocationCallbacks* pAllocator, + VkPipeline* pPipelines) { + + MVKDevice* mvkDev = (MVKDevice*)device; + return mvkDev->createPipelines(pipelineCache, count, pCreateInfos, pAllocator, pPipelines); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreateComputePipelines( + VkDevice device, + VkPipelineCache pipelineCache, + uint32_t count, + const VkComputePipelineCreateInfo* pCreateInfos, + const VkAllocationCallbacks* pAllocator, + VkPipeline* pPipelines) { + + MVKDevice* mvkDev = (MVKDevice*)device; + return mvkDev->createPipelines(pipelineCache, count, pCreateInfos, pAllocator, pPipelines); +} + +MVK_PUBLIC_SYMBOL void vkDestroyPipeline( + VkDevice device, + VkPipeline pipeline, + const VkAllocationCallbacks* pAllocator) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->destroyPipeline((MVKPipeline*)pipeline, pAllocator); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreatePipelineLayout( + VkDevice device, + const VkPipelineLayoutCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkPipelineLayout* pPipelineLayout) { + + MVKDevice* mvkDev = (MVKDevice*)device; + MVKPipelineLayout* mvkPLL = mvkDev->createPipelineLayout(pCreateInfo, pAllocator); + *pPipelineLayout = (VkPipelineLayout)mvkPLL; + return mvkPLL->getConfigurationResult(); +} + +MVK_PUBLIC_SYMBOL void vkDestroyPipelineLayout( + VkDevice device, + VkPipelineLayout pipelineLayout, + const VkAllocationCallbacks* pAllocator) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->destroyPipelineLayout((MVKPipelineLayout*)pipelineLayout, pAllocator); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreateSampler( + VkDevice device, + const VkSamplerCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSampler* pSampler) { + + MVKDevice* mvkDev = (MVKDevice*)device; + MVKSampler* mvkSamp = mvkDev->createSampler(pCreateInfo, pAllocator); + *pSampler = (VkSampler)mvkSamp; + return mvkSamp->getConfigurationResult(); +} + +MVK_PUBLIC_SYMBOL void vkDestroySampler( + VkDevice device, + VkSampler sampler, + const VkAllocationCallbacks* pAllocator) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->destroySampler((MVKSampler*)sampler, pAllocator); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreateDescriptorSetLayout( + VkDevice device, + const VkDescriptorSetLayoutCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDescriptorSetLayout* pSetLayout) { + + MVKDevice* mvkDev = (MVKDevice*)device; + MVKDescriptorSetLayout* mvkDSL = mvkDev->createDescriptorSetLayout(pCreateInfo, pAllocator); + *pSetLayout = (VkDescriptorSetLayout)mvkDSL; + return mvkDSL->getConfigurationResult(); +} + +MVK_PUBLIC_SYMBOL void vkDestroyDescriptorSetLayout( + VkDevice device, + VkDescriptorSetLayout descriptorSetLayout, + const VkAllocationCallbacks* pAllocator) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->destroyDescriptorSetLayout((MVKDescriptorSetLayout*)descriptorSetLayout, pAllocator); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreateDescriptorPool( + VkDevice device, + const VkDescriptorPoolCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDescriptorPool* pDescriptorPool) { + + MVKDevice* mvkDev = (MVKDevice*)device; + MVKDescriptorPool* mvkDP = mvkDev->createDescriptorPool(pCreateInfo, pAllocator); + *pDescriptorPool = (VkDescriptorPool)mvkDP; + return mvkDP->getConfigurationResult(); +} + +MVK_PUBLIC_SYMBOL void vkDestroyDescriptorPool( + VkDevice device, + VkDescriptorPool descriptorPool, + const VkAllocationCallbacks* pAllocator) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->destroyDescriptorPool((MVKDescriptorPool*)descriptorPool, pAllocator); +} + +MVK_PUBLIC_SYMBOL VkResult vkResetDescriptorPool( + VkDevice device, + VkDescriptorPool descriptorPool, + VkDescriptorPoolResetFlags flags) { + + MVKDescriptorPool* mvkDP = (MVKDescriptorPool*)descriptorPool; + return mvkDP->reset(flags); +} + +MVK_PUBLIC_SYMBOL VkResult vkAllocateDescriptorSets( + VkDevice device, + const VkDescriptorSetAllocateInfo* pAllocateInfo, + VkDescriptorSet* pDescriptorSets) { + + MVKDescriptorPool* mvkDP = (MVKDescriptorPool*)pAllocateInfo->descriptorPool; + return mvkDP->allocateDescriptorSets(pAllocateInfo->descriptorSetCount, + pAllocateInfo->pSetLayouts, + pDescriptorSets); +} + +MVK_PUBLIC_SYMBOL VkResult vkFreeDescriptorSets( + VkDevice device, + VkDescriptorPool descriptorPool, + uint32_t count, + const VkDescriptorSet* pDescriptorSets) { + + MVKDescriptorPool* mvkDP = (MVKDescriptorPool*)descriptorPool; + return mvkDP->freeDescriptorSets(count, pDescriptorSets); +} + +MVK_PUBLIC_SYMBOL void vkUpdateDescriptorSets( + VkDevice device, + uint32_t writeCount, + const VkWriteDescriptorSet* pDescriptorWrites, + uint32_t copyCount, + const VkCopyDescriptorSet* pDescriptorCopies) { + + mvkUpdateDescriptorSets(writeCount, pDescriptorWrites, copyCount, pDescriptorCopies); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreateFramebuffer( + VkDevice device, + const VkFramebufferCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkFramebuffer* pFramebuffer) { + + MVKDevice* mvkDev = (MVKDevice*)device; + MVKFramebuffer* mvkFB = mvkDev->createFramebuffer(pCreateInfo, pAllocator); + *pFramebuffer = (VkFramebuffer)mvkFB; + return mvkFB->getConfigurationResult(); +} + +MVK_PUBLIC_SYMBOL void vkDestroyFramebuffer( + VkDevice device, + VkFramebuffer framebuffer, + const VkAllocationCallbacks* pAllocator) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->destroyFramebuffer((MVKFramebuffer*)framebuffer, pAllocator); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreateRenderPass( + VkDevice device, + const VkRenderPassCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkRenderPass* pRenderPass) { + + MVKDevice* mvkDev = (MVKDevice*)device; + MVKRenderPass* mvkRendPass = mvkDev->createRenderPass(pCreateInfo, pAllocator); + *pRenderPass = (VkRenderPass)mvkRendPass; + return mvkRendPass->getConfigurationResult(); +} + +MVK_PUBLIC_SYMBOL void vkDestroyRenderPass( + VkDevice device, + VkRenderPass renderPass, + const VkAllocationCallbacks* pAllocator) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->destroyRenderPass((MVKRenderPass*)renderPass, pAllocator); +} + +MVK_PUBLIC_SYMBOL void vkGetRenderAreaGranularity( + VkDevice device, + VkRenderPass renderPass, + VkExtent2D* pGranularity) { + + if ( !pGranularity ) { return; } + + MVKRenderPass* mvkRendPass = (MVKRenderPass*)renderPass; + *pGranularity = mvkRendPass->getRenderAreaGranularity(); +} + +MVK_PUBLIC_SYMBOL VkResult vkCreateCommandPool( + VkDevice device, + const VkCommandPoolCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkCommandPool* pCmdPool) { + + MVKDevice* mvkDev = (MVKDevice*)device; + MVKCommandPool* mvkCmdPool = mvkDev->createCommandPool(pCreateInfo, pAllocator); + *pCmdPool = (VkCommandPool)mvkCmdPool; + return mvkCmdPool->getConfigurationResult(); +} + +MVK_PUBLIC_SYMBOL void vkDestroyCommandPool( + VkDevice device, + VkCommandPool commandPool, + const VkAllocationCallbacks* pAllocator) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->destroyCommandPool((MVKCommandPool*)commandPool, pAllocator); +} + +MVK_PUBLIC_SYMBOL VkResult vkResetCommandPool( + VkDevice device, + VkCommandPool commandPool, + VkCommandPoolResetFlags flags) { + + MVKCommandPool* mvkCmdPool = (MVKCommandPool*)commandPool; + return mvkCmdPool->reset(flags); +} + +MVK_PUBLIC_SYMBOL VkResult vkAllocateCommandBuffers( + VkDevice device, + const VkCommandBufferAllocateInfo* pAllocateInfo, + VkCommandBuffer* pCmdBuffer) { + + MVKCommandPool* mvkCmdPool = (MVKCommandPool*)pAllocateInfo->commandPool; + return mvkCmdPool->allocateCommandBuffers(pAllocateInfo, pCmdBuffer); +} + +MVK_PUBLIC_SYMBOL void vkFreeCommandBuffers( + VkDevice device, + VkCommandPool commandPool, + uint32_t commandBufferCount, + const VkCommandBuffer* pCommandBuffers) { + + MVKCommandPool* mvkCmdPool = (MVKCommandPool*)commandPool; + mvkCmdPool->freeCommandBuffers(commandBufferCount, pCommandBuffers); +} + +MVK_PUBLIC_SYMBOL VkResult vkBeginCommandBuffer( + VkCommandBuffer commandBuffer, + const VkCommandBufferBeginInfo* pBeginInfo) { + + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + return cmdBuff->begin(pBeginInfo); +} + +MVK_PUBLIC_SYMBOL VkResult vkEndCommandBuffer( + VkCommandBuffer commandBuffer) { + + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + return cmdBuff->end(); +} + +MVK_PUBLIC_SYMBOL VkResult vkResetCommandBuffer( + VkCommandBuffer commandBuffer, + VkCommandBufferResetFlags flags) { + + MVKCommandBuffer* cmdBuff = (MVKCommandBuffer*)commandBuffer; + return cmdBuff->reset(flags); +} + +MVK_PUBLIC_SYMBOL void vkCmdBindPipeline( + VkCommandBuffer commandBuffer, + VkPipelineBindPoint pipelineBindPoint, + VkPipeline pipeline) { + + mvkCmdBindPipeline(commandBuffer, pipelineBindPoint, pipeline); +} + +MVK_PUBLIC_SYMBOL void vkCmdSetViewport( + VkCommandBuffer commandBuffer, + uint32_t firstViewport, + uint32_t viewportCount, + const VkViewport* pViewports) { + + mvkCmdSetViewport(commandBuffer, firstViewport, viewportCount, pViewports); +} + +MVK_PUBLIC_SYMBOL void vkCmdSetScissor( + VkCommandBuffer commandBuffer, + uint32_t firstScissor, + uint32_t scissorCount, + const VkRect2D* pScissors) { + + mvkCmdSetScissor(commandBuffer, firstScissor, scissorCount, pScissors); +} + +MVK_PUBLIC_SYMBOL void vkCmdSetLineWidth( + VkCommandBuffer commandBuffer, + float lineWidth) { + + mvkCmdSetLineWidth(commandBuffer, lineWidth); +} + +MVK_PUBLIC_SYMBOL void vkCmdSetDepthBias( + VkCommandBuffer commandBuffer, + float depthBiasConstantFactor, + float depthBiasClamp, + float depthBiasSlopeFactor) { + + mvkCmdSetDepthBias(commandBuffer,depthBiasConstantFactor, depthBiasClamp, depthBiasSlopeFactor); +} + +MVK_PUBLIC_SYMBOL void vkCmdSetBlendConstants( + VkCommandBuffer commandBuffer, + const float blendConst[4]) { + + mvkCmdSetBlendConstants(commandBuffer, blendConst); +} + +MVK_PUBLIC_SYMBOL void vkCmdSetDepthBounds( + VkCommandBuffer commandBuffer, + float minDepthBounds, + float maxDepthBounds) { + + mvkCmdSetDepthBounds(commandBuffer, minDepthBounds, maxDepthBounds); +} + +MVK_PUBLIC_SYMBOL void vkCmdSetStencilCompareMask( + VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + uint32_t stencilCompareMask) { + + mvkCmdSetStencilCompareMask(commandBuffer, faceMask, stencilCompareMask); +} + +MVK_PUBLIC_SYMBOL void vkCmdSetStencilWriteMask( + VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + uint32_t stencilWriteMask) { + + mvkCmdSetStencilWriteMask(commandBuffer, faceMask, stencilWriteMask); +} + +MVK_PUBLIC_SYMBOL void vkCmdSetStencilReference( + VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + uint32_t stencilReference) { + + mvkCmdSetStencilReference(commandBuffer, faceMask, stencilReference); +} + + +MVK_PUBLIC_SYMBOL void vkCmdBindDescriptorSets( + VkCommandBuffer commandBuffer, + VkPipelineBindPoint pipelineBindPoint, + VkPipelineLayout layout, + uint32_t firstSet, + uint32_t setCount, + const VkDescriptorSet* pDescriptorSets, + uint32_t dynamicOffsetCount, + const uint32_t* pDynamicOffsets) { + + mvkCmdBindDescriptorSets(commandBuffer, pipelineBindPoint, layout, firstSet, setCount, + pDescriptorSets, dynamicOffsetCount, pDynamicOffsets); +} + +MVK_PUBLIC_SYMBOL void vkCmdBindIndexBuffer( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + VkIndexType indexType) { + + mvkCmdBindIndexBuffer(commandBuffer, buffer, offset, indexType); +} + +MVK_PUBLIC_SYMBOL void vkCmdBindVertexBuffers( + VkCommandBuffer commandBuffer, + uint32_t startBinding, + uint32_t bindingCount, + const VkBuffer* pBuffers, + const VkDeviceSize* pOffsets) { + + mvkCmdBindVertexBuffers(commandBuffer, startBinding, bindingCount, pBuffers, pOffsets); +} + +MVK_PUBLIC_SYMBOL void vkCmdDraw( + VkCommandBuffer commandBuffer, + uint32_t vertexCount, + uint32_t instanceCount, + uint32_t firstVertex, + uint32_t firstInstance) { + + mvkCmdDraw(commandBuffer, vertexCount, instanceCount, firstVertex, firstInstance); +} + +MVK_PUBLIC_SYMBOL void vkCmdDrawIndexed( + VkCommandBuffer commandBuffer, + uint32_t indexCount, + uint32_t instanceCount, + uint32_t firstIndex, + int32_t vertexOffset, + uint32_t firstInstance) { + + mvkCmdDrawIndexed(commandBuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance); +} + +MVK_PUBLIC_SYMBOL void vkCmdDrawIndirect( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + uint32_t count, + uint32_t stride) { + + mvkCmdDrawIndirect(commandBuffer, buffer, offset, count, stride); +} + +MVK_PUBLIC_SYMBOL void vkCmdDrawIndexedIndirect( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + uint32_t count, + uint32_t stride) { + + mvkCmdDrawIndexedIndirect(commandBuffer, buffer, offset, count, stride); +} + +MVK_PUBLIC_SYMBOL void vkCmdDispatch( + VkCommandBuffer commandBuffer, + uint32_t x, + uint32_t y, + uint32_t z) { + + mvkCmdDispatch(commandBuffer, x, y, z); +} + +MVK_PUBLIC_SYMBOL void vkCmdDispatchIndirect( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset) { + + mvkCmdDispatchIndirect(commandBuffer, buffer, offset); +} + +MVK_PUBLIC_SYMBOL void vkCmdCopyBuffer( + VkCommandBuffer commandBuffer, + VkBuffer srcBuffer, + VkBuffer destBuffer, + uint32_t regionCount, + const VkBufferCopy* pRegions) { + + mvkCmdCopyBuffer(commandBuffer, srcBuffer, destBuffer, regionCount, pRegions); +} + +MVK_PUBLIC_SYMBOL void vkCmdCopyImage( + VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageCopy* pRegions) { + + mvkCmdCopyImage(commandBuffer, + srcImage, srcImageLayout, + dstImage, dstImageLayout, + regionCount, pRegions); +} + +MVK_PUBLIC_SYMBOL void vkCmdBlitImage( + VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageBlit* pRegions, + VkFilter filter) { + + mvkCmdBlitImage(commandBuffer, + srcImage, srcImageLayout, + dstImage, dstImageLayout, + regionCount, pRegions, filter); +} + +MVK_PUBLIC_SYMBOL void vkCmdCopyBufferToImage( + VkCommandBuffer commandBuffer, + VkBuffer srcBuffer, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkBufferImageCopy* pRegions) { + + mvkCmdCopyBufferToImage(commandBuffer, srcBuffer, dstImage, + dstImageLayout, regionCount, pRegions); +} + +MVK_PUBLIC_SYMBOL void vkCmdCopyImageToBuffer( + VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkBuffer dstBuffer, + uint32_t regionCount, + const VkBufferImageCopy* pRegions) { + + mvkCmdCopyImageToBuffer(commandBuffer, srcImage, srcImageLayout, + dstBuffer, regionCount, pRegions); +} + +MVK_PUBLIC_SYMBOL void vkCmdUpdateBuffer( + VkCommandBuffer commandBuffer, + VkBuffer dstBuffer, + VkDeviceSize dstOffset, + VkDeviceSize dataSize, + const void* pData) { + + mvkCmdUpdateBuffer(commandBuffer, dstBuffer, dstOffset, dataSize, pData); +} + +MVK_PUBLIC_SYMBOL void vkCmdFillBuffer( + VkCommandBuffer commandBuffer, + VkBuffer dstBuffer, + VkDeviceSize dstOffset, + VkDeviceSize size, + uint32_t data) { + + mvkCmdFillBuffer(commandBuffer, dstBuffer, dstOffset, size, data); +} + +MVK_PUBLIC_SYMBOL void vkCmdClearColorImage( + VkCommandBuffer commandBuffer, + VkImage image, + VkImageLayout imageLayout, + const VkClearColorValue* pColor, + uint32_t rangeCount, + const VkImageSubresourceRange* pRanges) { + + mvkCmdClearImage(commandBuffer, image, imageLayout, pColor, rangeCount, pRanges); +} + +MVK_PUBLIC_SYMBOL void vkCmdClearDepthStencilImage( + VkCommandBuffer commandBuffer, + VkImage image, + VkImageLayout imageLayout, + const VkClearDepthStencilValue* pDepthStencil, + uint32_t rangeCount, + const VkImageSubresourceRange* pRanges) { + + mvkCmdClearDepthStencilImage(commandBuffer, image, imageLayout, pDepthStencil, rangeCount, pRanges); +} + +MVK_PUBLIC_SYMBOL void vkCmdClearAttachments( + VkCommandBuffer commandBuffer, + uint32_t attachmentCount, + const VkClearAttachment* pAttachments, + uint32_t rectCount, + const VkClearRect* pRects) { + + mvkCmdClearAttachments(commandBuffer, attachmentCount, pAttachments, rectCount, pRects); +} + +MVK_PUBLIC_SYMBOL void vkCmdResolveImage( + VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageResolve* pRegions) { + + mvkCmdResolveImage(commandBuffer, srcImage, srcImageLayout, + dstImage, dstImageLayout, regionCount, pRegions); +} + +MVK_PUBLIC_SYMBOL void vkCmdSetEvent( + VkCommandBuffer commandBuffer, + VkEvent event, + VkPipelineStageFlags stageMask) { + + MVKLogUnimplemented("vkCmdSetEvent"); +} + +MVK_PUBLIC_SYMBOL void vkCmdResetEvent( + VkCommandBuffer commandBuffer, + VkEvent event, + VkPipelineStageFlags stageMask) { + + MVKLogUnimplemented("vkCmdResetEvent"); +} + +MVK_PUBLIC_SYMBOL void vkCmdWaitEvents( + VkCommandBuffer commandBuffer, + uint32_t eventCount, + const VkEvent* pEvents, + VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + uint32_t memoryBarrierCount, + const VkMemoryBarrier* pMemoryBarriers, + uint32_t bufferMemoryBarrierCount, + const VkBufferMemoryBarrier* pBufferMemoryBarriers, + uint32_t imageMemoryBarrierCount, + const VkImageMemoryBarrier* pImageMemoryBarriers) { + + MVKLogUnimplemented("vkCmdWaitEvents"); +} + +MVK_PUBLIC_SYMBOL void vkCmdPipelineBarrier( + VkCommandBuffer commandBuffer, + VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkDependencyFlags dependencyFlags, + uint32_t memoryBarrierCount, + const VkMemoryBarrier* pMemoryBarriers, + uint32_t bufferMemoryBarrierCount, + const VkBufferMemoryBarrier* pBufferMemoryBarriers, + uint32_t imageMemoryBarrierCount, + const VkImageMemoryBarrier* pImageMemoryBarriers) { + + mvkCmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, + dependencyFlags, memoryBarrierCount, pMemoryBarriers, + bufferMemoryBarrierCount, pBufferMemoryBarriers, + imageMemoryBarrierCount, pImageMemoryBarriers); +} + +MVK_PUBLIC_SYMBOL void vkCmdBeginQuery( + VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t query, + VkQueryControlFlags flags) { + + mvkCmdBeginQuery(commandBuffer, queryPool, query, flags); +} + +MVK_PUBLIC_SYMBOL void vkCmdEndQuery( + VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t query) { + + mvkCmdEndQuery(commandBuffer, queryPool, query); +} + +MVK_PUBLIC_SYMBOL void vkCmdResetQueryPool( + VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t firstQuery, + uint32_t queryCount) { + + mvkCmdResetQueryPool(commandBuffer, queryPool, firstQuery, queryCount); +} + +MVK_PUBLIC_SYMBOL void vkCmdWriteTimestamp( + VkCommandBuffer commandBuffer, + VkPipelineStageFlagBits pipelineStage, + VkQueryPool queryPool, + uint32_t query) { + + mvkCmdWriteTimestamp(commandBuffer, pipelineStage, queryPool, query); +} + +MVK_PUBLIC_SYMBOL void vkCmdCopyQueryPoolResults( + VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t firstQuery, + uint32_t queryCount, + VkBuffer destBuffer, + VkDeviceSize destOffset, + VkDeviceSize destStride, + VkQueryResultFlags flags) { + + mvkCmdCopyQueryPoolResults(commandBuffer, queryPool, firstQuery, queryCount, + destBuffer, destOffset, destStride, flags); +} + +MVK_PUBLIC_SYMBOL void vkCmdPushConstants( + VkCommandBuffer commandBuffer, + VkPipelineLayout layout, + VkShaderStageFlags stageFlags, + uint32_t offset, + uint32_t size, + const void* pValues) { + + mvkCmdPushConstants(commandBuffer, layout, stageFlags, offset, size, pValues); +} + +MVK_PUBLIC_SYMBOL void vkCmdBeginRenderPass( + VkCommandBuffer commandBuffer, + const VkRenderPassBeginInfo* pRenderPassBegin, + VkSubpassContents contents) { + + mvkCmdBeginRenderPass(commandBuffer,pRenderPassBegin, contents); +} + +MVK_PUBLIC_SYMBOL void vkCmdNextSubpass( + VkCommandBuffer commandBuffer, + VkSubpassContents contents) { + + mvkCmdNextSubpass(commandBuffer, contents); +} + +MVK_PUBLIC_SYMBOL void vkCmdEndRenderPass( + VkCommandBuffer commandBuffer) { + + mvkCmdEndRenderPass(commandBuffer); +} + +MVK_PUBLIC_SYMBOL void vkCmdExecuteCommands( + VkCommandBuffer commandBuffer, + uint32_t cmdBuffersCount, + const VkCommandBuffer* pCmdBuffers) { + + mvkCmdExecuteCommands(commandBuffer, cmdBuffersCount, pCmdBuffers); +} + + +#pragma mark - +#pragma mark VK_KHR_swapchain extension + +MVK_PUBLIC_SYMBOL VkResult vkCreateSwapchainKHR( + VkDevice device, + const VkSwapchainCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSwapchainKHR* pSwapchain) { + + MVKDevice* mvkDev = (MVKDevice*)device; + MVKSwapchain* mvkSwpChn = mvkDev->createSwapchain(pCreateInfo, pAllocator); + *pSwapchain = (VkSwapchainKHR)(mvkSwpChn); + return mvkSwpChn->getConfigurationResult(); +} + +MVK_PUBLIC_SYMBOL void vkDestroySwapchainKHR( + VkDevice device, + VkSwapchainKHR swapchain, + const VkAllocationCallbacks* pAllocator) { + + MVKDevice* mvkDev = (MVKDevice*)device; + mvkDev->destroySwapchain((MVKSwapchain*)swapchain, pAllocator); +} + +MVK_PUBLIC_SYMBOL VkResult vkGetSwapchainImagesKHR( + VkDevice device, + VkSwapchainKHR swapchain, + uint32_t* pCount, + VkImage* pSwapchainImages) { + + MVKSwapchain* mvkSwapchain = (MVKSwapchain*)swapchain; + return mvkSwapchain->getImages(pCount, pSwapchainImages); +} + +MVK_PUBLIC_SYMBOL VkResult vkAcquireNextImageKHR( + VkDevice device, + VkSwapchainKHR swapchain, + uint64_t timeout, + VkSemaphore semaphore, + VkFence fence, + uint32_t* pImageIndex) { + + MVKSwapchain* mvkSwapchain = (MVKSwapchain*)swapchain; + return mvkSwapchain->acquireNextImageKHR(timeout, semaphore, fence, pImageIndex); +} + +MVK_PUBLIC_SYMBOL VkResult vkQueuePresentKHR( + VkQueue queue, + const VkPresentInfoKHR* pPresentInfo) { + + MVKQueue* mvkQ = (MVKQueue*)queue; + return mvkQ->submitPresentKHR(pPresentInfo); +} + + +#pragma mark - +#pragma mark VK_KHR_surface extension + +MVK_PUBLIC_SYMBOL void vkDestroySurfaceKHR( + VkInstance instance, + VkSurfaceKHR surface, + const VkAllocationCallbacks* pAllocator) { + + MVKInstance* mvkInst = (MVKInstance*)instance; + mvkInst->destroySurface((MVKSurface*)surface, pAllocator); +} + +MVK_PUBLIC_SYMBOL VkResult vkGetPhysicalDeviceSurfaceSupportKHR( + VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex, + VkSurfaceKHR surface, + VkBool32* pSupported) { + + MVKPhysicalDevice* mvkPD = (MVKPhysicalDevice*)physicalDevice; + MVKSurface* mvkSrfc = (MVKSurface*)surface; + return mvkPD->getSurfaceSupport(queueFamilyIndex, mvkSrfc, pSupported); +} + +MVK_PUBLIC_SYMBOL VkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR( + VkPhysicalDevice physicalDevice, + VkSurfaceKHR surface, + VkSurfaceCapabilitiesKHR* pSurfaceCapabilities) { + + MVKPhysicalDevice* mvkPD = (MVKPhysicalDevice*)physicalDevice; + MVKSurface* mvkSrfc = (MVKSurface*)surface; + return mvkPD->getSurfaceCapabilities(mvkSrfc, pSurfaceCapabilities); +} + +MVK_PUBLIC_SYMBOL VkResult vkGetPhysicalDeviceSurfaceFormatsKHR( + VkPhysicalDevice physicalDevice, + VkSurfaceKHR surface, + uint32_t* pSurfaceFormatCount, + VkSurfaceFormatKHR* pSurfaceFormats) { + + MVKPhysicalDevice* mvkPD = (MVKPhysicalDevice*)physicalDevice; + MVKSurface* mvkSrfc = (MVKSurface*)surface; + return mvkPD->getSurfaceFormats(mvkSrfc, pSurfaceFormatCount, pSurfaceFormats); +} + +MVK_PUBLIC_SYMBOL VkResult vkGetPhysicalDeviceSurfacePresentModesKHR( + VkPhysicalDevice physicalDevice, + VkSurfaceKHR surface, + uint32_t* pPresentModeCount, + VkPresentModeKHR* pPresentModes) { + + MVKPhysicalDevice* mvkPD = (MVKPhysicalDevice*)physicalDevice; + MVKSurface* mvkSrfc = (MVKSurface*)surface; + return mvkPD->getSurfacePresentModes(mvkSrfc, pPresentModeCount, pPresentModes); +} + + +#pragma mark - +#pragma mark iOS & macOS surface extensions + +MVK_PUBLIC_SYMBOL VkResult vkCreate_PLATFORM_SurfaceMVK( + VkInstance instance, + const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface) { + + MVKInstance* mvkInst = (MVKInstance*)instance; + MVKSurface* mvkSrfc = mvkInst->createSurface(pCreateInfo, pAllocator); + *pSurface = (VkSurfaceKHR)mvkSrfc; + return mvkSrfc->getConfigurationResult(); +} diff --git a/MoltenVK/ThirdPartyConfig.md b/MoltenVK/ThirdPartyConfig.md new file mode 100644 index 00000000..49ccd4c1 --- /dev/null +++ b/MoltenVK/ThirdPartyConfig.md @@ -0,0 +1,61 @@ + + +Copyright (c) 2014-2017 [The Brenwill Workshop Ltd.](http://www.brenwill.com) + +*This document is written in [Markdown](http://en.wikipedia.org/wiki/Markdown) format. +For best results, use a Markdown reader.* + + +Table of Contents +----------------- + +- [Using the *Vulkan-Hpp* Spec Repository with **MoltenVK**](#install_vulkan_spec) +- [Updating the *Vulkan-Hpp* library version](#update_vulkan_spec) + + + +Using the *Vulkan-Hpp* Spec Repository with *MoltenVK* +------------------------------------------------------ + +**MoltenVK** uses the official *Khronos Vulkan* specification repository to provide the standard +*Vulkan* API header files and *Vulkan Specification* documentation. + +To add the *Khronos Vulkan* specification repository to **MoltenVK**, open a *Terminal* +session and perform the following command-line steps: + +1. Ensure you have `python3` and `asciidoctor` installed: + + brew install python3 + sudo gem install asciidoctor + +2. If you used the `--recursive` option when cloning the `MoltenVK` repository, you should already + have the `Vulkan-Hpp` submodule, and you can skip to *Step 3* below. If you did **_not_** + use the `--recursive` option when cloning the `MoltenVK` repository, retrieve the `Vulkan-Hpp` + submodule into the `External` directory as follows, from within the `MoltenVK` repository directory: + + git submodule update --init --recursive External/Vulkan-Hpp + +3. In the `Externals` folder within the `MoltenVK` repository, build the spec and header files + as follows from the main directory of this `MoltenVK` repository: + + cd External + ./makeVulkanSpec + + + + +Updating the *Vulkan-Hpp* library version +----------------------------------------- + +If you are developing enhancements to **MoltenVK**, you can update the version of `Vulkan-Hpp` +used by **MoltenVK**, by re-cloning the library as follows: + + cd External + rm -rf Vulkan-Hpp + git clone --recursive https://github.com/KhronosGroup/Vulkan-Hpp.git + ./makeVulkanSpec + +The updated version will then be "locked in" the next time the `MoltenVK` repository is committed to `git`. + diff --git a/MoltenVK/Vulkan-Hpp b/MoltenVK/Vulkan-Hpp new file mode 120000 index 00000000..63bbce07 --- /dev/null +++ b/MoltenVK/Vulkan-Hpp @@ -0,0 +1 @@ +../External/Vulkan-Hpp \ No newline at end of file diff --git a/MoltenVK/iOS b/MoltenVK/iOS new file mode 120000 index 00000000..fcdc7b48 --- /dev/null +++ b/MoltenVK/iOS @@ -0,0 +1 @@ +../Package/Latest/MoltenVK/iOS \ No newline at end of file diff --git a/MoltenVK/include/MoltenVK b/MoltenVK/include/MoltenVK new file mode 120000 index 00000000..6b04e24b --- /dev/null +++ b/MoltenVK/include/MoltenVK @@ -0,0 +1 @@ +../MoltenVK/API \ No newline at end of file diff --git a/MoltenVK/include/vulkan b/MoltenVK/include/vulkan new file mode 120000 index 00000000..e9567183 --- /dev/null +++ b/MoltenVK/include/vulkan @@ -0,0 +1 @@ +../../External/Vulkan-Hpp/vulkan \ No newline at end of file diff --git a/MoltenVK/macOS b/MoltenVK/macOS new file mode 120000 index 00000000..c83d7e98 --- /dev/null +++ b/MoltenVK/macOS @@ -0,0 +1 @@ +../Package/Latest/MoltenVK/macOS \ No newline at end of file diff --git a/MoltenVKPackaging.xcodeproj/project.pbxproj b/MoltenVKPackaging.xcodeproj/project.pbxproj new file mode 100644 index 00000000..2a1065a5 --- /dev/null +++ b/MoltenVKPackaging.xcodeproj/project.pbxproj @@ -0,0 +1,440 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXAggregateTarget section */ + A9FEADBC1F3517480010240E /* MoltenVK */ = { + isa = PBXAggregateTarget; + buildConfigurationList = A9FEADDC1F3517480010240E /* Build configuration list for PBXAggregateTarget "MoltenVK" */; + buildPhases = ( + A9FEADD61F3517480010240E /* Package MoltenVK */, + A9FEADD71F3517480010240E /* Package MoltenVKShaderConverter */, + A9A5D8A61FB3AABA00F20475 /* Package Docs */, + A9FEADDB1F3517480010240E /* Update Latest */, + ); + dependencies = ( + A9FEADBD1F3517480010240E /* PBXTargetDependency */, + A9FEADBF1F3517480010240E /* PBXTargetDependency */, + A98149D11FB7689D005F00B4 /* PBXTargetDependency */, + A98149D31FB7689D005F00B4 /* PBXTargetDependency */, + A98149CD1FB7689D005F00B4 /* PBXTargetDependency */, + A98149CF1FB7689D005F00B4 /* PBXTargetDependency */, + A98149CB1FB7689D005F00B4 /* PBXTargetDependency */, + ); + name = MoltenVK; + productName = Package; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXContainerItemProxy section */ + A92DB3F41CE0F72500FBC835 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A92DB3EE1CE0F72500FBC835 /* MoltenVK.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = A9B8EE0A1A98D796009C5A02; + remoteInfo = "MoltenVK-iOS"; + }; + A92DB3F61CE0F72500FBC835 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A92DB3EE1CE0F72500FBC835 /* MoltenVK.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = A9CBEE011B6299D800E45FDC; + remoteInfo = "MoltenVK-macOS"; + }; + A981498A1FB6B566005F00B4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A92DB40E1CE0F89600FBC835 /* MoltenVKShaderConverter.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = A964BD5F1C57EFBD00D930D8; + remoteInfo = MoltenVKShaderConverter; + }; + A981498C1FB6B566005F00B4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A92DB40E1CE0F89600FBC835 /* MoltenVKShaderConverter.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = A964BD611C57EFBD00D930D8; + remoteInfo = "MoltenVKGLSLToSPIRVConverter-iOS"; + }; + A981498E1FB6B566005F00B4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A92DB40E1CE0F89600FBC835 /* MoltenVKShaderConverter.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = A964BD601C57EFBD00D930D8; + remoteInfo = "MoltenVKGLSLToSPIRVConverter-macOS"; + }; + A98149901FB6B566005F00B4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A92DB40E1CE0F89600FBC835 /* MoltenVKShaderConverter.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = A93903BF1C57E9D700FE90DC; + remoteInfo = "MoltenVKSPIRVToMSLConverter-iOS"; + }; + A98149921FB6B566005F00B4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A92DB40E1CE0F89600FBC835 /* MoltenVKShaderConverter.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = A93903C71C57E9ED00FE90DC; + remoteInfo = "MoltenVKSPIRVToMSLConverter-macOS"; + }; + A98149CA1FB7689D005F00B4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A92DB40E1CE0F89600FBC835 /* MoltenVKShaderConverter.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = A9092A8C1A81717B00051823; + remoteInfo = MoltenVKShaderConverter; + }; + A98149CC1FB7689D005F00B4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A92DB40E1CE0F89600FBC835 /* MoltenVKShaderConverter.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = A937472B1A9A8B2900F29B34; + remoteInfo = "MoltenVKGLSLToSPIRVConverter-iOS"; + }; + A98149CE1FB7689D005F00B4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A92DB40E1CE0F89600FBC835 /* MoltenVKShaderConverter.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = A93747701A9A98D000F29B34; + remoteInfo = "MoltenVKGLSLToSPIRVConverter-macOS"; + }; + A98149D01FB7689D005F00B4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A92DB40E1CE0F89600FBC835 /* MoltenVKShaderConverter.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = A93903B81C57E9D700FE90DC; + remoteInfo = "MoltenVKSPIRVToMSLConverter-iOS"; + }; + A98149D21FB7689D005F00B4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A92DB40E1CE0F89600FBC835 /* MoltenVKShaderConverter.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = A93903C01C57E9ED00FE90DC; + remoteInfo = "MoltenVKSPIRVToMSLConverter-macOS"; + }; + A9FEADBE1F3517480010240E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A92DB3EE1CE0F72500FBC835 /* MoltenVK.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = A9B8EE091A98D796009C5A02; + remoteInfo = "MoltenVK-iOS"; + }; + A9FEADC01F3517480010240E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A92DB3EE1CE0F72500FBC835 /* MoltenVK.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = A9CBED861B6299D800E45FDC; + remoteInfo = "MoltenVK-macOS"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + A92DB3E41CE0F37D00FBC835 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + A92DB3E51CE0F37D00FBC835 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; + A92DB3E61CE0F37D00FBC835 /* Whats_New.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = Whats_New.md; path = Docs/Whats_New.md; sourceTree = ""; }; + A92DB3EE1CE0F72500FBC835 /* MoltenVK.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = MoltenVK.xcodeproj; path = MoltenVK/MoltenVK.xcodeproj; sourceTree = ""; }; + A92DB40E1CE0F89600FBC835 /* MoltenVKShaderConverter.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = MoltenVKShaderConverter.xcodeproj; path = MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj; sourceTree = ""; }; + A939A6FC1F547A12006ACA0C /* makeAll */ = {isa = PBXFileReference; lastKnownFileType = text; path = makeAll; sourceTree = ""; }; + A939A6FD1F547A12006ACA0C /* makeglslang */ = {isa = PBXFileReference; lastKnownFileType = text; path = makeglslang; sourceTree = ""; }; + A939A6FE1F547A12006ACA0C /* makeSPIRVTools */ = {isa = PBXFileReference; lastKnownFileType = text; path = makeSPIRVTools; sourceTree = ""; }; + A939A6FF1F547A12006ACA0C /* makeVulkanSpec */ = {isa = PBXFileReference; lastKnownFileType = text; path = makeVulkanSpec; sourceTree = ""; }; + A98149E51FB78829005F00B4 /* MoltenVK_Runtime_UserGuide.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = MoltenVK_Runtime_UserGuide.md; path = Docs/MoltenVK_Runtime_UserGuide.md; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXGroup section */ + A90B2B1C1A9B6170008EE819 = { + isa = PBXGroup; + children = ( + A92DB3EE1CE0F72500FBC835 /* MoltenVK.xcodeproj */, + A92DB40E1CE0F89600FBC835 /* MoltenVKShaderConverter.xcodeproj */, + A92DB3E11CE0F34500FBC835 /* Docs */, + A939A6FB1F5479D0006ACA0C /* External */, + ); + sourceTree = ""; + }; + A92DB3E11CE0F34500FBC835 /* Docs */ = { + isa = PBXGroup; + children = ( + A92DB3E41CE0F37D00FBC835 /* README.md */, + A92DB3E51CE0F37D00FBC835 /* LICENSE */, + A98149E51FB78829005F00B4 /* MoltenVK_Runtime_UserGuide.md */, + A92DB3E61CE0F37D00FBC835 /* Whats_New.md */, + ); + name = Docs; + sourceTree = ""; + }; + A92DB3EF1CE0F72500FBC835 /* Products */ = { + isa = PBXGroup; + children = ( + A92DB3F51CE0F72500FBC835 /* MoltenVK.framework */, + A92DB3F71CE0F72500FBC835 /* MoltenVK.framework */, + ); + name = Products; + sourceTree = ""; + }; + A939A6FB1F5479D0006ACA0C /* External */ = { + isa = PBXGroup; + children = ( + A939A6FC1F547A12006ACA0C /* makeAll */, + A939A6FD1F547A12006ACA0C /* makeglslang */, + A939A6FE1F547A12006ACA0C /* makeSPIRVTools */, + A939A6FF1F547A12006ACA0C /* makeVulkanSpec */, + ); + path = External; + sourceTree = ""; + }; + A98149741FB6B565005F00B4 /* Products */ = { + isa = PBXGroup; + children = ( + A981498B1FB6B566005F00B4 /* MoltenVKShaderConverter */, + A981498D1FB6B566005F00B4 /* MoltenVKGLSLToSPIRVConverter.framework */, + A981498F1FB6B566005F00B4 /* MoltenVKGLSLToSPIRVConverter.framework */, + A98149911FB6B566005F00B4 /* MoltenVKSPIRVToMSLConverter.framework */, + A98149931FB6B566005F00B4 /* MoltenVKSPIRVToMSLConverter.framework */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXProject section */ + A90B2B1D1A9B6170008EE819 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0910; + TargetAttributes = { + A9FEADBC1F3517480010240E = { + DevelopmentTeam = VU3TCKU48B; + }; + }; + }; + buildConfigurationList = A90B2B201A9B6170008EE819 /* Build configuration list for PBXProject "MoltenVKPackaging" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = A90B2B1C1A9B6170008EE819; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = A92DB3EF1CE0F72500FBC835 /* Products */; + ProjectRef = A92DB3EE1CE0F72500FBC835 /* MoltenVK.xcodeproj */; + }, + { + ProductGroup = A98149741FB6B565005F00B4 /* Products */; + ProjectRef = A92DB40E1CE0F89600FBC835 /* MoltenVKShaderConverter.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + A9FEADBC1F3517480010240E /* MoltenVK */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + A92DB3F51CE0F72500FBC835 /* MoltenVK.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = MoltenVK.framework; + remoteRef = A92DB3F41CE0F72500FBC835 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + A92DB3F71CE0F72500FBC835 /* MoltenVK.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = MoltenVK.framework; + remoteRef = A92DB3F61CE0F72500FBC835 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + A981498B1FB6B566005F00B4 /* MoltenVKShaderConverter */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = MoltenVKShaderConverter; + remoteRef = A981498A1FB6B566005F00B4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + A981498D1FB6B566005F00B4 /* MoltenVKGLSLToSPIRVConverter.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = MoltenVKGLSLToSPIRVConverter.framework; + remoteRef = A981498C1FB6B566005F00B4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + A981498F1FB6B566005F00B4 /* MoltenVKGLSLToSPIRVConverter.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = MoltenVKGLSLToSPIRVConverter.framework; + remoteRef = A981498E1FB6B566005F00B4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + A98149911FB6B566005F00B4 /* MoltenVKSPIRVToMSLConverter.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = MoltenVKSPIRVToMSLConverter.framework; + remoteRef = A98149901FB6B566005F00B4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + A98149931FB6B566005F00B4 /* MoltenVKSPIRVToMSLConverter.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = MoltenVKSPIRVToMSLConverter.framework; + remoteRef = A98149921FB6B566005F00B4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXShellScriptBuildPhase section */ + A9A5D8A61FB3AABA00F20475 /* Package Docs */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Package Docs"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -e\n\n# Package folder\nexport MVK_WKSPC_PATH=\"${PROJECT_DIR}\"\nexport MVK_PKG_LOCN=\"${MVK_WKSPC_PATH}/Package\"\nexport MVK_PKG_CONFIG_PATH=\"${MVK_PKG_LOCN}/${CONFIGURATION}\"\n\n# Copy the docs\ncp -a \"${MVK_WKSPC_PATH}/LICENSE\" \"${MVK_PKG_CONFIG_PATH}\"\ncp -pRLf \"${MVK_WKSPC_PATH}/Docs\" \"${MVK_PKG_CONFIG_PATH}\"\n\n"; + }; + A9FEADD61F3517480010240E /* Package MoltenVK */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Package MoltenVK"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -e\n\n# Package folder\nexport MVK_PROD_NAME=\"MoltenVK\"\nexport MVK_DYLIB_NAME=\"lib${MVK_PROD_NAME}.dylib\"\nexport MVK_WKSPC_PATH=\"${PROJECT_DIR}\"\nexport MVK_PROD_PROJ_PATH=\"${MVK_WKSPC_PATH}/${MVK_PROD_NAME}\"\nexport MVK_PKG_LOCN=\"${MVK_WKSPC_PATH}/Package\"\nexport MVK_PKG_CONFIG_PATH=\"${MVK_PKG_LOCN}/${CONFIGURATION}\"\nexport MVK_PKG_PROD_PATH=\"${MVK_PKG_CONFIG_PATH}/${MVK_PROD_NAME}\"\n\n# Remove the product folder\nrm -rf \"${MVK_PKG_PROD_PATH}\"\n\n# Remove and replace the existing macOS framework folder and copy framework into it\nexport MVK_OS_PROD_PATH=\"${MVK_PKG_PROD_PATH}/macOS\"\nexport MVK_BUILT_PROD_PATH=\"${BUILT_PRODUCTS_DIR}\"\nrm -rf \"${MVK_OS_PROD_PATH}\"\nmkdir -p \"${MVK_OS_PROD_PATH}\"\ncp -a \"${MVK_BUILT_PROD_PATH}/${MVK_PROD_NAME}.framework\" \"${MVK_OS_PROD_PATH}\"\ncp -a \"${MVK_BUILT_PROD_PATH}/${MVK_DYLIB_NAME}\" \"${MVK_OS_PROD_PATH}\"\n\n# Remove and replace the existing iOS framework folder and copy framework into it\nexport MVK_OS_PROD_PATH=\"${MVK_PKG_PROD_PATH}/iOS\"\nexport MVK_BUILT_PROD_PATH=\"${BUILT_PRODUCTS_DIR}/../${CONFIGURATION}-iphoneos\"\nrm -rf \"${MVK_BUILT_PROD_PATH}/${MVK_PROD_NAME}.framework/_CodeSignature\"\nrm -rf \"${MVK_OS_PROD_PATH}\"\nmkdir -p \"${MVK_OS_PROD_PATH}\"\ncp -a \"${MVK_BUILT_PROD_PATH}/${MVK_PROD_NAME}.framework\" \"${MVK_OS_PROD_PATH}\"\ncp -a \"${MVK_BUILT_PROD_PATH}/${MVK_DYLIB_NAME}\" \"${MVK_OS_PROD_PATH}\"\n\n# Remove and replace header include folder\nrm -rf \"${MVK_PKG_PROD_PATH}/include\"\ncp -pRL \"${MVK_PROD_PROJ_PATH}/include\" \"${MVK_PKG_PROD_PATH}\"\n\n# Copy the demo apps to the Configuration package folder\nrm -rf \"${MVK_PKG_CONFIG_PATH}/Demos\"\ncp -af \"${MVK_WKSPC_PATH}/Demos\" \"${MVK_PKG_CONFIG_PATH}\"\n\n# Remove developer user info from demo projects\nexport MVK_PKG_DEMOS_PATH=\"${MVK_PKG_CONFIG_PATH}/Demos\"\nexport MVK_PKG_DEMO_PROJ_PATH=\"${MVK_PKG_DEMOS_PATH}/Demos.xcworkspace\"\nrm -rf \"${MVK_PKG_DEMO_PROJ_PATH}/xcuserdata\"\n\nexport MVK_DEMO_NAME=\"API-Samples\"\nexport MVK_PKG_DEMO_PROJ_PATH=\"${MVK_PKG_DEMOS_PATH}/LunarG-VulkanSamples/${MVK_DEMO_NAME}/${MVK_DEMO_NAME}.xcodeproj\"\nrm -rf \"${MVK_PKG_DEMO_PROJ_PATH}/xcuserdata\"\nrm -rf \"${MVK_PKG_DEMO_PROJ_PATH}/project.xcworkspace\"\n\nexport MVK_DEMO_NAME=\"Demos\"\nexport MVK_PKG_DEMO_PROJ_PATH=\"${MVK_PKG_DEMOS_PATH}/LunarG-VulkanSamples/${MVK_DEMO_NAME}/${MVK_DEMO_NAME}.xcodeproj\"\nrm -rf \"${MVK_PKG_DEMO_PROJ_PATH}/xcuserdata\"\nrm -rf \"${MVK_PKG_DEMO_PROJ_PATH}/project.xcworkspace\"\n\nexport MVK_DEMO_NAME=\"Hologram\"\nexport MVK_PKG_DEMO_PROJ_PATH=\"${MVK_PKG_DEMOS_PATH}/LunarG-VulkanSamples/${MVK_DEMO_NAME}/${MVK_DEMO_NAME}.xcodeproj\"\nrm -rf \"${MVK_PKG_DEMO_PROJ_PATH}/xcuserdata\"\nrm -rf \"${MVK_PKG_DEMO_PROJ_PATH}/project.xcworkspace\"\n\nexport MVK_DEMO_NAME=\"AsynchronousTimeWarp\"\nexport MVK_PKG_DEMO_PROJ_PATH=\"${MVK_PKG_DEMOS_PATH}/Khronos-Vulkan-Samples/${MVK_DEMO_NAME}/${MVK_DEMO_NAME}.xcodeproj\"\nrm -rf \"${MVK_PKG_DEMO_PROJ_PATH}/xcuserdata\"\nrm -rf \"${MVK_PKG_DEMO_PROJ_PATH}/project.xcworkspace\"\n"; + }; + A9FEADD71F3517480010240E /* Package MoltenVKShaderConverter */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Package MoltenVKShaderConverter"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -e\n\n# Package folder\nexport MVK_PROD_BASE_NAME=\"MoltenVKShaderConverter\"\nexport MVK_WKSPC_PATH=\"${PROJECT_DIR}\"\nexport MVK_PKG_LOCN=\"${MVK_WKSPC_PATH}/Package\"\n\n# Remove the base product folder\nrm -rf \"${MVK_PKG_LOCN}/${CONFIGURATION}/${MVK_PROD_BASE_NAME}\"\n\n#-----------------------------------\n# MoltenVKGLSLToSPIRVConverter\nexport MVK_PROD_NAME=\"MoltenVKGLSLToSPIRVConverter\"\nexport MVK_PKG_CONFIG_PATH=\"${MVK_PKG_LOCN}/${CONFIGURATION}/${MVK_PROD_BASE_NAME}/${MVK_PROD_NAME}\"\n\n# Remove and replace the existing macOS framework folder and copy framework into it\nexport MVK_OS_PROD_PATH=\"${MVK_PKG_CONFIG_PATH}/macOS\"\nexport MVK_BUILT_PROD_PATH=\"${BUILT_PRODUCTS_DIR}\"\nrm -rf \"${MVK_OS_PROD_PATH}\"\nmkdir -p \"${MVK_OS_PROD_PATH}\"\ncp -a \"${MVK_BUILT_PROD_PATH}/${MVK_PROD_NAME}.framework\" \"${MVK_OS_PROD_PATH}\"\n\n# Remove and replace the existing iOS framework folder and copy framework into it\nexport MVK_OS_PROD_PATH=\"${MVK_PKG_CONFIG_PATH}/iOS\"\nexport MVK_BUILT_PROD_PATH=\"${BUILT_PRODUCTS_DIR}/../${CONFIGURATION}-iphoneos\"\nrm -rf \"${MVK_BUILT_PROD_PATH}/${MVK_PROD_NAME}.framework/_CodeSignature\"\nrm -rf \"${MVK_OS_PROD_PATH}\"\nmkdir -p \"${MVK_OS_PROD_PATH}\"\ncp -a \"${MVK_BUILT_PROD_PATH}/${MVK_PROD_NAME}.framework\" \"${MVK_OS_PROD_PATH}\"\n\n#-----------------------------------\n# MoltenVKSPIRVToMSLConverter\nexport MVK_PROD_NAME=\"MoltenVKSPIRVToMSLConverter\"\nexport MVK_PKG_CONFIG_PATH=\"${MVK_PKG_LOCN}/${CONFIGURATION}/${MVK_PROD_BASE_NAME}/${MVK_PROD_NAME}\"\n\n# Remove and replace the existing macOS framework folder and copy framework into it\nexport MVK_OS_PROD_PATH=\"${MVK_PKG_CONFIG_PATH}/macOS\"\nexport MVK_BUILT_PROD_PATH=\"${BUILT_PRODUCTS_DIR}\"\nrm -rf \"${MVK_OS_PROD_PATH}\"\nmkdir -p \"${MVK_OS_PROD_PATH}\"\ncp -a \"${MVK_BUILT_PROD_PATH}/${MVK_PROD_NAME}.framework\" \"${MVK_OS_PROD_PATH}\"\n\n# Remove and replace the existing iOS framework folder and copy framework into it\nexport MVK_OS_PROD_PATH=\"${MVK_PKG_CONFIG_PATH}/iOS\"\nexport MVK_BUILT_PROD_PATH=\"${BUILT_PRODUCTS_DIR}/../${CONFIGURATION}-iphoneos\"\nrm -rf \"${MVK_BUILT_PROD_PATH}/${MVK_PROD_NAME}.framework/_CodeSignature\"\nrm -rf \"${MVK_OS_PROD_PATH}\"\nmkdir -p \"${MVK_OS_PROD_PATH}\"\ncp -a \"${MVK_BUILT_PROD_PATH}/${MVK_PROD_NAME}.framework\" \"${MVK_OS_PROD_PATH}\"\n\n#-----------------------------------\n# MoltenVKShaderConverter Tool\nexport MVK_PROD_NAME=\"MoltenVKShaderConverter\"\nexport MVK_PKG_CONFIG_PATH=\"${MVK_PKG_LOCN}/${CONFIGURATION}/${MVK_PROD_BASE_NAME}\"\n\n# Remove and replace the existing macOS framework folder and copy framework into it\nexport MVK_OS_PROD_PATH=\"${MVK_PKG_CONFIG_PATH}/Tools\"\nexport MVK_BUILT_PROD_PATH=\"${BUILT_PRODUCTS_DIR}\"\nrm -rf \"${MVK_OS_PROD_PATH}\"\nmkdir -p \"${MVK_OS_PROD_PATH}\"\ncp -a \"${MVK_BUILT_PROD_PATH}/${MVK_PROD_NAME}\" \"${MVK_OS_PROD_PATH}\"\n"; + }; + A9FEADDB1F3517480010240E /* Update Latest */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Update Latest"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -e\n\n# Package folder\nexport MVK_WKSPC_LOCN=\"${PROJECT_DIR}\"\nexport MVK_PKG_LOCN=\"${MVK_WKSPC_LOCN}/Package\"\n\n# Configuration package folder location\nexport MVK_PKG_CONFIG_LOCN=\"${CONFIGURATION}\"\nexport MVK_PKG_LATEST_LOCN=\"Latest\"\n\n# Assign symlink from Latest\nln -sfn \"${MVK_PKG_LOCN}/${MVK_PKG_CONFIG_LOCN}\" \"${MVK_PKG_LOCN}/${MVK_PKG_LATEST_LOCN}\""; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXTargetDependency section */ + A98149CB1FB7689D005F00B4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = MoltenVKShaderConverter; + targetProxy = A98149CA1FB7689D005F00B4 /* PBXContainerItemProxy */; + }; + A98149CD1FB7689D005F00B4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "MoltenVKGLSLToSPIRVConverter-iOS"; + targetProxy = A98149CC1FB7689D005F00B4 /* PBXContainerItemProxy */; + }; + A98149CF1FB7689D005F00B4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "MoltenVKGLSLToSPIRVConverter-macOS"; + targetProxy = A98149CE1FB7689D005F00B4 /* PBXContainerItemProxy */; + }; + A98149D11FB7689D005F00B4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "MoltenVKSPIRVToMSLConverter-iOS"; + targetProxy = A98149D01FB7689D005F00B4 /* PBXContainerItemProxy */; + }; + A98149D31FB7689D005F00B4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "MoltenVKSPIRVToMSLConverter-macOS"; + targetProxy = A98149D21FB7689D005F00B4 /* PBXContainerItemProxy */; + }; + A9FEADBD1F3517480010240E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "MoltenVK-iOS"; + targetProxy = A9FEADBE1F3517480010240E /* PBXContainerItemProxy */; + }; + A9FEADBF1F3517480010240E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "MoltenVK-macOS"; + targetProxy = A9FEADC01F3517480010240E /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + A90B2B211A9B6170008EE819 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Debug; + }; + A90B2B221A9B6170008EE819 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Release; + }; + A9FEADDD1F3517480010240E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + A9FEADDE1F3517480010240E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + A90B2B201A9B6170008EE819 /* Build configuration list for PBXProject "MoltenVKPackaging" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A90B2B211A9B6170008EE819 /* Debug */, + A90B2B221A9B6170008EE819 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A9FEADDC1F3517480010240E /* Build configuration list for PBXAggregateTarget "MoltenVK" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A9FEADDD1F3517480010240E /* Debug */, + A9FEADDE1F3517480010240E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = A90B2B1D1A9B6170008EE819 /* Project object */; +} diff --git a/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK (Debug).xcscheme b/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK (Debug).xcscheme new file mode 100644 index 00000000..88bead5a --- /dev/null +++ b/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK (Debug).xcscheme @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK (Release).xcscheme b/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK (Release).xcscheme new file mode 100644 index 00000000..03e94529 --- /dev/null +++ b/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK (Release).xcscheme @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLConversion.h b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLConversion.h new file mode 100644 index 00000000..0425ab2c --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLConversion.h @@ -0,0 +1,105 @@ +/* + * GLSLConversion.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __GLSLConversion_h_ +#define __GLSLConversion_h_ 1 + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + +#include + + +/** This file contains convenience functions for converting GLSL to SPIR-V, callable from standard C code. */ + + +/** Enumeration of the pipeline stages for which a shader can be compiled. */ +typedef enum { + kMVKShaderStageAuto, + kMVKShaderStageVertex, + kMVKShaderStageTessControl, + kMVKShaderStageTessEval, + kMVKShaderStageGeometry, + kMVKShaderStageFragment, + kMVKShaderStageCompute, +} MVKShaderStage; + + +/** + * Convenience function that converts the specified GLSL code to SPIR-V code, + * and returns whether the conversion was successful. + * + * If the pSPIRVCode parameter is not NULL, this function allocates space for the + * converted SPIR-V code, and returns a pointer to that SPIR-V code in the location + * indicated by this parameter. It is the responsibility of the caller to free() + * the memory returned in this parameter. + * + * If the pSPIRVLength parameter is not NULL, the length of the SPIR-V code (as + * returned in the pSPIRVCode parameter) is returned in the value pointed to by + * the pSPIRVLength parameter. + * + * If the pResultLog parameter is not NULL, a pointer to the contents of the converter + * results log will be set at the location pointed to by the pResultLog parameter. + * It is the responsibility of the caller to free() the memory returned in this parameter. + * + * The boolean flags indicate whether the original GLSL code and resulting SPIR-V code + * should be logged to the converter results log. This can be useful during shader debugging. + */ +bool mvkConvertGLSLToSPIRV(const char* glslSource, + MVKShaderStage shaderStage, + uint32_t** pSPIRVCode, + size_t *pSPIRVLength, + char** pResultLog, + bool shouldLogGLSL, + bool shouldLogSPIRV); +/** + * Convenience function that converts GLSL code in the specified file to SPIR-V code. + * The file path should either be absolute or relative to the resource directory. + * + * If the pSPIRVCode parameter is not NULL, this function allocates space for the + * converted SPIR-V code, and returns a pointer to that SPIR-V code in the location + * indicated by this parameter. It is the responsibility of the caller to free() + * the memory returned in this parameter. + * + * If the pSPIRVLength parameter is not NULL, the length of the SPIR-V code (as + * returned in the pSPIRVCode parameter) is returned in the value pointed to by + * the pSPIRVLength parameter. + * + * If the pResultLog parameter is not NULL, a pointer to the contents of the converter + * results log will be set at the location pointed to by the pResultLog parameter. + * It is the responsibility of the caller to free() the memory returned in this parameter. + * + * The boolean flags indicate whether the original GLSL code and resulting SPIR-V code + * should be logged to the converter results log. This can be useful during shader debugging. + */ +bool mvkConvertGLSLFileToSPIRV(const char* glslFilepath, + MVKShaderStage shaderStage, + uint32_t** pSPIRVCode, + size_t *pSPIRVLength, + char** pResultLog, + bool shouldLogGLSL, + bool shouldLogSPIRV); + + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif diff --git a/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLConversion.mm b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLConversion.mm new file mode 100644 index 00000000..21bf1365 --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLConversion.mm @@ -0,0 +1,92 @@ +/* + * GLSLConversion.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GLSLConversion.h" +#include "GLSLToSPIRVConverter.h" +#include "MVKCommonEnvironment.h" + +#include + +using namespace mvk; + + +MVK_PUBLIC_SYMBOL bool mvkConvertGLSLToSPIRV(const char* glslSource, + MVKShaderStage shaderStage, + uint32_t** pSPIRVCode, + size_t *pSPIRVLength, + char** pResultLog, + bool shouldLogGLSL, + bool shouldLogSPIRV) { + GLSLToSPIRVConverter glslConverter; + glslConverter.setGLSL(glslSource); + bool wasConverted = glslConverter.convert(shaderStage, shouldLogGLSL, shouldLogSPIRV); + + size_t spvLen = 0; + if (pSPIRVCode) { + uint32_t* spvCode = NULL; + if (wasConverted) { + auto spv = glslConverter.getSPIRV(); + spvLen = spv.size() * sizeof(uint32_t); + spvCode = (uint32_t*)malloc(spvLen); + memcpy(spvCode, spv.data(), spvLen); + } + *pSPIRVCode = spvCode; + } + if (pSPIRVLength) { *pSPIRVLength = spvLen; } + + if (pResultLog) { + auto log = glslConverter.getResultLog(); + *pResultLog = (char*)malloc(log.size() + 1); + strcpy(*pResultLog, log.data()); + } + + return wasConverted; +} + +MVK_PUBLIC_SYMBOL bool mvkConvertGLSLFileToSPIRV(const char* glslFilepath, + MVKShaderStage shaderStage, + uint32_t** pSPIRVCode, + size_t *pSPIRVLength, + char** pResultLog, + bool shouldLogGLSL, + bool shouldLogSPIRV) { + NSString* filePath = @(glslFilepath); + if( !filePath.absolutePath ) { + filePath =[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: filePath]; + } + NSError* err = nil; + NSString* glslSource = [NSString stringWithContentsOfFile: filePath + encoding: NSUTF8StringEncoding + error: &err]; + if (err) { + if (pResultLog) { + NSString* errMsg = [NSString stringWithFormat: @"Unable to convert GLSL in file %s to SPIR-V: %s (code %li) %s", + filePath.UTF8String, err.localizedDescription.UTF8String, + (long)err.code, err.localizedFailureReason.UTF8String]; + *pResultLog = (char*)malloc(errMsg.length + 1); + strcpy(*pResultLog, errMsg.UTF8String); + } + + if (pSPIRVCode) { *pSPIRVCode = NULL; } + if (pSPIRVLength) { *pSPIRVLength = 0; } + return false; + } + + return mvkConvertGLSLToSPIRV(glslSource.UTF8String, shaderStage, pSPIRVCode, pSPIRVLength, pResultLog, shouldLogGLSL, shouldLogSPIRV); +} + diff --git a/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLToSPIRVConverter.cpp b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLToSPIRVConverter.cpp new file mode 100644 index 00000000..2d324974 --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLToSPIRVConverter.cpp @@ -0,0 +1,271 @@ +/* + * GLSLToSPIRVConverter.cpp + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GLSLToSPIRVConverter.h" +#include "MVKCommonEnvironment.h" +#include "SPIRVToMSLConverter.h" +#include "MVKStrings.h" +#include "GlslangToSpv.h" +#include "disassemble.h" +#include "doc.h" +#include + +using namespace std; +using namespace mvk; + + +#pragma mark - +#pragma mark GLSLToSPIRVConverter + +/** Configures the specified limit resources structure used by the GLSL compiler. */ +void configureGLSLCompilerResources(TBuiltInResource* glslCompilerResources); + +/** Returns the GLSL compiler language type corresponding to the specified MoltenVK shader stage. */ +EShLanguage eshLanguageFromMVKShaderStage(const MVKShaderStage mvkShaderStage); + +MVK_PUBLIC_SYMBOL void GLSLToSPIRVConverter::setGLSL(const string& glslSrc) { _glsl = glslSrc; } + +MVK_PUBLIC_SYMBOL const string& GLSLToSPIRVConverter::getGLSL() { return _glsl; } + +MVK_PUBLIC_SYMBOL bool GLSLToSPIRVConverter::convert(MVKShaderStage shaderStage, + bool shouldLogGLSL, + bool shouldLogSPIRV) { + _wasConverted = true; + _resultLog.clear(); + _spirv.clear(); + + if (shouldLogGLSL) { logGLSL("Converting"); } + + EShMessages messages = (EShMessages)(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules); + + EShLanguage stage = eshLanguageFromMVKShaderStage(shaderStage); + TBuiltInResource glslCompilerResources; + configureGLSLCompilerResources(&glslCompilerResources); + const char *glslStrings[1]; + glslStrings[0] = _glsl.data(); + + // Create and compile a shader from the source code + glslang::TShader glslShader(stage); + glslShader.setStrings(glslStrings, 1); + if (glslShader.parse(&glslCompilerResources, 100, false, messages)) { + if (shouldLogGLSL) { + logMsg(glslShader.getInfoLog()); + logMsg(glslShader.getInfoDebugLog()); + } + } else { + logError(glslShader.getInfoLog()); + logError(glslShader.getInfoDebugLog()); + return logError("Error compiling GLSL when converting GLSL to SPIR-V."); + } + + // Create and link a shader program containing the single shader + glslang::TProgram glslProgram; + glslProgram.addShader(&glslShader); + if ( !glslProgram.link(messages) ) { + logError(glslProgram.getInfoLog()); + logError(glslProgram.getInfoDebugLog()); + return logError("Error creating GLSL program when converting GLSL to SPIR-V."); + } + + // Output the SPIR-V code from the shader program + glslang::GlslangToSpv(*glslProgram.getIntermediate(stage), _spirv); + + if (shouldLogSPIRV) { logSPIRV("Converted"); } + + return _wasConverted; +} + +MVK_PUBLIC_SYMBOL const vector& GLSLToSPIRVConverter::getSPIRV() { return _spirv; } + +MVK_PUBLIC_SYMBOL bool GLSLToSPIRVConverter::getWasConverted() { return _wasConverted; } + +MVK_PUBLIC_SYMBOL const string& GLSLToSPIRVConverter::getResultLog() { return _resultLog; } + +/** Appends the message text to the result log. */ +void GLSLToSPIRVConverter::logMsg(const char* logMsg) { + string trimMsg = trim(logMsg); + if ( !trimMsg.empty() ) { + _resultLog += trimMsg; + _resultLog += "\n\n"; + } +} + +/** Appends the error text to the result log, sets the wasConverted property to false, and returns it. */ +bool GLSLToSPIRVConverter::logError(const char* errMsg) { + logMsg(errMsg); + _wasConverted = false; + return _wasConverted; +} + +/** Appends the SPIR-V to the result log, indicating whether it is being converted or was converted. */ +void GLSLToSPIRVConverter::logSPIRV(const char* opDesc) { + + string spvLog; + mvk::logSPIRV(_spirv, spvLog); + + _resultLog += opDesc; + _resultLog += " SPIR-V:\n"; + _resultLog += spvLog; + _resultLog += "\nEnd SPIR-V\n\n"; +} + +/** Validates that the SPIR-V code will disassemble during logging. */ +bool GLSLToSPIRVConverter::validateSPIRV() { + if (_spirv.size() < 5) { return false; } + if (_spirv[0] != spv::MagicNumber) { return false; } + if (_spirv[4] != 0) { return false; } + return true; +} + +/** Appends the GLSL to the result log, indicating whether it is being converted or was converted. */ +void GLSLToSPIRVConverter::logGLSL(const char* opDesc) { + _resultLog += opDesc; + _resultLog += " GLSL:\n"; + _resultLog += _glsl; + _resultLog += "\nEnd GLSL\n\n"; +} + + +#pragma mark - +#pragma mark Support functions + +void configureGLSLCompilerResources(TBuiltInResource* glslCompilerResources) { + glslCompilerResources->maxLights = 32; + glslCompilerResources->maxClipPlanes = 6; + glslCompilerResources->maxTextureUnits = 32; + glslCompilerResources->maxTextureCoords = 32; + glslCompilerResources->maxVertexAttribs = 64; + glslCompilerResources->maxVertexUniformComponents = 4096; + glslCompilerResources->maxVaryingFloats = 64; + glslCompilerResources->maxVertexTextureImageUnits = 32; + glslCompilerResources->maxCombinedTextureImageUnits = 80; + glslCompilerResources->maxTextureImageUnits = 32; + glslCompilerResources->maxFragmentUniformComponents = 4096; + glslCompilerResources->maxDrawBuffers = 32; + glslCompilerResources->maxVertexUniformVectors = 128; + glslCompilerResources->maxVaryingVectors = 8; + glslCompilerResources->maxFragmentUniformVectors = 16; + glslCompilerResources->maxVertexOutputVectors = 16; + glslCompilerResources->maxFragmentInputVectors = 15; + glslCompilerResources->minProgramTexelOffset = -8; + glslCompilerResources->maxProgramTexelOffset = 7; + glslCompilerResources->maxClipDistances = 8; + glslCompilerResources->maxComputeWorkGroupCountX = 65535; + glslCompilerResources->maxComputeWorkGroupCountY = 65535; + glslCompilerResources->maxComputeWorkGroupCountZ = 65535; + glslCompilerResources->maxComputeWorkGroupSizeX = 1024; + glslCompilerResources->maxComputeWorkGroupSizeY = 1024; + glslCompilerResources->maxComputeWorkGroupSizeZ = 64; + glslCompilerResources->maxComputeUniformComponents = 1024; + glslCompilerResources->maxComputeTextureImageUnits = 16; + glslCompilerResources->maxComputeImageUniforms = 8; + glslCompilerResources->maxComputeAtomicCounters = 8; + glslCompilerResources->maxComputeAtomicCounterBuffers = 1; + glslCompilerResources->maxVaryingComponents = 60; + glslCompilerResources->maxVertexOutputComponents = 64; + glslCompilerResources->maxGeometryInputComponents = 64; + glslCompilerResources->maxGeometryOutputComponents = 128; + glslCompilerResources->maxFragmentInputComponents = 128; + glslCompilerResources->maxImageUnits = 8; + glslCompilerResources->maxCombinedImageUnitsAndFragmentOutputs = 8; + glslCompilerResources->maxCombinedShaderOutputResources = 8; + glslCompilerResources->maxImageSamples = 0; + glslCompilerResources->maxVertexImageUniforms = 0; + glslCompilerResources->maxTessControlImageUniforms = 0; + glslCompilerResources->maxTessEvaluationImageUniforms = 0; + glslCompilerResources->maxGeometryImageUniforms = 0; + glslCompilerResources->maxFragmentImageUniforms = 8; + glslCompilerResources->maxCombinedImageUniforms = 8; + glslCompilerResources->maxGeometryTextureImageUnits = 16; + glslCompilerResources->maxGeometryOutputVertices = 256; + glslCompilerResources->maxGeometryTotalOutputComponents = 1024; + glslCompilerResources->maxGeometryUniformComponents = 1024; + glslCompilerResources->maxGeometryVaryingComponents = 64; + glslCompilerResources->maxTessControlInputComponents = 128; + glslCompilerResources->maxTessControlOutputComponents = 128; + glslCompilerResources->maxTessControlTextureImageUnits = 16; + glslCompilerResources->maxTessControlUniformComponents = 1024; + glslCompilerResources->maxTessControlTotalOutputComponents = 4096; + glslCompilerResources->maxTessEvaluationInputComponents = 128; + glslCompilerResources->maxTessEvaluationOutputComponents = 128; + glslCompilerResources->maxTessEvaluationTextureImageUnits = 16; + glslCompilerResources->maxTessEvaluationUniformComponents = 1024; + glslCompilerResources->maxTessPatchComponents = 120; + glslCompilerResources->maxPatchVertices = 32; + glslCompilerResources->maxTessGenLevel = 64; + glslCompilerResources->maxViewports = 16; + glslCompilerResources->maxVertexAtomicCounters = 0; + glslCompilerResources->maxTessControlAtomicCounters = 0; + glslCompilerResources->maxTessEvaluationAtomicCounters = 0; + glslCompilerResources->maxGeometryAtomicCounters = 0; + glslCompilerResources->maxFragmentAtomicCounters = 8; + glslCompilerResources->maxCombinedAtomicCounters = 8; + glslCompilerResources->maxAtomicCounterBindings = 1; + glslCompilerResources->maxVertexAtomicCounterBuffers = 0; + glslCompilerResources->maxTessControlAtomicCounterBuffers = 0; + glslCompilerResources->maxTessEvaluationAtomicCounterBuffers = 0; + glslCompilerResources->maxGeometryAtomicCounterBuffers = 0; + glslCompilerResources->maxFragmentAtomicCounterBuffers = 1; + glslCompilerResources->maxCombinedAtomicCounterBuffers = 1; + glslCompilerResources->maxAtomicCounterBufferSize = 16384; + glslCompilerResources->maxTransformFeedbackBuffers = 4; + glslCompilerResources->maxTransformFeedbackInterleavedComponents = 64; + glslCompilerResources->maxCullDistances = 8; + glslCompilerResources->maxCombinedClipAndCullDistances = 8; + glslCompilerResources->maxSamples = 4; + glslCompilerResources->limits.nonInductiveForLoops = 1; + glslCompilerResources->limits.whileLoops = 1; + glslCompilerResources->limits.doWhileLoops = 1; + glslCompilerResources->limits.generalUniformIndexing = 1; + glslCompilerResources->limits.generalAttributeMatrixVectorIndexing = 1; + glslCompilerResources->limits.generalVaryingIndexing = 1; + glslCompilerResources->limits.generalSamplerIndexing = 1; + glslCompilerResources->limits.generalVariableIndexing = 1; + glslCompilerResources->limits.generalConstantMatrixVectorIndexing = 1; +} + +EShLanguage eshLanguageFromMVKShaderStage(const MVKShaderStage mvkShaderStage) { + switch (mvkShaderStage) { + case kMVKShaderStageVertex: return EShLangVertex; + case kMVKShaderStageTessControl: return EShLangTessControl; + case kMVKShaderStageTessEval: return EShLangTessEvaluation; + case kMVKShaderStageGeometry: return EShLangGeometry; + case kMVKShaderStageFragment: return EShLangFragment; + case kMVKShaderStageCompute: return EShLangCompute; + default: return EShLangVertex; + } +} + + +#pragma mark Library initialization + +/** + * Called automatically when the framework is loaded and initialized. + * + * Initialize the + */ +static bool _wasShaderConverterInitialized = false; +__attribute__((constructor)) static void MVKShaderConverterInit() { + if (_wasShaderConverterInitialized ) { return; } + + glslang::InitializeProcess(); + + _wasShaderConverterInitialized = true; +} + + diff --git a/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLToSPIRVConverter.h b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLToSPIRVConverter.h new file mode 100644 index 00000000..627dc8e4 --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLToSPIRVConverter.h @@ -0,0 +1,84 @@ +/* + * GLSLToSPIRVConverter.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __GLSLToSPIRVConverter_h_ +#define __GLSLToSPIRVConverter_h_ 1 + + +#include "GLSLConversion.h" +#include +#include + + +namespace mvk { + +#pragma mark - +#pragma mark GLSLToSPIRVConverter + + /** Converts GLSL code to SPIR-V code. */ + class GLSLToSPIRVConverter { + + public: + + /** Sets the GLSL source code that is to be converted to the specified null-terminated string. */ + void setGLSL(const std::string& glslSrc); + + /** Returns the GLSL source code that was set using the setGLSL() function. */ + const std::string& getGLSL(); + + /** + * Converts GLSL code, set with setGLSL(), to SPIR-V code, which can be retrieved using getSPIRV(). + * + * The boolean flags indicate whether the original GLSL code and resulting SPIR-V code should + * be logged to the result log of this converter. This can be useful during shader debugging. + */ + bool convert(MVKShaderStage shaderStage, bool shouldLogGLSL, bool shouldLogSPIRV); + + /** Returns the SPIRV code most recently converted by the convert() function. */ + const std::vector& getSPIRV(); + + /** + * Returns whether the most recent conversion was successful. + * + * The initial value of this property is NO. It is set to YES upon successful conversion. + */ + bool getWasConverted(); + + /** + * Returns a human-readable log of the most recent conversion activity. + * This may be empty if the conversion was successful. + */ + const std::string& getResultLog(); + + protected: + void logMsg(const char* logMsg); + bool logError(const char* errMsg); + void logGLSL(const char* opDesc); + void logSPIRV(const char* opDesc); + bool validateSPIRV(); + void initGLSLCompilerResources(); + + std::string _glsl; + std::vector _spirv; + std::string _resultLog; + bool _wasConverted = false; + }; + +} + +#endif diff --git a/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/glslang b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/glslang new file mode 120000 index 00000000..fe92c0b3 --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/glslang @@ -0,0 +1 @@ +../../External/glslang \ No newline at end of file diff --git a/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/iOS b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/iOS new file mode 120000 index 00000000..4a4b357e --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/iOS @@ -0,0 +1 @@ +../../Package/Latest/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/iOS \ No newline at end of file diff --git a/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/macOS b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/macOS new file mode 120000 index 00000000..ffa407be --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/macOS @@ -0,0 +1 @@ +../../Package/Latest/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/macOS \ No newline at end of file diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRV-Cross b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRV-Cross new file mode 120000 index 00000000..130b260e --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRV-Cross @@ -0,0 +1 @@ +../../External/SPIRV-Cross \ No newline at end of file diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRV-Headers b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRV-Headers new file mode 120000 index 00000000..33da554e --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRV-Headers @@ -0,0 +1 @@ +../../External/SPIRV-Headers \ No newline at end of file diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRV-Tools b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRV-Tools new file mode 120000 index 00000000..a93b33ec --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRV-Tools @@ -0,0 +1 @@ +../../External/SPIRV-Tools \ No newline at end of file diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVConversion.h b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVConversion.h new file mode 100644 index 00000000..ef4d5dd0 --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVConversion.h @@ -0,0 +1,84 @@ +/* + * SPIRVConversion.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __SPIRVConversion_h_ +#define __SPIRVConversion_h_ 1 + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + +#include + + +/** This file contains convenience functions for converting SPIR-V to MSL, callable from standard C code. */ + + +/** + * Convenience function that converts the specified SPIR-V code to Metal Shading Language (MSL), + * source code, and returns whether the conversion was successful. + * + * If the pMSL parameter is not NULL, this function allocates space for the converted + * MSL source code, and returns a pointer to that MSL code in the location indicated + * by this parameter. It is the responsibility of the caller to free() the memory returned + * in this parameter. + * + * If the pResultLog parameter is not NULL, a pointer to the contents of the converter + * results log will be set at the location pointed to by the pResultLog parameter. + * It is the responsibility of the caller to free() the memory returned in this parameter. + * + * The boolean flags indicate whether the original SPIR-V code and resulting MSL source code + * should be logged to the converter results log. This can be useful during shader debugging. + */ +bool mvkConvertSPIRVToMSL(uint32_t* spvCode, + size_t spvLength, + char** pMSL, + char** pResultLog, + bool shouldLogSPIRV, + bool shouldLogMSL); + +/** + * Convenience function that converts SPIR-V code in the specified file to + * Metal Shading Language (MSL) source code. The file path should either be + * absolute or relative to the resource directory. + * + * If the pMSL parameter is not NULL, this function allocates space for the converted + * MSL source code, and returns a pointer to that MSL code in the location indicated + * by this parameter. It is the responsibility of the caller to free() the memory returned + * in this parameter. + * + * If the pResultLog parameter is not NULL, a pointer to the contents of the converter + * results log will be set at the location pointed to by the pResultLog parameter. + * It is the responsibility of the caller to free() the memory returned in this parameter. + * + * The boolean flags indicate whether the original SPIR-V code and resulting MSL source code + * should be logged to the converter results log. This can be useful during shader debugging. + */ +bool mvkConvertSPIRVFileToMSL(const char* spvFilepath, + char** pMSL, + char** pResultLog, + bool shouldLogSPIRV, + bool shouldLogMSL); + + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVConversion.mm b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVConversion.mm new file mode 100644 index 00000000..89a0c811 --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVConversion.mm @@ -0,0 +1,81 @@ +/* + * SPIRVConversion.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SPIRVConversion.h" +#include "SPIRVToMSLConverter.h" +#include "MVKCommonEnvironment.h" + +#include + +using namespace mvk; + + +MVK_PUBLIC_SYMBOL bool mvkConvertSPIRVToMSL(uint32_t* spvCode, + size_t spvLength, + char** pMSL, + char** pResultLog, + bool shouldLogSPIRV, + bool shouldLogMSL) { + SPIRVToMSLConverterContext spvCtx; + SPIRVToMSLConverter spvConverter; + spvConverter.setSPIRV(spvCode, spvLength); + bool wasConverted = spvConverter.convert(spvCtx, shouldLogSPIRV, shouldLogMSL); + + if (pMSL) { + auto& msl = spvConverter.getMSL(); + *pMSL = (char*)malloc(msl.size() + 1); + strcpy(*pMSL, msl.data()); + } + + if (pResultLog) { + auto log = spvConverter.getResultLog(); + *pResultLog = (char*)malloc(log.size() + 1); + strcpy(*pResultLog, log.data()); + } + + return wasConverted; +} + +MVK_PUBLIC_SYMBOL bool mvkConvertSPIRVFileToMSL(const char* spvFilepath, + char** pMSL, + char** pResultLog, + bool shouldLogSPIRV, + bool shouldLogMSL) { + NSString* filePath = @(spvFilepath); + if( !filePath.absolutePath ) { + filePath =[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: filePath]; + } + NSError* err = nil; + NSData* spv = [NSData dataWithContentsOfFile: filePath options: 0 error: &err]; + if (err) { + if (pResultLog) { + NSString* errMsg = [NSString stringWithFormat: @"Unable to convert SPIR-V in file %s to MSL: %s (code %li) %s", + filePath.UTF8String, err.localizedDescription.UTF8String, + (long)err.code, err.localizedFailureReason.UTF8String]; + *pResultLog = (char*)malloc(errMsg.length + 1); + strcpy(*pResultLog, errMsg.UTF8String); + } + + if (pMSL) { *pMSL = NULL; } + return false; + } + + return mvkConvertSPIRVToMSL((uint32_t*)spv.bytes, spv.length, pMSL, pResultLog, shouldLogSPIRV, shouldLogMSL); +} + + diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp new file mode 100644 index 00000000..6c1a22d9 --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp @@ -0,0 +1,324 @@ +/* + * SPIRVToMSLConverter.cpp + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SPIRVToMSLConverter.h" +#include "MVKCommonEnvironment.h" +#include "MVKStrings.h" +#include "spirv_msl.hpp" +#include "spirv_glsl.hpp" +#include + +using namespace mvk; +using namespace std; + + +#pragma mark - +#pragma mark SPIRVToMSLConverterContext + +// Returns whether the vector contains the value (using a matches(T&) comparison member function). */ +template +bool contains(vector& vec, T& val) { + for (T& vecVal : vec) { if (vecVal.matches(val)) { return true; } } + return false; +} + +MVK_PUBLIC_SYMBOL bool SPIRVToMSLConverterOptions::matches(SPIRVToMSLConverterOptions& other) { + if (mslVersion != other.mslVersion) { return false; } + if (!!shouldFlipVertexY != !!other.shouldFlipVertexY) { return false; } + if (!!isRenderingPoints != !!other.isRenderingPoints) { return false; } + return true; +} + +MVK_PUBLIC_SYMBOL bool MSLVertexAttribute::matches(MSLVertexAttribute& other) { + if (location != other.location) { return false; } + if (mslBuffer != other.mslBuffer) { return false; } + if (mslOffset != other.mslOffset) { return false; } + if (mslStride != other.mslStride) { return false; } + if (!!isPerInstance != !!other.isPerInstance) { return false; } + return true; +} + +MVK_PUBLIC_SYMBOL bool MSLResourceBinding::matches(MSLResourceBinding& other) { + if (stage != other.stage) { return false; } + if (descriptorSet != other.descriptorSet) { return false; } + if (binding != other.binding) { return false; } + if (mslBuffer != other.mslBuffer) { return false; } + if (mslTexture != other.mslTexture) { return false; } + if (mslSampler != other.mslSampler) { return false; } + return true; +} + +// Check them all in case inactive VA's duplicate locations used by active VA's. +MVK_PUBLIC_SYMBOL bool SPIRVToMSLConverterContext::isVertexAttributeLocationUsed(uint32_t location) { + for (auto& va : vertexAttributes) { + if ((va.location == location) && va.isUsedByShader) { return true; } + } + return false; +} + +// Check them all in case inactive VA's duplicate buffers used by active VA's. +MVK_PUBLIC_SYMBOL bool SPIRVToMSLConverterContext::isVertexBufferUsed(uint32_t mslBuffer) { + for (auto& va : vertexAttributes) { + if ((va.mslBuffer == mslBuffer) && va.isUsedByShader) { return true; } + } + return false; +} + +MVK_PUBLIC_SYMBOL bool SPIRVToMSLConverterContext::matches(SPIRVToMSLConverterContext& other) { + + if ( !options.matches(other.options) ) { return false; } + + for (auto& va : vertexAttributes) { + if (va.isUsedByShader && !contains(other.vertexAttributes, va)) { return false; } + } + + for (auto& rb : resourceBindings) { + if (rb.isUsedByShader && !contains(other.resourceBindings, rb)) { return false; } + } + + return true; +} + +// Aligns the usage of the destination context to that of the source context. +MVK_PUBLIC_SYMBOL void SPIRVToMSLConverterContext::alignUsageWith(SPIRVToMSLConverterContext& srcContext) { + + for (auto& va : vertexAttributes) { + va.isUsedByShader = false; + for (auto& srcVA : srcContext.vertexAttributes) { + if (va.matches(srcVA)) { va.isUsedByShader = srcVA.isUsedByShader; } + } + } + + for (auto& rb : resourceBindings) { + rb.isUsedByShader = false; + for (auto& srcRB : srcContext.resourceBindings) { + if (rb.matches(srcRB)) { rb.isUsedByShader = srcRB.isUsedByShader; } + } + } +} + + +#pragma mark - +#pragma mark SPIRVToMSLConverter + +/** Populates content extracted from the SPRI-V compiler. */ +void populateFromCompiler(spirv_cross::Compiler& compiler, + unordered_map& entryPointNameMap, + SPIRVLocalSizesByEntryPointName& localSizes); + +MVK_PUBLIC_SYMBOL void SPIRVToMSLConverter::setSPIRV(const vector& spirv) { _spirv = spirv; } + +MVK_PUBLIC_SYMBOL void SPIRVToMSLConverter::setSPIRV(const uint32_t* spirvCode, size_t length) { + _spirv.clear(); // Clear for reuse + _spirv.reserve(length); + for (size_t i = 0; i < length; i++) { + _spirv.push_back(spirvCode[i]); + } +} + +MVK_PUBLIC_SYMBOL const vector& SPIRVToMSLConverter::getSPIRV() { return _spirv; } + +MVK_PUBLIC_SYMBOL bool SPIRVToMSLConverter::convert(SPIRVToMSLConverterContext& context, + bool shouldLogSPIRV, + bool shouldLogMSL, + bool shouldLogGLSL) { + _wasConverted = true; + _resultLog.clear(); + _msl.clear(); + + if (shouldLogSPIRV) { logSPIRV("Converting"); } + + // Add vertex attributes + vector vtxAttrs; + spirv_cross::MSLVertexAttr va; + for (auto& ctxVA : context.vertexAttributes) { + va.location = ctxVA.location; + va.msl_buffer = ctxVA.mslBuffer; + va.msl_offset = ctxVA.mslOffset; + va.msl_stride = ctxVA.mslStride; + va.per_instance = ctxVA.isPerInstance; + va.used_by_shader = ctxVA.isUsedByShader; + vtxAttrs.push_back(va); + } + + // Add resource bindings + vector resBindings; + spirv_cross::MSLResourceBinding rb; + for (auto& ctxRB : context.resourceBindings) { + rb.desc_set = ctxRB.descriptorSet; + rb.binding = ctxRB.binding; + rb.stage = ctxRB.stage; + rb.msl_buffer = ctxRB.mslBuffer; + rb.msl_texture = ctxRB.mslTexture; + rb.msl_sampler = ctxRB.mslSampler; + rb.used_by_shader = ctxRB.isUsedByShader; + resBindings.push_back(rb); + } + + spirv_cross::CompilerMSL mslCompiler(_spirv); + + // Establish the MSL options for the compiler + // This needs to be done in two steps...for CompilerMSL and its superclass. + auto mslOpts = mslCompiler.get_options(); + mslOpts.msl_version = context.options.mslVersion; + mslOpts.enable_point_size_builtin = context.options.isRenderingPoints; + mslOpts.resolve_specialized_array_lengths = true; + mslCompiler.set_options(mslOpts); + + auto scOpts = mslCompiler.CompilerGLSL::get_options(); + scOpts.vertex.flip_vert_y = context.options.shouldFlipVertexY; + mslCompiler.CompilerGLSL::set_options(scOpts); + + try { + _msl = mslCompiler.compile(&vtxAttrs, &resBindings); + if (shouldLogMSL) { logSource(_msl, "MSL", "Converted"); } + } catch (spirv_cross::CompilerError& ex) { + string errMsg("MSL conversion error: "); + errMsg += ex.what(); + logError(errMsg.data()); + if (shouldLogMSL) { + _msl = mslCompiler.get_partial_source(); + logSource(_msl, "MSL", "Partially converted"); + } + } + + // Populate content extracted from the SPRI-V compiler. + populateFromCompiler(mslCompiler, _entryPointNameMap, _localSizes); + + // To check GLSL conversion + if (shouldLogGLSL) { + spirv_cross::CompilerGLSL glslCompiler(_spirv); + string glsl; + try { + glsl = glslCompiler.compile(); + logSource(glsl, "GLSL", "Estimated original"); + } catch (spirv_cross::CompilerError& ex) { + string errMsg("Original GLSL extraction error: "); + errMsg += ex.what(); + logMsg(errMsg.data()); + glsl = glslCompiler.get_partial_source(); + logSource(glsl, "GLSL", "Partially converted"); + } + } + + // Copy whether the vertex attributes and resource bindings are used by the shader + uint32_t vaCnt = (uint32_t)vtxAttrs.size(); + for (uint32_t vaIdx = 0; vaIdx < vaCnt; vaIdx++) { + context.vertexAttributes[vaIdx].isUsedByShader = vtxAttrs[vaIdx].used_by_shader; + } + uint32_t rbCnt = (uint32_t)resBindings.size(); + for (uint32_t rbIdx = 0; rbIdx < rbCnt; rbIdx++) { + context.resourceBindings[rbIdx].isUsedByShader = resBindings[rbIdx].used_by_shader; + } + + return _wasConverted; +} + +/** Appends the message text to the result log. */ +void SPIRVToMSLConverter::logMsg(const char* logMsg) { + string trimMsg = trim(logMsg); + if ( !trimMsg.empty() ) { + _resultLog += trimMsg; + _resultLog += "\n\n"; + } +} + +/** Appends the error text to the result log, sets the wasConverted property to false, and returns it. */ +bool SPIRVToMSLConverter::logError(const char* errMsg) { + logMsg(errMsg); + _wasConverted = false; + return _wasConverted; +} + +/** Appends the SPIR-V to the result log, indicating whether it is being converted or was converted. */ +void SPIRVToMSLConverter::logSPIRV(const char* opDesc) { + + string spvLog; + mvk::logSPIRV(_spirv, spvLog); + + _resultLog += opDesc; + _resultLog += " SPIR-V:\n"; + _resultLog += spvLog; + _resultLog += "\nEnd SPIR-V\n\n"; +} + +/** Validates that the SPIR-V code will disassemble during logging. */ +bool SPIRVToMSLConverter::validateSPIRV() { + if (_spirv.size() < 5) { return false; } + if (_spirv[0] != spv::MagicNumber) { return false; } + if (_spirv[4] != 0) { return false; } + return true; +} + +/** Appends the source to the result log, prepending with the operation. */ +void SPIRVToMSLConverter::logSource(string& src, const char* srcLang, const char* opDesc) { + _resultLog += opDesc; + _resultLog += " "; + _resultLog += srcLang; + _resultLog += ":\n"; + _resultLog += src; + _resultLog += "\nEnd "; + _resultLog += srcLang; + _resultLog += "\n\n"; +} + + +#pragma mark Support functions + +void populateFromCompiler(spirv_cross::Compiler& compiler, + unordered_map& entryPointNameMap, + SPIRVLocalSizesByEntryPointName& localSizes) { + + uint32_t minDim = 1; + entryPointNameMap.clear(); + localSizes.clear(); + for (string& epOrigName : compiler.get_entry_points()) { + auto& ep = compiler.get_entry_point(epOrigName); + + entryPointNameMap[epOrigName] = ep.name; + + auto& wgSize = ep.workgroup_size; + SPIRVLocalSize spvLS; + spvLS.width = max(wgSize.x, minDim); + spvLS.height = max(wgSize.y, minDim); + spvLS.depth = max(wgSize.z, minDim); + localSizes[epOrigName] = spvLS; + } +} + +MVK_PUBLIC_SYMBOL void mvk::logSPIRV(vector& spirv, string& spvLog) { + if ( !((spirv.size() > 4) && + (spirv[0] == spv::MagicNumber) && + (spirv[4] == 0)) ) { return; } + + uint32_t options = (SPV_BINARY_TO_TEXT_OPTION_INDENT); + spv_text text; + spv_diagnostic diagnostic = nullptr; + spv_context context = spvContextCreate(SPV_ENV_VULKAN_1_0); + spv_result_t error = spvBinaryToText(context, spirv.data(), spirv.size(), options, &text, &diagnostic); + spvContextDestroy(context); + if (error) { + spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + return; + } + spvLog.append(text->str, text->length); + spvTextDestroy(text); +} + + diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h new file mode 100644 index 00000000..9a751a41 --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h @@ -0,0 +1,245 @@ +/* + * SPIRVToMSLConverter.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __SPIRVToMSLConverter_h_ +#define __SPIRVToMSLConverter_h_ 1 + +#include "spirv.hpp" +#include +#include +#include + +namespace mvk { + + +#pragma mark - +#pragma mark SPIRVToMSLConverterContext + + /** Options for converting SPIR-V to Metal Shading Language */ + typedef struct SPIRVToMSLConverterOptions { + uint32_t mslVersion = makeMSLVersion(2); + bool shouldFlipVertexY = true; + bool isRenderingPoints = false; + + /** + * Returns whether the specified options match this one. + * It does if all corresponding elements are equal. + */ + bool matches(SPIRVToMSLConverterOptions& other); + + void setMSLVersion(uint32_t major, uint32_t minor = 0, uint32_t point = 0) { + mslVersion = makeMSLVersion(major, minor, point); + } + + bool supportsMSLVersion(uint32_t major, uint32_t minor = 0, uint32_t point = 0) { + return mslVersion >= makeMSLVersion(major, minor, point); + } + + static uint32_t makeMSLVersion(uint32_t major, uint32_t minor = 0, uint32_t patch = 0) { + return (major * 10000) + (minor * 100) + patch; + } + + } SPIRVToMSLConverterOptions; + + /** + * Defines MSL characteristics of a vertex attribute at a particular location. + * The isUsedByShader flag is set to true during conversion of SPIR-V to MSL + * if the shader makes use of this vertex attribute. + */ + typedef struct MSLVertexAttribute { + uint32_t location = 0; + uint32_t mslBuffer = 0; + uint32_t mslOffset = 0; + uint32_t mslStride = 0; + bool isPerInstance = false; + + bool isUsedByShader = false; + + /** + * Returns whether the specified vertex attribute match this one. + * It does if all corresponding elements except isUsedByShader are equal. + */ + bool matches(MSLVertexAttribute& other); + + } MSLVertexAttribute; + + /** + * Matches the binding index of a MSL resource for a binding within a descriptor set. + * Taken together, the stage, desc_set and binding combine to form a reference to a resource + * descriptor used in a particular shading stage. Generally, only one of the buffer, texture, + * or sampler elements will be populated. The isUsedByShader flag is set to true during + * compilation of SPIR-V to MSL if the shader makes use of this vertex attribute. + */ + typedef struct MSLResourceBinding { + spv::ExecutionModel stage; + uint32_t descriptorSet = 0; + uint32_t binding = 0; + + uint32_t mslBuffer = 0; + uint32_t mslTexture = 0; + uint32_t mslSampler = 0; + + bool isUsedByShader = false; + + /** + * Returns whether the specified resource binding match this one. + * It does if all corresponding elements except isUsedByShader are equal. + */ + bool matches(MSLResourceBinding& other); + + } MSLResourceBinding; + + /** Context passed to the SPIRVToMSLConverter to map SPIR-V descriptors to Metal resource indices. */ + typedef struct SPIRVToMSLConverterContext { + SPIRVToMSLConverterOptions options; + std::vector vertexAttributes; + std::vector resourceBindings; + + /** Returns whether the vertex attribute at the specified location is used by the shader. */ + bool isVertexAttributeLocationUsed(uint32_t location); + + /** Returns whether the vertex buffer at the specified Metal binding index is used by the shader. */ + bool isVertexBufferUsed(uint32_t mslBuffer); + + /** + * Returns whether this context matches the other context. It does if the respective + * options match and any vertex attributes and resource bindings used by this context + * can be found in the other context. Vertex attributes and resource bindings that are + * in the other context but are not used by the shader that created this context, are ignored. + */ + bool matches(SPIRVToMSLConverterContext& other); + + /** Aligns the usage of this context with that of the source context. */ + void alignUsageWith(SPIRVToMSLConverterContext& srcContext); + + } SPIRVToMSLConverterContext; + + /** Specifies the SPIRV LocalSize, which is the number of threads in a compute shader workgroup. */ + typedef struct { + uint32_t width = 1; + uint32_t height = 1; + uint32_t depth = 1; + } SPIRVLocalSize; + + /** Holds a map of the LocalSize value for each compute function, indexed by SPIRV entry point name. */ + typedef std::unordered_map SPIRVLocalSizesByEntryPointName; + + /** Special constant used in a MSLResourceBinding descriptorSet element to indicate the bindings for the push constants. */ + static const uint32_t kPushConstDescSet = std::numeric_limits::max(); + + /** Special constant used in a MSLResourceBinding binding element to indicate the bindings for the push constants. */ + static const uint32_t kPushConstBinding = 0; + + +#pragma mark - +#pragma mark SPIRVToMSLConverter + + /** Converts SPIR-V code to Metal Shading Language code. */ + class SPIRVToMSLConverter { + + public: + + /** Sets the SPIRV code. */ + void setSPIRV(const std::vector& spirv); + + /** + * Sets the SPIRV code from the specified array of values. + * The length parameter indicates the number of uint values to store. + */ + void setSPIRV(const uint32_t* spirvCode, size_t length); + + /** Returns a reference to the SPIRV code, set by one of the setSPIRV() functions. */ + const std::vector& getSPIRV(); + + /** + * Converts SPIR-V code, set using setSPIRV() to MSL code, which can be retrieved using getMSL(). + * + * The boolean flags indicate whether the original SPIR-V code, the resulting MSL code, + * and optionally, the original GLSL (as converted from the SPIR_V), should be logged + * to the result log of this converter. This can be useful during shader debugging. + */ + bool convert(SPIRVToMSLConverterContext& context, + bool shouldLogSPIRV = false, + bool shouldLogMSL = false, + bool shouldLogGLSL = false); + + /** + * Returns the Metal Shading Language source code most recently converted + * by the convert() function, or set directly using the setMSL() function. + */ + const std::string& getMSL() { return _msl; } + + /** + * Returns a mapping between the original entry point name in the SPIR-V and a + * possibly modified name as required to bypass restrictions in naming entry + * points within MSL. Specifically, the entry point name "main" is illegal in MSL, + * and is replaced by "main0" in this mapping. + */ + const std::unordered_map& getEntryPointNameMap() { return _entryPointNameMap; } + + /** + * Returns a mapping of the local size of each entry point. + * This is only meaningful for compute shaders. + */ + const SPIRVLocalSizesByEntryPointName& getLocalSizes() { return _localSizes; } + + /** + * Returns whether the most recent conversion was successful. + * + * The initial value of this property is NO. It is set to YES upon successful conversion. + */ + bool getWasConverted() { return _wasConverted; } + + /** + * Returns a human-readable log of the most recent conversion activity. + * This may be empty if the conversion was successful. + */ + const std::string& getResultLog() { return _resultLog; } + + /** Sets MSL source code. This can be used when MSL is supplied directly. */ + void setMSL(const std::string& msl, + const std::unordered_map& entryPointNameMap, + const SPIRVLocalSizesByEntryPointName& localSizes) { + _msl = msl; + _entryPointNameMap = entryPointNameMap; + _localSizes = localSizes; + } + + protected: + void logMsg(const char* logMsg); + bool logError(const char* errMsg); + void logSPIRV(const char* opDesc); + bool validateSPIRV(); + void logSource(std::string& src, const char* srcLang, const char* opDesc); + + std::vector _spirv; + std::string _msl; + std::string _resultLog; + std::unordered_map _entryPointNameMap; + SPIRVLocalSizesByEntryPointName _localSizes; + bool _wasConverted = false; + }; + + +#pragma mark Support functions + + /** Appends the SPIR-V in human-readable form to the specified log string. */ + void logSPIRV(std::vector& spirv, std::string& spvLog); + +} +#endif diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/iOS b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/iOS new file mode 120000 index 00000000..4a4b357e --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/iOS @@ -0,0 +1 @@ +../../Package/Latest/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/iOS \ No newline at end of file diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/macOS b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/macOS new file mode 120000 index 00000000..ffa407be --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/macOS @@ -0,0 +1 @@ +../../Package/Latest/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/macOS \ No newline at end of file diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/spirv.hpp b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/spirv.hpp new file mode 120000 index 00000000..388971fe --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/spirv.hpp @@ -0,0 +1 @@ +SPIRV-Cross/spirv.hpp \ No newline at end of file diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj new file mode 100644 index 00000000..aecbd15e --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj @@ -0,0 +1,2589 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + A909408A1C58013E0094110D /* SPIRVToMSLConverter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9093F5A1C58013E0094110D /* SPIRVToMSLConverter.cpp */; }; + A909408B1C58013E0094110D /* SPIRVToMSLConverter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9093F5A1C58013E0094110D /* SPIRVToMSLConverter.cpp */; }; + A909408C1C58013E0094110D /* SPIRVToMSLConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = A9093F5B1C58013E0094110D /* SPIRVToMSLConverter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A909408D1C58013E0094110D /* SPIRVToMSLConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = A9093F5B1C58013E0094110D /* SPIRVToMSLConverter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A90940A71C5808BB0094110D /* GLSLToSPIRVConverter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A90940A31C5808BB0094110D /* GLSLToSPIRVConverter.cpp */; }; + A90940A81C5808BB0094110D /* GLSLToSPIRVConverter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A90940A31C5808BB0094110D /* GLSLToSPIRVConverter.cpp */; }; + A90940A91C5808BB0094110D /* GLSLToSPIRVConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = A90940A41C5808BB0094110D /* GLSLToSPIRVConverter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A90940AA1C5808BB0094110D /* GLSLToSPIRVConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = A90940A41C5808BB0094110D /* GLSLToSPIRVConverter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A90941A31C581F840094110D /* GLSLConversion.mm in Sources */ = {isa = PBXBuildFile; fileRef = A90941A11C581F840094110D /* GLSLConversion.mm */; }; + A90941A41C581F840094110D /* GLSLConversion.mm in Sources */ = {isa = PBXBuildFile; fileRef = A90941A11C581F840094110D /* GLSLConversion.mm */; }; + A90941A51C581F840094110D /* GLSLConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = A90941A21C581F840094110D /* GLSLConversion.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A90941A61C581F840094110D /* GLSLConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = A90941A21C581F840094110D /* GLSLConversion.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A91425E31EF9542E00891AFD /* InitializeDll.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A91425E11EF9542E00891AFD /* InitializeDll.cpp */; }; + A91425E41EF9542E00891AFD /* InitializeDll.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A91425E11EF9542E00891AFD /* InitializeDll.cpp */; }; + A91425E51EF9542E00891AFD /* InitializeDll.h in Headers */ = {isa = PBXBuildFile; fileRef = A91425E21EF9542E00891AFD /* InitializeDll.h */; }; + A91425E61EF9542E00891AFD /* InitializeDll.h in Headers */ = {isa = PBXBuildFile; fileRef = A91425E21EF9542E00891AFD /* InitializeDll.h */; }; + A925B70C1C7754B2006E7ECD /* FileSupport.mm in Sources */ = {isa = PBXBuildFile; fileRef = A925B70A1C7754B2006E7ECD /* FileSupport.mm */; }; + A925B71A1C78DEB2006E7ECD /* MoltenVKGLSLToSPIRVConverter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A964BD601C57EFBD00D930D8 /* MoltenVKGLSLToSPIRVConverter.framework */; }; + A925B71B1C78DEB2006E7ECD /* MoltenVKSPIRVToMSLConverter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A93903C71C57E9ED00FE90DC /* MoltenVKSPIRVToMSLConverter.framework */; }; + A928C9191D0488DC00071B88 /* SPIRVConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = A928C9171D0488DC00071B88 /* SPIRVConversion.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A928C91A1D0488DC00071B88 /* SPIRVConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = A928C9171D0488DC00071B88 /* SPIRVConversion.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A928C91B1D0488DC00071B88 /* SPIRVConversion.mm in Sources */ = {isa = PBXBuildFile; fileRef = A928C9181D0488DC00071B88 /* SPIRVConversion.mm */; }; + A928C91C1D0488DC00071B88 /* SPIRVConversion.mm in Sources */ = {isa = PBXBuildFile; fileRef = A928C9181D0488DC00071B88 /* SPIRVConversion.mm */; }; + A95C5F3F1DEA9070000D17B6 /* spirv_cfg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A95C5F3D1DEA9070000D17B6 /* spirv_cfg.cpp */; }; + A95C5F401DEA9070000D17B6 /* spirv_cfg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A95C5F3D1DEA9070000D17B6 /* spirv_cfg.cpp */; }; + A95C5F411DEA9070000D17B6 /* spirv_cfg.hpp in Headers */ = {isa = PBXBuildFile; fileRef = A95C5F3E1DEA9070000D17B6 /* spirv_cfg.hpp */; }; + A95C5F421DEA9070000D17B6 /* spirv_cfg.hpp in Headers */ = {isa = PBXBuildFile; fileRef = A95C5F3E1DEA9070000D17B6 /* spirv_cfg.hpp */; }; + A97CC7401C7527F3004A5C7E /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A97CC73D1C7527F3004A5C7E /* main.cpp */; }; + A97CC7411C7527F3004A5C7E /* MoltenVKShaderConverterTool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A97CC73E1C7527F3004A5C7E /* MoltenVKShaderConverterTool.cpp */; }; + A98149661FB6A98A005F00B4 /* MVKStrings.h in Headers */ = {isa = PBXBuildFile; fileRef = A98149651FB6A98A005F00B4 /* MVKStrings.h */; }; + A98149671FB6A98A005F00B4 /* MVKStrings.h in Headers */ = {isa = PBXBuildFile; fileRef = A98149651FB6A98A005F00B4 /* MVKStrings.h */; }; + A98149681FB6A98A005F00B4 /* MVKStrings.h in Headers */ = {isa = PBXBuildFile; fileRef = A98149651FB6A98A005F00B4 /* MVKStrings.h */; }; + A98149691FB6A98A005F00B4 /* MVKStrings.h in Headers */ = {isa = PBXBuildFile; fileRef = A98149651FB6A98A005F00B4 /* MVKStrings.h */; }; + A994BF381FBF3F9700278EB9 /* assembly_grammar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE731FBF3F9700278EB9 /* assembly_grammar.cpp */; }; + A994BF391FBF3F9700278EB9 /* assembly_grammar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE731FBF3F9700278EB9 /* assembly_grammar.cpp */; }; + A994BF3A1FBF3F9700278EB9 /* assembly_grammar.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE741FBF3F9700278EB9 /* assembly_grammar.h */; }; + A994BF3B1FBF3F9700278EB9 /* assembly_grammar.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE741FBF3F9700278EB9 /* assembly_grammar.h */; }; + A994BF3C1FBF3F9700278EB9 /* binary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE751FBF3F9700278EB9 /* binary.cpp */; }; + A994BF3D1FBF3F9700278EB9 /* binary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE751FBF3F9700278EB9 /* binary.cpp */; }; + A994BF3E1FBF3F9700278EB9 /* binary.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE761FBF3F9700278EB9 /* binary.h */; }; + A994BF3F1FBF3F9700278EB9 /* binary.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE761FBF3F9700278EB9 /* binary.h */; }; + A994BF401FBF3F9700278EB9 /* cfa.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE771FBF3F9700278EB9 /* cfa.h */; }; + A994BF411FBF3F9700278EB9 /* cfa.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE771FBF3F9700278EB9 /* cfa.h */; }; + A994BF421FBF3F9700278EB9 /* markv.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE7B1FBF3F9700278EB9 /* markv.h */; }; + A994BF431FBF3F9700278EB9 /* markv.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE7B1FBF3F9700278EB9 /* markv.h */; }; + A994BF441FBF3F9700278EB9 /* markv_codec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE7C1FBF3F9700278EB9 /* markv_codec.cpp */; }; + A994BF451FBF3F9700278EB9 /* markv_codec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE7C1FBF3F9700278EB9 /* markv_codec.cpp */; }; + A994BF461FBF3F9700278EB9 /* markv_model.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE7D1FBF3F9700278EB9 /* markv_model.h */; }; + A994BF471FBF3F9700278EB9 /* markv_model.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE7D1FBF3F9700278EB9 /* markv_model.h */; }; + A994BF481FBF3F9700278EB9 /* diagnostic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE7E1FBF3F9700278EB9 /* diagnostic.cpp */; }; + A994BF491FBF3F9700278EB9 /* diagnostic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE7E1FBF3F9700278EB9 /* diagnostic.cpp */; }; + A994BF4A1FBF3F9700278EB9 /* diagnostic.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE7F1FBF3F9700278EB9 /* diagnostic.h */; }; + A994BF4B1FBF3F9700278EB9 /* diagnostic.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE7F1FBF3F9700278EB9 /* diagnostic.h */; }; + A994BF4C1FBF3F9700278EB9 /* disassemble.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE801FBF3F9700278EB9 /* disassemble.cpp */; }; + A994BF4D1FBF3F9700278EB9 /* disassemble.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE801FBF3F9700278EB9 /* disassemble.cpp */; }; + A994BF4E1FBF3F9700278EB9 /* enum_set.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE811FBF3F9700278EB9 /* enum_set.h */; }; + A994BF4F1FBF3F9700278EB9 /* enum_set.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE811FBF3F9700278EB9 /* enum_set.h */; }; + A994BF501FBF3F9700278EB9 /* enum_string_mapping.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE821FBF3F9700278EB9 /* enum_string_mapping.cpp */; }; + A994BF511FBF3F9700278EB9 /* enum_string_mapping.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE821FBF3F9700278EB9 /* enum_string_mapping.cpp */; }; + A994BF521FBF3F9700278EB9 /* enum_string_mapping.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE831FBF3F9700278EB9 /* enum_string_mapping.h */; }; + A994BF531FBF3F9700278EB9 /* enum_string_mapping.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE831FBF3F9700278EB9 /* enum_string_mapping.h */; }; + A994BF541FBF3F9700278EB9 /* ext_inst.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE841FBF3F9700278EB9 /* ext_inst.cpp */; }; + A994BF551FBF3F9700278EB9 /* ext_inst.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE841FBF3F9700278EB9 /* ext_inst.cpp */; }; + A994BF561FBF3F9700278EB9 /* ext_inst.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE851FBF3F9700278EB9 /* ext_inst.h */; }; + A994BF571FBF3F9700278EB9 /* ext_inst.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE851FBF3F9700278EB9 /* ext_inst.h */; }; + A994BF581FBF3F9700278EB9 /* extensions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE861FBF3F9700278EB9 /* extensions.cpp */; }; + A994BF591FBF3F9700278EB9 /* extensions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE861FBF3F9700278EB9 /* extensions.cpp */; }; + A994BF5A1FBF3F9700278EB9 /* extensions.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE871FBF3F9700278EB9 /* extensions.h */; }; + A994BF5B1FBF3F9700278EB9 /* extensions.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE871FBF3F9700278EB9 /* extensions.h */; }; + A994BF5C1FBF3F9700278EB9 /* id_descriptor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE8C1FBF3F9700278EB9 /* id_descriptor.cpp */; }; + A994BF5D1FBF3F9700278EB9 /* id_descriptor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE8C1FBF3F9700278EB9 /* id_descriptor.cpp */; }; + A994BF5E1FBF3F9700278EB9 /* id_descriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE8D1FBF3F9700278EB9 /* id_descriptor.h */; }; + A994BF5F1FBF3F9700278EB9 /* id_descriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE8D1FBF3F9700278EB9 /* id_descriptor.h */; }; + A994BF601FBF3F9700278EB9 /* instruction.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE8E1FBF3F9700278EB9 /* instruction.h */; }; + A994BF611FBF3F9700278EB9 /* instruction.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE8E1FBF3F9700278EB9 /* instruction.h */; }; + A994BF621FBF3F9700278EB9 /* libspirv.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE8F1FBF3F9700278EB9 /* libspirv.cpp */; }; + A994BF631FBF3F9700278EB9 /* libspirv.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE8F1FBF3F9700278EB9 /* libspirv.cpp */; }; + A994BF641FBF3F9700278EB9 /* linker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE921FBF3F9700278EB9 /* linker.cpp */; }; + A994BF651FBF3F9700278EB9 /* linker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE921FBF3F9700278EB9 /* linker.cpp */; }; + A994BF661FBF3F9700278EB9 /* macro.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE931FBF3F9700278EB9 /* macro.h */; }; + A994BF671FBF3F9700278EB9 /* macro.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE931FBF3F9700278EB9 /* macro.h */; }; + A994BF681FBF3F9700278EB9 /* message.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE941FBF3F9700278EB9 /* message.cpp */; }; + A994BF691FBF3F9700278EB9 /* message.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE941FBF3F9700278EB9 /* message.cpp */; }; + A994BF6A1FBF3F9700278EB9 /* message.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE951FBF3F9700278EB9 /* message.h */; }; + A994BF6B1FBF3F9700278EB9 /* message.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE951FBF3F9700278EB9 /* message.h */; }; + A994BF6C1FBF3F9700278EB9 /* name_mapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE961FBF3F9700278EB9 /* name_mapper.cpp */; }; + A994BF6D1FBF3F9700278EB9 /* name_mapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE961FBF3F9700278EB9 /* name_mapper.cpp */; }; + A994BF6E1FBF3F9700278EB9 /* name_mapper.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE971FBF3F9700278EB9 /* name_mapper.h */; }; + A994BF6F1FBF3F9700278EB9 /* name_mapper.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE971FBF3F9700278EB9 /* name_mapper.h */; }; + A994BF701FBF3F9700278EB9 /* opcode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE981FBF3F9700278EB9 /* opcode.cpp */; }; + A994BF711FBF3F9700278EB9 /* opcode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE981FBF3F9700278EB9 /* opcode.cpp */; }; + A994BF721FBF3F9700278EB9 /* opcode.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE991FBF3F9700278EB9 /* opcode.h */; }; + A994BF731FBF3F9700278EB9 /* opcode.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE991FBF3F9700278EB9 /* opcode.h */; }; + A994BF741FBF3F9700278EB9 /* operand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE9A1FBF3F9700278EB9 /* operand.cpp */; }; + A994BF751FBF3F9700278EB9 /* operand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE9A1FBF3F9700278EB9 /* operand.cpp */; }; + A994BF761FBF3F9700278EB9 /* operand.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE9B1FBF3F9700278EB9 /* operand.h */; }; + A994BF771FBF3F9700278EB9 /* operand.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE9B1FBF3F9700278EB9 /* operand.h */; }; + A994BF781FBF3F9700278EB9 /* aggressive_dead_code_elim_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE9D1FBF3F9700278EB9 /* aggressive_dead_code_elim_pass.cpp */; }; + A994BF791FBF3F9700278EB9 /* aggressive_dead_code_elim_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE9D1FBF3F9700278EB9 /* aggressive_dead_code_elim_pass.cpp */; }; + A994BF7A1FBF3F9700278EB9 /* aggressive_dead_code_elim_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE9E1FBF3F9700278EB9 /* aggressive_dead_code_elim_pass.h */; }; + A994BF7B1FBF3F9700278EB9 /* aggressive_dead_code_elim_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BE9E1FBF3F9700278EB9 /* aggressive_dead_code_elim_pass.h */; }; + A994BF7C1FBF3F9700278EB9 /* basic_block.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE9F1FBF3F9700278EB9 /* basic_block.cpp */; }; + A994BF7D1FBF3F9700278EB9 /* basic_block.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BE9F1FBF3F9700278EB9 /* basic_block.cpp */; }; + A994BF7E1FBF3F9700278EB9 /* basic_block.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEA01FBF3F9700278EB9 /* basic_block.h */; }; + A994BF7F1FBF3F9700278EB9 /* basic_block.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEA01FBF3F9700278EB9 /* basic_block.h */; }; + A994BF801FBF3F9700278EB9 /* block_merge_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEA11FBF3F9700278EB9 /* block_merge_pass.cpp */; }; + A994BF811FBF3F9700278EB9 /* block_merge_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEA11FBF3F9700278EB9 /* block_merge_pass.cpp */; }; + A994BF821FBF3F9700278EB9 /* block_merge_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEA21FBF3F9700278EB9 /* block_merge_pass.h */; }; + A994BF831FBF3F9700278EB9 /* block_merge_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEA21FBF3F9700278EB9 /* block_merge_pass.h */; }; + A994BF841FBF3F9700278EB9 /* build_module.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEA31FBF3F9700278EB9 /* build_module.cpp */; }; + A994BF851FBF3F9700278EB9 /* build_module.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEA31FBF3F9700278EB9 /* build_module.cpp */; }; + A994BF861FBF3F9700278EB9 /* build_module.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEA41FBF3F9700278EB9 /* build_module.h */; }; + A994BF871FBF3F9700278EB9 /* build_module.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEA41FBF3F9700278EB9 /* build_module.h */; }; + A994BF881FBF3F9700278EB9 /* cfg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEA51FBF3F9700278EB9 /* cfg.cpp */; }; + A994BF891FBF3F9700278EB9 /* cfg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEA51FBF3F9700278EB9 /* cfg.cpp */; }; + A994BF8A1FBF3F9700278EB9 /* cfg.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEA61FBF3F9700278EB9 /* cfg.h */; }; + A994BF8B1FBF3F9700278EB9 /* cfg.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEA61FBF3F9700278EB9 /* cfg.h */; }; + A994BF8C1FBF3F9700278EB9 /* cfg_cleanup_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEA71FBF3F9700278EB9 /* cfg_cleanup_pass.cpp */; }; + A994BF8D1FBF3F9700278EB9 /* cfg_cleanup_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEA71FBF3F9700278EB9 /* cfg_cleanup_pass.cpp */; }; + A994BF8E1FBF3F9700278EB9 /* cfg_cleanup_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEA81FBF3F9700278EB9 /* cfg_cleanup_pass.h */; }; + A994BF8F1FBF3F9700278EB9 /* cfg_cleanup_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEA81FBF3F9700278EB9 /* cfg_cleanup_pass.h */; }; + A994BF901FBF3F9700278EB9 /* common_uniform_elim_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEAA1FBF3F9700278EB9 /* common_uniform_elim_pass.cpp */; }; + A994BF911FBF3F9700278EB9 /* common_uniform_elim_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEAA1FBF3F9700278EB9 /* common_uniform_elim_pass.cpp */; }; + A994BF921FBF3F9700278EB9 /* common_uniform_elim_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEAB1FBF3F9700278EB9 /* common_uniform_elim_pass.h */; }; + A994BF931FBF3F9700278EB9 /* common_uniform_elim_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEAB1FBF3F9700278EB9 /* common_uniform_elim_pass.h */; }; + A994BF941FBF3F9700278EB9 /* compact_ids_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEAC1FBF3F9700278EB9 /* compact_ids_pass.cpp */; }; + A994BF951FBF3F9700278EB9 /* compact_ids_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEAC1FBF3F9700278EB9 /* compact_ids_pass.cpp */; }; + A994BF961FBF3F9700278EB9 /* compact_ids_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEAD1FBF3F9700278EB9 /* compact_ids_pass.h */; }; + A994BF971FBF3F9700278EB9 /* compact_ids_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEAD1FBF3F9700278EB9 /* compact_ids_pass.h */; }; + A994BF981FBF3F9700278EB9 /* constants.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEAE1FBF3F9700278EB9 /* constants.h */; }; + A994BF991FBF3F9700278EB9 /* constants.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEAE1FBF3F9700278EB9 /* constants.h */; }; + A994BF9A1FBF3F9700278EB9 /* dead_branch_elim_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEAF1FBF3F9700278EB9 /* dead_branch_elim_pass.cpp */; }; + A994BF9B1FBF3F9700278EB9 /* dead_branch_elim_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEAF1FBF3F9700278EB9 /* dead_branch_elim_pass.cpp */; }; + A994BF9C1FBF3F9700278EB9 /* dead_branch_elim_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEB01FBF3F9700278EB9 /* dead_branch_elim_pass.h */; }; + A994BF9D1FBF3F9700278EB9 /* dead_branch_elim_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEB01FBF3F9700278EB9 /* dead_branch_elim_pass.h */; }; + A994BF9E1FBF3F9700278EB9 /* dead_variable_elimination.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEB11FBF3F9700278EB9 /* dead_variable_elimination.cpp */; }; + A994BF9F1FBF3F9700278EB9 /* dead_variable_elimination.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEB11FBF3F9700278EB9 /* dead_variable_elimination.cpp */; }; + A994BFA01FBF3F9700278EB9 /* dead_variable_elimination.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEB21FBF3F9700278EB9 /* dead_variable_elimination.h */; }; + A994BFA11FBF3F9700278EB9 /* dead_variable_elimination.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEB21FBF3F9700278EB9 /* dead_variable_elimination.h */; }; + A994BFA21FBF3F9700278EB9 /* decoration_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEB31FBF3F9700278EB9 /* decoration_manager.cpp */; }; + A994BFA31FBF3F9700278EB9 /* decoration_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEB31FBF3F9700278EB9 /* decoration_manager.cpp */; }; + A994BFA41FBF3F9700278EB9 /* decoration_manager.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEB41FBF3F9700278EB9 /* decoration_manager.h */; }; + A994BFA51FBF3F9700278EB9 /* decoration_manager.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEB41FBF3F9700278EB9 /* decoration_manager.h */; }; + A994BFA61FBF3F9700278EB9 /* def_use_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEB51FBF3F9700278EB9 /* def_use_manager.cpp */; }; + A994BFA71FBF3F9700278EB9 /* def_use_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEB51FBF3F9700278EB9 /* def_use_manager.cpp */; }; + A994BFA81FBF3F9700278EB9 /* def_use_manager.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEB61FBF3F9700278EB9 /* def_use_manager.h */; }; + A994BFA91FBF3F9700278EB9 /* def_use_manager.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEB61FBF3F9700278EB9 /* def_use_manager.h */; }; + A994BFAA1FBF3F9700278EB9 /* eliminate_dead_constant_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEB71FBF3F9700278EB9 /* eliminate_dead_constant_pass.cpp */; }; + A994BFAB1FBF3F9700278EB9 /* eliminate_dead_constant_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEB71FBF3F9700278EB9 /* eliminate_dead_constant_pass.cpp */; }; + A994BFAC1FBF3F9700278EB9 /* eliminate_dead_constant_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEB81FBF3F9700278EB9 /* eliminate_dead_constant_pass.h */; }; + A994BFAD1FBF3F9700278EB9 /* eliminate_dead_constant_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEB81FBF3F9700278EB9 /* eliminate_dead_constant_pass.h */; }; + A994BFAE1FBF3F9700278EB9 /* eliminate_dead_functions_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEB91FBF3F9700278EB9 /* eliminate_dead_functions_pass.cpp */; }; + A994BFAF1FBF3F9700278EB9 /* eliminate_dead_functions_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEB91FBF3F9700278EB9 /* eliminate_dead_functions_pass.cpp */; }; + A994BFB01FBF3F9700278EB9 /* eliminate_dead_functions_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEBA1FBF3F9700278EB9 /* eliminate_dead_functions_pass.h */; }; + A994BFB11FBF3F9700278EB9 /* eliminate_dead_functions_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEBA1FBF3F9700278EB9 /* eliminate_dead_functions_pass.h */; }; + A994BFB21FBF3F9700278EB9 /* flatten_decoration_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEBB1FBF3F9700278EB9 /* flatten_decoration_pass.cpp */; }; + A994BFB31FBF3F9700278EB9 /* flatten_decoration_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEBB1FBF3F9700278EB9 /* flatten_decoration_pass.cpp */; }; + A994BFB41FBF3F9700278EB9 /* flatten_decoration_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEBC1FBF3F9700278EB9 /* flatten_decoration_pass.h */; }; + A994BFB51FBF3F9700278EB9 /* flatten_decoration_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEBC1FBF3F9700278EB9 /* flatten_decoration_pass.h */; }; + A994BFB61FBF3F9700278EB9 /* fold.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEBD1FBF3F9700278EB9 /* fold.cpp */; }; + A994BFB71FBF3F9700278EB9 /* fold.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEBD1FBF3F9700278EB9 /* fold.cpp */; }; + A994BFB81FBF3F9700278EB9 /* fold.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEBE1FBF3F9700278EB9 /* fold.h */; }; + A994BFB91FBF3F9700278EB9 /* fold.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEBE1FBF3F9700278EB9 /* fold.h */; }; + A994BFBA1FBF3F9700278EB9 /* fold_spec_constant_op_and_composite_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEBF1FBF3F9700278EB9 /* fold_spec_constant_op_and_composite_pass.cpp */; }; + A994BFBB1FBF3F9700278EB9 /* fold_spec_constant_op_and_composite_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEBF1FBF3F9700278EB9 /* fold_spec_constant_op_and_composite_pass.cpp */; }; + A994BFBC1FBF3F9700278EB9 /* fold_spec_constant_op_and_composite_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEC01FBF3F9700278EB9 /* fold_spec_constant_op_and_composite_pass.h */; }; + A994BFBD1FBF3F9700278EB9 /* fold_spec_constant_op_and_composite_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEC01FBF3F9700278EB9 /* fold_spec_constant_op_and_composite_pass.h */; }; + A994BFBE1FBF3F9700278EB9 /* freeze_spec_constant_value_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEC11FBF3F9700278EB9 /* freeze_spec_constant_value_pass.cpp */; }; + A994BFBF1FBF3F9700278EB9 /* freeze_spec_constant_value_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEC11FBF3F9700278EB9 /* freeze_spec_constant_value_pass.cpp */; }; + A994BFC01FBF3F9700278EB9 /* freeze_spec_constant_value_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEC21FBF3F9700278EB9 /* freeze_spec_constant_value_pass.h */; }; + A994BFC11FBF3F9700278EB9 /* freeze_spec_constant_value_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEC21FBF3F9700278EB9 /* freeze_spec_constant_value_pass.h */; }; + A994BFC21FBF3F9700278EB9 /* function.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEC31FBF3F9700278EB9 /* function.cpp */; }; + A994BFC31FBF3F9700278EB9 /* function.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEC31FBF3F9700278EB9 /* function.cpp */; }; + A994BFC41FBF3F9700278EB9 /* function.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEC41FBF3F9700278EB9 /* function.h */; }; + A994BFC51FBF3F9700278EB9 /* function.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEC41FBF3F9700278EB9 /* function.h */; }; + A994BFC61FBF3F9700278EB9 /* inline_exhaustive_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEC51FBF3F9700278EB9 /* inline_exhaustive_pass.cpp */; }; + A994BFC71FBF3F9700278EB9 /* inline_exhaustive_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEC51FBF3F9700278EB9 /* inline_exhaustive_pass.cpp */; }; + A994BFC81FBF3F9700278EB9 /* inline_exhaustive_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEC61FBF3F9700278EB9 /* inline_exhaustive_pass.h */; }; + A994BFC91FBF3F9700278EB9 /* inline_exhaustive_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEC61FBF3F9700278EB9 /* inline_exhaustive_pass.h */; }; + A994BFCA1FBF3F9700278EB9 /* inline_opaque_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEC71FBF3F9700278EB9 /* inline_opaque_pass.cpp */; }; + A994BFCB1FBF3F9700278EB9 /* inline_opaque_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEC71FBF3F9700278EB9 /* inline_opaque_pass.cpp */; }; + A994BFCC1FBF3F9700278EB9 /* inline_opaque_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEC81FBF3F9700278EB9 /* inline_opaque_pass.h */; }; + A994BFCD1FBF3F9700278EB9 /* inline_opaque_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEC81FBF3F9700278EB9 /* inline_opaque_pass.h */; }; + A994BFCE1FBF3F9700278EB9 /* inline_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEC91FBF3F9700278EB9 /* inline_pass.cpp */; }; + A994BFCF1FBF3F9700278EB9 /* inline_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEC91FBF3F9700278EB9 /* inline_pass.cpp */; }; + A994BFD01FBF3F9700278EB9 /* inline_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BECA1FBF3F9700278EB9 /* inline_pass.h */; }; + A994BFD11FBF3F9700278EB9 /* inline_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BECA1FBF3F9700278EB9 /* inline_pass.h */; }; + A994BFD21FBF3F9700278EB9 /* insert_extract_elim.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BECB1FBF3F9700278EB9 /* insert_extract_elim.cpp */; }; + A994BFD31FBF3F9700278EB9 /* insert_extract_elim.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BECB1FBF3F9700278EB9 /* insert_extract_elim.cpp */; }; + A994BFD41FBF3F9700278EB9 /* insert_extract_elim.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BECC1FBF3F9700278EB9 /* insert_extract_elim.h */; }; + A994BFD51FBF3F9700278EB9 /* insert_extract_elim.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BECC1FBF3F9700278EB9 /* insert_extract_elim.h */; }; + A994BFD61FBF3F9700278EB9 /* instruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BECD1FBF3F9700278EB9 /* instruction.cpp */; }; + A994BFD71FBF3F9700278EB9 /* instruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BECD1FBF3F9700278EB9 /* instruction.cpp */; }; + A994BFD81FBF3F9700278EB9 /* instruction.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BECE1FBF3F9700278EB9 /* instruction.h */; }; + A994BFD91FBF3F9700278EB9 /* instruction.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BECE1FBF3F9700278EB9 /* instruction.h */; }; + A994BFDA1FBF3F9700278EB9 /* instruction_list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BECF1FBF3F9700278EB9 /* instruction_list.cpp */; }; + A994BFDB1FBF3F9700278EB9 /* instruction_list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BECF1FBF3F9700278EB9 /* instruction_list.cpp */; }; + A994BFDC1FBF3F9700278EB9 /* instruction_list.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BED01FBF3F9700278EB9 /* instruction_list.h */; }; + A994BFDD1FBF3F9700278EB9 /* instruction_list.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BED01FBF3F9700278EB9 /* instruction_list.h */; }; + A994BFDE1FBF3F9700278EB9 /* ir_context.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BED11FBF3F9700278EB9 /* ir_context.cpp */; }; + A994BFDF1FBF3F9700278EB9 /* ir_context.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BED11FBF3F9700278EB9 /* ir_context.cpp */; }; + A994BFE01FBF3F9700278EB9 /* ir_context.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BED21FBF3F9700278EB9 /* ir_context.h */; }; + A994BFE11FBF3F9700278EB9 /* ir_context.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BED21FBF3F9700278EB9 /* ir_context.h */; }; + A994BFE21FBF3F9700278EB9 /* ir_loader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BED31FBF3F9700278EB9 /* ir_loader.cpp */; }; + A994BFE31FBF3F9700278EB9 /* ir_loader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BED31FBF3F9700278EB9 /* ir_loader.cpp */; }; + A994BFE41FBF3F9700278EB9 /* ir_loader.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BED41FBF3F9700278EB9 /* ir_loader.h */; }; + A994BFE51FBF3F9700278EB9 /* ir_loader.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BED41FBF3F9700278EB9 /* ir_loader.h */; }; + A994BFE61FBF3F9700278EB9 /* iterator.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BED51FBF3F9700278EB9 /* iterator.h */; }; + A994BFE71FBF3F9700278EB9 /* iterator.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BED51FBF3F9700278EB9 /* iterator.h */; }; + A994BFE81FBF3F9700278EB9 /* local_access_chain_convert_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BED61FBF3F9700278EB9 /* local_access_chain_convert_pass.cpp */; }; + A994BFE91FBF3F9700278EB9 /* local_access_chain_convert_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BED61FBF3F9700278EB9 /* local_access_chain_convert_pass.cpp */; }; + A994BFEA1FBF3F9700278EB9 /* local_access_chain_convert_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BED71FBF3F9700278EB9 /* local_access_chain_convert_pass.h */; }; + A994BFEB1FBF3F9700278EB9 /* local_access_chain_convert_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BED71FBF3F9700278EB9 /* local_access_chain_convert_pass.h */; }; + A994BFEC1FBF3F9700278EB9 /* local_single_block_elim_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BED81FBF3F9700278EB9 /* local_single_block_elim_pass.cpp */; }; + A994BFED1FBF3F9700278EB9 /* local_single_block_elim_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BED81FBF3F9700278EB9 /* local_single_block_elim_pass.cpp */; }; + A994BFEE1FBF3F9700278EB9 /* local_single_block_elim_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BED91FBF3F9700278EB9 /* local_single_block_elim_pass.h */; }; + A994BFEF1FBF3F9700278EB9 /* local_single_block_elim_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BED91FBF3F9700278EB9 /* local_single_block_elim_pass.h */; }; + A994BFF01FBF3F9700278EB9 /* local_single_store_elim_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEDA1FBF3F9700278EB9 /* local_single_store_elim_pass.cpp */; }; + A994BFF11FBF3F9700278EB9 /* local_single_store_elim_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEDA1FBF3F9700278EB9 /* local_single_store_elim_pass.cpp */; }; + A994BFF21FBF3F9700278EB9 /* local_single_store_elim_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEDB1FBF3F9700278EB9 /* local_single_store_elim_pass.h */; }; + A994BFF31FBF3F9700278EB9 /* local_single_store_elim_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEDB1FBF3F9700278EB9 /* local_single_store_elim_pass.h */; }; + A994BFF41FBF3F9700278EB9 /* local_ssa_elim_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEDC1FBF3F9700278EB9 /* local_ssa_elim_pass.cpp */; }; + A994BFF51FBF3F9700278EB9 /* local_ssa_elim_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEDC1FBF3F9700278EB9 /* local_ssa_elim_pass.cpp */; }; + A994BFF61FBF3F9700278EB9 /* local_ssa_elim_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEDD1FBF3F9700278EB9 /* local_ssa_elim_pass.h */; }; + A994BFF71FBF3F9700278EB9 /* local_ssa_elim_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEDD1FBF3F9700278EB9 /* local_ssa_elim_pass.h */; }; + A994BFF81FBF3F9700278EB9 /* log.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEDE1FBF3F9700278EB9 /* log.h */; }; + A994BFF91FBF3F9700278EB9 /* log.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEDE1FBF3F9700278EB9 /* log.h */; }; + A994BFFA1FBF3F9700278EB9 /* make_unique.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEDF1FBF3F9700278EB9 /* make_unique.h */; }; + A994BFFB1FBF3F9700278EB9 /* make_unique.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEDF1FBF3F9700278EB9 /* make_unique.h */; }; + A994BFFC1FBF3F9700278EB9 /* mem_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEE01FBF3F9700278EB9 /* mem_pass.cpp */; }; + A994BFFD1FBF3F9700278EB9 /* mem_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEE01FBF3F9700278EB9 /* mem_pass.cpp */; }; + A994BFFE1FBF3F9700278EB9 /* mem_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEE11FBF3F9700278EB9 /* mem_pass.h */; }; + A994BFFF1FBF3F9700278EB9 /* mem_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEE11FBF3F9700278EB9 /* mem_pass.h */; }; + A994C0001FBF3F9700278EB9 /* merge_return_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEE21FBF3F9700278EB9 /* merge_return_pass.cpp */; }; + A994C0011FBF3F9700278EB9 /* merge_return_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEE21FBF3F9700278EB9 /* merge_return_pass.cpp */; }; + A994C0021FBF3F9700278EB9 /* merge_return_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEE31FBF3F9700278EB9 /* merge_return_pass.h */; }; + A994C0031FBF3F9700278EB9 /* merge_return_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEE31FBF3F9700278EB9 /* merge_return_pass.h */; }; + A994C0041FBF3F9700278EB9 /* module.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEE41FBF3F9700278EB9 /* module.cpp */; }; + A994C0051FBF3F9700278EB9 /* module.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEE41FBF3F9700278EB9 /* module.cpp */; }; + A994C0061FBF3F9700278EB9 /* module.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEE51FBF3F9700278EB9 /* module.h */; }; + A994C0071FBF3F9700278EB9 /* module.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEE51FBF3F9700278EB9 /* module.h */; }; + A994C0081FBF3F9700278EB9 /* null_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEE61FBF3F9700278EB9 /* null_pass.h */; }; + A994C0091FBF3F9700278EB9 /* null_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEE61FBF3F9700278EB9 /* null_pass.h */; }; + A994C00A1FBF3F9700278EB9 /* optimizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEE71FBF3F9700278EB9 /* optimizer.cpp */; }; + A994C00B1FBF3F9700278EB9 /* optimizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEE71FBF3F9700278EB9 /* optimizer.cpp */; }; + A994C00C1FBF3F9700278EB9 /* pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEE81FBF3F9700278EB9 /* pass.cpp */; }; + A994C00D1FBF3F9700278EB9 /* pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEE81FBF3F9700278EB9 /* pass.cpp */; }; + A994C00E1FBF3F9700278EB9 /* pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEE91FBF3F9700278EB9 /* pass.h */; }; + A994C00F1FBF3F9700278EB9 /* pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEE91FBF3F9700278EB9 /* pass.h */; }; + A994C0101FBF3F9700278EB9 /* pass_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEEA1FBF3F9700278EB9 /* pass_manager.cpp */; }; + A994C0111FBF3F9700278EB9 /* pass_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEEA1FBF3F9700278EB9 /* pass_manager.cpp */; }; + A994C0121FBF3F9700278EB9 /* pass_manager.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEEB1FBF3F9700278EB9 /* pass_manager.h */; }; + A994C0131FBF3F9700278EB9 /* pass_manager.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEEB1FBF3F9700278EB9 /* pass_manager.h */; }; + A994C0141FBF3F9700278EB9 /* passes.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEEC1FBF3F9700278EB9 /* passes.h */; }; + A994C0151FBF3F9700278EB9 /* passes.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEEC1FBF3F9700278EB9 /* passes.h */; }; + A994C0161FBF3F9700278EB9 /* reflect.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEED1FBF3F9700278EB9 /* reflect.h */; }; + A994C0171FBF3F9700278EB9 /* reflect.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEED1FBF3F9700278EB9 /* reflect.h */; }; + A994C0181FBF3F9700278EB9 /* remove_duplicates_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEEE1FBF3F9700278EB9 /* remove_duplicates_pass.cpp */; }; + A994C0191FBF3F9700278EB9 /* remove_duplicates_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEEE1FBF3F9700278EB9 /* remove_duplicates_pass.cpp */; }; + A994C01A1FBF3F9700278EB9 /* remove_duplicates_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEEF1FBF3F9700278EB9 /* remove_duplicates_pass.h */; }; + A994C01B1FBF3F9700278EB9 /* remove_duplicates_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEEF1FBF3F9700278EB9 /* remove_duplicates_pass.h */; }; + A994C01C1FBF3F9700278EB9 /* set_spec_constant_default_value_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEF01FBF3F9700278EB9 /* set_spec_constant_default_value_pass.cpp */; }; + A994C01D1FBF3F9700278EB9 /* set_spec_constant_default_value_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEF01FBF3F9700278EB9 /* set_spec_constant_default_value_pass.cpp */; }; + A994C01E1FBF3F9700278EB9 /* set_spec_constant_default_value_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEF11FBF3F9700278EB9 /* set_spec_constant_default_value_pass.h */; }; + A994C01F1FBF3F9700278EB9 /* set_spec_constant_default_value_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEF11FBF3F9700278EB9 /* set_spec_constant_default_value_pass.h */; }; + A994C0201FBF3F9700278EB9 /* strength_reduction_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEF21FBF3F9700278EB9 /* strength_reduction_pass.cpp */; }; + A994C0211FBF3F9700278EB9 /* strength_reduction_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEF21FBF3F9700278EB9 /* strength_reduction_pass.cpp */; }; + A994C0221FBF3F9700278EB9 /* strength_reduction_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEF31FBF3F9700278EB9 /* strength_reduction_pass.h */; }; + A994C0231FBF3F9700278EB9 /* strength_reduction_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEF31FBF3F9700278EB9 /* strength_reduction_pass.h */; }; + A994C0241FBF3F9700278EB9 /* strip_debug_info_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEF41FBF3F9700278EB9 /* strip_debug_info_pass.cpp */; }; + A994C0251FBF3F9700278EB9 /* strip_debug_info_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEF41FBF3F9700278EB9 /* strip_debug_info_pass.cpp */; }; + A994C0261FBF3F9700278EB9 /* strip_debug_info_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEF51FBF3F9700278EB9 /* strip_debug_info_pass.h */; }; + A994C0271FBF3F9700278EB9 /* strip_debug_info_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEF51FBF3F9700278EB9 /* strip_debug_info_pass.h */; }; + A994C0281FBF3F9700278EB9 /* type_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEF61FBF3F9700278EB9 /* type_manager.cpp */; }; + A994C0291FBF3F9700278EB9 /* type_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEF61FBF3F9700278EB9 /* type_manager.cpp */; }; + A994C02A1FBF3F9700278EB9 /* type_manager.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEF71FBF3F9700278EB9 /* type_manager.h */; }; + A994C02B1FBF3F9700278EB9 /* type_manager.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEF71FBF3F9700278EB9 /* type_manager.h */; }; + A994C02C1FBF3F9700278EB9 /* types.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEF81FBF3F9700278EB9 /* types.cpp */; }; + A994C02D1FBF3F9700278EB9 /* types.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEF81FBF3F9700278EB9 /* types.cpp */; }; + A994C02E1FBF3F9700278EB9 /* types.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEF91FBF3F9700278EB9 /* types.h */; }; + A994C02F1FBF3F9700278EB9 /* types.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEF91FBF3F9700278EB9 /* types.h */; }; + A994C0301FBF3F9700278EB9 /* unify_const_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEFA1FBF3F9700278EB9 /* unify_const_pass.cpp */; }; + A994C0311FBF3F9700278EB9 /* unify_const_pass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEFA1FBF3F9700278EB9 /* unify_const_pass.cpp */; }; + A994C0321FBF3F9700278EB9 /* unify_const_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEFB1FBF3F9700278EB9 /* unify_const_pass.h */; }; + A994C0331FBF3F9700278EB9 /* unify_const_pass.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEFB1FBF3F9700278EB9 /* unify_const_pass.h */; }; + A994C0341FBF3F9700278EB9 /* parsed_operand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEFC1FBF3F9700278EB9 /* parsed_operand.cpp */; }; + A994C0351FBF3F9700278EB9 /* parsed_operand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEFC1FBF3F9700278EB9 /* parsed_operand.cpp */; }; + A994C0361FBF3F9700278EB9 /* parsed_operand.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEFD1FBF3F9700278EB9 /* parsed_operand.h */; }; + A994C0371FBF3F9700278EB9 /* parsed_operand.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEFD1FBF3F9700278EB9 /* parsed_operand.h */; }; + A994C0381FBF3F9700278EB9 /* print.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEFE1FBF3F9700278EB9 /* print.cpp */; }; + A994C0391FBF3F9700278EB9 /* print.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BEFE1FBF3F9700278EB9 /* print.cpp */; }; + A994C03A1FBF3F9700278EB9 /* print.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEFF1FBF3F9700278EB9 /* print.h */; }; + A994C03B1FBF3F9700278EB9 /* print.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BEFF1FBF3F9700278EB9 /* print.h */; }; + A994C03C1FBF3F9700278EB9 /* software_version.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF001FBF3F9700278EB9 /* software_version.cpp */; }; + A994C03D1FBF3F9700278EB9 /* software_version.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF001FBF3F9700278EB9 /* software_version.cpp */; }; + A994C03E1FBF3F9700278EB9 /* spirv_constant.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF011FBF3F9700278EB9 /* spirv_constant.h */; }; + A994C03F1FBF3F9700278EB9 /* spirv_constant.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF011FBF3F9700278EB9 /* spirv_constant.h */; }; + A994C0401FBF3F9700278EB9 /* spirv_definition.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF021FBF3F9700278EB9 /* spirv_definition.h */; }; + A994C0411FBF3F9700278EB9 /* spirv_definition.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF021FBF3F9700278EB9 /* spirv_definition.h */; }; + A994C0421FBF3F9700278EB9 /* spirv_endian.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF031FBF3F9700278EB9 /* spirv_endian.cpp */; }; + A994C0431FBF3F9700278EB9 /* spirv_endian.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF031FBF3F9700278EB9 /* spirv_endian.cpp */; }; + A994C0441FBF3F9700278EB9 /* spirv_endian.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF041FBF3F9700278EB9 /* spirv_endian.h */; }; + A994C0451FBF3F9700278EB9 /* spirv_endian.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF041FBF3F9700278EB9 /* spirv_endian.h */; }; + A994C0461FBF3F9700278EB9 /* spirv_stats.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF051FBF3F9700278EB9 /* spirv_stats.cpp */; }; + A994C0471FBF3F9700278EB9 /* spirv_stats.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF051FBF3F9700278EB9 /* spirv_stats.cpp */; }; + A994C0481FBF3F9700278EB9 /* spirv_stats.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF061FBF3F9700278EB9 /* spirv_stats.h */; }; + A994C0491FBF3F9700278EB9 /* spirv_stats.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF061FBF3F9700278EB9 /* spirv_stats.h */; }; + A994C04A1FBF3F9700278EB9 /* spirv_target_env.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF071FBF3F9700278EB9 /* spirv_target_env.cpp */; }; + A994C04B1FBF3F9700278EB9 /* spirv_target_env.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF071FBF3F9700278EB9 /* spirv_target_env.cpp */; }; + A994C04C1FBF3F9700278EB9 /* spirv_target_env.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF081FBF3F9700278EB9 /* spirv_target_env.h */; }; + A994C04D1FBF3F9700278EB9 /* spirv_target_env.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF081FBF3F9700278EB9 /* spirv_target_env.h */; }; + A994C04E1FBF3F9700278EB9 /* spirv_validator_options.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF091FBF3F9700278EB9 /* spirv_validator_options.cpp */; }; + A994C04F1FBF3F9700278EB9 /* spirv_validator_options.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF091FBF3F9700278EB9 /* spirv_validator_options.cpp */; }; + A994C0501FBF3F9700278EB9 /* spirv_validator_options.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF0A1FBF3F9700278EB9 /* spirv_validator_options.h */; }; + A994C0511FBF3F9700278EB9 /* spirv_validator_options.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF0A1FBF3F9700278EB9 /* spirv_validator_options.h */; }; + A994C0521FBF3F9700278EB9 /* table.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF0B1FBF3F9700278EB9 /* table.cpp */; }; + A994C0531FBF3F9700278EB9 /* table.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF0B1FBF3F9700278EB9 /* table.cpp */; }; + A994C0541FBF3F9700278EB9 /* table.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF0C1FBF3F9700278EB9 /* table.h */; }; + A994C0551FBF3F9700278EB9 /* table.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF0C1FBF3F9700278EB9 /* table.h */; }; + A994C0561FBF3F9700278EB9 /* text.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF0D1FBF3F9700278EB9 /* text.cpp */; }; + A994C0571FBF3F9700278EB9 /* text.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF0D1FBF3F9700278EB9 /* text.cpp */; }; + A994C0581FBF3F9700278EB9 /* text.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF0E1FBF3F9700278EB9 /* text.h */; }; + A994C0591FBF3F9700278EB9 /* text.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF0E1FBF3F9700278EB9 /* text.h */; }; + A994C05A1FBF3F9700278EB9 /* text_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF0F1FBF3F9700278EB9 /* text_handler.cpp */; }; + A994C05B1FBF3F9700278EB9 /* text_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF0F1FBF3F9700278EB9 /* text_handler.cpp */; }; + A994C05C1FBF3F9700278EB9 /* text_handler.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF101FBF3F9700278EB9 /* text_handler.h */; }; + A994C05D1FBF3F9700278EB9 /* text_handler.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF101FBF3F9700278EB9 /* text_handler.h */; }; + A994C05E1FBF3F9700278EB9 /* bit_stream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF121FBF3F9700278EB9 /* bit_stream.cpp */; }; + A994C05F1FBF3F9700278EB9 /* bit_stream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF121FBF3F9700278EB9 /* bit_stream.cpp */; }; + A994C0601FBF3F9700278EB9 /* bit_stream.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF131FBF3F9700278EB9 /* bit_stream.h */; }; + A994C0611FBF3F9700278EB9 /* bit_stream.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF131FBF3F9700278EB9 /* bit_stream.h */; }; + A994C0621FBF3F9700278EB9 /* bitutils.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF141FBF3F9700278EB9 /* bitutils.h */; }; + A994C0631FBF3F9700278EB9 /* bitutils.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF141FBF3F9700278EB9 /* bitutils.h */; }; + A994C0641FBF3F9700278EB9 /* hex_float.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF151FBF3F9700278EB9 /* hex_float.h */; }; + A994C0651FBF3F9700278EB9 /* hex_float.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF151FBF3F9700278EB9 /* hex_float.h */; }; + A994C0661FBF3F9700278EB9 /* huffman_codec.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF161FBF3F9700278EB9 /* huffman_codec.h */; }; + A994C0671FBF3F9700278EB9 /* huffman_codec.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF161FBF3F9700278EB9 /* huffman_codec.h */; }; + A994C0681FBF3F9700278EB9 /* ilist.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF171FBF3F9700278EB9 /* ilist.h */; }; + A994C0691FBF3F9700278EB9 /* ilist.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF171FBF3F9700278EB9 /* ilist.h */; }; + A994C06A1FBF3F9700278EB9 /* ilist_node.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF181FBF3F9700278EB9 /* ilist_node.h */; }; + A994C06B1FBF3F9700278EB9 /* ilist_node.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF181FBF3F9700278EB9 /* ilist_node.h */; }; + A994C06C1FBF3F9700278EB9 /* move_to_front.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF191FBF3F9700278EB9 /* move_to_front.h */; }; + A994C06D1FBF3F9700278EB9 /* move_to_front.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF191FBF3F9700278EB9 /* move_to_front.h */; }; + A994C06E1FBF3F9700278EB9 /* parse_number.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF1A1FBF3F9700278EB9 /* parse_number.cpp */; }; + A994C06F1FBF3F9700278EB9 /* parse_number.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF1A1FBF3F9700278EB9 /* parse_number.cpp */; }; + A994C0701FBF3F9700278EB9 /* parse_number.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF1B1FBF3F9700278EB9 /* parse_number.h */; }; + A994C0711FBF3F9700278EB9 /* parse_number.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF1B1FBF3F9700278EB9 /* parse_number.h */; }; + A994C0721FBF3F9700278EB9 /* string_utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF1C1FBF3F9700278EB9 /* string_utils.cpp */; }; + A994C0731FBF3F9700278EB9 /* string_utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF1C1FBF3F9700278EB9 /* string_utils.cpp */; }; + A994C0741FBF3F9700278EB9 /* string_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF1D1FBF3F9700278EB9 /* string_utils.h */; }; + A994C0751FBF3F9700278EB9 /* string_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF1D1FBF3F9700278EB9 /* string_utils.h */; }; + A994C0761FBF3F9700278EB9 /* basic_block.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF1F1FBF3F9700278EB9 /* basic_block.cpp */; }; + A994C0771FBF3F9700278EB9 /* basic_block.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF1F1FBF3F9700278EB9 /* basic_block.cpp */; }; + A994C0781FBF3F9700278EB9 /* basic_block.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF201FBF3F9700278EB9 /* basic_block.h */; }; + A994C0791FBF3F9700278EB9 /* basic_block.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF201FBF3F9700278EB9 /* basic_block.h */; }; + A994C07A1FBF3F9700278EB9 /* construct.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF211FBF3F9700278EB9 /* construct.cpp */; }; + A994C07B1FBF3F9700278EB9 /* construct.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF211FBF3F9700278EB9 /* construct.cpp */; }; + A994C07C1FBF3F9700278EB9 /* construct.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF221FBF3F9700278EB9 /* construct.h */; }; + A994C07D1FBF3F9700278EB9 /* construct.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF221FBF3F9700278EB9 /* construct.h */; }; + A994C07E1FBF3F9700278EB9 /* decoration.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF231FBF3F9700278EB9 /* decoration.h */; }; + A994C07F1FBF3F9700278EB9 /* decoration.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF231FBF3F9700278EB9 /* decoration.h */; }; + A994C0801FBF3F9700278EB9 /* function.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF241FBF3F9700278EB9 /* function.cpp */; }; + A994C0811FBF3F9700278EB9 /* function.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF241FBF3F9700278EB9 /* function.cpp */; }; + A994C0821FBF3F9700278EB9 /* function.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF251FBF3F9700278EB9 /* function.h */; }; + A994C0831FBF3F9700278EB9 /* function.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF251FBF3F9700278EB9 /* function.h */; }; + A994C0841FBF3F9700278EB9 /* instruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF261FBF3F9700278EB9 /* instruction.cpp */; }; + A994C0851FBF3F9700278EB9 /* instruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF261FBF3F9700278EB9 /* instruction.cpp */; }; + A994C0861FBF3F9700278EB9 /* instruction.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF271FBF3F9700278EB9 /* instruction.h */; }; + A994C0871FBF3F9700278EB9 /* instruction.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF271FBF3F9700278EB9 /* instruction.h */; }; + A994C0881FBF3F9700278EB9 /* validation_state.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF281FBF3F9700278EB9 /* validation_state.cpp */; }; + A994C0891FBF3F9700278EB9 /* validation_state.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF281FBF3F9700278EB9 /* validation_state.cpp */; }; + A994C08A1FBF3F9700278EB9 /* validation_state.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF291FBF3F9700278EB9 /* validation_state.h */; }; + A994C08B1FBF3F9700278EB9 /* validation_state.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF291FBF3F9700278EB9 /* validation_state.h */; }; + A994C08C1FBF3F9700278EB9 /* validate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF2A1FBF3F9700278EB9 /* validate.cpp */; }; + A994C08D1FBF3F9700278EB9 /* validate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF2A1FBF3F9700278EB9 /* validate.cpp */; }; + A994C08E1FBF3F9700278EB9 /* validate.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF2B1FBF3F9700278EB9 /* validate.h */; }; + A994C08F1FBF3F9700278EB9 /* validate.h in Headers */ = {isa = PBXBuildFile; fileRef = A994BF2B1FBF3F9700278EB9 /* validate.h */; }; + A994C0901FBF3F9700278EB9 /* validate_arithmetics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF2C1FBF3F9700278EB9 /* validate_arithmetics.cpp */; }; + A994C0911FBF3F9700278EB9 /* validate_arithmetics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF2C1FBF3F9700278EB9 /* validate_arithmetics.cpp */; }; + A994C0921FBF3F9700278EB9 /* validate_bitwise.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF2D1FBF3F9700278EB9 /* validate_bitwise.cpp */; }; + A994C0931FBF3F9700278EB9 /* validate_bitwise.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF2D1FBF3F9700278EB9 /* validate_bitwise.cpp */; }; + A994C0941FBF3F9700278EB9 /* validate_capability.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF2E1FBF3F9700278EB9 /* validate_capability.cpp */; }; + A994C0951FBF3F9700278EB9 /* validate_capability.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF2E1FBF3F9700278EB9 /* validate_capability.cpp */; }; + A994C0961FBF3F9700278EB9 /* validate_cfg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF2F1FBF3F9700278EB9 /* validate_cfg.cpp */; }; + A994C0971FBF3F9700278EB9 /* validate_cfg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF2F1FBF3F9700278EB9 /* validate_cfg.cpp */; }; + A994C0981FBF3F9700278EB9 /* validate_conversion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF301FBF3F9700278EB9 /* validate_conversion.cpp */; }; + A994C0991FBF3F9700278EB9 /* validate_conversion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF301FBF3F9700278EB9 /* validate_conversion.cpp */; }; + A994C09A1FBF3F9700278EB9 /* validate_datarules.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF311FBF3F9700278EB9 /* validate_datarules.cpp */; }; + A994C09B1FBF3F9700278EB9 /* validate_datarules.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF311FBF3F9700278EB9 /* validate_datarules.cpp */; }; + A994C09C1FBF3F9700278EB9 /* validate_decorations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF321FBF3F9700278EB9 /* validate_decorations.cpp */; }; + A994C09D1FBF3F9700278EB9 /* validate_decorations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF321FBF3F9700278EB9 /* validate_decorations.cpp */; }; + A994C09E1FBF3F9700278EB9 /* validate_id.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF331FBF3F9700278EB9 /* validate_id.cpp */; }; + A994C09F1FBF3F9700278EB9 /* validate_id.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF331FBF3F9700278EB9 /* validate_id.cpp */; }; + A994C0A01FBF3F9700278EB9 /* validate_instruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF341FBF3F9700278EB9 /* validate_instruction.cpp */; }; + A994C0A11FBF3F9700278EB9 /* validate_instruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF341FBF3F9700278EB9 /* validate_instruction.cpp */; }; + A994C0A21FBF3F9700278EB9 /* validate_layout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF351FBF3F9700278EB9 /* validate_layout.cpp */; }; + A994C0A31FBF3F9700278EB9 /* validate_layout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF351FBF3F9700278EB9 /* validate_layout.cpp */; }; + A994C0A41FBF3F9700278EB9 /* validate_logicals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF361FBF3F9700278EB9 /* validate_logicals.cpp */; }; + A994C0A51FBF3F9700278EB9 /* validate_logicals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF361FBF3F9700278EB9 /* validate_logicals.cpp */; }; + A994C0A61FBF3F9700278EB9 /* validate_type_unique.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF371FBF3F9700278EB9 /* validate_type_unique.cpp */; }; + A994C0A71FBF3F9700278EB9 /* validate_type_unique.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A994BF371FBF3F9700278EB9 /* validate_type_unique.cpp */; }; + A9AB19971CB5B5A80001E7F9 /* spirv_common.hpp in Headers */ = {isa = PBXBuildFile; fileRef = A9AB19901CB5B5A80001E7F9 /* spirv_common.hpp */; }; + A9AB19981CB5B5A80001E7F9 /* spirv_common.hpp in Headers */ = {isa = PBXBuildFile; fileRef = A9AB19901CB5B5A80001E7F9 /* spirv_common.hpp */; }; + A9AB19991CB5B5A80001E7F9 /* spirv_cross.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9AB19911CB5B5A80001E7F9 /* spirv_cross.cpp */; }; + A9AB199A1CB5B5A80001E7F9 /* spirv_cross.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9AB19911CB5B5A80001E7F9 /* spirv_cross.cpp */; }; + A9AB199B1CB5B5A80001E7F9 /* spirv_cross.hpp in Headers */ = {isa = PBXBuildFile; fileRef = A9AB19921CB5B5A80001E7F9 /* spirv_cross.hpp */; }; + A9AB199C1CB5B5A80001E7F9 /* spirv_cross.hpp in Headers */ = {isa = PBXBuildFile; fileRef = A9AB19921CB5B5A80001E7F9 /* spirv_cross.hpp */; }; + A9AB199D1CB5B5A80001E7F9 /* spirv_glsl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9AB19931CB5B5A80001E7F9 /* spirv_glsl.cpp */; }; + A9AB199E1CB5B5A80001E7F9 /* spirv_glsl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9AB19931CB5B5A80001E7F9 /* spirv_glsl.cpp */; }; + A9AB199F1CB5B5A80001E7F9 /* spirv_glsl.hpp in Headers */ = {isa = PBXBuildFile; fileRef = A9AB19941CB5B5A80001E7F9 /* spirv_glsl.hpp */; }; + A9AB19A01CB5B5A80001E7F9 /* spirv_glsl.hpp in Headers */ = {isa = PBXBuildFile; fileRef = A9AB19941CB5B5A80001E7F9 /* spirv_glsl.hpp */; }; + A9AB19A11CB5B5A80001E7F9 /* spirv_msl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9AB19951CB5B5A80001E7F9 /* spirv_msl.cpp */; }; + A9AB19A21CB5B5A80001E7F9 /* spirv_msl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9AB19951CB5B5A80001E7F9 /* spirv_msl.cpp */; }; + A9AB19A31CB5B5A80001E7F9 /* spirv_msl.hpp in Headers */ = {isa = PBXBuildFile; fileRef = A9AB19961CB5B5A80001E7F9 /* spirv_msl.hpp */; }; + A9AB19A41CB5B5A80001E7F9 /* spirv_msl.hpp in Headers */ = {isa = PBXBuildFile; fileRef = A9AB19961CB5B5A80001E7F9 /* spirv_msl.hpp */; }; + A9BB09761CEF89B100CCAB22 /* spirv.hpp in Headers */ = {isa = PBXBuildFile; fileRef = A9BB09751CEF89B100CCAB22 /* spirv.hpp */; settings = {ATTRIBUTES = (Public, ); }; }; + A9BB09771CEF89B100CCAB22 /* spirv.hpp in Headers */ = {isa = PBXBuildFile; fileRef = A9BB09751CEF89B100CCAB22 /* spirv.hpp */; settings = {ATTRIBUTES = (Public, ); }; }; + A9D008481EF952E200B49F9D /* CodeGen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D007E71EF952E200B49F9D /* CodeGen.cpp */; }; + A9D008491EF952E200B49F9D /* CodeGen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D007E71EF952E200B49F9D /* CodeGen.cpp */; }; + A9D0084A1EF952E200B49F9D /* Link.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D007E81EF952E200B49F9D /* Link.cpp */; }; + A9D0084B1EF952E200B49F9D /* Link.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D007E81EF952E200B49F9D /* Link.cpp */; }; + A9D0084C1EF952E200B49F9D /* arrays.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007EA1EF952E200B49F9D /* arrays.h */; }; + A9D0084D1EF952E200B49F9D /* arrays.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007EA1EF952E200B49F9D /* arrays.h */; }; + A9D0084E1EF952E200B49F9D /* BaseTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007EB1EF952E200B49F9D /* BaseTypes.h */; }; + A9D0084F1EF952E200B49F9D /* BaseTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007EB1EF952E200B49F9D /* BaseTypes.h */; }; + A9D008501EF952E200B49F9D /* Common.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007EC1EF952E200B49F9D /* Common.h */; }; + A9D008511EF952E200B49F9D /* Common.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007EC1EF952E200B49F9D /* Common.h */; }; + A9D008521EF952E200B49F9D /* ConstantUnion.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007ED1EF952E200B49F9D /* ConstantUnion.h */; }; + A9D008531EF952E200B49F9D /* ConstantUnion.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007ED1EF952E200B49F9D /* ConstantUnion.h */; }; + A9D008541EF952E200B49F9D /* InfoSink.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007EE1EF952E200B49F9D /* InfoSink.h */; }; + A9D008551EF952E200B49F9D /* InfoSink.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007EE1EF952E200B49F9D /* InfoSink.h */; }; + A9D008561EF952E200B49F9D /* InitializeGlobals.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007EF1EF952E200B49F9D /* InitializeGlobals.h */; }; + A9D008571EF952E200B49F9D /* InitializeGlobals.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007EF1EF952E200B49F9D /* InitializeGlobals.h */; }; + A9D008581EF952E200B49F9D /* intermediate.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007F01EF952E200B49F9D /* intermediate.h */; }; + A9D008591EF952E200B49F9D /* intermediate.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007F01EF952E200B49F9D /* intermediate.h */; }; + A9D0085A1EF952E200B49F9D /* PoolAlloc.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007F11EF952E200B49F9D /* PoolAlloc.h */; }; + A9D0085B1EF952E200B49F9D /* PoolAlloc.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007F11EF952E200B49F9D /* PoolAlloc.h */; }; + A9D0085C1EF952E200B49F9D /* ResourceLimits.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007F21EF952E200B49F9D /* ResourceLimits.h */; }; + A9D0085D1EF952E200B49F9D /* ResourceLimits.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007F21EF952E200B49F9D /* ResourceLimits.h */; }; + A9D0085E1EF952E200B49F9D /* revision.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007F31EF952E200B49F9D /* revision.h */; }; + A9D0085F1EF952E200B49F9D /* revision.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007F31EF952E200B49F9D /* revision.h */; }; + A9D008601EF952E200B49F9D /* ShHandle.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007F51EF952E200B49F9D /* ShHandle.h */; }; + A9D008611EF952E200B49F9D /* ShHandle.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007F51EF952E200B49F9D /* ShHandle.h */; }; + A9D008621EF952E200B49F9D /* Types.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007F61EF952E200B49F9D /* Types.h */; }; + A9D008631EF952E200B49F9D /* Types.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007F61EF952E200B49F9D /* Types.h */; }; + A9D008641EF952E200B49F9D /* Constant.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D007F81EF952E200B49F9D /* Constant.cpp */; }; + A9D008651EF952E200B49F9D /* Constant.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D007F81EF952E200B49F9D /* Constant.cpp */; }; + A9D008661EF952E200B49F9D /* gl_types.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007F91EF952E200B49F9D /* gl_types.h */; }; + A9D008671EF952E200B49F9D /* gl_types.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007F91EF952E200B49F9D /* gl_types.h */; }; + A9D0086A1EF952E200B49F9D /* glslang_tab.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D007FB1EF952E200B49F9D /* glslang_tab.cpp */; }; + A9D0086B1EF952E200B49F9D /* glslang_tab.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D007FB1EF952E200B49F9D /* glslang_tab.cpp */; }; + A9D0086C1EF952E200B49F9D /* glslang_tab.cpp.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007FC1EF952E200B49F9D /* glslang_tab.cpp.h */; }; + A9D0086D1EF952E200B49F9D /* glslang_tab.cpp.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007FC1EF952E200B49F9D /* glslang_tab.cpp.h */; }; + A9D0086E1EF952E200B49F9D /* InfoSink.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D007FD1EF952E200B49F9D /* InfoSink.cpp */; }; + A9D0086F1EF952E200B49F9D /* InfoSink.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D007FD1EF952E200B49F9D /* InfoSink.cpp */; }; + A9D008701EF952E200B49F9D /* Initialize.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D007FE1EF952E200B49F9D /* Initialize.cpp */; }; + A9D008711EF952E200B49F9D /* Initialize.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D007FE1EF952E200B49F9D /* Initialize.cpp */; }; + A9D008721EF952E200B49F9D /* Initialize.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007FF1EF952E200B49F9D /* Initialize.h */; }; + A9D008731EF952E200B49F9D /* Initialize.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D007FF1EF952E200B49F9D /* Initialize.h */; }; + A9D008741EF952E200B49F9D /* Intermediate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008001EF952E200B49F9D /* Intermediate.cpp */; }; + A9D008751EF952E200B49F9D /* Intermediate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008001EF952E200B49F9D /* Intermediate.cpp */; }; + A9D008761EF952E200B49F9D /* intermOut.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008011EF952E200B49F9D /* intermOut.cpp */; }; + A9D008771EF952E200B49F9D /* intermOut.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008011EF952E200B49F9D /* intermOut.cpp */; }; + A9D008781EF952E200B49F9D /* IntermTraverse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008021EF952E200B49F9D /* IntermTraverse.cpp */; }; + A9D008791EF952E200B49F9D /* IntermTraverse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008021EF952E200B49F9D /* IntermTraverse.cpp */; }; + A9D0087A1EF952E200B49F9D /* iomapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008031EF952E200B49F9D /* iomapper.cpp */; }; + A9D0087B1EF952E200B49F9D /* iomapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008031EF952E200B49F9D /* iomapper.cpp */; }; + A9D0087C1EF952E200B49F9D /* iomapper.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008041EF952E200B49F9D /* iomapper.h */; }; + A9D0087D1EF952E200B49F9D /* iomapper.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008041EF952E200B49F9D /* iomapper.h */; }; + A9D0087E1EF952E200B49F9D /* limits.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008051EF952E200B49F9D /* limits.cpp */; }; + A9D0087F1EF952E200B49F9D /* limits.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008051EF952E200B49F9D /* limits.cpp */; }; + A9D008801EF952E200B49F9D /* linkValidate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008061EF952E200B49F9D /* linkValidate.cpp */; }; + A9D008811EF952E200B49F9D /* linkValidate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008061EF952E200B49F9D /* linkValidate.cpp */; }; + A9D008821EF952E200B49F9D /* LiveTraverser.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008071EF952E200B49F9D /* LiveTraverser.h */; }; + A9D008831EF952E200B49F9D /* LiveTraverser.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008071EF952E200B49F9D /* LiveTraverser.h */; }; + A9D008841EF952E200B49F9D /* localintermediate.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008081EF952E200B49F9D /* localintermediate.h */; }; + A9D008851EF952E200B49F9D /* localintermediate.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008081EF952E200B49F9D /* localintermediate.h */; }; + A9D008861EF952E200B49F9D /* parseConst.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008091EF952E200B49F9D /* parseConst.cpp */; }; + A9D008871EF952E200B49F9D /* parseConst.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008091EF952E200B49F9D /* parseConst.cpp */; }; + A9D008881EF952E200B49F9D /* ParseContextBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D0080A1EF952E200B49F9D /* ParseContextBase.cpp */; }; + A9D008891EF952E200B49F9D /* ParseContextBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D0080A1EF952E200B49F9D /* ParseContextBase.cpp */; }; + A9D0088A1EF952E200B49F9D /* ParseHelper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D0080B1EF952E200B49F9D /* ParseHelper.cpp */; }; + A9D0088B1EF952E200B49F9D /* ParseHelper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D0080B1EF952E200B49F9D /* ParseHelper.cpp */; }; + A9D0088C1EF952E200B49F9D /* ParseHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0080C1EF952E200B49F9D /* ParseHelper.h */; }; + A9D0088D1EF952E200B49F9D /* ParseHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0080C1EF952E200B49F9D /* ParseHelper.h */; }; + A9D0088E1EF952E200B49F9D /* parseVersions.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0080D1EF952E200B49F9D /* parseVersions.h */; }; + A9D0088F1EF952E200B49F9D /* parseVersions.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0080D1EF952E200B49F9D /* parseVersions.h */; }; + A9D008901EF952E200B49F9D /* PoolAlloc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D0080E1EF952E200B49F9D /* PoolAlloc.cpp */; }; + A9D008911EF952E200B49F9D /* PoolAlloc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D0080E1EF952E200B49F9D /* PoolAlloc.cpp */; }; + A9D008921EF952E200B49F9D /* Pp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008101EF952E200B49F9D /* Pp.cpp */; }; + A9D008931EF952E200B49F9D /* Pp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008101EF952E200B49F9D /* Pp.cpp */; }; + A9D008941EF952E200B49F9D /* PpAtom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008111EF952E200B49F9D /* PpAtom.cpp */; }; + A9D008951EF952E200B49F9D /* PpAtom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008111EF952E200B49F9D /* PpAtom.cpp */; }; + A9D008961EF952E200B49F9D /* PpContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008121EF952E200B49F9D /* PpContext.cpp */; }; + A9D008971EF952E200B49F9D /* PpContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008121EF952E200B49F9D /* PpContext.cpp */; }; + A9D008981EF952E200B49F9D /* PpContext.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008131EF952E200B49F9D /* PpContext.h */; }; + A9D008991EF952E200B49F9D /* PpContext.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008131EF952E200B49F9D /* PpContext.h */; }; + A9D0089A1EF952E200B49F9D /* PpScanner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008141EF952E200B49F9D /* PpScanner.cpp */; }; + A9D0089B1EF952E200B49F9D /* PpScanner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008141EF952E200B49F9D /* PpScanner.cpp */; }; + A9D0089C1EF952E200B49F9D /* PpTokens.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008151EF952E200B49F9D /* PpTokens.cpp */; }; + A9D0089D1EF952E200B49F9D /* PpTokens.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008151EF952E200B49F9D /* PpTokens.cpp */; }; + A9D0089E1EF952E200B49F9D /* PpTokens.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008161EF952E200B49F9D /* PpTokens.h */; }; + A9D0089F1EF952E200B49F9D /* PpTokens.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008161EF952E200B49F9D /* PpTokens.h */; }; + A9D008A01EF952E200B49F9D /* propagateNoContraction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008171EF952E200B49F9D /* propagateNoContraction.cpp */; }; + A9D008A11EF952E200B49F9D /* propagateNoContraction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008171EF952E200B49F9D /* propagateNoContraction.cpp */; }; + A9D008A21EF952E200B49F9D /* propagateNoContraction.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008181EF952E200B49F9D /* propagateNoContraction.h */; }; + A9D008A31EF952E200B49F9D /* propagateNoContraction.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008181EF952E200B49F9D /* propagateNoContraction.h */; }; + A9D008A41EF952E200B49F9D /* reflection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008191EF952E200B49F9D /* reflection.cpp */; }; + A9D008A51EF952E200B49F9D /* reflection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008191EF952E200B49F9D /* reflection.cpp */; }; + A9D008A61EF952E200B49F9D /* reflection.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0081A1EF952E200B49F9D /* reflection.h */; }; + A9D008A71EF952E200B49F9D /* reflection.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0081A1EF952E200B49F9D /* reflection.h */; }; + A9D008A81EF952E200B49F9D /* RemoveTree.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D0081B1EF952E200B49F9D /* RemoveTree.cpp */; }; + A9D008A91EF952E200B49F9D /* RemoveTree.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D0081B1EF952E200B49F9D /* RemoveTree.cpp */; }; + A9D008AA1EF952E200B49F9D /* RemoveTree.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0081C1EF952E200B49F9D /* RemoveTree.h */; }; + A9D008AB1EF952E200B49F9D /* RemoveTree.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0081C1EF952E200B49F9D /* RemoveTree.h */; }; + A9D008AC1EF952E200B49F9D /* Scan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D0081D1EF952E200B49F9D /* Scan.cpp */; }; + A9D008AD1EF952E200B49F9D /* Scan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D0081D1EF952E200B49F9D /* Scan.cpp */; }; + A9D008AE1EF952E200B49F9D /* Scan.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0081E1EF952E200B49F9D /* Scan.h */; }; + A9D008AF1EF952E200B49F9D /* Scan.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0081E1EF952E200B49F9D /* Scan.h */; }; + A9D008B01EF952E200B49F9D /* ScanContext.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0081F1EF952E200B49F9D /* ScanContext.h */; }; + A9D008B11EF952E200B49F9D /* ScanContext.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0081F1EF952E200B49F9D /* ScanContext.h */; }; + A9D008B21EF952E200B49F9D /* ShaderLang.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008201EF952E200B49F9D /* ShaderLang.cpp */; }; + A9D008B31EF952E200B49F9D /* ShaderLang.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008201EF952E200B49F9D /* ShaderLang.cpp */; }; + A9D008B41EF952E200B49F9D /* SymbolTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008211EF952E200B49F9D /* SymbolTable.cpp */; }; + A9D008B51EF952E200B49F9D /* SymbolTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008211EF952E200B49F9D /* SymbolTable.cpp */; }; + A9D008B61EF952E200B49F9D /* SymbolTable.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008221EF952E200B49F9D /* SymbolTable.h */; }; + A9D008B71EF952E200B49F9D /* SymbolTable.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008221EF952E200B49F9D /* SymbolTable.h */; }; + A9D008B81EF952E200B49F9D /* Versions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008231EF952E200B49F9D /* Versions.cpp */; }; + A9D008B91EF952E200B49F9D /* Versions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008231EF952E200B49F9D /* Versions.cpp */; }; + A9D008BA1EF952E200B49F9D /* Versions.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008241EF952E200B49F9D /* Versions.h */; }; + A9D008BB1EF952E200B49F9D /* Versions.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008241EF952E200B49F9D /* Versions.h */; }; + A9D008BC1EF952E200B49F9D /* osinclude.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008261EF952E200B49F9D /* osinclude.h */; }; + A9D008BD1EF952E200B49F9D /* osinclude.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008261EF952E200B49F9D /* osinclude.h */; }; + A9D008BE1EF952E200B49F9D /* ossource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008291EF952E200B49F9D /* ossource.cpp */; }; + A9D008BF1EF952E200B49F9D /* ossource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008291EF952E200B49F9D /* ossource.cpp */; }; + A9D008C41EF952E200B49F9D /* ShaderLang.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0082F1EF952E200B49F9D /* ShaderLang.h */; }; + A9D008C51EF952E200B49F9D /* ShaderLang.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0082F1EF952E200B49F9D /* ShaderLang.h */; }; + A9D008C61EF952E200B49F9D /* bitutils.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008321EF952E200B49F9D /* bitutils.h */; }; + A9D008C71EF952E200B49F9D /* bitutils.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008321EF952E200B49F9D /* bitutils.h */; }; + A9D008C81EF952E200B49F9D /* disassemble.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008341EF952E200B49F9D /* disassemble.cpp */; }; + A9D008C91EF952E200B49F9D /* disassemble.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008341EF952E200B49F9D /* disassemble.cpp */; }; + A9D008CA1EF952E200B49F9D /* disassemble.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008351EF952E200B49F9D /* disassemble.h */; }; + A9D008CB1EF952E200B49F9D /* disassemble.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008351EF952E200B49F9D /* disassemble.h */; }; + A9D008CC1EF952E200B49F9D /* doc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008361EF952E200B49F9D /* doc.cpp */; }; + A9D008CD1EF952E200B49F9D /* doc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008361EF952E200B49F9D /* doc.cpp */; }; + A9D008CE1EF952E200B49F9D /* doc.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008371EF952E200B49F9D /* doc.h */; }; + A9D008CF1EF952E200B49F9D /* doc.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008371EF952E200B49F9D /* doc.h */; }; + A9D008D01EF952E200B49F9D /* GLSL.ext.AMD.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008381EF952E200B49F9D /* GLSL.ext.AMD.h */; }; + A9D008D11EF952E200B49F9D /* GLSL.ext.AMD.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008381EF952E200B49F9D /* GLSL.ext.AMD.h */; }; + A9D008D21EF952E200B49F9D /* GLSL.ext.KHR.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008391EF952E200B49F9D /* GLSL.ext.KHR.h */; }; + A9D008D31EF952E200B49F9D /* GLSL.ext.KHR.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008391EF952E200B49F9D /* GLSL.ext.KHR.h */; }; + A9D008D41EF952E200B49F9D /* GLSL.ext.NV.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0083A1EF952E200B49F9D /* GLSL.ext.NV.h */; }; + A9D008D51EF952E200B49F9D /* GLSL.ext.NV.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0083A1EF952E200B49F9D /* GLSL.ext.NV.h */; }; + A9D008D61EF952E200B49F9D /* GLSL.std.450.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0083B1EF952E200B49F9D /* GLSL.std.450.h */; }; + A9D008D71EF952E200B49F9D /* GLSL.std.450.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0083B1EF952E200B49F9D /* GLSL.std.450.h */; }; + A9D008D81EF952E200B49F9D /* GlslangToSpv.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D0083C1EF952E200B49F9D /* GlslangToSpv.cpp */; }; + A9D008D91EF952E200B49F9D /* GlslangToSpv.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D0083C1EF952E200B49F9D /* GlslangToSpv.cpp */; }; + A9D008DA1EF952E200B49F9D /* GlslangToSpv.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0083D1EF952E200B49F9D /* GlslangToSpv.h */; }; + A9D008DB1EF952E200B49F9D /* GlslangToSpv.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0083D1EF952E200B49F9D /* GlslangToSpv.h */; }; + A9D008DC1EF952E200B49F9D /* hex_float.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0083E1EF952E200B49F9D /* hex_float.h */; }; + A9D008DD1EF952E200B49F9D /* hex_float.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D0083E1EF952E200B49F9D /* hex_float.h */; }; + A9D008DE1EF952E200B49F9D /* InReadableOrder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D0083F1EF952E200B49F9D /* InReadableOrder.cpp */; }; + A9D008DF1EF952E200B49F9D /* InReadableOrder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D0083F1EF952E200B49F9D /* InReadableOrder.cpp */; }; + A9D008E01EF952E200B49F9D /* Logger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008401EF952E200B49F9D /* Logger.cpp */; }; + A9D008E11EF952E200B49F9D /* Logger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008401EF952E200B49F9D /* Logger.cpp */; }; + A9D008E21EF952E200B49F9D /* Logger.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008411EF952E200B49F9D /* Logger.h */; }; + A9D008E31EF952E200B49F9D /* Logger.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008411EF952E200B49F9D /* Logger.h */; }; + A9D008E41EF952E200B49F9D /* spirv.hpp in Headers */ = {isa = PBXBuildFile; fileRef = A9D008421EF952E200B49F9D /* spirv.hpp */; }; + A9D008E51EF952E200B49F9D /* spirv.hpp in Headers */ = {isa = PBXBuildFile; fileRef = A9D008421EF952E200B49F9D /* spirv.hpp */; }; + A9D008E61EF952E200B49F9D /* SpvBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008431EF952E200B49F9D /* SpvBuilder.cpp */; }; + A9D008E71EF952E200B49F9D /* SpvBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008431EF952E200B49F9D /* SpvBuilder.cpp */; }; + A9D008E81EF952E200B49F9D /* SpvBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008441EF952E200B49F9D /* SpvBuilder.h */; }; + A9D008E91EF952E200B49F9D /* SpvBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008441EF952E200B49F9D /* SpvBuilder.h */; }; + A9D008EA1EF952E200B49F9D /* spvIR.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008451EF952E200B49F9D /* spvIR.h */; }; + A9D008EB1EF952E200B49F9D /* spvIR.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008451EF952E200B49F9D /* spvIR.h */; }; + A9D008EC1EF952E200B49F9D /* SPVRemapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008461EF952E200B49F9D /* SPVRemapper.cpp */; }; + A9D008ED1EF952E200B49F9D /* SPVRemapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D008461EF952E200B49F9D /* SPVRemapper.cpp */; }; + A9D008EE1EF952E200B49F9D /* SPVRemapper.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008471EF952E200B49F9D /* SPVRemapper.h */; }; + A9D008EF1EF952E200B49F9D /* SPVRemapper.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D008471EF952E200B49F9D /* SPVRemapper.h */; }; + A9F042B01FB4D060009FCCB8 /* MVKCommonEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = A9F042AA1FB4D060009FCCB8 /* MVKCommonEnvironment.h */; }; + A9F042B11FB4D060009FCCB8 /* MVKCommonEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = A9F042AA1FB4D060009FCCB8 /* MVKCommonEnvironment.h */; }; + A9F042B21FB4D060009FCCB8 /* MVKCommonEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = A9F042AA1FB4D060009FCCB8 /* MVKCommonEnvironment.h */; }; + A9F042B31FB4D060009FCCB8 /* MVKCommonEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = A9F042AA1FB4D060009FCCB8 /* MVKCommonEnvironment.h */; }; + A9F042B41FB4D060009FCCB8 /* MVKLogging.h in Headers */ = {isa = PBXBuildFile; fileRef = A9F042AB1FB4D060009FCCB8 /* MVKLogging.h */; }; + A9F042B51FB4D060009FCCB8 /* MVKLogging.h in Headers */ = {isa = PBXBuildFile; fileRef = A9F042AB1FB4D060009FCCB8 /* MVKLogging.h */; }; + A9F042B61FB4D060009FCCB8 /* MVKLogging.h in Headers */ = {isa = PBXBuildFile; fileRef = A9F042AB1FB4D060009FCCB8 /* MVKLogging.h */; }; + A9F042B71FB4D060009FCCB8 /* MVKLogging.h in Headers */ = {isa = PBXBuildFile; fileRef = A9F042AB1FB4D060009FCCB8 /* MVKLogging.h */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + A925B71C1C78DEBF006E7ECD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A9F55D25198BE6A7004EC31B /* Project object */; + proxyType = 1; + remoteGlobalIDString = A93903C01C57E9ED00FE90DC; + remoteInfo = "MoltenVKSPIRVToMSLConverter-macOS"; + }; + A93749CE1A9AA7AF00F29B34 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A9F55D25198BE6A7004EC31B /* Project object */; + proxyType = 1; + remoteGlobalIDString = A93747701A9A98D000F29B34; + remoteInfo = "MetalGLShaderConverter-macOS"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + A9093F5A1C58013E0094110D /* SPIRVToMSLConverter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SPIRVToMSLConverter.cpp; sourceTree = ""; }; + A9093F5B1C58013E0094110D /* SPIRVToMSLConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPIRVToMSLConverter.h; sourceTree = ""; }; + A90940A31C5808BB0094110D /* GLSLToSPIRVConverter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GLSLToSPIRVConverter.cpp; sourceTree = ""; }; + A90940A41C5808BB0094110D /* GLSLToSPIRVConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLSLToSPIRVConverter.h; sourceTree = ""; }; + A90941A11C581F840094110D /* GLSLConversion.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = GLSLConversion.mm; sourceTree = ""; }; + A90941A21C581F840094110D /* GLSLConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLSLConversion.h; sourceTree = ""; }; + A91425E01EF9542E00891AFD /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + A91425E11EF9542E00891AFD /* InitializeDll.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InitializeDll.cpp; sourceTree = ""; }; + A91425E21EF9542E00891AFD /* InitializeDll.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InitializeDll.h; sourceTree = ""; }; + A925B70A1C7754B2006E7ECD /* FileSupport.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FileSupport.mm; sourceTree = ""; }; + A925B70B1C7754B2006E7ECD /* FileSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileSupport.h; sourceTree = ""; }; + A928C8F51D047C2300071B88 /* ThirdPartyConfig.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = ThirdPartyConfig.md; sourceTree = ""; }; + A928C9171D0488DC00071B88 /* SPIRVConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPIRVConversion.h; sourceTree = ""; }; + A928C9181D0488DC00071B88 /* SPIRVConversion.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SPIRVConversion.mm; sourceTree = ""; }; + A93903BF1C57E9D700FE90DC /* MoltenVKSPIRVToMSLConverter.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MoltenVKSPIRVToMSLConverter.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A93903C71C57E9ED00FE90DC /* MoltenVKSPIRVToMSLConverter.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MoltenVKSPIRVToMSLConverter.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A95C5F3D1DEA9070000D17B6 /* spirv_cfg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = spirv_cfg.cpp; path = "SPIRV-Cross/spirv_cfg.cpp"; sourceTree = ""; }; + A95C5F3E1DEA9070000D17B6 /* spirv_cfg.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = spirv_cfg.hpp; path = "SPIRV-Cross/spirv_cfg.hpp"; sourceTree = ""; }; + A964BD5F1C57EFBD00D930D8 /* MoltenVKShaderConverter */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = MoltenVKShaderConverter; sourceTree = BUILT_PRODUCTS_DIR; }; + A964BD601C57EFBD00D930D8 /* MoltenVKGLSLToSPIRVConverter.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MoltenVKGLSLToSPIRVConverter.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A964BD611C57EFBD00D930D8 /* MoltenVKGLSLToSPIRVConverter.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MoltenVKGLSLToSPIRVConverter.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A97CC73D1C7527F3004A5C7E /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; + A97CC73E1C7527F3004A5C7E /* MoltenVKShaderConverterTool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MoltenVKShaderConverterTool.cpp; sourceTree = ""; }; + A97CC73F1C7527F3004A5C7E /* MoltenVKShaderConverterTool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MoltenVKShaderConverterTool.h; sourceTree = ""; }; + A98149651FB6A98A005F00B4 /* MVKStrings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKStrings.h; sourceTree = ""; }; + A994BE731FBF3F9700278EB9 /* assembly_grammar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = assembly_grammar.cpp; sourceTree = ""; }; + A994BE741FBF3F9700278EB9 /* assembly_grammar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = assembly_grammar.h; sourceTree = ""; }; + A994BE751FBF3F9700278EB9 /* binary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = binary.cpp; sourceTree = ""; }; + A994BE761FBF3F9700278EB9 /* binary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = binary.h; sourceTree = ""; }; + A994BE771FBF3F9700278EB9 /* cfa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cfa.h; sourceTree = ""; }; + A994BE781FBF3F9700278EB9 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + A994BE7A1FBF3F9700278EB9 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + A994BE7B1FBF3F9700278EB9 /* markv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = markv.h; sourceTree = ""; }; + A994BE7C1FBF3F9700278EB9 /* markv_codec.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = markv_codec.cpp; sourceTree = ""; }; + A994BE7D1FBF3F9700278EB9 /* markv_model.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = markv_model.h; sourceTree = ""; }; + A994BE7E1FBF3F9700278EB9 /* diagnostic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = diagnostic.cpp; sourceTree = ""; }; + A994BE7F1FBF3F9700278EB9 /* diagnostic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = diagnostic.h; sourceTree = ""; }; + A994BE801FBF3F9700278EB9 /* disassemble.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = disassemble.cpp; sourceTree = ""; }; + A994BE811FBF3F9700278EB9 /* enum_set.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = enum_set.h; sourceTree = ""; }; + A994BE821FBF3F9700278EB9 /* enum_string_mapping.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = enum_string_mapping.cpp; sourceTree = ""; }; + A994BE831FBF3F9700278EB9 /* enum_string_mapping.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = enum_string_mapping.h; sourceTree = ""; }; + A994BE841FBF3F9700278EB9 /* ext_inst.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ext_inst.cpp; sourceTree = ""; }; + A994BE851FBF3F9700278EB9 /* ext_inst.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ext_inst.h; sourceTree = ""; }; + A994BE861FBF3F9700278EB9 /* extensions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = extensions.cpp; sourceTree = ""; }; + A994BE871FBF3F9700278EB9 /* extensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = extensions.h; sourceTree = ""; }; + A994BE881FBF3F9700278EB9 /* extinst.spv-amd-gcn-shader.grammar.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "extinst.spv-amd-gcn-shader.grammar.json"; sourceTree = ""; }; + A994BE891FBF3F9700278EB9 /* extinst.spv-amd-shader-ballot.grammar.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "extinst.spv-amd-shader-ballot.grammar.json"; sourceTree = ""; }; + A994BE8A1FBF3F9700278EB9 /* extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json"; sourceTree = ""; }; + A994BE8B1FBF3F9700278EB9 /* extinst.spv-amd-shader-trinary-minmax.grammar.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "extinst.spv-amd-shader-trinary-minmax.grammar.json"; sourceTree = ""; }; + A994BE8C1FBF3F9700278EB9 /* id_descriptor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = id_descriptor.cpp; sourceTree = ""; }; + A994BE8D1FBF3F9700278EB9 /* id_descriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = id_descriptor.h; sourceTree = ""; }; + A994BE8E1FBF3F9700278EB9 /* instruction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = instruction.h; sourceTree = ""; }; + A994BE8F1FBF3F9700278EB9 /* libspirv.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = libspirv.cpp; sourceTree = ""; }; + A994BE911FBF3F9700278EB9 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + A994BE921FBF3F9700278EB9 /* linker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = linker.cpp; sourceTree = ""; }; + A994BE931FBF3F9700278EB9 /* macro.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macro.h; sourceTree = ""; }; + A994BE941FBF3F9700278EB9 /* message.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = message.cpp; sourceTree = ""; }; + A994BE951FBF3F9700278EB9 /* message.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = message.h; sourceTree = ""; }; + A994BE961FBF3F9700278EB9 /* name_mapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = name_mapper.cpp; sourceTree = ""; }; + A994BE971FBF3F9700278EB9 /* name_mapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = name_mapper.h; sourceTree = ""; }; + A994BE981FBF3F9700278EB9 /* opcode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcode.cpp; sourceTree = ""; }; + A994BE991FBF3F9700278EB9 /* opcode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opcode.h; sourceTree = ""; }; + A994BE9A1FBF3F9700278EB9 /* operand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = operand.cpp; sourceTree = ""; }; + A994BE9B1FBF3F9700278EB9 /* operand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = operand.h; sourceTree = ""; }; + A994BE9D1FBF3F9700278EB9 /* aggressive_dead_code_elim_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = aggressive_dead_code_elim_pass.cpp; sourceTree = ""; }; + A994BE9E1FBF3F9700278EB9 /* aggressive_dead_code_elim_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aggressive_dead_code_elim_pass.h; sourceTree = ""; }; + A994BE9F1FBF3F9700278EB9 /* basic_block.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = basic_block.cpp; sourceTree = ""; }; + A994BEA01FBF3F9700278EB9 /* basic_block.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = basic_block.h; sourceTree = ""; }; + A994BEA11FBF3F9700278EB9 /* block_merge_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = block_merge_pass.cpp; sourceTree = ""; }; + A994BEA21FBF3F9700278EB9 /* block_merge_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = block_merge_pass.h; sourceTree = ""; }; + A994BEA31FBF3F9700278EB9 /* build_module.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = build_module.cpp; sourceTree = ""; }; + A994BEA41FBF3F9700278EB9 /* build_module.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = build_module.h; sourceTree = ""; }; + A994BEA51FBF3F9700278EB9 /* cfg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cfg.cpp; sourceTree = ""; }; + A994BEA61FBF3F9700278EB9 /* cfg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cfg.h; sourceTree = ""; }; + A994BEA71FBF3F9700278EB9 /* cfg_cleanup_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cfg_cleanup_pass.cpp; sourceTree = ""; }; + A994BEA81FBF3F9700278EB9 /* cfg_cleanup_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cfg_cleanup_pass.h; sourceTree = ""; }; + A994BEA91FBF3F9700278EB9 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + A994BEAA1FBF3F9700278EB9 /* common_uniform_elim_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = common_uniform_elim_pass.cpp; sourceTree = ""; }; + A994BEAB1FBF3F9700278EB9 /* common_uniform_elim_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = common_uniform_elim_pass.h; sourceTree = ""; }; + A994BEAC1FBF3F9700278EB9 /* compact_ids_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = compact_ids_pass.cpp; sourceTree = ""; }; + A994BEAD1FBF3F9700278EB9 /* compact_ids_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = compact_ids_pass.h; sourceTree = ""; }; + A994BEAE1FBF3F9700278EB9 /* constants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = constants.h; sourceTree = ""; }; + A994BEAF1FBF3F9700278EB9 /* dead_branch_elim_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dead_branch_elim_pass.cpp; sourceTree = ""; }; + A994BEB01FBF3F9700278EB9 /* dead_branch_elim_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dead_branch_elim_pass.h; sourceTree = ""; }; + A994BEB11FBF3F9700278EB9 /* dead_variable_elimination.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dead_variable_elimination.cpp; sourceTree = ""; }; + A994BEB21FBF3F9700278EB9 /* dead_variable_elimination.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dead_variable_elimination.h; sourceTree = ""; }; + A994BEB31FBF3F9700278EB9 /* decoration_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = decoration_manager.cpp; sourceTree = ""; }; + A994BEB41FBF3F9700278EB9 /* decoration_manager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = decoration_manager.h; sourceTree = ""; }; + A994BEB51FBF3F9700278EB9 /* def_use_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = def_use_manager.cpp; sourceTree = ""; }; + A994BEB61FBF3F9700278EB9 /* def_use_manager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = def_use_manager.h; sourceTree = ""; }; + A994BEB71FBF3F9700278EB9 /* eliminate_dead_constant_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = eliminate_dead_constant_pass.cpp; sourceTree = ""; }; + A994BEB81FBF3F9700278EB9 /* eliminate_dead_constant_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = eliminate_dead_constant_pass.h; sourceTree = ""; }; + A994BEB91FBF3F9700278EB9 /* eliminate_dead_functions_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = eliminate_dead_functions_pass.cpp; sourceTree = ""; }; + A994BEBA1FBF3F9700278EB9 /* eliminate_dead_functions_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = eliminate_dead_functions_pass.h; sourceTree = ""; }; + A994BEBB1FBF3F9700278EB9 /* flatten_decoration_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = flatten_decoration_pass.cpp; sourceTree = ""; }; + A994BEBC1FBF3F9700278EB9 /* flatten_decoration_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = flatten_decoration_pass.h; sourceTree = ""; }; + A994BEBD1FBF3F9700278EB9 /* fold.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fold.cpp; sourceTree = ""; }; + A994BEBE1FBF3F9700278EB9 /* fold.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fold.h; sourceTree = ""; }; + A994BEBF1FBF3F9700278EB9 /* fold_spec_constant_op_and_composite_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fold_spec_constant_op_and_composite_pass.cpp; sourceTree = ""; }; + A994BEC01FBF3F9700278EB9 /* fold_spec_constant_op_and_composite_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fold_spec_constant_op_and_composite_pass.h; sourceTree = ""; }; + A994BEC11FBF3F9700278EB9 /* freeze_spec_constant_value_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = freeze_spec_constant_value_pass.cpp; sourceTree = ""; }; + A994BEC21FBF3F9700278EB9 /* freeze_spec_constant_value_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = freeze_spec_constant_value_pass.h; sourceTree = ""; }; + A994BEC31FBF3F9700278EB9 /* function.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = function.cpp; sourceTree = ""; }; + A994BEC41FBF3F9700278EB9 /* function.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = function.h; sourceTree = ""; }; + A994BEC51FBF3F9700278EB9 /* inline_exhaustive_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = inline_exhaustive_pass.cpp; sourceTree = ""; }; + A994BEC61FBF3F9700278EB9 /* inline_exhaustive_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = inline_exhaustive_pass.h; sourceTree = ""; }; + A994BEC71FBF3F9700278EB9 /* inline_opaque_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = inline_opaque_pass.cpp; sourceTree = ""; }; + A994BEC81FBF3F9700278EB9 /* inline_opaque_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = inline_opaque_pass.h; sourceTree = ""; }; + A994BEC91FBF3F9700278EB9 /* inline_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = inline_pass.cpp; sourceTree = ""; }; + A994BECA1FBF3F9700278EB9 /* inline_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = inline_pass.h; sourceTree = ""; }; + A994BECB1FBF3F9700278EB9 /* insert_extract_elim.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = insert_extract_elim.cpp; sourceTree = ""; }; + A994BECC1FBF3F9700278EB9 /* insert_extract_elim.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = insert_extract_elim.h; sourceTree = ""; }; + A994BECD1FBF3F9700278EB9 /* instruction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = instruction.cpp; sourceTree = ""; }; + A994BECE1FBF3F9700278EB9 /* instruction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = instruction.h; sourceTree = ""; }; + A994BECF1FBF3F9700278EB9 /* instruction_list.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = instruction_list.cpp; sourceTree = ""; }; + A994BED01FBF3F9700278EB9 /* instruction_list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = instruction_list.h; sourceTree = ""; }; + A994BED11FBF3F9700278EB9 /* ir_context.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ir_context.cpp; sourceTree = ""; }; + A994BED21FBF3F9700278EB9 /* ir_context.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ir_context.h; sourceTree = ""; }; + A994BED31FBF3F9700278EB9 /* ir_loader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ir_loader.cpp; sourceTree = ""; }; + A994BED41FBF3F9700278EB9 /* ir_loader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ir_loader.h; sourceTree = ""; }; + A994BED51FBF3F9700278EB9 /* iterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iterator.h; sourceTree = ""; }; + A994BED61FBF3F9700278EB9 /* local_access_chain_convert_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = local_access_chain_convert_pass.cpp; sourceTree = ""; }; + A994BED71FBF3F9700278EB9 /* local_access_chain_convert_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = local_access_chain_convert_pass.h; sourceTree = ""; }; + A994BED81FBF3F9700278EB9 /* local_single_block_elim_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = local_single_block_elim_pass.cpp; sourceTree = ""; }; + A994BED91FBF3F9700278EB9 /* local_single_block_elim_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = local_single_block_elim_pass.h; sourceTree = ""; }; + A994BEDA1FBF3F9700278EB9 /* local_single_store_elim_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = local_single_store_elim_pass.cpp; sourceTree = ""; }; + A994BEDB1FBF3F9700278EB9 /* local_single_store_elim_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = local_single_store_elim_pass.h; sourceTree = ""; }; + A994BEDC1FBF3F9700278EB9 /* local_ssa_elim_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = local_ssa_elim_pass.cpp; sourceTree = ""; }; + A994BEDD1FBF3F9700278EB9 /* local_ssa_elim_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = local_ssa_elim_pass.h; sourceTree = ""; }; + A994BEDE1FBF3F9700278EB9 /* log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = log.h; sourceTree = ""; }; + A994BEDF1FBF3F9700278EB9 /* make_unique.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = make_unique.h; sourceTree = ""; }; + A994BEE01FBF3F9700278EB9 /* mem_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mem_pass.cpp; sourceTree = ""; }; + A994BEE11FBF3F9700278EB9 /* mem_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mem_pass.h; sourceTree = ""; }; + A994BEE21FBF3F9700278EB9 /* merge_return_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = merge_return_pass.cpp; sourceTree = ""; }; + A994BEE31FBF3F9700278EB9 /* merge_return_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = merge_return_pass.h; sourceTree = ""; }; + A994BEE41FBF3F9700278EB9 /* module.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = module.cpp; sourceTree = ""; }; + A994BEE51FBF3F9700278EB9 /* module.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = module.h; sourceTree = ""; }; + A994BEE61FBF3F9700278EB9 /* null_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = null_pass.h; sourceTree = ""; }; + A994BEE71FBF3F9700278EB9 /* optimizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = optimizer.cpp; sourceTree = ""; }; + A994BEE81FBF3F9700278EB9 /* pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pass.cpp; sourceTree = ""; }; + A994BEE91FBF3F9700278EB9 /* pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pass.h; sourceTree = ""; }; + A994BEEA1FBF3F9700278EB9 /* pass_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pass_manager.cpp; sourceTree = ""; }; + A994BEEB1FBF3F9700278EB9 /* pass_manager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pass_manager.h; sourceTree = ""; }; + A994BEEC1FBF3F9700278EB9 /* passes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = passes.h; sourceTree = ""; }; + A994BEED1FBF3F9700278EB9 /* reflect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = reflect.h; sourceTree = ""; }; + A994BEEE1FBF3F9700278EB9 /* remove_duplicates_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = remove_duplicates_pass.cpp; sourceTree = ""; }; + A994BEEF1FBF3F9700278EB9 /* remove_duplicates_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = remove_duplicates_pass.h; sourceTree = ""; }; + A994BEF01FBF3F9700278EB9 /* set_spec_constant_default_value_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = set_spec_constant_default_value_pass.cpp; sourceTree = ""; }; + A994BEF11FBF3F9700278EB9 /* set_spec_constant_default_value_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = set_spec_constant_default_value_pass.h; sourceTree = ""; }; + A994BEF21FBF3F9700278EB9 /* strength_reduction_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = strength_reduction_pass.cpp; sourceTree = ""; }; + A994BEF31FBF3F9700278EB9 /* strength_reduction_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = strength_reduction_pass.h; sourceTree = ""; }; + A994BEF41FBF3F9700278EB9 /* strip_debug_info_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = strip_debug_info_pass.cpp; sourceTree = ""; }; + A994BEF51FBF3F9700278EB9 /* strip_debug_info_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = strip_debug_info_pass.h; sourceTree = ""; }; + A994BEF61FBF3F9700278EB9 /* type_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = type_manager.cpp; sourceTree = ""; }; + A994BEF71FBF3F9700278EB9 /* type_manager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = type_manager.h; sourceTree = ""; }; + A994BEF81FBF3F9700278EB9 /* types.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = types.cpp; sourceTree = ""; }; + A994BEF91FBF3F9700278EB9 /* types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = types.h; sourceTree = ""; }; + A994BEFA1FBF3F9700278EB9 /* unify_const_pass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = unify_const_pass.cpp; sourceTree = ""; }; + A994BEFB1FBF3F9700278EB9 /* unify_const_pass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = unify_const_pass.h; sourceTree = ""; }; + A994BEFC1FBF3F9700278EB9 /* parsed_operand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = parsed_operand.cpp; sourceTree = ""; }; + A994BEFD1FBF3F9700278EB9 /* parsed_operand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parsed_operand.h; sourceTree = ""; }; + A994BEFE1FBF3F9700278EB9 /* print.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = print.cpp; sourceTree = ""; }; + A994BEFF1FBF3F9700278EB9 /* print.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = print.h; sourceTree = ""; }; + A994BF001FBF3F9700278EB9 /* software_version.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = software_version.cpp; sourceTree = ""; }; + A994BF011FBF3F9700278EB9 /* spirv_constant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = spirv_constant.h; sourceTree = ""; }; + A994BF021FBF3F9700278EB9 /* spirv_definition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = spirv_definition.h; sourceTree = ""; }; + A994BF031FBF3F9700278EB9 /* spirv_endian.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = spirv_endian.cpp; sourceTree = ""; }; + A994BF041FBF3F9700278EB9 /* spirv_endian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = spirv_endian.h; sourceTree = ""; }; + A994BF051FBF3F9700278EB9 /* spirv_stats.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = spirv_stats.cpp; sourceTree = ""; }; + A994BF061FBF3F9700278EB9 /* spirv_stats.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = spirv_stats.h; sourceTree = ""; }; + A994BF071FBF3F9700278EB9 /* spirv_target_env.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = spirv_target_env.cpp; sourceTree = ""; }; + A994BF081FBF3F9700278EB9 /* spirv_target_env.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = spirv_target_env.h; sourceTree = ""; }; + A994BF091FBF3F9700278EB9 /* spirv_validator_options.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = spirv_validator_options.cpp; sourceTree = ""; }; + A994BF0A1FBF3F9700278EB9 /* spirv_validator_options.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = spirv_validator_options.h; sourceTree = ""; }; + A994BF0B1FBF3F9700278EB9 /* table.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = table.cpp; sourceTree = ""; }; + A994BF0C1FBF3F9700278EB9 /* table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = table.h; sourceTree = ""; }; + A994BF0D1FBF3F9700278EB9 /* text.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = text.cpp; sourceTree = ""; }; + A994BF0E1FBF3F9700278EB9 /* text.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = text.h; sourceTree = ""; }; + A994BF0F1FBF3F9700278EB9 /* text_handler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = text_handler.cpp; sourceTree = ""; }; + A994BF101FBF3F9700278EB9 /* text_handler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = text_handler.h; sourceTree = ""; }; + A994BF121FBF3F9700278EB9 /* bit_stream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bit_stream.cpp; sourceTree = ""; }; + A994BF131FBF3F9700278EB9 /* bit_stream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bit_stream.h; sourceTree = ""; }; + A994BF141FBF3F9700278EB9 /* bitutils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bitutils.h; sourceTree = ""; }; + A994BF151FBF3F9700278EB9 /* hex_float.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hex_float.h; sourceTree = ""; }; + A994BF161FBF3F9700278EB9 /* huffman_codec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = huffman_codec.h; sourceTree = ""; }; + A994BF171FBF3F9700278EB9 /* ilist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ilist.h; sourceTree = ""; }; + A994BF181FBF3F9700278EB9 /* ilist_node.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ilist_node.h; sourceTree = ""; }; + A994BF191FBF3F9700278EB9 /* move_to_front.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = move_to_front.h; sourceTree = ""; }; + A994BF1A1FBF3F9700278EB9 /* parse_number.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = parse_number.cpp; sourceTree = ""; }; + A994BF1B1FBF3F9700278EB9 /* parse_number.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parse_number.h; sourceTree = ""; }; + A994BF1C1FBF3F9700278EB9 /* string_utils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_utils.cpp; sourceTree = ""; }; + A994BF1D1FBF3F9700278EB9 /* string_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_utils.h; sourceTree = ""; }; + A994BF1F1FBF3F9700278EB9 /* basic_block.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = basic_block.cpp; sourceTree = ""; }; + A994BF201FBF3F9700278EB9 /* basic_block.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = basic_block.h; sourceTree = ""; }; + A994BF211FBF3F9700278EB9 /* construct.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = construct.cpp; sourceTree = ""; }; + A994BF221FBF3F9700278EB9 /* construct.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = construct.h; sourceTree = ""; }; + A994BF231FBF3F9700278EB9 /* decoration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = decoration.h; sourceTree = ""; }; + A994BF241FBF3F9700278EB9 /* function.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = function.cpp; sourceTree = ""; }; + A994BF251FBF3F9700278EB9 /* function.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = function.h; sourceTree = ""; }; + A994BF261FBF3F9700278EB9 /* instruction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = instruction.cpp; sourceTree = ""; }; + A994BF271FBF3F9700278EB9 /* instruction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = instruction.h; sourceTree = ""; }; + A994BF281FBF3F9700278EB9 /* validation_state.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = validation_state.cpp; sourceTree = ""; }; + A994BF291FBF3F9700278EB9 /* validation_state.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = validation_state.h; sourceTree = ""; }; + A994BF2A1FBF3F9700278EB9 /* validate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = validate.cpp; sourceTree = ""; }; + A994BF2B1FBF3F9700278EB9 /* validate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = validate.h; sourceTree = ""; }; + A994BF2C1FBF3F9700278EB9 /* validate_arithmetics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = validate_arithmetics.cpp; sourceTree = ""; }; + A994BF2D1FBF3F9700278EB9 /* validate_bitwise.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = validate_bitwise.cpp; sourceTree = ""; }; + A994BF2E1FBF3F9700278EB9 /* validate_capability.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = validate_capability.cpp; sourceTree = ""; }; + A994BF2F1FBF3F9700278EB9 /* validate_cfg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = validate_cfg.cpp; sourceTree = ""; }; + A994BF301FBF3F9700278EB9 /* validate_conversion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = validate_conversion.cpp; sourceTree = ""; }; + A994BF311FBF3F9700278EB9 /* validate_datarules.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = validate_datarules.cpp; sourceTree = ""; }; + A994BF321FBF3F9700278EB9 /* validate_decorations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = validate_decorations.cpp; sourceTree = ""; }; + A994BF331FBF3F9700278EB9 /* validate_id.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = validate_id.cpp; sourceTree = ""; }; + A994BF341FBF3F9700278EB9 /* validate_instruction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = validate_instruction.cpp; sourceTree = ""; }; + A994BF351FBF3F9700278EB9 /* validate_layout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = validate_layout.cpp; sourceTree = ""; }; + A994BF361FBF3F9700278EB9 /* validate_logicals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = validate_logicals.cpp; sourceTree = ""; }; + A994BF371FBF3F9700278EB9 /* validate_type_unique.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = validate_type_unique.cpp; sourceTree = ""; }; + A9AB19901CB5B5A80001E7F9 /* spirv_common.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = spirv_common.hpp; path = "SPIRV-Cross/spirv_common.hpp"; sourceTree = ""; }; + A9AB19911CB5B5A80001E7F9 /* spirv_cross.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = spirv_cross.cpp; path = "SPIRV-Cross/spirv_cross.cpp"; sourceTree = ""; }; + A9AB19921CB5B5A80001E7F9 /* spirv_cross.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = spirv_cross.hpp; path = "SPIRV-Cross/spirv_cross.hpp"; sourceTree = ""; }; + A9AB19931CB5B5A80001E7F9 /* spirv_glsl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = spirv_glsl.cpp; path = "SPIRV-Cross/spirv_glsl.cpp"; sourceTree = ""; }; + A9AB19941CB5B5A80001E7F9 /* spirv_glsl.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = spirv_glsl.hpp; path = "SPIRV-Cross/spirv_glsl.hpp"; sourceTree = ""; }; + A9AB19951CB5B5A80001E7F9 /* spirv_msl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = spirv_msl.cpp; path = "SPIRV-Cross/spirv_msl.cpp"; sourceTree = ""; }; + A9AB19961CB5B5A80001E7F9 /* spirv_msl.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = spirv_msl.hpp; path = "SPIRV-Cross/spirv_msl.hpp"; sourceTree = ""; }; + A9BB09751CEF89B100CCAB22 /* spirv.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = spirv.hpp; sourceTree = ""; }; + A9D007E51EF952E200B49F9D /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + A9D007E71EF952E200B49F9D /* CodeGen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CodeGen.cpp; sourceTree = ""; }; + A9D007E81EF952E200B49F9D /* Link.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Link.cpp; sourceTree = ""; }; + A9D007EA1EF952E200B49F9D /* arrays.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = arrays.h; sourceTree = ""; }; + A9D007EB1EF952E200B49F9D /* BaseTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseTypes.h; sourceTree = ""; }; + A9D007EC1EF952E200B49F9D /* Common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Common.h; sourceTree = ""; }; + A9D007ED1EF952E200B49F9D /* ConstantUnion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConstantUnion.h; sourceTree = ""; }; + A9D007EE1EF952E200B49F9D /* InfoSink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InfoSink.h; sourceTree = ""; }; + A9D007EF1EF952E200B49F9D /* InitializeGlobals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InitializeGlobals.h; sourceTree = ""; }; + A9D007F01EF952E200B49F9D /* intermediate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = intermediate.h; sourceTree = ""; }; + A9D007F11EF952E200B49F9D /* PoolAlloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PoolAlloc.h; sourceTree = ""; }; + A9D007F21EF952E200B49F9D /* ResourceLimits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResourceLimits.h; sourceTree = ""; }; + A9D007F31EF952E200B49F9D /* revision.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = revision.h; sourceTree = ""; }; + A9D007F41EF952E200B49F9D /* revision.template */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = revision.template; sourceTree = ""; }; + A9D007F51EF952E200B49F9D /* ShHandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShHandle.h; sourceTree = ""; }; + A9D007F61EF952E200B49F9D /* Types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Types.h; sourceTree = ""; }; + A9D007F81EF952E200B49F9D /* Constant.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Constant.cpp; sourceTree = ""; }; + A9D007F91EF952E200B49F9D /* gl_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gl_types.h; sourceTree = ""; }; + A9D007FB1EF952E200B49F9D /* glslang_tab.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = glslang_tab.cpp; sourceTree = ""; }; + A9D007FC1EF952E200B49F9D /* glslang_tab.cpp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = glslang_tab.cpp.h; sourceTree = ""; }; + A9D007FD1EF952E200B49F9D /* InfoSink.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InfoSink.cpp; sourceTree = ""; }; + A9D007FE1EF952E200B49F9D /* Initialize.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Initialize.cpp; sourceTree = ""; }; + A9D007FF1EF952E200B49F9D /* Initialize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Initialize.h; sourceTree = ""; }; + A9D008001EF952E200B49F9D /* Intermediate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Intermediate.cpp; sourceTree = ""; }; + A9D008011EF952E200B49F9D /* intermOut.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = intermOut.cpp; sourceTree = ""; }; + A9D008021EF952E200B49F9D /* IntermTraverse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IntermTraverse.cpp; sourceTree = ""; }; + A9D008031EF952E200B49F9D /* iomapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = iomapper.cpp; sourceTree = ""; }; + A9D008041EF952E200B49F9D /* iomapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iomapper.h; sourceTree = ""; }; + A9D008051EF952E200B49F9D /* limits.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = limits.cpp; sourceTree = ""; }; + A9D008061EF952E200B49F9D /* linkValidate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = linkValidate.cpp; sourceTree = ""; }; + A9D008071EF952E200B49F9D /* LiveTraverser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LiveTraverser.h; sourceTree = ""; }; + A9D008081EF952E200B49F9D /* localintermediate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = localintermediate.h; sourceTree = ""; }; + A9D008091EF952E200B49F9D /* parseConst.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = parseConst.cpp; sourceTree = ""; }; + A9D0080A1EF952E200B49F9D /* ParseContextBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParseContextBase.cpp; sourceTree = ""; }; + A9D0080B1EF952E200B49F9D /* ParseHelper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParseHelper.cpp; sourceTree = ""; }; + A9D0080C1EF952E200B49F9D /* ParseHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParseHelper.h; sourceTree = ""; }; + A9D0080D1EF952E200B49F9D /* parseVersions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parseVersions.h; sourceTree = ""; }; + A9D0080E1EF952E200B49F9D /* PoolAlloc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PoolAlloc.cpp; sourceTree = ""; }; + A9D008101EF952E200B49F9D /* Pp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Pp.cpp; sourceTree = ""; }; + A9D008111EF952E200B49F9D /* PpAtom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PpAtom.cpp; sourceTree = ""; }; + A9D008121EF952E200B49F9D /* PpContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PpContext.cpp; sourceTree = ""; }; + A9D008131EF952E200B49F9D /* PpContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PpContext.h; sourceTree = ""; }; + A9D008141EF952E200B49F9D /* PpScanner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PpScanner.cpp; sourceTree = ""; }; + A9D008151EF952E200B49F9D /* PpTokens.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PpTokens.cpp; sourceTree = ""; }; + A9D008161EF952E200B49F9D /* PpTokens.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PpTokens.h; sourceTree = ""; }; + A9D008171EF952E200B49F9D /* propagateNoContraction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = propagateNoContraction.cpp; sourceTree = ""; }; + A9D008181EF952E200B49F9D /* propagateNoContraction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = propagateNoContraction.h; sourceTree = ""; }; + A9D008191EF952E200B49F9D /* reflection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = reflection.cpp; sourceTree = ""; }; + A9D0081A1EF952E200B49F9D /* reflection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = reflection.h; sourceTree = ""; }; + A9D0081B1EF952E200B49F9D /* RemoveTree.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RemoveTree.cpp; sourceTree = ""; }; + A9D0081C1EF952E200B49F9D /* RemoveTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RemoveTree.h; sourceTree = ""; }; + A9D0081D1EF952E200B49F9D /* Scan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Scan.cpp; sourceTree = ""; }; + A9D0081E1EF952E200B49F9D /* Scan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Scan.h; sourceTree = ""; }; + A9D0081F1EF952E200B49F9D /* ScanContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScanContext.h; sourceTree = ""; }; + A9D008201EF952E200B49F9D /* ShaderLang.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ShaderLang.cpp; sourceTree = ""; }; + A9D008211EF952E200B49F9D /* SymbolTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SymbolTable.cpp; sourceTree = ""; }; + A9D008221EF952E200B49F9D /* SymbolTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SymbolTable.h; sourceTree = ""; }; + A9D008231EF952E200B49F9D /* Versions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Versions.cpp; sourceTree = ""; }; + A9D008241EF952E200B49F9D /* Versions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Versions.h; sourceTree = ""; }; + A9D008261EF952E200B49F9D /* osinclude.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = osinclude.h; sourceTree = ""; }; + A9D008281EF952E200B49F9D /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + A9D008291EF952E200B49F9D /* ossource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ossource.cpp; sourceTree = ""; }; + A9D0082F1EF952E200B49F9D /* ShaderLang.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShaderLang.h; sourceTree = ""; }; + A9D008301EF952E200B49F9D /* updateGrammar */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = updateGrammar; sourceTree = ""; }; + A9D008321EF952E200B49F9D /* bitutils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bitutils.h; sourceTree = ""; }; + A9D008331EF952E200B49F9D /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + A9D008341EF952E200B49F9D /* disassemble.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = disassemble.cpp; sourceTree = ""; }; + A9D008351EF952E200B49F9D /* disassemble.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = disassemble.h; sourceTree = ""; }; + A9D008361EF952E200B49F9D /* doc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = doc.cpp; sourceTree = ""; }; + A9D008371EF952E200B49F9D /* doc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = doc.h; sourceTree = ""; }; + A9D008381EF952E200B49F9D /* GLSL.ext.AMD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLSL.ext.AMD.h; sourceTree = ""; }; + A9D008391EF952E200B49F9D /* GLSL.ext.KHR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLSL.ext.KHR.h; sourceTree = ""; }; + A9D0083A1EF952E200B49F9D /* GLSL.ext.NV.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLSL.ext.NV.h; sourceTree = ""; }; + A9D0083B1EF952E200B49F9D /* GLSL.std.450.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLSL.std.450.h; sourceTree = ""; }; + A9D0083C1EF952E200B49F9D /* GlslangToSpv.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GlslangToSpv.cpp; sourceTree = ""; }; + A9D0083D1EF952E200B49F9D /* GlslangToSpv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GlslangToSpv.h; sourceTree = ""; }; + A9D0083E1EF952E200B49F9D /* hex_float.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hex_float.h; sourceTree = ""; }; + A9D0083F1EF952E200B49F9D /* InReadableOrder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InReadableOrder.cpp; sourceTree = ""; }; + A9D008401EF952E200B49F9D /* Logger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Logger.cpp; sourceTree = ""; }; + A9D008411EF952E200B49F9D /* Logger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logger.h; sourceTree = ""; }; + A9D008421EF952E200B49F9D /* spirv.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = spirv.hpp; sourceTree = ""; }; + A9D008431EF952E200B49F9D /* SpvBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpvBuilder.cpp; sourceTree = ""; }; + A9D008441EF952E200B49F9D /* SpvBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpvBuilder.h; sourceTree = ""; }; + A9D008451EF952E200B49F9D /* spvIR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = spvIR.h; sourceTree = ""; }; + A9D008461EF952E200B49F9D /* SPVRemapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SPVRemapper.cpp; sourceTree = ""; }; + A9D008471EF952E200B49F9D /* SPVRemapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPVRemapper.h; sourceTree = ""; }; + A9F042AA1FB4D060009FCCB8 /* MVKCommonEnvironment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKCommonEnvironment.h; sourceTree = ""; }; + A9F042AB1FB4D060009FCCB8 /* MVKLogging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKLogging.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + A9092A8A1A81717B00051823 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A925B71A1C78DEB2006E7ECD /* MoltenVKGLSLToSPIRVConverter.framework in Frameworks */, + A925B71B1C78DEB2006E7ECD /* MoltenVKSPIRVToMSLConverter.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + A9093C561C58013D0094110D /* MoltenVKSPIRVToMSLConverter */ = { + isa = PBXGroup; + children = ( + A9FE521B1D440E17001DD573 /* SPIRV-Tools */, + A9381E5C1CB55F4600287A79 /* SPIRV-Cross */, + A928C9171D0488DC00071B88 /* SPIRVConversion.h */, + A928C9181D0488DC00071B88 /* SPIRVConversion.mm */, + A9093F5A1C58013E0094110D /* SPIRVToMSLConverter.cpp */, + A9093F5B1C58013E0094110D /* SPIRVToMSLConverter.h */, + A9BB09751CEF89B100CCAB22 /* spirv.hpp */, + ); + path = MoltenVKSPIRVToMSLConverter; + sourceTree = ""; + }; + A90940A11C5808BB0094110D /* MoltenVKGLSLToSPIRVConverter */ = { + isa = PBXGroup; + children = ( + A97B21311C80B38D003F0FB4 /* glslang */, + A90940A31C5808BB0094110D /* GLSLToSPIRVConverter.cpp */, + A90940A41C5808BB0094110D /* GLSLToSPIRVConverter.h */, + A90941A11C581F840094110D /* GLSLConversion.mm */, + A90941A21C581F840094110D /* GLSLConversion.h */, + ); + path = MoltenVKGLSLToSPIRVConverter; + sourceTree = ""; + }; + A91425DF1EF9542E00891AFD /* OGLCompilersDLL */ = { + isa = PBXGroup; + children = ( + A91425E01EF9542E00891AFD /* CMakeLists.txt */, + A91425E11EF9542E00891AFD /* InitializeDll.cpp */, + A91425E21EF9542E00891AFD /* InitializeDll.h */, + ); + name = OGLCompilersDLL; + path = glslang/OGLCompilersDLL; + sourceTree = ""; + }; + A9381E5C1CB55F4600287A79 /* SPIRV-Cross */ = { + isa = PBXGroup; + children = ( + A95C5F3D1DEA9070000D17B6 /* spirv_cfg.cpp */, + A95C5F3E1DEA9070000D17B6 /* spirv_cfg.hpp */, + A9AB19901CB5B5A80001E7F9 /* spirv_common.hpp */, + A9AB19911CB5B5A80001E7F9 /* spirv_cross.cpp */, + A9AB19921CB5B5A80001E7F9 /* spirv_cross.hpp */, + A9AB19931CB5B5A80001E7F9 /* spirv_glsl.cpp */, + A9AB19941CB5B5A80001E7F9 /* spirv_glsl.hpp */, + A9AB19951CB5B5A80001E7F9 /* spirv_msl.cpp */, + A9AB19961CB5B5A80001E7F9 /* spirv_msl.hpp */, + ); + name = "SPIRV-Cross"; + sourceTree = ""; + }; + A964B28D1C57EBC400D930D8 /* Products */ = { + isa = PBXGroup; + children = ( + A93903C71C57E9ED00FE90DC /* MoltenVKSPIRVToMSLConverter.framework */, + A93903BF1C57E9D700FE90DC /* MoltenVKSPIRVToMSLConverter.framework */, + A964BD5F1C57EFBD00D930D8 /* MoltenVKShaderConverter */, + A964BD601C57EFBD00D930D8 /* MoltenVKGLSLToSPIRVConverter.framework */, + A964BD611C57EFBD00D930D8 /* MoltenVKGLSLToSPIRVConverter.framework */, + ); + name = Products; + sourceTree = ""; + }; + A97B21311C80B38D003F0FB4 /* glslang */ = { + isa = PBXGroup; + children = ( + A9D007E41EF952E200B49F9D /* glslang */, + A91425DF1EF9542E00891AFD /* OGLCompilersDLL */, + A9D008311EF952E200B49F9D /* SPIRV */, + ); + name = glslang; + sourceTree = ""; + }; + A97CC73C1C7527F3004A5C7E /* MoltenVKShaderConverterTool */ = { + isa = PBXGroup; + children = ( + A925B70A1C7754B2006E7ECD /* FileSupport.mm */, + A925B70B1C7754B2006E7ECD /* FileSupport.h */, + A97CC73D1C7527F3004A5C7E /* main.cpp */, + A97CC73E1C7527F3004A5C7E /* MoltenVKShaderConverterTool.cpp */, + A97CC73F1C7527F3004A5C7E /* MoltenVKShaderConverterTool.h */, + ); + path = MoltenVKShaderConverterTool; + sourceTree = ""; + }; + A994BE721FBF3F9700278EB9 /* source */ = { + isa = PBXGroup; + children = ( + A994BE731FBF3F9700278EB9 /* assembly_grammar.cpp */, + A994BE741FBF3F9700278EB9 /* assembly_grammar.h */, + A994BE751FBF3F9700278EB9 /* binary.cpp */, + A994BE761FBF3F9700278EB9 /* binary.h */, + A994BE771FBF3F9700278EB9 /* cfa.h */, + A994BE781FBF3F9700278EB9 /* CMakeLists.txt */, + A994BE791FBF3F9700278EB9 /* comp */, + A994BE7E1FBF3F9700278EB9 /* diagnostic.cpp */, + A994BE7F1FBF3F9700278EB9 /* diagnostic.h */, + A994BE801FBF3F9700278EB9 /* disassemble.cpp */, + A994BE811FBF3F9700278EB9 /* enum_set.h */, + A994BE821FBF3F9700278EB9 /* enum_string_mapping.cpp */, + A994BE831FBF3F9700278EB9 /* enum_string_mapping.h */, + A994BE841FBF3F9700278EB9 /* ext_inst.cpp */, + A994BE851FBF3F9700278EB9 /* ext_inst.h */, + A994BE861FBF3F9700278EB9 /* extensions.cpp */, + A994BE871FBF3F9700278EB9 /* extensions.h */, + A994BE881FBF3F9700278EB9 /* extinst.spv-amd-gcn-shader.grammar.json */, + A994BE891FBF3F9700278EB9 /* extinst.spv-amd-shader-ballot.grammar.json */, + A994BE8A1FBF3F9700278EB9 /* extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json */, + A994BE8B1FBF3F9700278EB9 /* extinst.spv-amd-shader-trinary-minmax.grammar.json */, + A994BE8C1FBF3F9700278EB9 /* id_descriptor.cpp */, + A994BE8D1FBF3F9700278EB9 /* id_descriptor.h */, + A994BE8E1FBF3F9700278EB9 /* instruction.h */, + A994BE8F1FBF3F9700278EB9 /* libspirv.cpp */, + A994BE901FBF3F9700278EB9 /* link */, + A994BE931FBF3F9700278EB9 /* macro.h */, + A994BE941FBF3F9700278EB9 /* message.cpp */, + A994BE951FBF3F9700278EB9 /* message.h */, + A994BE961FBF3F9700278EB9 /* name_mapper.cpp */, + A994BE971FBF3F9700278EB9 /* name_mapper.h */, + A994BE981FBF3F9700278EB9 /* opcode.cpp */, + A994BE991FBF3F9700278EB9 /* opcode.h */, + A994BE9A1FBF3F9700278EB9 /* operand.cpp */, + A994BE9B1FBF3F9700278EB9 /* operand.h */, + A994BE9C1FBF3F9700278EB9 /* opt */, + A994BEFC1FBF3F9700278EB9 /* parsed_operand.cpp */, + A994BEFD1FBF3F9700278EB9 /* parsed_operand.h */, + A994BEFE1FBF3F9700278EB9 /* print.cpp */, + A994BEFF1FBF3F9700278EB9 /* print.h */, + A994BF001FBF3F9700278EB9 /* software_version.cpp */, + A994BF011FBF3F9700278EB9 /* spirv_constant.h */, + A994BF021FBF3F9700278EB9 /* spirv_definition.h */, + A994BF031FBF3F9700278EB9 /* spirv_endian.cpp */, + A994BF041FBF3F9700278EB9 /* spirv_endian.h */, + A994BF051FBF3F9700278EB9 /* spirv_stats.cpp */, + A994BF061FBF3F9700278EB9 /* spirv_stats.h */, + A994BF071FBF3F9700278EB9 /* spirv_target_env.cpp */, + A994BF081FBF3F9700278EB9 /* spirv_target_env.h */, + A994BF091FBF3F9700278EB9 /* spirv_validator_options.cpp */, + A994BF0A1FBF3F9700278EB9 /* spirv_validator_options.h */, + A994BF0B1FBF3F9700278EB9 /* table.cpp */, + A994BF0C1FBF3F9700278EB9 /* table.h */, + A994BF0D1FBF3F9700278EB9 /* text.cpp */, + A994BF0E1FBF3F9700278EB9 /* text.h */, + A994BF0F1FBF3F9700278EB9 /* text_handler.cpp */, + A994BF101FBF3F9700278EB9 /* text_handler.h */, + A994BF111FBF3F9700278EB9 /* util */, + A994BF1E1FBF3F9700278EB9 /* val */, + A994BF2A1FBF3F9700278EB9 /* validate.cpp */, + A994BF2B1FBF3F9700278EB9 /* validate.h */, + A994BF2C1FBF3F9700278EB9 /* validate_arithmetics.cpp */, + A994BF2D1FBF3F9700278EB9 /* validate_bitwise.cpp */, + A994BF2E1FBF3F9700278EB9 /* validate_capability.cpp */, + A994BF2F1FBF3F9700278EB9 /* validate_cfg.cpp */, + A994BF301FBF3F9700278EB9 /* validate_conversion.cpp */, + A994BF311FBF3F9700278EB9 /* validate_datarules.cpp */, + A994BF321FBF3F9700278EB9 /* validate_decorations.cpp */, + A994BF331FBF3F9700278EB9 /* validate_id.cpp */, + A994BF341FBF3F9700278EB9 /* validate_instruction.cpp */, + A994BF351FBF3F9700278EB9 /* validate_layout.cpp */, + A994BF361FBF3F9700278EB9 /* validate_logicals.cpp */, + A994BF371FBF3F9700278EB9 /* validate_type_unique.cpp */, + ); + name = source; + path = "source"; + sourceTree = ""; + }; + A994BE791FBF3F9700278EB9 /* comp */ = { + isa = PBXGroup; + children = ( + A994BE7A1FBF3F9700278EB9 /* CMakeLists.txt */, + A994BE7B1FBF3F9700278EB9 /* markv.h */, + A994BE7C1FBF3F9700278EB9 /* markv_codec.cpp */, + A994BE7D1FBF3F9700278EB9 /* markv_model.h */, + ); + path = comp; + sourceTree = ""; + }; + A994BE901FBF3F9700278EB9 /* link */ = { + isa = PBXGroup; + children = ( + A994BE911FBF3F9700278EB9 /* CMakeLists.txt */, + A994BE921FBF3F9700278EB9 /* linker.cpp */, + ); + path = link; + sourceTree = ""; + }; + A994BE9C1FBF3F9700278EB9 /* opt */ = { + isa = PBXGroup; + children = ( + A994BE9D1FBF3F9700278EB9 /* aggressive_dead_code_elim_pass.cpp */, + A994BE9E1FBF3F9700278EB9 /* aggressive_dead_code_elim_pass.h */, + A994BE9F1FBF3F9700278EB9 /* basic_block.cpp */, + A994BEA01FBF3F9700278EB9 /* basic_block.h */, + A994BEA11FBF3F9700278EB9 /* block_merge_pass.cpp */, + A994BEA21FBF3F9700278EB9 /* block_merge_pass.h */, + A994BEA31FBF3F9700278EB9 /* build_module.cpp */, + A994BEA41FBF3F9700278EB9 /* build_module.h */, + A994BEA51FBF3F9700278EB9 /* cfg.cpp */, + A994BEA61FBF3F9700278EB9 /* cfg.h */, + A994BEA71FBF3F9700278EB9 /* cfg_cleanup_pass.cpp */, + A994BEA81FBF3F9700278EB9 /* cfg_cleanup_pass.h */, + A994BEA91FBF3F9700278EB9 /* CMakeLists.txt */, + A994BEAA1FBF3F9700278EB9 /* common_uniform_elim_pass.cpp */, + A994BEAB1FBF3F9700278EB9 /* common_uniform_elim_pass.h */, + A994BEAC1FBF3F9700278EB9 /* compact_ids_pass.cpp */, + A994BEAD1FBF3F9700278EB9 /* compact_ids_pass.h */, + A994BEAE1FBF3F9700278EB9 /* constants.h */, + A994BEAF1FBF3F9700278EB9 /* dead_branch_elim_pass.cpp */, + A994BEB01FBF3F9700278EB9 /* dead_branch_elim_pass.h */, + A994BEB11FBF3F9700278EB9 /* dead_variable_elimination.cpp */, + A994BEB21FBF3F9700278EB9 /* dead_variable_elimination.h */, + A994BEB31FBF3F9700278EB9 /* decoration_manager.cpp */, + A994BEB41FBF3F9700278EB9 /* decoration_manager.h */, + A994BEB51FBF3F9700278EB9 /* def_use_manager.cpp */, + A994BEB61FBF3F9700278EB9 /* def_use_manager.h */, + A994BEB71FBF3F9700278EB9 /* eliminate_dead_constant_pass.cpp */, + A994BEB81FBF3F9700278EB9 /* eliminate_dead_constant_pass.h */, + A994BEB91FBF3F9700278EB9 /* eliminate_dead_functions_pass.cpp */, + A994BEBA1FBF3F9700278EB9 /* eliminate_dead_functions_pass.h */, + A994BEBB1FBF3F9700278EB9 /* flatten_decoration_pass.cpp */, + A994BEBC1FBF3F9700278EB9 /* flatten_decoration_pass.h */, + A994BEBD1FBF3F9700278EB9 /* fold.cpp */, + A994BEBE1FBF3F9700278EB9 /* fold.h */, + A994BEBF1FBF3F9700278EB9 /* fold_spec_constant_op_and_composite_pass.cpp */, + A994BEC01FBF3F9700278EB9 /* fold_spec_constant_op_and_composite_pass.h */, + A994BEC11FBF3F9700278EB9 /* freeze_spec_constant_value_pass.cpp */, + A994BEC21FBF3F9700278EB9 /* freeze_spec_constant_value_pass.h */, + A994BEC31FBF3F9700278EB9 /* function.cpp */, + A994BEC41FBF3F9700278EB9 /* function.h */, + A994BEC51FBF3F9700278EB9 /* inline_exhaustive_pass.cpp */, + A994BEC61FBF3F9700278EB9 /* inline_exhaustive_pass.h */, + A994BEC71FBF3F9700278EB9 /* inline_opaque_pass.cpp */, + A994BEC81FBF3F9700278EB9 /* inline_opaque_pass.h */, + A994BEC91FBF3F9700278EB9 /* inline_pass.cpp */, + A994BECA1FBF3F9700278EB9 /* inline_pass.h */, + A994BECB1FBF3F9700278EB9 /* insert_extract_elim.cpp */, + A994BECC1FBF3F9700278EB9 /* insert_extract_elim.h */, + A994BECD1FBF3F9700278EB9 /* instruction.cpp */, + A994BECE1FBF3F9700278EB9 /* instruction.h */, + A994BECF1FBF3F9700278EB9 /* instruction_list.cpp */, + A994BED01FBF3F9700278EB9 /* instruction_list.h */, + A994BED11FBF3F9700278EB9 /* ir_context.cpp */, + A994BED21FBF3F9700278EB9 /* ir_context.h */, + A994BED31FBF3F9700278EB9 /* ir_loader.cpp */, + A994BED41FBF3F9700278EB9 /* ir_loader.h */, + A994BED51FBF3F9700278EB9 /* iterator.h */, + A994BED61FBF3F9700278EB9 /* local_access_chain_convert_pass.cpp */, + A994BED71FBF3F9700278EB9 /* local_access_chain_convert_pass.h */, + A994BED81FBF3F9700278EB9 /* local_single_block_elim_pass.cpp */, + A994BED91FBF3F9700278EB9 /* local_single_block_elim_pass.h */, + A994BEDA1FBF3F9700278EB9 /* local_single_store_elim_pass.cpp */, + A994BEDB1FBF3F9700278EB9 /* local_single_store_elim_pass.h */, + A994BEDC1FBF3F9700278EB9 /* local_ssa_elim_pass.cpp */, + A994BEDD1FBF3F9700278EB9 /* local_ssa_elim_pass.h */, + A994BEDE1FBF3F9700278EB9 /* log.h */, + A994BEDF1FBF3F9700278EB9 /* make_unique.h */, + A994BEE01FBF3F9700278EB9 /* mem_pass.cpp */, + A994BEE11FBF3F9700278EB9 /* mem_pass.h */, + A994BEE21FBF3F9700278EB9 /* merge_return_pass.cpp */, + A994BEE31FBF3F9700278EB9 /* merge_return_pass.h */, + A994BEE41FBF3F9700278EB9 /* module.cpp */, + A994BEE51FBF3F9700278EB9 /* module.h */, + A994BEE61FBF3F9700278EB9 /* null_pass.h */, + A994BEE71FBF3F9700278EB9 /* optimizer.cpp */, + A994BEE81FBF3F9700278EB9 /* pass.cpp */, + A994BEE91FBF3F9700278EB9 /* pass.h */, + A994BEEA1FBF3F9700278EB9 /* pass_manager.cpp */, + A994BEEB1FBF3F9700278EB9 /* pass_manager.h */, + A994BEEC1FBF3F9700278EB9 /* passes.h */, + A994BEED1FBF3F9700278EB9 /* reflect.h */, + A994BEEE1FBF3F9700278EB9 /* remove_duplicates_pass.cpp */, + A994BEEF1FBF3F9700278EB9 /* remove_duplicates_pass.h */, + A994BEF01FBF3F9700278EB9 /* set_spec_constant_default_value_pass.cpp */, + A994BEF11FBF3F9700278EB9 /* set_spec_constant_default_value_pass.h */, + A994BEF21FBF3F9700278EB9 /* strength_reduction_pass.cpp */, + A994BEF31FBF3F9700278EB9 /* strength_reduction_pass.h */, + A994BEF41FBF3F9700278EB9 /* strip_debug_info_pass.cpp */, + A994BEF51FBF3F9700278EB9 /* strip_debug_info_pass.h */, + A994BEF61FBF3F9700278EB9 /* type_manager.cpp */, + A994BEF71FBF3F9700278EB9 /* type_manager.h */, + A994BEF81FBF3F9700278EB9 /* types.cpp */, + A994BEF91FBF3F9700278EB9 /* types.h */, + A994BEFA1FBF3F9700278EB9 /* unify_const_pass.cpp */, + A994BEFB1FBF3F9700278EB9 /* unify_const_pass.h */, + ); + path = opt; + sourceTree = ""; + }; + A994BF111FBF3F9700278EB9 /* util */ = { + isa = PBXGroup; + children = ( + A994BF121FBF3F9700278EB9 /* bit_stream.cpp */, + A994BF131FBF3F9700278EB9 /* bit_stream.h */, + A994BF141FBF3F9700278EB9 /* bitutils.h */, + A994BF151FBF3F9700278EB9 /* hex_float.h */, + A994BF161FBF3F9700278EB9 /* huffman_codec.h */, + A994BF171FBF3F9700278EB9 /* ilist.h */, + A994BF181FBF3F9700278EB9 /* ilist_node.h */, + A994BF191FBF3F9700278EB9 /* move_to_front.h */, + A994BF1A1FBF3F9700278EB9 /* parse_number.cpp */, + A994BF1B1FBF3F9700278EB9 /* parse_number.h */, + A994BF1C1FBF3F9700278EB9 /* string_utils.cpp */, + A994BF1D1FBF3F9700278EB9 /* string_utils.h */, + ); + path = util; + sourceTree = ""; + }; + A994BF1E1FBF3F9700278EB9 /* val */ = { + isa = PBXGroup; + children = ( + A994BF1F1FBF3F9700278EB9 /* basic_block.cpp */, + A994BF201FBF3F9700278EB9 /* basic_block.h */, + A994BF211FBF3F9700278EB9 /* construct.cpp */, + A994BF221FBF3F9700278EB9 /* construct.h */, + A994BF231FBF3F9700278EB9 /* decoration.h */, + A994BF241FBF3F9700278EB9 /* function.cpp */, + A994BF251FBF3F9700278EB9 /* function.h */, + A994BF261FBF3F9700278EB9 /* instruction.cpp */, + A994BF271FBF3F9700278EB9 /* instruction.h */, + A994BF281FBF3F9700278EB9 /* validation_state.cpp */, + A994BF291FBF3F9700278EB9 /* validation_state.h */, + ); + path = val; + sourceTree = ""; + }; + A9D007E41EF952E200B49F9D /* glslang */ = { + isa = PBXGroup; + children = ( + A9D007E51EF952E200B49F9D /* CMakeLists.txt */, + A9D007E61EF952E200B49F9D /* GenericCodeGen */, + A9D007E91EF952E200B49F9D /* Include */, + A9D007F71EF952E200B49F9D /* MachineIndependent */, + A9D008251EF952E200B49F9D /* OSDependent */, + A9D0082E1EF952E200B49F9D /* Public */, + A9D008301EF952E200B49F9D /* updateGrammar */, + ); + name = glslang; + path = glslang/glslang; + sourceTree = ""; + }; + A9D007E61EF952E200B49F9D /* GenericCodeGen */ = { + isa = PBXGroup; + children = ( + A9D007E71EF952E200B49F9D /* CodeGen.cpp */, + A9D007E81EF952E200B49F9D /* Link.cpp */, + ); + path = GenericCodeGen; + sourceTree = ""; + }; + A9D007E91EF952E200B49F9D /* Include */ = { + isa = PBXGroup; + children = ( + A9D007EA1EF952E200B49F9D /* arrays.h */, + A9D007EB1EF952E200B49F9D /* BaseTypes.h */, + A9D007EC1EF952E200B49F9D /* Common.h */, + A9D007ED1EF952E200B49F9D /* ConstantUnion.h */, + A9D007EE1EF952E200B49F9D /* InfoSink.h */, + A9D007EF1EF952E200B49F9D /* InitializeGlobals.h */, + A9D007F01EF952E200B49F9D /* intermediate.h */, + A9D007F11EF952E200B49F9D /* PoolAlloc.h */, + A9D007F21EF952E200B49F9D /* ResourceLimits.h */, + A9D007F31EF952E200B49F9D /* revision.h */, + A9D007F41EF952E200B49F9D /* revision.template */, + A9D007F51EF952E200B49F9D /* ShHandle.h */, + A9D007F61EF952E200B49F9D /* Types.h */, + ); + path = Include; + sourceTree = ""; + }; + A9D007F71EF952E200B49F9D /* MachineIndependent */ = { + isa = PBXGroup; + children = ( + A9D007F81EF952E200B49F9D /* Constant.cpp */, + A9D007F91EF952E200B49F9D /* gl_types.h */, + A9D007FB1EF952E200B49F9D /* glslang_tab.cpp */, + A9D007FC1EF952E200B49F9D /* glslang_tab.cpp.h */, + A9D007FD1EF952E200B49F9D /* InfoSink.cpp */, + A9D007FE1EF952E200B49F9D /* Initialize.cpp */, + A9D007FF1EF952E200B49F9D /* Initialize.h */, + A9D008001EF952E200B49F9D /* Intermediate.cpp */, + A9D008011EF952E200B49F9D /* intermOut.cpp */, + A9D008021EF952E200B49F9D /* IntermTraverse.cpp */, + A9D008031EF952E200B49F9D /* iomapper.cpp */, + A9D008041EF952E200B49F9D /* iomapper.h */, + A9D008051EF952E200B49F9D /* limits.cpp */, + A9D008061EF952E200B49F9D /* linkValidate.cpp */, + A9D008071EF952E200B49F9D /* LiveTraverser.h */, + A9D008081EF952E200B49F9D /* localintermediate.h */, + A9D008091EF952E200B49F9D /* parseConst.cpp */, + A9D0080A1EF952E200B49F9D /* ParseContextBase.cpp */, + A9D0080B1EF952E200B49F9D /* ParseHelper.cpp */, + A9D0080C1EF952E200B49F9D /* ParseHelper.h */, + A9D0080D1EF952E200B49F9D /* parseVersions.h */, + A9D0080E1EF952E200B49F9D /* PoolAlloc.cpp */, + A9D0080F1EF952E200B49F9D /* preprocessor */, + A9D008171EF952E200B49F9D /* propagateNoContraction.cpp */, + A9D008181EF952E200B49F9D /* propagateNoContraction.h */, + A9D008191EF952E200B49F9D /* reflection.cpp */, + A9D0081A1EF952E200B49F9D /* reflection.h */, + A9D0081B1EF952E200B49F9D /* RemoveTree.cpp */, + A9D0081C1EF952E200B49F9D /* RemoveTree.h */, + A9D0081D1EF952E200B49F9D /* Scan.cpp */, + A9D0081E1EF952E200B49F9D /* Scan.h */, + A9D0081F1EF952E200B49F9D /* ScanContext.h */, + A9D008201EF952E200B49F9D /* ShaderLang.cpp */, + A9D008211EF952E200B49F9D /* SymbolTable.cpp */, + A9D008221EF952E200B49F9D /* SymbolTable.h */, + A9D008231EF952E200B49F9D /* Versions.cpp */, + A9D008241EF952E200B49F9D /* Versions.h */, + ); + path = MachineIndependent; + sourceTree = ""; + }; + A9D0080F1EF952E200B49F9D /* preprocessor */ = { + isa = PBXGroup; + children = ( + A9D008101EF952E200B49F9D /* Pp.cpp */, + A9D008111EF952E200B49F9D /* PpAtom.cpp */, + A9D008121EF952E200B49F9D /* PpContext.cpp */, + A9D008131EF952E200B49F9D /* PpContext.h */, + A9D008141EF952E200B49F9D /* PpScanner.cpp */, + A9D008151EF952E200B49F9D /* PpTokens.cpp */, + A9D008161EF952E200B49F9D /* PpTokens.h */, + ); + path = preprocessor; + sourceTree = ""; + }; + A9D008251EF952E200B49F9D /* OSDependent */ = { + isa = PBXGroup; + children = ( + A9D008261EF952E200B49F9D /* osinclude.h */, + A9D008271EF952E200B49F9D /* Unix */, + ); + path = OSDependent; + sourceTree = ""; + }; + A9D008271EF952E200B49F9D /* Unix */ = { + isa = PBXGroup; + children = ( + A9D008281EF952E200B49F9D /* CMakeLists.txt */, + A9D008291EF952E200B49F9D /* ossource.cpp */, + ); + path = Unix; + sourceTree = ""; + }; + A9D0082E1EF952E200B49F9D /* Public */ = { + isa = PBXGroup; + children = ( + A9D0082F1EF952E200B49F9D /* ShaderLang.h */, + ); + path = Public; + sourceTree = ""; + }; + A9D008311EF952E200B49F9D /* SPIRV */ = { + isa = PBXGroup; + children = ( + A9D008321EF952E200B49F9D /* bitutils.h */, + A9D008331EF952E200B49F9D /* CMakeLists.txt */, + A9D008341EF952E200B49F9D /* disassemble.cpp */, + A9D008351EF952E200B49F9D /* disassemble.h */, + A9D008361EF952E200B49F9D /* doc.cpp */, + A9D008371EF952E200B49F9D /* doc.h */, + A9D008381EF952E200B49F9D /* GLSL.ext.AMD.h */, + A9D008391EF952E200B49F9D /* GLSL.ext.KHR.h */, + A9D0083A1EF952E200B49F9D /* GLSL.ext.NV.h */, + A9D0083B1EF952E200B49F9D /* GLSL.std.450.h */, + A9D0083C1EF952E200B49F9D /* GlslangToSpv.cpp */, + A9D0083D1EF952E200B49F9D /* GlslangToSpv.h */, + A9D0083E1EF952E200B49F9D /* hex_float.h */, + A9D0083F1EF952E200B49F9D /* InReadableOrder.cpp */, + A9D008401EF952E200B49F9D /* Logger.cpp */, + A9D008411EF952E200B49F9D /* Logger.h */, + A9D008421EF952E200B49F9D /* spirv.hpp */, + A9D008431EF952E200B49F9D /* SpvBuilder.cpp */, + A9D008441EF952E200B49F9D /* SpvBuilder.h */, + A9D008451EF952E200B49F9D /* spvIR.h */, + A9D008461EF952E200B49F9D /* SPVRemapper.cpp */, + A9D008471EF952E200B49F9D /* SPVRemapper.h */, + ); + name = SPIRV; + path = glslang/SPIRV; + sourceTree = ""; + }; + A9F042A81FB4D060009FCCB8 /* Common */ = { + isa = PBXGroup; + children = ( + A9F042AA1FB4D060009FCCB8 /* MVKCommonEnvironment.h */, + A9F042AB1FB4D060009FCCB8 /* MVKLogging.h */, + A98149651FB6A98A005F00B4 /* MVKStrings.h */, + ); + name = Common; + path = ../Common; + sourceTree = ""; + }; + A9F55D24198BE6A7004EC31B = { + isa = PBXGroup; + children = ( + A9093C561C58013D0094110D /* MoltenVKSPIRVToMSLConverter */, + A90940A11C5808BB0094110D /* MoltenVKGLSLToSPIRVConverter */, + A97CC73C1C7527F3004A5C7E /* MoltenVKShaderConverterTool */, + A9F042A81FB4D060009FCCB8 /* Common */, + A928C8F51D047C2300071B88 /* ThirdPartyConfig.md */, + A964B28D1C57EBC400D930D8 /* Products */, + ); + sourceTree = ""; + }; + A9FE521B1D440E17001DD573 /* SPIRV-Tools */ = { + isa = PBXGroup; + children = ( + A994BE721FBF3F9700278EB9 /* source */, + ); + path = "SPIRV-Tools"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + A93747291A9A8B2900F29B34 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + A9D008DA1EF952E200B49F9D /* GlslangToSpv.h in Headers */, + A9D008B61EF952E200B49F9D /* SymbolTable.h in Headers */, + A9D008981EF952E200B49F9D /* PpContext.h in Headers */, + A9D0089E1EF952E200B49F9D /* PpTokens.h in Headers */, + A9D0084C1EF952E200B49F9D /* arrays.h in Headers */, + A9D0087C1EF952E200B49F9D /* iomapper.h in Headers */, + A9D008821EF952E200B49F9D /* LiveTraverser.h in Headers */, + A90941A51C581F840094110D /* GLSLConversion.h in Headers */, + A9D008581EF952E200B49F9D /* intermediate.h in Headers */, + A9D008EE1EF952E200B49F9D /* SPVRemapper.h in Headers */, + A9D008CE1EF952E200B49F9D /* doc.h in Headers */, + A9D0085E1EF952E200B49F9D /* revision.h in Headers */, + A9D008541EF952E200B49F9D /* InfoSink.h in Headers */, + A9D008D61EF952E200B49F9D /* GLSL.std.450.h in Headers */, + A9D0084E1EF952E200B49F9D /* BaseTypes.h in Headers */, + A9D0088E1EF952E200B49F9D /* parseVersions.h in Headers */, + A9D008D21EF952E200B49F9D /* GLSL.ext.KHR.h in Headers */, + A9D008AA1EF952E200B49F9D /* RemoveTree.h in Headers */, + A9D008E81EF952E200B49F9D /* SpvBuilder.h in Headers */, + A9D008E21EF952E200B49F9D /* Logger.h in Headers */, + A9D008B01EF952E200B49F9D /* ScanContext.h in Headers */, + A9D008621EF952E200B49F9D /* Types.h in Headers */, + A91425E51EF9542E00891AFD /* InitializeDll.h in Headers */, + A9D008601EF952E200B49F9D /* ShHandle.h in Headers */, + A9D008A21EF952E200B49F9D /* propagateNoContraction.h in Headers */, + A9D008AE1EF952E200B49F9D /* Scan.h in Headers */, + A9D008E41EF952E200B49F9D /* spirv.hpp in Headers */, + A9D008D01EF952E200B49F9D /* GLSL.ext.AMD.h in Headers */, + A9D008A61EF952E200B49F9D /* reflection.h in Headers */, + A9D008561EF952E200B49F9D /* InitializeGlobals.h in Headers */, + A9D008521EF952E200B49F9D /* ConstantUnion.h in Headers */, + A9D008C41EF952E200B49F9D /* ShaderLang.h in Headers */, + A9D008841EF952E200B49F9D /* localintermediate.h in Headers */, + A9D0085A1EF952E200B49F9D /* PoolAlloc.h in Headers */, + A9D008EA1EF952E200B49F9D /* spvIR.h in Headers */, + A9D008BA1EF952E200B49F9D /* Versions.h in Headers */, + A9D008D41EF952E200B49F9D /* GLSL.ext.NV.h in Headers */, + A9D008DC1EF952E200B49F9D /* hex_float.h in Headers */, + A9D008661EF952E200B49F9D /* gl_types.h in Headers */, + A9F042B41FB4D060009FCCB8 /* MVKLogging.h in Headers */, + A9D0085C1EF952E200B49F9D /* ResourceLimits.h in Headers */, + A90940A91C5808BB0094110D /* GLSLToSPIRVConverter.h in Headers */, + A9D008721EF952E200B49F9D /* Initialize.h in Headers */, + A9D008CA1EF952E200B49F9D /* disassemble.h in Headers */, + A9D008501EF952E200B49F9D /* Common.h in Headers */, + A9D008C61EF952E200B49F9D /* bitutils.h in Headers */, + A9F042B01FB4D060009FCCB8 /* MVKCommonEnvironment.h in Headers */, + A9D008BC1EF952E200B49F9D /* osinclude.h in Headers */, + A9D0088C1EF952E200B49F9D /* ParseHelper.h in Headers */, + A9D0086C1EF952E200B49F9D /* glslang_tab.cpp.h in Headers */, + A98149661FB6A98A005F00B4 /* MVKStrings.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A937476E1A9A98D000F29B34 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + A9D008DB1EF952E200B49F9D /* GlslangToSpv.h in Headers */, + A9D008B71EF952E200B49F9D /* SymbolTable.h in Headers */, + A9D008991EF952E200B49F9D /* PpContext.h in Headers */, + A9D0089F1EF952E200B49F9D /* PpTokens.h in Headers */, + A9D0084D1EF952E200B49F9D /* arrays.h in Headers */, + A9D0087D1EF952E200B49F9D /* iomapper.h in Headers */, + A9D008831EF952E200B49F9D /* LiveTraverser.h in Headers */, + A90941A61C581F840094110D /* GLSLConversion.h in Headers */, + A9D008591EF952E200B49F9D /* intermediate.h in Headers */, + A9D008EF1EF952E200B49F9D /* SPVRemapper.h in Headers */, + A9D008CF1EF952E200B49F9D /* doc.h in Headers */, + A9D0085F1EF952E200B49F9D /* revision.h in Headers */, + A9D008551EF952E200B49F9D /* InfoSink.h in Headers */, + A9D008D71EF952E200B49F9D /* GLSL.std.450.h in Headers */, + A9D0084F1EF952E200B49F9D /* BaseTypes.h in Headers */, + A9D0088F1EF952E200B49F9D /* parseVersions.h in Headers */, + A9D008D31EF952E200B49F9D /* GLSL.ext.KHR.h in Headers */, + A9D008AB1EF952E200B49F9D /* RemoveTree.h in Headers */, + A9D008E91EF952E200B49F9D /* SpvBuilder.h in Headers */, + A9D008E31EF952E200B49F9D /* Logger.h in Headers */, + A9D008B11EF952E200B49F9D /* ScanContext.h in Headers */, + A9D008631EF952E200B49F9D /* Types.h in Headers */, + A91425E61EF9542E00891AFD /* InitializeDll.h in Headers */, + A9D008611EF952E200B49F9D /* ShHandle.h in Headers */, + A9D008A31EF952E200B49F9D /* propagateNoContraction.h in Headers */, + A9D008AF1EF952E200B49F9D /* Scan.h in Headers */, + A9D008E51EF952E200B49F9D /* spirv.hpp in Headers */, + A9D008D11EF952E200B49F9D /* GLSL.ext.AMD.h in Headers */, + A9D008A71EF952E200B49F9D /* reflection.h in Headers */, + A9D008571EF952E200B49F9D /* InitializeGlobals.h in Headers */, + A9D008531EF952E200B49F9D /* ConstantUnion.h in Headers */, + A9D008C51EF952E200B49F9D /* ShaderLang.h in Headers */, + A9D008851EF952E200B49F9D /* localintermediate.h in Headers */, + A9D0085B1EF952E200B49F9D /* PoolAlloc.h in Headers */, + A9D008EB1EF952E200B49F9D /* spvIR.h in Headers */, + A9D008BB1EF952E200B49F9D /* Versions.h in Headers */, + A9D008D51EF952E200B49F9D /* GLSL.ext.NV.h in Headers */, + A9D008DD1EF952E200B49F9D /* hex_float.h in Headers */, + A9D008671EF952E200B49F9D /* gl_types.h in Headers */, + A9F042B51FB4D060009FCCB8 /* MVKLogging.h in Headers */, + A9D0085D1EF952E200B49F9D /* ResourceLimits.h in Headers */, + A90940AA1C5808BB0094110D /* GLSLToSPIRVConverter.h in Headers */, + A9D008731EF952E200B49F9D /* Initialize.h in Headers */, + A9D008CB1EF952E200B49F9D /* disassemble.h in Headers */, + A9D008511EF952E200B49F9D /* Common.h in Headers */, + A9D008C71EF952E200B49F9D /* bitutils.h in Headers */, + A9F042B11FB4D060009FCCB8 /* MVKCommonEnvironment.h in Headers */, + A9D008BD1EF952E200B49F9D /* osinclude.h in Headers */, + A9D0088D1EF952E200B49F9D /* ParseHelper.h in Headers */, + A9D0086D1EF952E200B49F9D /* glslang_tab.cpp.h in Headers */, + A98149671FB6A98A005F00B4 /* MVKStrings.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A93903B91C57E9D700FE90DC /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + A994BF8A1FBF3F9700278EB9 /* cfg.h in Headers */, + A994C0621FBF3F9700278EB9 /* bitutils.h in Headers */, + A994BF8E1FBF3F9700278EB9 /* cfg_cleanup_pass.h in Headers */, + A994C0601FBF3F9700278EB9 /* bit_stream.h in Headers */, + A994BFE41FBF3F9700278EB9 /* ir_loader.h in Headers */, + A98149681FB6A98A005F00B4 /* MVKStrings.h in Headers */, + A994BFDC1FBF3F9700278EB9 /* instruction_list.h in Headers */, + A994C0701FBF3F9700278EB9 /* parse_number.h in Headers */, + A994BF3E1FBF3F9700278EB9 /* binary.h in Headers */, + A994C0081FBF3F9700278EB9 /* null_pass.h in Headers */, + A994C01A1FBF3F9700278EB9 /* remove_duplicates_pass.h in Headers */, + A994C02A1FBF3F9700278EB9 /* type_manager.h in Headers */, + A994BF721FBF3F9700278EB9 /* opcode.h in Headers */, + A994BFC81FBF3F9700278EB9 /* inline_exhaustive_pass.h in Headers */, + A9BB09761CEF89B100CCAB22 /* spirv.hpp in Headers */, + A994BFF61FBF3F9700278EB9 /* local_ssa_elim_pass.h in Headers */, + A994C07E1FBF3F9700278EB9 /* decoration.h in Headers */, + A994C0221FBF3F9700278EB9 /* strength_reduction_pass.h in Headers */, + A994C0161FBF3F9700278EB9 /* reflect.h in Headers */, + A928C9191D0488DC00071B88 /* SPIRVConversion.h in Headers */, + A994BF461FBF3F9700278EB9 /* markv_model.h in Headers */, + A994C0641FBF3F9700278EB9 /* hex_float.h in Headers */, + A994C0581FBF3F9700278EB9 /* text.h in Headers */, + A994C06A1FBF3F9700278EB9 /* ilist_node.h in Headers */, + A994BF9C1FBF3F9700278EB9 /* dead_branch_elim_pass.h in Headers */, + A994BF961FBF3F9700278EB9 /* compact_ids_pass.h in Headers */, + A9AB19971CB5B5A80001E7F9 /* spirv_common.hpp in Headers */, + A994BF561FBF3F9700278EB9 /* ext_inst.h in Headers */, + A994C0821FBF3F9700278EB9 /* function.h in Headers */, + A9F042B61FB4D060009FCCB8 /* MVKLogging.h in Headers */, + A994C05C1FBF3F9700278EB9 /* text_handler.h in Headers */, + A994BF981FBF3F9700278EB9 /* constants.h in Headers */, + A994BFB41FBF3F9700278EB9 /* flatten_decoration_pass.h in Headers */, + A994BF7A1FBF3F9700278EB9 /* aggressive_dead_code_elim_pass.h in Headers */, + A994BF4A1FBF3F9700278EB9 /* diagnostic.h in Headers */, + A994C04C1FBF3F9700278EB9 /* spirv_target_env.h in Headers */, + A994BF921FBF3F9700278EB9 /* common_uniform_elim_pass.h in Headers */, + A994C0121FBF3F9700278EB9 /* pass_manager.h in Headers */, + A994BF601FBF3F9700278EB9 /* instruction.h in Headers */, + A994BFEE1FBF3F9700278EB9 /* local_single_block_elim_pass.h in Headers */, + A994C02E1FBF3F9700278EB9 /* types.h in Headers */, + A994C0441FBF3F9700278EB9 /* spirv_endian.h in Headers */, + A994C0681FBF3F9700278EB9 /* ilist.h in Headers */, + A994BF6A1FBF3F9700278EB9 /* message.h in Headers */, + A994C0781FBF3F9700278EB9 /* basic_block.h in Headers */, + A994BFE61FBF3F9700278EB9 /* iterator.h in Headers */, + A994C0261FBF3F9700278EB9 /* strip_debug_info_pass.h in Headers */, + A994BFE01FBF3F9700278EB9 /* ir_context.h in Headers */, + A994C07C1FBF3F9700278EB9 /* construct.h in Headers */, + A9AB199B1CB5B5A80001E7F9 /* spirv_cross.hpp in Headers */, + A994BFFA1FBF3F9700278EB9 /* make_unique.h in Headers */, + A994BFA41FBF3F9700278EB9 /* decoration_manager.h in Headers */, + A994BFC41FBF3F9700278EB9 /* function.h in Headers */, + A994C0501FBF3F9700278EB9 /* spirv_validator_options.h in Headers */, + A994C0541FBF3F9700278EB9 /* table.h in Headers */, + A994BF3A1FBF3F9700278EB9 /* assembly_grammar.h in Headers */, + A994C0861FBF3F9700278EB9 /* instruction.h in Headers */, + A994C0481FBF3F9700278EB9 /* spirv_stats.h in Headers */, + A994BF7E1FBF3F9700278EB9 /* basic_block.h in Headers */, + A994C03E1FBF3F9700278EB9 /* spirv_constant.h in Headers */, + A909408C1C58013E0094110D /* SPIRVToMSLConverter.h in Headers */, + A994BFF81FBF3F9700278EB9 /* log.h in Headers */, + A994BFB81FBF3F9700278EB9 /* fold.h in Headers */, + A994BF521FBF3F9700278EB9 /* enum_string_mapping.h in Headers */, + A994BFB01FBF3F9700278EB9 /* eliminate_dead_functions_pass.h in Headers */, + A994BFC01FBF3F9700278EB9 /* freeze_spec_constant_value_pass.h in Headers */, + A9AB19A31CB5B5A80001E7F9 /* spirv_msl.hpp in Headers */, + A994C0361FBF3F9700278EB9 /* parsed_operand.h in Headers */, + A994BF6E1FBF3F9700278EB9 /* name_mapper.h in Headers */, + A994BFCC1FBF3F9700278EB9 /* inline_opaque_pass.h in Headers */, + A994BFA01FBF3F9700278EB9 /* dead_variable_elimination.h in Headers */, + A994C01E1FBF3F9700278EB9 /* set_spec_constant_default_value_pass.h in Headers */, + A994BFEA1FBF3F9700278EB9 /* local_access_chain_convert_pass.h in Headers */, + A994BF661FBF3F9700278EB9 /* macro.h in Headers */, + A994C08E1FBF3F9700278EB9 /* validate.h in Headers */, + A994BFD41FBF3F9700278EB9 /* insert_extract_elim.h in Headers */, + A9F042B21FB4D060009FCCB8 /* MVKCommonEnvironment.h in Headers */, + A994C06C1FBF3F9700278EB9 /* move_to_front.h in Headers */, + A994C0021FBF3F9700278EB9 /* merge_return_pass.h in Headers */, + A994C0321FBF3F9700278EB9 /* unify_const_pass.h in Headers */, + A994BF401FBF3F9700278EB9 /* cfa.h in Headers */, + A994C0141FBF3F9700278EB9 /* passes.h in Headers */, + A994BF4E1FBF3F9700278EB9 /* enum_set.h in Headers */, + A994C0741FBF3F9700278EB9 /* string_utils.h in Headers */, + A994C00E1FBF3F9700278EB9 /* pass.h in Headers */, + A994C0661FBF3F9700278EB9 /* huffman_codec.h in Headers */, + A994BF421FBF3F9700278EB9 /* markv.h in Headers */, + A994BFD81FBF3F9700278EB9 /* instruction.h in Headers */, + A994BF861FBF3F9700278EB9 /* build_module.h in Headers */, + A994BF821FBF3F9700278EB9 /* block_merge_pass.h in Headers */, + A994BFFE1FBF3F9700278EB9 /* mem_pass.h in Headers */, + A994BFBC1FBF3F9700278EB9 /* fold_spec_constant_op_and_composite_pass.h in Headers */, + A994BF5E1FBF3F9700278EB9 /* id_descriptor.h in Headers */, + A994BF761FBF3F9700278EB9 /* operand.h in Headers */, + A994C0061FBF3F9700278EB9 /* module.h in Headers */, + A994BFA81FBF3F9700278EB9 /* def_use_manager.h in Headers */, + A994BFAC1FBF3F9700278EB9 /* eliminate_dead_constant_pass.h in Headers */, + A994C03A1FBF3F9700278EB9 /* print.h in Headers */, + A994BFD01FBF3F9700278EB9 /* inline_pass.h in Headers */, + A994BFF21FBF3F9700278EB9 /* local_single_store_elim_pass.h in Headers */, + A994C08A1FBF3F9700278EB9 /* validation_state.h in Headers */, + A9AB199F1CB5B5A80001E7F9 /* spirv_glsl.hpp in Headers */, + A95C5F411DEA9070000D17B6 /* spirv_cfg.hpp in Headers */, + A994C0401FBF3F9700278EB9 /* spirv_definition.h in Headers */, + A994BF5A1FBF3F9700278EB9 /* extensions.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A93903C11C57E9ED00FE90DC /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + A994BF8B1FBF3F9700278EB9 /* cfg.h in Headers */, + A994C0631FBF3F9700278EB9 /* bitutils.h in Headers */, + A994BF8F1FBF3F9700278EB9 /* cfg_cleanup_pass.h in Headers */, + A994C0611FBF3F9700278EB9 /* bit_stream.h in Headers */, + A994BFE51FBF3F9700278EB9 /* ir_loader.h in Headers */, + A98149691FB6A98A005F00B4 /* MVKStrings.h in Headers */, + A994BFDD1FBF3F9700278EB9 /* instruction_list.h in Headers */, + A994C0711FBF3F9700278EB9 /* parse_number.h in Headers */, + A994BF3F1FBF3F9700278EB9 /* binary.h in Headers */, + A994C0091FBF3F9700278EB9 /* null_pass.h in Headers */, + A994C01B1FBF3F9700278EB9 /* remove_duplicates_pass.h in Headers */, + A994C02B1FBF3F9700278EB9 /* type_manager.h in Headers */, + A994BF731FBF3F9700278EB9 /* opcode.h in Headers */, + A994BFC91FBF3F9700278EB9 /* inline_exhaustive_pass.h in Headers */, + A9BB09771CEF89B100CCAB22 /* spirv.hpp in Headers */, + A994BFF71FBF3F9700278EB9 /* local_ssa_elim_pass.h in Headers */, + A994C07F1FBF3F9700278EB9 /* decoration.h in Headers */, + A994C0231FBF3F9700278EB9 /* strength_reduction_pass.h in Headers */, + A994C0171FBF3F9700278EB9 /* reflect.h in Headers */, + A928C91A1D0488DC00071B88 /* SPIRVConversion.h in Headers */, + A994BF471FBF3F9700278EB9 /* markv_model.h in Headers */, + A994C0651FBF3F9700278EB9 /* hex_float.h in Headers */, + A994C0591FBF3F9700278EB9 /* text.h in Headers */, + A994C06B1FBF3F9700278EB9 /* ilist_node.h in Headers */, + A994BF9D1FBF3F9700278EB9 /* dead_branch_elim_pass.h in Headers */, + A994BF971FBF3F9700278EB9 /* compact_ids_pass.h in Headers */, + A9AB19981CB5B5A80001E7F9 /* spirv_common.hpp in Headers */, + A994BF571FBF3F9700278EB9 /* ext_inst.h in Headers */, + A994C0831FBF3F9700278EB9 /* function.h in Headers */, + A9F042B71FB4D060009FCCB8 /* MVKLogging.h in Headers */, + A994C05D1FBF3F9700278EB9 /* text_handler.h in Headers */, + A994BF991FBF3F9700278EB9 /* constants.h in Headers */, + A994BFB51FBF3F9700278EB9 /* flatten_decoration_pass.h in Headers */, + A994BF7B1FBF3F9700278EB9 /* aggressive_dead_code_elim_pass.h in Headers */, + A994BF4B1FBF3F9700278EB9 /* diagnostic.h in Headers */, + A994C04D1FBF3F9700278EB9 /* spirv_target_env.h in Headers */, + A994BF931FBF3F9700278EB9 /* common_uniform_elim_pass.h in Headers */, + A994C0131FBF3F9700278EB9 /* pass_manager.h in Headers */, + A994BF611FBF3F9700278EB9 /* instruction.h in Headers */, + A994BFEF1FBF3F9700278EB9 /* local_single_block_elim_pass.h in Headers */, + A994C02F1FBF3F9700278EB9 /* types.h in Headers */, + A994C0451FBF3F9700278EB9 /* spirv_endian.h in Headers */, + A994C0691FBF3F9700278EB9 /* ilist.h in Headers */, + A994BF6B1FBF3F9700278EB9 /* message.h in Headers */, + A994C0791FBF3F9700278EB9 /* basic_block.h in Headers */, + A994BFE71FBF3F9700278EB9 /* iterator.h in Headers */, + A994C0271FBF3F9700278EB9 /* strip_debug_info_pass.h in Headers */, + A994BFE11FBF3F9700278EB9 /* ir_context.h in Headers */, + A994C07D1FBF3F9700278EB9 /* construct.h in Headers */, + A9AB199C1CB5B5A80001E7F9 /* spirv_cross.hpp in Headers */, + A994BFFB1FBF3F9700278EB9 /* make_unique.h in Headers */, + A994BFA51FBF3F9700278EB9 /* decoration_manager.h in Headers */, + A994BFC51FBF3F9700278EB9 /* function.h in Headers */, + A994C0511FBF3F9700278EB9 /* spirv_validator_options.h in Headers */, + A994C0551FBF3F9700278EB9 /* table.h in Headers */, + A994BF3B1FBF3F9700278EB9 /* assembly_grammar.h in Headers */, + A994C0871FBF3F9700278EB9 /* instruction.h in Headers */, + A994C0491FBF3F9700278EB9 /* spirv_stats.h in Headers */, + A994BF7F1FBF3F9700278EB9 /* basic_block.h in Headers */, + A994C03F1FBF3F9700278EB9 /* spirv_constant.h in Headers */, + A909408D1C58013E0094110D /* SPIRVToMSLConverter.h in Headers */, + A994BFF91FBF3F9700278EB9 /* log.h in Headers */, + A994BFB91FBF3F9700278EB9 /* fold.h in Headers */, + A994BF531FBF3F9700278EB9 /* enum_string_mapping.h in Headers */, + A994BFB11FBF3F9700278EB9 /* eliminate_dead_functions_pass.h in Headers */, + A994BFC11FBF3F9700278EB9 /* freeze_spec_constant_value_pass.h in Headers */, + A9AB19A41CB5B5A80001E7F9 /* spirv_msl.hpp in Headers */, + A994C0371FBF3F9700278EB9 /* parsed_operand.h in Headers */, + A994BF6F1FBF3F9700278EB9 /* name_mapper.h in Headers */, + A994BFCD1FBF3F9700278EB9 /* inline_opaque_pass.h in Headers */, + A994BFA11FBF3F9700278EB9 /* dead_variable_elimination.h in Headers */, + A994C01F1FBF3F9700278EB9 /* set_spec_constant_default_value_pass.h in Headers */, + A994BFEB1FBF3F9700278EB9 /* local_access_chain_convert_pass.h in Headers */, + A994BF671FBF3F9700278EB9 /* macro.h in Headers */, + A994C08F1FBF3F9700278EB9 /* validate.h in Headers */, + A994BFD51FBF3F9700278EB9 /* insert_extract_elim.h in Headers */, + A9F042B31FB4D060009FCCB8 /* MVKCommonEnvironment.h in Headers */, + A994C06D1FBF3F9700278EB9 /* move_to_front.h in Headers */, + A994C0031FBF3F9700278EB9 /* merge_return_pass.h in Headers */, + A994C0331FBF3F9700278EB9 /* unify_const_pass.h in Headers */, + A994BF411FBF3F9700278EB9 /* cfa.h in Headers */, + A994C0151FBF3F9700278EB9 /* passes.h in Headers */, + A994BF4F1FBF3F9700278EB9 /* enum_set.h in Headers */, + A994C0751FBF3F9700278EB9 /* string_utils.h in Headers */, + A994C00F1FBF3F9700278EB9 /* pass.h in Headers */, + A994C0671FBF3F9700278EB9 /* huffman_codec.h in Headers */, + A994BF431FBF3F9700278EB9 /* markv.h in Headers */, + A994BFD91FBF3F9700278EB9 /* instruction.h in Headers */, + A994BF871FBF3F9700278EB9 /* build_module.h in Headers */, + A994BF831FBF3F9700278EB9 /* block_merge_pass.h in Headers */, + A994BFFF1FBF3F9700278EB9 /* mem_pass.h in Headers */, + A994BFBD1FBF3F9700278EB9 /* fold_spec_constant_op_and_composite_pass.h in Headers */, + A994BF5F1FBF3F9700278EB9 /* id_descriptor.h in Headers */, + A994BF771FBF3F9700278EB9 /* operand.h in Headers */, + A994C0071FBF3F9700278EB9 /* module.h in Headers */, + A994BFA91FBF3F9700278EB9 /* def_use_manager.h in Headers */, + A994BFAD1FBF3F9700278EB9 /* eliminate_dead_constant_pass.h in Headers */, + A994C03B1FBF3F9700278EB9 /* print.h in Headers */, + A994BFD11FBF3F9700278EB9 /* inline_pass.h in Headers */, + A994BFF31FBF3F9700278EB9 /* local_single_store_elim_pass.h in Headers */, + A994C08B1FBF3F9700278EB9 /* validation_state.h in Headers */, + A9AB19A01CB5B5A80001E7F9 /* spirv_glsl.hpp in Headers */, + A95C5F421DEA9070000D17B6 /* spirv_cfg.hpp in Headers */, + A994C0411FBF3F9700278EB9 /* spirv_definition.h in Headers */, + A994BF5B1FBF3F9700278EB9 /* extensions.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + A9092A8C1A81717B00051823 /* MoltenVKShaderConverter */ = { + isa = PBXNativeTarget; + buildConfigurationList = A9092A931A81717C00051823 /* Build configuration list for PBXNativeTarget "MoltenVKShaderConverter" */; + buildPhases = ( + A9092A891A81717B00051823 /* Sources */, + A9092A8A1A81717B00051823 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + A925B71D1C78DEBF006E7ECD /* PBXTargetDependency */, + A93749CF1A9AA7AF00F29B34 /* PBXTargetDependency */, + ); + name = MoltenVKShaderConverter; + productName = MetalGLShaderConverterTool; + productReference = A964BD5F1C57EFBD00D930D8 /* MoltenVKShaderConverter */; + productType = "com.apple.product-type.tool"; + }; + A937472B1A9A8B2900F29B34 /* MoltenVKGLSLToSPIRVConverter-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = A937473F1A9A8B2900F29B34 /* Build configuration list for PBXNativeTarget "MoltenVKGLSLToSPIRVConverter-iOS" */; + buildPhases = ( + A93747291A9A8B2900F29B34 /* Headers */, + A93747271A9A8B2900F29B34 /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "MoltenVKGLSLToSPIRVConverter-iOS"; + productName = "MetalGLShaderConverter-iOS"; + productReference = A964BD611C57EFBD00D930D8 /* MoltenVKGLSLToSPIRVConverter.framework */; + productType = "com.apple.product-type.framework"; + }; + A93747701A9A98D000F29B34 /* MoltenVKGLSLToSPIRVConverter-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = A93747841A9A98D000F29B34 /* Build configuration list for PBXNativeTarget "MoltenVKGLSLToSPIRVConverter-macOS" */; + buildPhases = ( + A937476E1A9A98D000F29B34 /* Headers */, + A937476C1A9A98D000F29B34 /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "MoltenVKGLSLToSPIRVConverter-macOS"; + productName = "MetalGLShaderConverter-macOS"; + productReference = A964BD601C57EFBD00D930D8 /* MoltenVKGLSLToSPIRVConverter.framework */; + productType = "com.apple.product-type.framework"; + }; + A93903B81C57E9D700FE90DC /* MoltenVKSPIRVToMSLConverter-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = A93903BC1C57E9D700FE90DC /* Build configuration list for PBXNativeTarget "MoltenVKSPIRVToMSLConverter-iOS" */; + buildPhases = ( + A93903B91C57E9D700FE90DC /* Headers */, + A93903BA1C57E9D700FE90DC /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "MoltenVKSPIRVToMSLConverter-iOS"; + productName = "MetalGLShaderConverter-iOS"; + productReference = A93903BF1C57E9D700FE90DC /* MoltenVKSPIRVToMSLConverter.framework */; + productType = "com.apple.product-type.framework"; + }; + A93903C01C57E9ED00FE90DC /* MoltenVKSPIRVToMSLConverter-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = A93903C41C57E9ED00FE90DC /* Build configuration list for PBXNativeTarget "MoltenVKSPIRVToMSLConverter-macOS" */; + buildPhases = ( + A93903C11C57E9ED00FE90DC /* Headers */, + A93903C21C57E9ED00FE90DC /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "MoltenVKSPIRVToMSLConverter-macOS"; + productName = "MetalGLShaderConverter-macOS"; + productReference = A93903C71C57E9ED00FE90DC /* MoltenVKSPIRVToMSLConverter.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + A9F55D25198BE6A7004EC31B /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0910; + ORGANIZATIONNAME = "The Brenwill Workshop Ltd."; + TargetAttributes = { + A9092A8C1A81717B00051823 = { + CreatedOnToolsVersion = 6.1.1; + DevelopmentTeam = VU3TCKU48B; + }; + A937472B1A9A8B2900F29B34 = { + CreatedOnToolsVersion = 6.1.1; + DevelopmentTeam = VU3TCKU48B; + }; + A93747701A9A98D000F29B34 = { + CreatedOnToolsVersion = 6.1.1; + DevelopmentTeam = VU3TCKU48B; + }; + A93903B81C57E9D700FE90DC = { + DevelopmentTeam = VU3TCKU48B; + }; + A93903C01C57E9ED00FE90DC = { + DevelopmentTeam = VU3TCKU48B; + }; + }; + }; + buildConfigurationList = A9F55D28198BE6A7004EC31B /* Build configuration list for PBXProject "MoltenVKShaderConverter" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = A9F55D24198BE6A7004EC31B; + productRefGroup = A9F55D24198BE6A7004EC31B; + projectDirPath = ""; + projectRoot = ""; + targets = ( + A9092A8C1A81717B00051823 /* MoltenVKShaderConverter */, + A937472B1A9A8B2900F29B34 /* MoltenVKGLSLToSPIRVConverter-iOS */, + A93747701A9A98D000F29B34 /* MoltenVKGLSLToSPIRVConverter-macOS */, + A93903B81C57E9D700FE90DC /* MoltenVKSPIRVToMSLConverter-iOS */, + A93903C01C57E9ED00FE90DC /* MoltenVKSPIRVToMSLConverter-macOS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + A9092A891A81717B00051823 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A97CC7411C7527F3004A5C7E /* MoltenVKShaderConverterTool.cpp in Sources */, + A925B70C1C7754B2006E7ECD /* FileSupport.mm in Sources */, + A97CC7401C7527F3004A5C7E /* main.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A93747271A9A8B2900F29B34 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A9D008881EF952E200B49F9D /* ParseContextBase.cpp in Sources */, + A9D0087A1EF952E200B49F9D /* iomapper.cpp in Sources */, + A9D008861EF952E200B49F9D /* parseConst.cpp in Sources */, + A9D0086A1EF952E200B49F9D /* glslang_tab.cpp in Sources */, + A9D008781EF952E200B49F9D /* IntermTraverse.cpp in Sources */, + A9D008D81EF952E200B49F9D /* GlslangToSpv.cpp in Sources */, + A9D008E01EF952E200B49F9D /* Logger.cpp in Sources */, + A9D008DE1EF952E200B49F9D /* InReadableOrder.cpp in Sources */, + A9D008801EF952E200B49F9D /* linkValidate.cpp in Sources */, + A9D008961EF952E200B49F9D /* PpContext.cpp in Sources */, + A9D008481EF952E200B49F9D /* CodeGen.cpp in Sources */, + A9D008901EF952E200B49F9D /* PoolAlloc.cpp in Sources */, + A9D008AC1EF952E200B49F9D /* Scan.cpp in Sources */, + A90941A31C581F840094110D /* GLSLConversion.mm in Sources */, + A9D0088A1EF952E200B49F9D /* ParseHelper.cpp in Sources */, + A9D008941EF952E200B49F9D /* PpAtom.cpp in Sources */, + A9D008B41EF952E200B49F9D /* SymbolTable.cpp in Sources */, + A9D008761EF952E200B49F9D /* intermOut.cpp in Sources */, + A9D008BE1EF952E200B49F9D /* ossource.cpp in Sources */, + A9D008641EF952E200B49F9D /* Constant.cpp in Sources */, + A9D008CC1EF952E200B49F9D /* doc.cpp in Sources */, + A90940A71C5808BB0094110D /* GLSLToSPIRVConverter.cpp in Sources */, + A9D008A81EF952E200B49F9D /* RemoveTree.cpp in Sources */, + A9D0089A1EF952E200B49F9D /* PpScanner.cpp in Sources */, + A9D0089C1EF952E200B49F9D /* PpTokens.cpp in Sources */, + A9D008B21EF952E200B49F9D /* ShaderLang.cpp in Sources */, + A9D008A01EF952E200B49F9D /* propagateNoContraction.cpp in Sources */, + A9D0087E1EF952E200B49F9D /* limits.cpp in Sources */, + A9D008921EF952E200B49F9D /* Pp.cpp in Sources */, + A9D008741EF952E200B49F9D /* Intermediate.cpp in Sources */, + A9D0086E1EF952E200B49F9D /* InfoSink.cpp in Sources */, + A9D0084A1EF952E200B49F9D /* Link.cpp in Sources */, + A9D008C81EF952E200B49F9D /* disassemble.cpp in Sources */, + A9D008A41EF952E200B49F9D /* reflection.cpp in Sources */, + A9D008701EF952E200B49F9D /* Initialize.cpp in Sources */, + A9D008EC1EF952E200B49F9D /* SPVRemapper.cpp in Sources */, + A91425E31EF9542E00891AFD /* InitializeDll.cpp in Sources */, + A9D008B81EF952E200B49F9D /* Versions.cpp in Sources */, + A9D008E61EF952E200B49F9D /* SpvBuilder.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A937476C1A9A98D000F29B34 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A9D008891EF952E200B49F9D /* ParseContextBase.cpp in Sources */, + A9D0087B1EF952E200B49F9D /* iomapper.cpp in Sources */, + A9D008871EF952E200B49F9D /* parseConst.cpp in Sources */, + A9D0086B1EF952E200B49F9D /* glslang_tab.cpp in Sources */, + A9D008791EF952E200B49F9D /* IntermTraverse.cpp in Sources */, + A9D008D91EF952E200B49F9D /* GlslangToSpv.cpp in Sources */, + A9D008E11EF952E200B49F9D /* Logger.cpp in Sources */, + A9D008DF1EF952E200B49F9D /* InReadableOrder.cpp in Sources */, + A9D008811EF952E200B49F9D /* linkValidate.cpp in Sources */, + A9D008971EF952E200B49F9D /* PpContext.cpp in Sources */, + A9D008491EF952E200B49F9D /* CodeGen.cpp in Sources */, + A9D008911EF952E200B49F9D /* PoolAlloc.cpp in Sources */, + A9D008AD1EF952E200B49F9D /* Scan.cpp in Sources */, + A90941A41C581F840094110D /* GLSLConversion.mm in Sources */, + A9D0088B1EF952E200B49F9D /* ParseHelper.cpp in Sources */, + A9D008951EF952E200B49F9D /* PpAtom.cpp in Sources */, + A9D008B51EF952E200B49F9D /* SymbolTable.cpp in Sources */, + A9D008771EF952E200B49F9D /* intermOut.cpp in Sources */, + A9D008BF1EF952E200B49F9D /* ossource.cpp in Sources */, + A9D008651EF952E200B49F9D /* Constant.cpp in Sources */, + A9D008CD1EF952E200B49F9D /* doc.cpp in Sources */, + A90940A81C5808BB0094110D /* GLSLToSPIRVConverter.cpp in Sources */, + A9D008A91EF952E200B49F9D /* RemoveTree.cpp in Sources */, + A9D0089B1EF952E200B49F9D /* PpScanner.cpp in Sources */, + A9D0089D1EF952E200B49F9D /* PpTokens.cpp in Sources */, + A9D008B31EF952E200B49F9D /* ShaderLang.cpp in Sources */, + A9D008A11EF952E200B49F9D /* propagateNoContraction.cpp in Sources */, + A9D0087F1EF952E200B49F9D /* limits.cpp in Sources */, + A9D008931EF952E200B49F9D /* Pp.cpp in Sources */, + A9D008751EF952E200B49F9D /* Intermediate.cpp in Sources */, + A9D0086F1EF952E200B49F9D /* InfoSink.cpp in Sources */, + A9D0084B1EF952E200B49F9D /* Link.cpp in Sources */, + A9D008C91EF952E200B49F9D /* disassemble.cpp in Sources */, + A9D008A51EF952E200B49F9D /* reflection.cpp in Sources */, + A9D008711EF952E200B49F9D /* Initialize.cpp in Sources */, + A9D008ED1EF952E200B49F9D /* SPVRemapper.cpp in Sources */, + A91425E41EF9542E00891AFD /* InitializeDll.cpp in Sources */, + A9D008B91EF952E200B49F9D /* Versions.cpp in Sources */, + A9D008E71EF952E200B49F9D /* SpvBuilder.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A93903BA1C57E9D700FE90DC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A994C04E1FBF3F9700278EB9 /* spirv_validator_options.cpp in Sources */, + A994BF9E1FBF3F9700278EB9 /* dead_variable_elimination.cpp in Sources */, + A994BFA61FBF3F9700278EB9 /* def_use_manager.cpp in Sources */, + A994C07A1FBF3F9700278EB9 /* construct.cpp in Sources */, + A994BF741FBF3F9700278EB9 /* operand.cpp in Sources */, + A994C0561FBF3F9700278EB9 /* text.cpp in Sources */, + A994BFC21FBF3F9700278EB9 /* function.cpp in Sources */, + A994C0921FBF3F9700278EB9 /* validate_bitwise.cpp in Sources */, + A994BF3C1FBF3F9700278EB9 /* binary.cpp in Sources */, + A994C03C1FBF3F9700278EB9 /* software_version.cpp in Sources */, + A994C0A61FBF3F9700278EB9 /* validate_type_unique.cpp in Sources */, + A994C0901FBF3F9700278EB9 /* validate_arithmetics.cpp in Sources */, + A994BF8C1FBF3F9700278EB9 /* cfg_cleanup_pass.cpp in Sources */, + A994BFCE1FBF3F9700278EB9 /* inline_pass.cpp in Sources */, + A994BFBA1FBF3F9700278EB9 /* fold_spec_constant_op_and_composite_pass.cpp in Sources */, + A994C0881FBF3F9700278EB9 /* validation_state.cpp in Sources */, + A994BF701FBF3F9700278EB9 /* opcode.cpp in Sources */, + A994BFD21FBF3F9700278EB9 /* insert_extract_elim.cpp in Sources */, + A994BF6C1FBF3F9700278EB9 /* name_mapper.cpp in Sources */, + A994C09E1FBF3F9700278EB9 /* validate_id.cpp in Sources */, + A994BFAE1FBF3F9700278EB9 /* eliminate_dead_functions_pass.cpp in Sources */, + A9AB19991CB5B5A80001E7F9 /* spirv_cross.cpp in Sources */, + A9AB19A11CB5B5A80001E7F9 /* spirv_msl.cpp in Sources */, + A994BFB61FBF3F9700278EB9 /* fold.cpp in Sources */, + A994BF9A1FBF3F9700278EB9 /* dead_branch_elim_pass.cpp in Sources */, + A994C0421FBF3F9700278EB9 /* spirv_endian.cpp in Sources */, + A994BFA21FBF3F9700278EB9 /* decoration_manager.cpp in Sources */, + A994C0181FBF3F9700278EB9 /* remove_duplicates_pass.cpp in Sources */, + A994C0801FBF3F9700278EB9 /* function.cpp in Sources */, + A994BFE81FBF3F9700278EB9 /* local_access_chain_convert_pass.cpp in Sources */, + A994C04A1FBF3F9700278EB9 /* spirv_target_env.cpp in Sources */, + A994BF441FBF3F9700278EB9 /* markv_codec.cpp in Sources */, + A994C05A1FBF3F9700278EB9 /* text_handler.cpp in Sources */, + A994BF941FBF3F9700278EB9 /* compact_ids_pass.cpp in Sources */, + A994C0281FBF3F9700278EB9 /* type_manager.cpp in Sources */, + A994BFFC1FBF3F9700278EB9 /* mem_pass.cpp in Sources */, + A994BF481FBF3F9700278EB9 /* diagnostic.cpp in Sources */, + A994C0041FBF3F9700278EB9 /* module.cpp in Sources */, + A994BFDA1FBF3F9700278EB9 /* instruction_list.cpp in Sources */, + A994BFB21FBF3F9700278EB9 /* flatten_decoration_pass.cpp in Sources */, + A994BF381FBF3F9700278EB9 /* assembly_grammar.cpp in Sources */, + A994BF541FBF3F9700278EB9 /* ext_inst.cpp in Sources */, + A994BFF41FBF3F9700278EB9 /* local_ssa_elim_pass.cpp in Sources */, + A994BF841FBF3F9700278EB9 /* build_module.cpp in Sources */, + A909408A1C58013E0094110D /* SPIRVToMSLConverter.cpp in Sources */, + A994C0001FBF3F9700278EB9 /* merge_return_pass.cpp in Sources */, + A994C0A21FBF3F9700278EB9 /* validate_layout.cpp in Sources */, + A994BF901FBF3F9700278EB9 /* common_uniform_elim_pass.cpp in Sources */, + A994C00C1FBF3F9700278EB9 /* pass.cpp in Sources */, + A994BF621FBF3F9700278EB9 /* libspirv.cpp in Sources */, + A994C00A1FBF3F9700278EB9 /* optimizer.cpp in Sources */, + A994BFBE1FBF3F9700278EB9 /* freeze_spec_constant_value_pass.cpp in Sources */, + A994C0721FBF3F9700278EB9 /* string_utils.cpp in Sources */, + A994C06E1FBF3F9700278EB9 /* parse_number.cpp in Sources */, + A994C0761FBF3F9700278EB9 /* basic_block.cpp in Sources */, + A994BFF01FBF3F9700278EB9 /* local_single_store_elim_pass.cpp in Sources */, + A994BFAA1FBF3F9700278EB9 /* eliminate_dead_constant_pass.cpp in Sources */, + A928C91B1D0488DC00071B88 /* SPIRVConversion.mm in Sources */, + A994BFE21FBF3F9700278EB9 /* ir_loader.cpp in Sources */, + A994C0A41FBF3F9700278EB9 /* validate_logicals.cpp in Sources */, + A994C09C1FBF3F9700278EB9 /* validate_decorations.cpp in Sources */, + A994C0341FBF3F9700278EB9 /* parsed_operand.cpp in Sources */, + A994C08C1FBF3F9700278EB9 /* validate.cpp in Sources */, + A994BF4C1FBF3F9700278EB9 /* disassemble.cpp in Sources */, + A994C0461FBF3F9700278EB9 /* spirv_stats.cpp in Sources */, + A994BF641FBF3F9700278EB9 /* linker.cpp in Sources */, + A994BF881FBF3F9700278EB9 /* cfg.cpp in Sources */, + A994BFD61FBF3F9700278EB9 /* instruction.cpp in Sources */, + A994BFC61FBF3F9700278EB9 /* inline_exhaustive_pass.cpp in Sources */, + A994C02C1FBF3F9700278EB9 /* types.cpp in Sources */, + A994C01C1FBF3F9700278EB9 /* set_spec_constant_default_value_pass.cpp in Sources */, + A994C0381FBF3F9700278EB9 /* print.cpp in Sources */, + A994BFEC1FBF3F9700278EB9 /* local_single_block_elim_pass.cpp in Sources */, + A9AB199D1CB5B5A80001E7F9 /* spirv_glsl.cpp in Sources */, + A994C0201FBF3F9700278EB9 /* strength_reduction_pass.cpp in Sources */, + A994C0841FBF3F9700278EB9 /* instruction.cpp in Sources */, + A994BF501FBF3F9700278EB9 /* enum_string_mapping.cpp in Sources */, + A994BFCA1FBF3F9700278EB9 /* inline_opaque_pass.cpp in Sources */, + A95C5F3F1DEA9070000D17B6 /* spirv_cfg.cpp in Sources */, + A994BF7C1FBF3F9700278EB9 /* basic_block.cpp in Sources */, + A994C05E1FBF3F9700278EB9 /* bit_stream.cpp in Sources */, + A994BF781FBF3F9700278EB9 /* aggressive_dead_code_elim_pass.cpp in Sources */, + A994C0981FBF3F9700278EB9 /* validate_conversion.cpp in Sources */, + A994BF581FBF3F9700278EB9 /* extensions.cpp in Sources */, + A994BFDE1FBF3F9700278EB9 /* ir_context.cpp in Sources */, + A994C0101FBF3F9700278EB9 /* pass_manager.cpp in Sources */, + A994C0A01FBF3F9700278EB9 /* validate_instruction.cpp in Sources */, + A994C0301FBF3F9700278EB9 /* unify_const_pass.cpp in Sources */, + A994BF801FBF3F9700278EB9 /* block_merge_pass.cpp in Sources */, + A994C0961FBF3F9700278EB9 /* validate_cfg.cpp in Sources */, + A994C0941FBF3F9700278EB9 /* validate_capability.cpp in Sources */, + A994C09A1FBF3F9700278EB9 /* validate_datarules.cpp in Sources */, + A994BF5C1FBF3F9700278EB9 /* id_descriptor.cpp in Sources */, + A994C0521FBF3F9700278EB9 /* table.cpp in Sources */, + A994BF681FBF3F9700278EB9 /* message.cpp in Sources */, + A994C0241FBF3F9700278EB9 /* strip_debug_info_pass.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A93903C21C57E9ED00FE90DC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A994C04F1FBF3F9700278EB9 /* spirv_validator_options.cpp in Sources */, + A994BF9F1FBF3F9700278EB9 /* dead_variable_elimination.cpp in Sources */, + A994BFA71FBF3F9700278EB9 /* def_use_manager.cpp in Sources */, + A994C07B1FBF3F9700278EB9 /* construct.cpp in Sources */, + A994BF751FBF3F9700278EB9 /* operand.cpp in Sources */, + A994C0571FBF3F9700278EB9 /* text.cpp in Sources */, + A994BFC31FBF3F9700278EB9 /* function.cpp in Sources */, + A994C0931FBF3F9700278EB9 /* validate_bitwise.cpp in Sources */, + A994BF3D1FBF3F9700278EB9 /* binary.cpp in Sources */, + A994C03D1FBF3F9700278EB9 /* software_version.cpp in Sources */, + A994C0A71FBF3F9700278EB9 /* validate_type_unique.cpp in Sources */, + A994C0911FBF3F9700278EB9 /* validate_arithmetics.cpp in Sources */, + A994BF8D1FBF3F9700278EB9 /* cfg_cleanup_pass.cpp in Sources */, + A994BFCF1FBF3F9700278EB9 /* inline_pass.cpp in Sources */, + A994BFBB1FBF3F9700278EB9 /* fold_spec_constant_op_and_composite_pass.cpp in Sources */, + A994C0891FBF3F9700278EB9 /* validation_state.cpp in Sources */, + A994BF711FBF3F9700278EB9 /* opcode.cpp in Sources */, + A994BFD31FBF3F9700278EB9 /* insert_extract_elim.cpp in Sources */, + A994BF6D1FBF3F9700278EB9 /* name_mapper.cpp in Sources */, + A994C09F1FBF3F9700278EB9 /* validate_id.cpp in Sources */, + A994BFAF1FBF3F9700278EB9 /* eliminate_dead_functions_pass.cpp in Sources */, + A9AB199A1CB5B5A80001E7F9 /* spirv_cross.cpp in Sources */, + A9AB19A21CB5B5A80001E7F9 /* spirv_msl.cpp in Sources */, + A994BFB71FBF3F9700278EB9 /* fold.cpp in Sources */, + A994BF9B1FBF3F9700278EB9 /* dead_branch_elim_pass.cpp in Sources */, + A994C0431FBF3F9700278EB9 /* spirv_endian.cpp in Sources */, + A994BFA31FBF3F9700278EB9 /* decoration_manager.cpp in Sources */, + A994C0191FBF3F9700278EB9 /* remove_duplicates_pass.cpp in Sources */, + A994C0811FBF3F9700278EB9 /* function.cpp in Sources */, + A994BFE91FBF3F9700278EB9 /* local_access_chain_convert_pass.cpp in Sources */, + A994C04B1FBF3F9700278EB9 /* spirv_target_env.cpp in Sources */, + A994BF451FBF3F9700278EB9 /* markv_codec.cpp in Sources */, + A994C05B1FBF3F9700278EB9 /* text_handler.cpp in Sources */, + A994BF951FBF3F9700278EB9 /* compact_ids_pass.cpp in Sources */, + A994C0291FBF3F9700278EB9 /* type_manager.cpp in Sources */, + A994BFFD1FBF3F9700278EB9 /* mem_pass.cpp in Sources */, + A994BF491FBF3F9700278EB9 /* diagnostic.cpp in Sources */, + A994C0051FBF3F9700278EB9 /* module.cpp in Sources */, + A994BFDB1FBF3F9700278EB9 /* instruction_list.cpp in Sources */, + A994BFB31FBF3F9700278EB9 /* flatten_decoration_pass.cpp in Sources */, + A994BF391FBF3F9700278EB9 /* assembly_grammar.cpp in Sources */, + A994BF551FBF3F9700278EB9 /* ext_inst.cpp in Sources */, + A994BFF51FBF3F9700278EB9 /* local_ssa_elim_pass.cpp in Sources */, + A994BF851FBF3F9700278EB9 /* build_module.cpp in Sources */, + A909408B1C58013E0094110D /* SPIRVToMSLConverter.cpp in Sources */, + A994C0011FBF3F9700278EB9 /* merge_return_pass.cpp in Sources */, + A994C0A31FBF3F9700278EB9 /* validate_layout.cpp in Sources */, + A994BF911FBF3F9700278EB9 /* common_uniform_elim_pass.cpp in Sources */, + A994C00D1FBF3F9700278EB9 /* pass.cpp in Sources */, + A994BF631FBF3F9700278EB9 /* libspirv.cpp in Sources */, + A994C00B1FBF3F9700278EB9 /* optimizer.cpp in Sources */, + A994BFBF1FBF3F9700278EB9 /* freeze_spec_constant_value_pass.cpp in Sources */, + A994C0731FBF3F9700278EB9 /* string_utils.cpp in Sources */, + A994C06F1FBF3F9700278EB9 /* parse_number.cpp in Sources */, + A994C0771FBF3F9700278EB9 /* basic_block.cpp in Sources */, + A994BFF11FBF3F9700278EB9 /* local_single_store_elim_pass.cpp in Sources */, + A994BFAB1FBF3F9700278EB9 /* eliminate_dead_constant_pass.cpp in Sources */, + A928C91C1D0488DC00071B88 /* SPIRVConversion.mm in Sources */, + A994BFE31FBF3F9700278EB9 /* ir_loader.cpp in Sources */, + A994C0A51FBF3F9700278EB9 /* validate_logicals.cpp in Sources */, + A994C09D1FBF3F9700278EB9 /* validate_decorations.cpp in Sources */, + A994C0351FBF3F9700278EB9 /* parsed_operand.cpp in Sources */, + A994C08D1FBF3F9700278EB9 /* validate.cpp in Sources */, + A994BF4D1FBF3F9700278EB9 /* disassemble.cpp in Sources */, + A994C0471FBF3F9700278EB9 /* spirv_stats.cpp in Sources */, + A994BF651FBF3F9700278EB9 /* linker.cpp in Sources */, + A994BF891FBF3F9700278EB9 /* cfg.cpp in Sources */, + A994BFD71FBF3F9700278EB9 /* instruction.cpp in Sources */, + A994BFC71FBF3F9700278EB9 /* inline_exhaustive_pass.cpp in Sources */, + A994C02D1FBF3F9700278EB9 /* types.cpp in Sources */, + A994C01D1FBF3F9700278EB9 /* set_spec_constant_default_value_pass.cpp in Sources */, + A994C0391FBF3F9700278EB9 /* print.cpp in Sources */, + A994BFED1FBF3F9700278EB9 /* local_single_block_elim_pass.cpp in Sources */, + A9AB199E1CB5B5A80001E7F9 /* spirv_glsl.cpp in Sources */, + A994C0211FBF3F9700278EB9 /* strength_reduction_pass.cpp in Sources */, + A994C0851FBF3F9700278EB9 /* instruction.cpp in Sources */, + A994BF511FBF3F9700278EB9 /* enum_string_mapping.cpp in Sources */, + A994BFCB1FBF3F9700278EB9 /* inline_opaque_pass.cpp in Sources */, + A95C5F401DEA9070000D17B6 /* spirv_cfg.cpp in Sources */, + A994BF7D1FBF3F9700278EB9 /* basic_block.cpp in Sources */, + A994C05F1FBF3F9700278EB9 /* bit_stream.cpp in Sources */, + A994BF791FBF3F9700278EB9 /* aggressive_dead_code_elim_pass.cpp in Sources */, + A994C0991FBF3F9700278EB9 /* validate_conversion.cpp in Sources */, + A994BF591FBF3F9700278EB9 /* extensions.cpp in Sources */, + A994BFDF1FBF3F9700278EB9 /* ir_context.cpp in Sources */, + A994C0111FBF3F9700278EB9 /* pass_manager.cpp in Sources */, + A994C0A11FBF3F9700278EB9 /* validate_instruction.cpp in Sources */, + A994C0311FBF3F9700278EB9 /* unify_const_pass.cpp in Sources */, + A994BF811FBF3F9700278EB9 /* block_merge_pass.cpp in Sources */, + A994C0971FBF3F9700278EB9 /* validate_cfg.cpp in Sources */, + A994C0951FBF3F9700278EB9 /* validate_capability.cpp in Sources */, + A994C09B1FBF3F9700278EB9 /* validate_datarules.cpp in Sources */, + A994BF5D1FBF3F9700278EB9 /* id_descriptor.cpp in Sources */, + A994C0531FBF3F9700278EB9 /* table.cpp in Sources */, + A994BF691FBF3F9700278EB9 /* message.cpp in Sources */, + A994C0251FBF3F9700278EB9 /* strip_debug_info_pass.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + A925B71D1C78DEBF006E7ECD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = A93903C01C57E9ED00FE90DC /* MoltenVKSPIRVToMSLConverter-macOS */; + targetProxy = A925B71C1C78DEBF006E7ECD /* PBXContainerItemProxy */; + }; + A93749CF1A9AA7AF00F29B34 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = A93747701A9A98D000F29B34 /* MoltenVKGLSLToSPIRVConverter-macOS */; + targetProxy = A93749CE1A9AA7AF00F29B34 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + A9092A911A81717C00051823 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GENERATE_MASTER_OBJECT_FILE = NO; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = MoltenVKShaderConverter; + SDKROOT = macosx; + }; + name = Debug; + }; + A9092A921A81717C00051823 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GENERATE_MASTER_OBJECT_FILE = NO; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = MoltenVKShaderConverter; + SDKROOT = macosx; + }; + name = Release; + }; + A93747401A9A8B2900F29B34 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = NO; + CLANG_WARN_UNREACHABLE_CODE = NO; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = NO; + GCC_WARN_CHECK_SWITCH_STATEMENTS = NO; + GCC_WARN_UNUSED_PARAMETER = NO; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACH_O_TYPE = staticlib; + PRODUCT_NAME = MoltenVKGLSLToSPIRVConverter; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = arm64; + }; + name = Debug; + }; + A93747411A9A8B2900F29B34 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = NO; + CLANG_WARN_UNREACHABLE_CODE = NO; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = NO; + GCC_WARN_CHECK_SWITCH_STATEMENTS = NO; + GCC_WARN_UNUSED_PARAMETER = NO; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACH_O_TYPE = staticlib; + PRODUCT_NAME = MoltenVKGLSLToSPIRVConverter; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = arm64; + }; + name = Release; + }; + A93747851A9A98D000F29B34 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = NO; + CLANG_WARN_UNREACHABLE_CODE = NO; + COMBINE_HIDPI_IMAGES = YES; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = NO; + GCC_WARN_CHECK_SWITCH_STATEMENTS = NO; + GCC_WARN_UNUSED_PARAMETER = NO; + MACH_O_TYPE = staticlib; + PRODUCT_NAME = MoltenVKGLSLToSPIRVConverter; + SDKROOT = macosx; + }; + name = Debug; + }; + A93747861A9A98D000F29B34 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = NO; + CLANG_WARN_UNREACHABLE_CODE = NO; + COMBINE_HIDPI_IMAGES = YES; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = NO; + GCC_WARN_CHECK_SWITCH_STATEMENTS = NO; + GCC_WARN_UNUSED_PARAMETER = NO; + MACH_O_TYPE = staticlib; + PRODUCT_NAME = MoltenVKGLSLToSPIRVConverter; + SDKROOT = macosx; + }; + name = Release; + }; + A93903BD1C57E9D700FE90DC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/MoltenVKSPIRVToMSLConverter/SPIRV-Tools/include\"", + "\"$(SRCROOT)/MoltenVKSPIRVToMSLConverter/SPIRV-Tools/source\"", + "\"$(SRCROOT)/MoltenVKSPIRVToMSLConverter/SPIRV-Tools/build\"", + "\"$(SRCROOT)/MoltenVKSPIRVToMSLConverter/SPIRV-Headers/include\"", + ); + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACH_O_TYPE = staticlib; + PRODUCT_NAME = MoltenVKSPIRVToMSLConverter; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = arm64; + }; + name = Debug; + }; + A93903BE1C57E9D700FE90DC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/MoltenVKSPIRVToMSLConverter/SPIRV-Tools/include\"", + "\"$(SRCROOT)/MoltenVKSPIRVToMSLConverter/SPIRV-Tools/source\"", + "\"$(SRCROOT)/MoltenVKSPIRVToMSLConverter/SPIRV-Tools/build\"", + "\"$(SRCROOT)/MoltenVKSPIRVToMSLConverter/SPIRV-Headers/include\"", + ); + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACH_O_TYPE = staticlib; + PRODUCT_NAME = MoltenVKSPIRVToMSLConverter; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = arm64; + }; + name = Release; + }; + A93903C51C57E9ED00FE90DC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/MoltenVKSPIRVToMSLConverter/SPIRV-Tools/include\"", + "\"$(SRCROOT)/MoltenVKSPIRVToMSLConverter/SPIRV-Tools/source\"", + "\"$(SRCROOT)/MoltenVKSPIRVToMSLConverter/SPIRV-Tools/build\"", + "\"$(SRCROOT)/MoltenVKSPIRVToMSLConverter/SPIRV-Headers/include\"", + ); + MACH_O_TYPE = staticlib; + PRODUCT_NAME = MoltenVKSPIRVToMSLConverter; + SDKROOT = macosx; + }; + name = Debug; + }; + A93903C61C57E9ED00FE90DC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/MoltenVKSPIRVToMSLConverter/SPIRV-Tools/include\"", + "\"$(SRCROOT)/MoltenVKSPIRVToMSLConverter/SPIRV-Tools/source\"", + "\"$(SRCROOT)/MoltenVKSPIRVToMSLConverter/SPIRV-Tools/build\"", + "\"$(SRCROOT)/MoltenVKSPIRVToMSLConverter/SPIRV-Headers/include\"", + ); + MACH_O_TYPE = staticlib; + PRODUCT_NAME = MoltenVKSPIRVToMSLConverter; + SDKROOT = macosx; + }; + name = Release; + }; + A9F55D3F198BE6A8004EC31B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_CXX0X_EXTENSIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + 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_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + "SPIRV_CROSS_FLT_FMT=\\\"%.6g\\\"", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_PARAMETER = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_MASTER_OBJECT_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + ONLY_ACTIVE_ARCH = YES; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + A9F55D40198BE6A8004EC31B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_CXX0X_EXTENSIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + 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_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = fast; + GCC_PREPROCESSOR_DEFINITIONS = "SPIRV_CROSS_FLT_FMT=\\\"%.6g\\\""; + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_PARAMETER = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_MASTER_OBJECT_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + SKIP_INSTALL = YES; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + A9092A931A81717C00051823 /* Build configuration list for PBXNativeTarget "MoltenVKShaderConverter" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A9092A911A81717C00051823 /* Debug */, + A9092A921A81717C00051823 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A937473F1A9A8B2900F29B34 /* Build configuration list for PBXNativeTarget "MoltenVKGLSLToSPIRVConverter-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A93747401A9A8B2900F29B34 /* Debug */, + A93747411A9A8B2900F29B34 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A93747841A9A98D000F29B34 /* Build configuration list for PBXNativeTarget "MoltenVKGLSLToSPIRVConverter-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A93747851A9A98D000F29B34 /* Debug */, + A93747861A9A98D000F29B34 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A93903BC1C57E9D700FE90DC /* Build configuration list for PBXNativeTarget "MoltenVKSPIRVToMSLConverter-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A93903BD1C57E9D700FE90DC /* Debug */, + A93903BE1C57E9D700FE90DC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A93903C41C57E9ED00FE90DC /* Build configuration list for PBXNativeTarget "MoltenVKSPIRVToMSLConverter-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A93903C51C57E9ED00FE90DC /* Debug */, + A93903C61C57E9ED00FE90DC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A9F55D28198BE6A7004EC31B /* Build configuration list for PBXProject "MoltenVKShaderConverter" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A9F55D3F198BE6A8004EC31B /* Debug */, + A9F55D40198BE6A8004EC31B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = A9F55D25198BE6A7004EC31B /* Project object */; +} diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-iOS.xcscheme b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-iOS.xcscheme new file mode 100644 index 00000000..f497874a --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-iOS.xcscheme @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-macOS.xcscheme b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-macOS.xcscheme new file mode 100644 index 00000000..c6cd37bf --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-macOS.xcscheme @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-iOS.xcscheme b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-iOS.xcscheme new file mode 100644 index 00000000..20a7564e --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-iOS.xcscheme @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-macOS.xcscheme b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-macOS.xcscheme new file mode 100644 index 00000000..4c8bd6ac --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-macOS.xcscheme @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKShaderConverter.xcscheme b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKShaderConverter.xcscheme new file mode 100644 index 00000000..6c8cb773 --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKShaderConverter.xcscheme @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverterTool/FileSupport.h b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/FileSupport.h new file mode 100644 index 00000000..affa71e1 --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/FileSupport.h @@ -0,0 +1,91 @@ +/* + * FileSupport.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + + +#include +#include + + +namespace mvk { + + + /** Returns an absolute path from the specified path, which may be absolute or relative. */ + std::string absolutePath(const std::string& path); + + /** Returns the last component of the specified path. */ + std::string lastPathComponent(const std::string& path); + + /** Returns the extension component (after the .) of the specified path. */ + std::string pathExtension(const std::string& path); + + /** Returns whether the specified path exists and is a readable file. */ + bool canReadFile(const std::string& path); + + /** Returns whether the specified path is a file that is writable. */ + bool canWriteFile(const std::string& path); + + /** + * Returns a copy of the specified path, with the extension of the path set or changed + * to the specified extension. If includeOrigPathExtn is true, the original file extension + * of the path will be appended to the file name (before the new separator), separated + * by origPathExtnSep string (eg. myshader.vsh -> myshader_vsh.spv). + */ + std::string pathWithExtension(const std::string& path, + const std::string pathExtn, + bool includeOrigPathExtn, + const std::string origPathExtnSep); + + /** + * Reads the contents of the specified file path into the specified contents vector. + * and returns whether the file read was successful. + * + * If successful, copies the file contents into the contents vector and returns true. + * If unsuccessful, places an explanatory error message in the errMsg string and returns false. + * If file was partially read, copies what could be read into the contents vector, places an + * error message in errMsg, and returns false. + */ + bool readFile(const std::string& path, std::vector& contents, std::string& errMsg); + + /** + * Writes the contents of the specified contents string to the file in the specified file + * path, creating the file if necessary, and returns whether the file write was successful. + * + * If successful, overwrites the entire contents of the file and returns true. + * If unsuccessful, places an explanatory error message in the errMsg string and returns false. + */ + bool writeFile(const std::string& path, const std::vector& contents, std::string& errMsg); + + /** + * Iterates through the directory at the specified path, which may be either a relative + * or absolute path, and calls the processFile(std::string filePath) member function + * on the fileProcessor for each file in the directory. If the isRecursive parameter + * is true, the iteration will include all files in all sub-directories as well. + * The processFile(std::string filePath) member function on the fileProcessor should + * return true to cause the processing of any further files to halt, and this function + * to return, or should return false to allow further files to be iterated. + * Returns false if the directory could not be found or iterated. Returns true otherwise. + */ + template + bool iterateDirectory(const std::string& dirPath, + FileProcessor& fileProcessor, + bool isRecursive, + std::string& errMsg); + +} diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverterTool/FileSupport.mm b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/FileSupport.mm new file mode 100644 index 00000000..9a6a3904 --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/FileSupport.mm @@ -0,0 +1,175 @@ +/* + * FileSupport.mm + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FileSupport.h" +#include "MoltenVKShaderConverterTool.h" +#include + +#import + +using namespace std; +using namespace mvk; + + +string mvk::absolutePath(const string& path) { + NSString* nsPath = @(path.data()); + if(nsPath.absolutePath) return path; + nsPath = [NSFileManager.defaultManager.currentDirectoryPath stringByAppendingPathComponent: nsPath]; + return nsPath.UTF8String; +} + +string mvk::lastPathComponent(const string& path) { + NSString* nsPath = @(path.data()); + return nsPath.lastPathComponent.UTF8String; +} + +string mvk::pathExtension(const string& path) { + NSString* nsPath = @(path.data()); + return nsPath.pathExtension.UTF8String; +} + +string mvk::pathWithExtension(const string& path, + const string pathExtn, + bool includeOrigPathExtn, + const string origPathExtnSep) { + NSString* nsPath = @(path.data()); + NSString* currExtn = nsPath.pathExtension; + nsPath = nsPath.stringByDeletingPathExtension; + if (includeOrigPathExtn) { + nsPath = [nsPath stringByAppendingString: @(origPathExtnSep.data())]; + nsPath = [nsPath stringByAppendingString: currExtn]; + } + nsPath = [nsPath stringByAppendingPathExtension: @(pathExtn.data())]; + return nsPath.UTF8String; +} + +bool mvk::canReadFile(const string& path) { + NSString* nsAbsDirPath = @(absolutePath(path).data()); + NSFileManager* fileMgr = NSFileManager.defaultManager; + BOOL isDir = false; + BOOL exists = [fileMgr fileExistsAtPath: nsAbsDirPath isDirectory: &isDir]; + return exists && !isDir && [fileMgr isReadableFileAtPath: nsAbsDirPath]; +} + +bool mvk::canWriteFile(const string& path) { + NSString* nsAbsDirPath = @(absolutePath(path).data()); + NSFileManager* fileMgr = NSFileManager.defaultManager; + BOOL isDir = false; + BOOL exists = [fileMgr fileExistsAtPath: nsAbsDirPath isDirectory: &isDir]; + return !exists || (!isDir && [fileMgr isWritableFileAtPath: nsAbsDirPath]); +} + +bool mvk::readFile(const string& path, vector& contents, string& errMsg) { + + contents.clear(); // Ensure contents are empty in case we leave early + errMsg.clear(); // Assume success, so clear the error message + + string absPath = absolutePath(path); + + if ( !canReadFile(absPath) ) { + errMsg = absPath + " is not a readable file"; + return false; + } + + ifstream inFile(absPath, (ifstream::in | ifstream::binary)); // Stream closed when destroyed + if (inFile.fail()) { + errMsg = "Could not open file for reading: " + absPath; + return false; + } + + // Get file length + inFile.seekg (0, inFile.end); + streampos filePos = inFile.tellg(); + inFile.seekg (0, inFile.beg); + size_t fileLen = filePos; + + // Read the contents of file into the vector + contents.reserve(fileLen); + char c; + while (inFile.get(c)) { contents.push_back(c); } + + // Check if successful + if (inFile.bad()) { + errMsg = "Could not read entire contents of file: " + absPath; + return false; + } + + return true; +} + +bool mvk::writeFile(const string& path, const vector& contents, string& errMsg) { + + errMsg.clear(); // Assume success, so clear the error message + + string absPath = absolutePath(path); + + if ( !canWriteFile(path) ) { + errMsg = "Cannot write to file:" + absPath; + return false; + } + + ofstream outFile(absPath); // Stream closed when destroyed + if (outFile.fail()) { + errMsg = "Could not open file for writing: " + absPath; + return false; + } + + for (auto iter = contents.begin(), end = contents.end(); iter != end; iter++) { + outFile.put(*iter); + if (outFile.bad()) { + errMsg = "Could not write entire contents of file: " + absPath; + return false; + } + } + return true; +} + +template +bool mvk::iterateDirectory(const string& dirPath, + FileProcessor& fileProcessor, + bool isRecursive, + string& errMsg) { + NSString* nsAbsDirPath = @(absolutePath(dirPath).data()); + NSFileManager* fileMgr = NSFileManager.defaultManager; + BOOL isDir = false; + BOOL exists = [fileMgr fileExistsAtPath: nsAbsDirPath isDirectory: &isDir]; + if ( !exists ) { + errMsg = "Could not locate directory: " + absolutePath(dirPath); + return false; + } + if ( !isDir ) { + errMsg = absolutePath(dirPath) + " is not a directory."; + return false; + } + + NSDirectoryEnumerator* dirEnum = [fileMgr enumeratorAtPath: nsAbsDirPath]; + NSString* filePath; + while ((filePath = dirEnum.nextObject)) { + if ( !isRecursive ) { [dirEnum skipDescendants]; } + NSString* absFilePath = [nsAbsDirPath stringByAppendingPathComponent: filePath]; + if(fileProcessor.processFile(absFilePath.UTF8String)) { return true; } + } + return true; +} + +/** Concrete template implementation to allow MoltenVKShaderConverterTool to iterate the files in a directory. */ +template bool mvk::iterateDirectory(const string& dirPath, + MoltenVKShaderConverterTool& fileProcessor, + bool isRecursive, + string& errMsg); + diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.cpp b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.cpp new file mode 100644 index 00000000..82bd8f24 --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.cpp @@ -0,0 +1,558 @@ +/* + * MoltenVKShaderConverterTool.cpp + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MoltenVKShaderConverterTool.h" +#include "FileSupport.h" +#include "GLSLToSPIRVConverter.h" +#include "SPIRVToMSLConverter.h" +#import + +using namespace std; +using namespace mvk; + + +/** The default list of vertex file extensions. */ +static const char* _defaultVertexShaderExtns = "vs vsh vert vertex"; + +/** The default list of fragment file extensions. */ +static const char* _defaultFragShaderExtns = "fs fsh frag fragment"; + +/** The default list of compute file extensions. */ +static const char* _defaultCompShaderExtns = "cp cmp comp compute kn kl krn kern kernel"; + +/** The default list of SPIR-V file extensions. */ +static const char* _defaultSPIRVShaderExtns = "spv spirv"; + + +#pragma mark - +#pragma mark MoltenVKShaderConverterTool + + +int MoltenVKShaderConverterTool::run() { + bool success = false; + if ( !_directoryPath.empty() ) { + string errMsg; + success = iterateDirectory(_directoryPath, *this, _shouldUseDirectoryRecursion, errMsg); + if ( !success ) { log(errMsg.data()); } + } else { + if (_shouldReadGLSL) { + success = convertGLSL(_glslInFilePath, _spvOutFilePath, _mslOutFilePath, _shaderStage); + } else if (_shouldReadSPIRV) { + success = convertSPIRV(_spvInFilePath, _mslOutFilePath); + } else { + showUsage(); + } + } + return success ? EXIT_SUCCESS : EXIT_FAILURE; +} + +bool MoltenVKShaderConverterTool::processFile(string filePath) { + string absPath = absolutePath(filePath); + string emptyPath; + + string pathExtn = pathExtension(absPath); + if (_shouldReadGLSL && isGLSLFileExtension(pathExtn)) { + convertGLSL(absPath, emptyPath, emptyPath, kMVKShaderStageAuto); + } else if (_shouldReadSPIRV && isSPIRVFileExtension(pathExtn)) { + convertSPIRV(absPath, emptyPath); + } + + return false; +} + +/** + * Read GLSL code from a GLSL file, convert to SPIR-V, and optionally MSL, + * and write the SPIR-V and/or MSL code to files. + */ +bool MoltenVKShaderConverterTool::convertGLSL(string& glslInFile, + string& spvOutFile, + string& mslOutFile, + MVKShaderStage shaderStage) { + string path; + vector fileContents; + string glslCode; + string errMsg; + + // Read the GLSL + if (glslInFile.empty()) { + log("The GLSL file to read from was not specified"); + return false; + } + + path = glslInFile; + if (readFile(path, fileContents, errMsg)) { + string logMsg = "Read GLSL from file: " + lastPathComponent(path); + log(logMsg.data()); + } else { + errMsg = "Could not read GLSL file. " + errMsg; + log(errMsg.data()); + return false; + } + glslCode.append(fileContents.begin(), fileContents.end()); + + if (shaderStage == kMVKShaderStageAuto) { + string pathExtn = pathExtension(glslInFile); + shaderStage = shaderStageFromFileExtension(pathExtn); + } + if (shaderStage == kMVKShaderStageAuto) { + errMsg = "Could not determine shader type from GLSL file: " + absolutePath(path); + log(errMsg.data()); + return false; + } + + // Convert GLSL to SPIR-V + GLSLToSPIRVConverter glslConverter; + glslConverter.setGLSL(glslCode); + if (glslConverter.convert(shaderStage, _shouldLogConversions, _shouldLogConversions)) { + if (_shouldLogConversions) { log(glslConverter.getResultLog().data()); } + } else { + string logMsg = "Could not convert GLSL in file: " + absolutePath(path); + log(logMsg.data()); + log(glslConverter.getResultLog().data()); + return false; + } + + const vector& spv = glslConverter.getSPIRV(); + + // Write the SPIR-V code to a file. + // If no file has been supplied, create one from the GLSL file name. + if (_shouldWriteSPIRV) { + path = spvOutFile; + if (path.empty()) { path = pathWithExtension(glslInFile, "spv", _shouldIncludeOrigPathExtn, _origPathExtnSep); } + + spirvToBytes(spv, fileContents); + if (writeFile(path, fileContents, errMsg)) { + string logMsg = "Saved SPIR-V to file: " + lastPathComponent(path); + log(logMsg.data()); + } else { + errMsg = "Could not write SPIR-V file. " + errMsg; + log(errMsg.data()); + return false; + } + } + + return convertSPIRV(spv, glslInFile, mslOutFile, false); +} + +/** Read SPIR-V code from a SPIR-V file, convert to MSL, and write the MSL code to files. */ +bool MoltenVKShaderConverterTool::convertSPIRV(string& spvInFile, + string& mslOutFile) { + string path; + vector fileContents; + vector spv; + string errMsg; + + // Read the SPIRV + if (spvInFile.empty()) { + log("The SPIR-V file to read from was not specified"); + return false; + } + + path = spvInFile; + if (readFile(path, fileContents, errMsg)) { + string logMsg = "Read SPIR-V from file: " + lastPathComponent(path); + log(logMsg.data()); + } else { + errMsg = "Could not read SPIR-V file. " + errMsg; + log(errMsg.data()); + return false; + } + bytesToSPIRV(fileContents, spv); + + return convertSPIRV(spv, spvInFile, mslOutFile, _shouldLogConversions); +} + +/** Read SPIR-V code from an array, convert to MSL, and write the MSL code to files. */ +bool MoltenVKShaderConverterTool::convertSPIRV(const vector& spv, + string& inFile, + string& mslOutFile, + bool shouldLogSPV) { + if ( !_shouldWriteMSL ) { return true; } + + // Derive the context under which conversion will occur + SPIRVToMSLConverterContext mslContext; + mslContext.options.shouldFlipVertexY = _shouldFlipVertexY; + + SPIRVToMSLConverter spvConverter; + spvConverter.setSPIRV(spv); + if (spvConverter.convert(mslContext, shouldLogSPV, _shouldLogConversions, (_shouldLogConversions && shouldLogSPV))) { + if (_shouldLogConversions) { log(spvConverter.getResultLog().data()); } + } else { + string errMsg = "Could not convert SPIR-V in file: " + absolutePath(inFile); + log(errMsg.data()); + log(spvConverter.getResultLog().data()); + return false; + } + + // Write the MSL to file + string path = mslOutFile; + if (mslOutFile.empty()) { path = pathWithExtension(inFile, "metal", _shouldIncludeOrigPathExtn, _origPathExtnSep); } + const string& msl = spvConverter.getMSL(); + vector fileContents; + fileContents.insert(fileContents.end(), msl.begin(), msl.end()); + string errMsg; + if (writeFile(path, fileContents, errMsg)) { + string logMsg = "Saved MSL to file: " + lastPathComponent(path); + log(logMsg.data()); + return true; + } else { + errMsg = "Could not write MSL file. " + errMsg; + log(errMsg.data()); + return false; + } +} + +MVKShaderStage MoltenVKShaderConverterTool::shaderStageFromFileExtension(string& pathExtension) { + for (auto& fx : _glslVtxFileExtns) { if (fx == pathExtension) { return kMVKShaderStageVertex; } } + for (auto& fx : _glslFragFileExtns) { if (fx == pathExtension) { return kMVKShaderStageFragment; } } + for (auto& fx : _glslCompFileExtns) { if (fx == pathExtension) { return kMVKShaderStageCompute; } } + return kMVKShaderStageAuto; +} + +bool MoltenVKShaderConverterTool::isGLSLFileExtension(string& pathExtension) { + for (auto& fx : _glslVtxFileExtns) { if (fx == pathExtension) { return true; } } + for (auto& fx : _glslFragFileExtns) { if (fx == pathExtension) { return true; } } + for (auto& fx : _glslCompFileExtns) { if (fx == pathExtension) { return true; } } + return false; +} + +bool MoltenVKShaderConverterTool::isSPIRVFileExtension(string& pathExtension) { + for (auto& fx : _spvFileExtns) { if (fx == pathExtension) { return true; } } + return false; +} + +/** Log the specified message to the console. */ +void MoltenVKShaderConverterTool::log(const char* logMsg) { printf("%s\n", logMsg); } + +/** Display usage information about this application on the console. */ +void MoltenVKShaderConverterTool::showUsage() { + string line = "\n\e[1m" + _processName + "\e[0m converts OpenGL Shading Language (GLSL) source code to"; + log((const char*)line.c_str()); + log("SPIR-V code, and/or to Metal Shading Language (MSL) source code, or converts"); + log("SPIR-V code to Metal Shading Language source code."); + log("\nTo convert a single GLSL or SPIR-V file, include a file reference with the -gi"); + log("or -si option, respectively. To convert an entire directory of shader files,"); + log("use the -d option, along with the -gi or -si option. When using the -d option,"); + log("any file name supplied with the -gi or -si option will be ignored."); + log("\nUse the -so or -mo option to indicate the desired type of output"); + log("(SPIR-V or MSL, respectively)."); + log("\nUsage:"); + log(" -d [\"dirPath\"] - Path to a directory containing GLSL shader source code"); + log(" files. The dirPath may be omitted to use the current"); + log(" working directory."); + log(" -r - (when using -d) Process directories recursively."); + log(" -gi [\"glslInFile\"] - Indicates that GLSL shader code should be input."); + log(" The optional path parameter specifies the path to a"); + log(" single file containing GLSL source code to be converted."); + log(" When using the -d option, the path parameter is ignored."); + log(" -si [\"spvInFile\"] - Indicates that SPIR-V shader code should be input."); + log(" The optional path parameter specifies the path to a"); + log(" single file containing SPIR-V code to be converted."); + log(" When using the -d option, the path parameter is ignored."); + log(" -so [\"spvOutFile\"] - Indicates that SPIR-V shader code should be output."); + log(" The optional path parameter specifies the path to a single"); + log(" file to contain the SPIR-V code. When using the -d option,"); + log(" the path parameter is ignored."); + log(" -mo [\"mslOutFile\"] - Indicates that MSL shader source code should be output."); + log(" The optional path parameter specifies the path to a single"); + log(" file to contain the MSL code. When using the -d option,"); + log(" the path parameter is ignored."); + log(" -t shaderType - Shader type: vertex or fragment. Must be one of v, f, or c."); + log(" May be omitted to auto-detect."); + log(" -c - Combine the GLSL and converted Metal Shader source code"); + log(" into a single ouput file."); + log(" -Iv - Disable inversion of the vertex coordinate Y-axis"); + log(" (default is to invert vertex coordinates)."); + log(" -xs \"xtnSep\" - Separator to use when including file extension of original"); + log(" code file name in derived converted code file name."); + log(" Default is \"_\" (myshdr.vsh -> myshdr_vsh.metal)."); + log(" -XS - Disable including file extension of original code"); + log(" file name in derived converted code file name"); + log(" (myshdr.vsh -> myshdr.metal)."); + log(" -vx \"fileExtns\" - List of GLSL vertex shader file extensions."); + log(" May be omitted for defaults (\"vs vsh vert vertex\")."); + log(" -fx \"fileExtns\" - List of GLSL fragment shader file extensions."); + log(" May be omitted for defaults (\"fs fsh frag fragment\")."); + log(" -cx \"fileExtns\" - List of GLSL compute shader file extensions."); + log(" May be omitted for defaults (\"cp cmp comp compute kn kl krn kern kernel\")."); + log(" -sx \"fileExtns\" - List of SPIR-V shader file extensions."); + log(" May be omitted for defaults (\"spv spirv\")."); + log(" -l - Log the conversion results to the console (to aid debugging)."); + log(""); +} + + +#pragma mark Construction + +MoltenVKShaderConverterTool::MoltenVKShaderConverterTool(int argc, const char* argv[]) { + extractTokens(_defaultVertexShaderExtns, _glslVtxFileExtns); + extractTokens(_defaultFragShaderExtns, _glslFragFileExtns); + extractTokens(_defaultCompShaderExtns, _glslCompFileExtns); + extractTokens(_defaultSPIRVShaderExtns, _spvFileExtns); + _origPathExtnSep = "_"; + _shaderStage = kMVKShaderStageAuto; + _isActive = false; + _shouldUseDirectoryRecursion = false; + _shouldReadGLSL = false; + _shouldReadSPIRV = false; + _shouldWriteSPIRV = false; + _shouldWriteMSL = false; + _shouldCombineGLSLAndMSL = false; + _shouldFlipVertexY = true; + _shouldIncludeOrigPathExtn = true; + _shouldLogConversions = false; + + _isActive = parseArgs(argc, argv); + if ( !_isActive ) { showUsage(); } +} + +bool MoltenVKShaderConverterTool::parseArgs(int argc, const char* argv[]) { + if (argc == 0) { return false; } + + string execPath(argv[0]); + _processName = lastPathComponent(execPath); + + for (int argIdx = 1; argIdx < argc; argIdx++) { + string arg = argv[argIdx]; + + if ( !isOptionArg(arg) ) { return false; } + + if (equal(arg, "-d", false)) { + int optIdx = argIdx; + argIdx = optionParam(_directoryPath, argIdx, argc, argv); + if (argIdx == optIdx) { return false; } + _directoryPath = absolutePath(_directoryPath); + continue; + } + + if(equal(arg, "-r", true)) { + _shouldUseDirectoryRecursion = true; + continue; + } + + if (equal(arg, "-gi", true)) { + _shouldReadGLSL = true; + argIdx = optionParam(_glslInFilePath, argIdx, argc, argv); + continue; + } + + if (equal(arg, "-si", true)) { + _shouldReadSPIRV = true; + argIdx = optionParam(_spvInFilePath, argIdx, argc, argv); + continue; + } + + if (equal(arg, "-so", true)) { + _shouldWriteSPIRV = true; + argIdx = optionParam(_spvOutFilePath, argIdx, argc, argv); + continue; + } + + if (equal(arg, "-mo", true)) { + _shouldWriteMSL = true; + argIdx = optionParam(_mslOutFilePath, argIdx, argc, argv); + continue; + } + + if (equal(arg, "-t", true)) { + int optIdx = argIdx; + string shdrTypeStr; + argIdx = optionParam(shdrTypeStr, argIdx, argc, argv); + if (argIdx == optIdx || shdrTypeStr.length() == 0) { return false; } + + switch (shdrTypeStr.front()) { + case 'v': + _shaderStage = kMVKShaderStageVertex; + break; + case 'f': + _shaderStage = kMVKShaderStageFragment; + break; + case 'c': + _shaderStage = kMVKShaderStageCompute; + break; + default: + break; + } + continue; + } + + if(equal(arg, "-c", true)) { + _shouldCombineGLSLAndMSL = true; + continue; + } + + if(equal(arg, "-Iv", true)) { + _shouldFlipVertexY = false; + continue; + } + + if (equal(arg, "-xs", true)) { + _shouldIncludeOrigPathExtn = true; + argIdx++; + if (argIdx < argc) { _origPathExtnSep = argv[argIdx]; } + continue; + } + + if(equal(arg, "-XS", true)) { + _shouldIncludeOrigPathExtn = false; + continue; + } + + if (equal(arg, "-vx", true)) { + int optIdx = argIdx; + string shdrExtnStr; + argIdx = optionParam(shdrExtnStr, argIdx, argc, argv); + if (argIdx == optIdx || shdrExtnStr.length() == 0) { return false; } + extractTokens(shdrExtnStr, _glslVtxFileExtns); + continue; + } + + if (equal(arg, "-fx", true)) { + int optIdx = argIdx; + string shdrExtnStr; + argIdx = optionParam(shdrExtnStr, argIdx, argc, argv); + if (argIdx == optIdx || shdrExtnStr.length() == 0) { return false; } + extractTokens(shdrExtnStr, _glslFragFileExtns); + continue; + } + + if (equal(arg, "-cx", true)) { + int optIdx = argIdx; + string shdrExtnStr; + argIdx = optionParam(shdrExtnStr, argIdx, argc, argv); + if (argIdx == optIdx || shdrExtnStr.length() == 0) { return false; } + extractTokens(shdrExtnStr, _glslCompFileExtns); + continue; + } + + if (equal(arg, "-sx", true)) { + int optIdx = argIdx; + string shdrExtnStr; + argIdx = optionParam(shdrExtnStr, argIdx, argc, argv); + if (argIdx == optIdx || shdrExtnStr.length() == 0) { return false; } + extractTokens(shdrExtnStr, _spvFileExtns); + continue; + } + + if(equal(arg, "-l", true)) { + _shouldLogConversions = true; + continue; + } + + } + + return true; +} + +/** Returns whether the specified command line arg is an option arg. */ +bool MoltenVKShaderConverterTool::isOptionArg(string& arg) { + return (arg.length() > 1 && arg.front() == '-'); +} + +/** + * Sets the contents of the specified string to the parameter part of the option at the + * specified arg index, and increments and returns the option index. If no parameter was + * provided for the option, the string will be set to an empty string, and the returned + * index will be the same as the specified index. + */ +int MoltenVKShaderConverterTool::optionParam(string& optionParamResult, + int optionArgIndex, + int argc, + const char* argv[]) { + int optParamIdx = optionArgIndex + 1; + if (optParamIdx < argc) { + string arg(argv[optParamIdx]); + if ( !isOptionArg(arg) ) { + optionParamResult = arg; + return optParamIdx; + } + } + optionParamResult.clear(); + return optionArgIndex; +} + + +#pragma mark - +#pragma mark Support functions + +/** Template function for tokenizing the components of a string into a vector. */ +template +Container& split(Container& result, + const typename Container::value_type& s, + const typename Container::value_type& delimiters, + bool includeEmptyElements) { + result.clear(); + size_t current; + size_t next = -1; + do { + if (includeEmptyElements) { + next = s.find_first_not_of( delimiters, next + 1 ); + if (next == Container::value_type::npos) break; + next -= 1; + } + current = next + 1; + next = s.find_first_of( delimiters, current ); + result.push_back( s.substr( current, next - current ) ); + } while (next != Container::value_type::npos); + return result; +} + +void mvk::extractTokens(string str, vector& tokens) { + split(tokens, str, " \t\n\f", false); +} + +/** Compares the specified characters ignoring case. */ +static bool compareIgnoringCase(unsigned char a, unsigned char b) { + return tolower(a) == tolower(b); +} + +bool mvk::equal(string const& a, string const& b, bool checkCase) { + if (a.length() != b.length()) { return false; } + return checkCase ? (a == b) : (equal(b.begin(), b.end(), a.begin(), compareIgnoringCase)); +} + +void mvk::spirvToBytes(const vector& spv, vector& bytes) { + // Assumes desired endianness. + size_t byteCnt = spv.size() * sizeof(uint32_t); + char* cBytes = (char*)spv.data(); + bytes.clear(); + bytes.insert(bytes.end(), cBytes, cBytes + byteCnt); +} + +void mvk::bytesToSPIRV(const vector& bytes, vector& spv) { + size_t spvCnt = bytes.size() / sizeof(uint32_t); + uint32_t* cSPV = (uint32_t*)bytes.data(); + spv.clear(); + spv.insert(spv.end(), cSPV, cSPV + spvCnt); + ensureSPIRVEndianness(spv); +} + +bool mvk::ensureSPIRVEndianness(vector& spv) { + if (spv.empty()) { return false; } // Nothing to convert + + uint32_t magNum = spv.front(); + if (magNum == spv::MagicNumber) { return false; } // No need to convert + + if (CFSwapInt32(magNum) == spv::MagicNumber) { // Yep, it's SPIR-V, but wrong endianness + for (auto& elem : spv) { elem = CFSwapInt32(elem); } + return true; + } + return false; // Not SPIR-V, so don't convert +} + + diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.h b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.h new file mode 100644 index 00000000..2da2ad33 --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.h @@ -0,0 +1,133 @@ +/* + * MoltenVKShaderConverterTool.h + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + + +#include "GLSLConversion.h" +#include +#include + + +namespace mvk { + + +#pragma mark - +#pragma mark MoltenVKShaderConverterTool + + /** Converts GLSL files to SPIR-V and MSL files, and SPIR-V files to MSL files. */ + class MoltenVKShaderConverterTool { + + public: + + /** + * Called automatically during the conversion of all the files in a directory. + * Processes the specified file (which can contain either GLSL or SPIR-V code. + * Always returns false. + */ + bool processFile(std::string filePath); + + /** + * Run the converter based on command line arguments. + * Returns zero if all went well, or an error code if not. + */ + int run(); + + /** Constructor with specified command line arguments. */ + MoltenVKShaderConverterTool(int argc, const char* argv[]); + + protected: + MVKShaderStage shaderStageFromFileExtension(std::string& pathExtension); + bool isGLSLFileExtension(std::string& pathExtension); + bool isSPIRVFileExtension(std::string& pathExtension); + bool convertGLSL(std::string& glslInFile, + std::string& spvOutFile, + std::string& mslOutFile, + MVKShaderStage shaderStage); + bool convertSPIRV(std::string& spvInFile, + std::string& mslOutFile); + bool convertSPIRV(const std::vector& spv, + std::string& inFile, + std::string& mslOutFile, + bool shouldLogSPV); + bool parseArgs(int argc, const char* argv[]); + void log(const char* logMsg); + void showUsage(); + bool isOptionArg(std::string& arg); + int optionParam(std::string& optionParamResult, + int optionArgIndex, + int argc, + const char* argv[]); + + std::string _processName; + std::string _directoryPath; + std::string _glslInFilePath; + std::string _spvInFilePath; + std::string _spvOutFilePath; + std::string _mslOutFilePath; + std::string _origPathExtnSep; + std::vector _glslVtxFileExtns; + std::vector _glslFragFileExtns; + std::vector _glslCompFileExtns; + std::vector _spvFileExtns; + MVKShaderStage _shaderStage; + bool _isActive; + bool _shouldUseDirectoryRecursion; + bool _shouldReadGLSL; + bool _shouldReadSPIRV; + bool _shouldWriteSPIRV; + bool _shouldWriteMSL; + bool _shouldCombineGLSLAndMSL; + bool _shouldFlipVertexY; + bool _shouldIncludeOrigPathExtn; + bool _shouldLogConversions; + }; + + +#pragma mark - +#pragma mark Support functions + + /** + * Extracts whitespace-delimited tokens from the specified string and + * appends them to the specified vector. The vector is not cleared first. + */ + void extractTokens(std::string str, std::vector& tokens); + + /** Compares the specified strings, with or without sensitivity to case. */ + bool equal(std::string const& a, std::string const& b, bool checkCase = true); + + /** Converts the SPIR-V code to an array of bytes (suitable for writing to a file). */ + void spirvToBytes(const std::vector& spv, std::vector& bytes); + + /** Converts an array of bytes (as read from a file) to SPIR-V code. */ + void bytesToSPIRV(const std::vector& bytes, std::vector& spv); + + /** + * Ensures that the specified SPIR-V code has the correct endianness for this system, + * and converts it in place if necessary. This can be used after loading SPIR-V code + * from a file that may have been encoded on a system with the opposite endianness. + * + * This function tests for the SPIR-V magic number (in both endian states) to determine + * whether conversion is required. It will not convert arrays of uint32_t values that + * are not SPIR-V code. + * + * Returns whether the endianness was changed. + */ + bool ensureSPIRVEndianness(std::vector& spv); + +} diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverterTool/main.cpp b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/main.cpp new file mode 100644 index 00000000..45025507 --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/main.cpp @@ -0,0 +1,27 @@ +/* + * main.cpp + * + * Copyright (c) 2014-2017 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MoltenVKShaderConverterTool.h" + +using namespace mvk; + + +int main(int argc, const char * argv[]) { + MoltenVKShaderConverterTool converter(argc, argv); + return converter.run(); +} diff --git a/MoltenVKShaderConverter/ThirdPartyConfig.md b/MoltenVKShaderConverter/ThirdPartyConfig.md new file mode 100644 index 00000000..b3470889 --- /dev/null +++ b/MoltenVKShaderConverter/ThirdPartyConfig.md @@ -0,0 +1,313 @@ + + +Copyright (c) 2014-2017 [The Brenwill Workshop Ltd.](http://www.brenwill.com) + +*This document is written in [Markdown](http://en.wikipedia.org/wiki/Markdown) format. +For best results, use a Markdown reader.* + + +Table of Contents +----------------- + +- *SPIRV-Cross* + - [Using the *SPIRV-Cross* library with **MoltenVKShaderConverter**](#install_spirv-cross) + - [Updating the *SPIRV-Cross* library version](#update_spirv-cross) + - [Adding the *SPIRV-Cross* library to a new *Xcode* project](#add_spirv-cross) + +- *SPIRV-Tools* + - [Using the *SPIRV-Tools* library with **MoltenVKShaderConverter**](#install_spirv-tools) + - [Updating the *SPIRV-Tools* library version](#update_spirv-tools) + - [Adding the *SPIRV-Tools* library to a new *Xcode* project](#add_spirv-tools) + +- *glslang* + - [Using the *glslang* library with **MoltenVKShaderConverter**](#install_glslang) + - [Updating the *glslang* library version](#update_glslang) + - [Adding the *glslang* library to a new *Xcode* project](#add_glslang) + + + + +Using the *SPIRV-Cross* library with *MoltenVKShaderConverter* +-------------------------------------------------------------- + +**MoltenVKShaderConverter** uses `SPIRV-Cross` to convert *SPIR-V* code to *Metal Shading Language (MSL)* source code. + +If you used the `--recursive` option when cloning the `MoltenVK` repository, you should already +have the `SPIRV-Cross` submodule. If you did **_not_** use the `--recursive` option when cloning +the `MoltenVK` repository, retrieve the `SPIRV-Cross` submodule into the `External` directory +as follows, from within the `MoltenVK` repository directory: + + git submodule update --init External/SPIRV-Cross + + + + +Updating the *SPIRV-Cross* library version +------------------------------------------ + +If you are developing enhancements to **MoltenVKShaderConverter**, you can update the version of +`SPIRV-Cross` used by **MoltenVKShaderConverter**, as follows: + + cd External + rm -rf SPIRV-Cross + git clone https://github.com/KhronosGroup/SPIRV-Cross.git + +The updated version will then be "locked in" the next time the `MoltenVK` repository is committed to `git`. + +>***Note:*** If after updating to a new verions of `SPIRV-Cross`, you encounter build errors when + building **MoltenVKShaderConverter**, review the [instructions below](#add_spirv-cross) to ensure + all necessary `SPIRV-Cross` files are included in the **MoltenVKShaderConverter** builds. + +>***Note:*** As new features are added to **MoltenVK**, many are powered by the ability to convert + sophisticated *SPIRV* code into *MSL* code. Sometimes new **MoltenVK** features and capabilities are + provided solely via new `SPIRV-Cross` features. ***If you are developing enhancements for + MoltenVKShaderConverter, be sure to update the `SPIRV-Cross` submodule often***. + + +### Regression Testing Your Changes to *SPIRV-Cross* + +If you make changes to the `SPIRV-Cross` submodule, you can regression test your changes by building the +`spirv-cross` executable and running the `test_shaders.py` regression test script, using the following steps: + + +1. If you did not run the `External/makeAll` script, build the `SPIRV-Tools` and `glslangValidator`tools + (you should only need to do this once): + + cd External + ./makeSPIRVTools + ./makeglslang + +2. Set your `PATH` environment variable so that the `spirv-cross` tool can find the + `glslangValidator` and `SPIRV-Tools` tools: + + export PATH=$PATH:"../glslang/build/StandAlone:../SPIRV-Tools/build/tools" + +3. Build the `spirv-cross` executable: + + cd External/SPIRV-Cross + make + +4. Run the regression tests: + + ./test_shaders.py --msl shaders-msl + +5. If your changes result in different expected output for a reference shader, you can update + the reference shader for a particular regression test: + + 1. Termporarily rename the existing reference shader file in `External/SPIRV-Cross/reference/shaders-msl`. + 2. Run the regression tests. A new reference shader will be automatically generated. + 3. Compare the new reference shader to the old one using a tool like *Xcode Version Editor*, + or *Xcode FileMerge*, or equivalent. + 4. Delete the old copy of the reference shader. + + + +Adding the *SPIRV-Cross* library to a new *Xcode* project +--------------------------------------------------------- + +The `MoltenVKShaderConverter` project is already configured to use the `SPIRV-Cross` library. +However, to add the `SPIRV-Cross` library to a new *Xcode* project: + +1. Follow the [instructions above](#install_spirv-cross) to create a symlink from your project + to the location of your local clone of the `SPIRV-Cross` repository. + +2. In the project navigator, add a new *Group* named `SPIRV-Cross`. + +3. Add the following files from the `SPIRV-Cross` file folder to the `SPIRV-Cross` + group in the *Project Navigator* panel: + + spirv_cfg.cpp + spirv_cfg.hpp + spirv_common.hpp + spirv_cross.cpp + spirv_cross.hpp + spirv_glsl.cpp + spirv_glsl.hpp + spirv_msl.cpp + spirv_msl.hpp + + In the ***Choose options for adding these files*** dialog that opens, select the + ***Create groups*** option, add the files to *both* the `MoltenVKSPIRVToMSLConverter-iOS` + and `MoltenVKSPIRVToMSLConverter-macOS` targets, and click the ***Finish*** button. + +4. ***(Optional)*** If you want *Xcode* to reference the added files through symlinks (to increase + portability) instead of resolving them, perform the following steps: + + 1. **Create a backup of your project!** This is an intrusive and dangerous operation! + 2. In the *Finder*, right-click your `MyApp.xcodeproj` file and select *Show Package Contents*. + 3. Open the `project.pbxproj` file in a text editor. + 4. Replace all occurrences of the `path-to-SPIRV-Cross-repo-folder` (as defined by the symlink added + [above](#install_spirv-cross)) with simply `SPIRV-Cross` (the name of the symlink). Be sure you only + replace the part of the path that matches the `path-to-SPIRV-Cross-repo-folder`. Do not replace + any part of the path that indicates a subfolder within that repository folder. + + + + +Using the *SPIRV-Tools* library with *MoltenVKShaderConverter* +-------------------------------------------------------------- + +**MoltenVKShaderConverter** uses `SPIRV-Tools` to log *SPIR-V* code during conversion to *Metal Shading Language (MSL)* +source code. The `SPIRV-Tools` also requires the `SPIRV-Headers` library. + +To add the `SPIRV-Tools` and `SPIRV-Headers` libraries to **MoltenVK**, open a *Terminal* session and +perform the following command-line steps: + +1. If you used the `--recursive` option when cloning the `MoltenVK` repository, you should already + have the `SPIRV-Tools` and `SPIRV-Headers` submodules, and you can skip to *Step 2* below. + If you did **_not_** use the `--recursive` option when cloning the `MoltenVK` repository, + retrieve the `SPIRV-Tools` and `SPIRV-Headers` submodules into the `External` directory + as follows, from within the `MoltenVK` repository directory: + + git submodule update --init External/SPIRV-Headers + git submodule update --init External/SPIRV-Tools + +3. In the `Externals` folder within the `MoltenVK` repository, build `SPIRV-Tools` + as follows from the main directory of this `MoltenVK` repository: + + cd External + ./makeSPIRVTools + + + + +Updating the *SPIRV-Tools* library version +------------------------------------------ + +If you are developing enhancements to **MoltenVKShaderConverter**, you can update the version of +`SPIRV-Tools` used by **MoltenVKShaderConverter**, as follows: + + cd External + + rm -rf SPIRV-Headers + git clone https://github.com/KhronosGroup/SPIRV-Headers.git + + rm -rf SPIRV-Tools + git clone https://github.com/KhronosGroup/SPIRV-Tools.git + + ./makeSPIRVTools + +The updated version will then be "locked in" the next time the `MoltenVK` repository is committed to `git`. + +>***Note:*** If after updating to a new verions of `SPIRV-Tools`, you encounter build errors when +>building **MoltenVKShaderConverter**, review the [instructions below](#add_spirv-tools) to ensure +>all necessary `SPIRV-Tools` files are included in the **MoltenVKShaderConverter** builds. + + + + +Adding the *SPIRV-Tools* library to a new *Xcode* project +--------------------------------------------------------- + +The `MoltenVKShaderConverter` project is already configured to use the `SPIRV-Tools` library. +However, to add the `SPIRV-Tools` library to a new *Xcode* project: + +1. Follow the [instructions above](#install_spirv) to create a symlink from your project + to the location of your local clone of the `SPIRV-Tools` repository. + +2. In the project navigator, add a new *Group* named `SPIRV-Tools`. + +3. Drag the `SPIRV-Tools/source` folder to the `SPIRV-Tools` group in the *Project Navigator* panel. + In the _**Choose options for adding these files**_ dialog that opens, select the + _**Create groups**_ option, add the files to *both* the `MoltenVKSPIRVToMSLConverter-iOS` + and `MoltenVKSPIRVToMSLConverter-macOS` targets, and click the ***Finish*** button. + +4. In the *Project Navigator* panel, select your application's target, and open the + *Build Settings* tab. Locate the build setting entry **Header Search Paths** + (`HEADER_SEARCH_PATHS`) and add the following paths: + + "$(SRCROOT)/MoltenVKSPIRVToMSLConverter/SPIRV-Tools/include" + "$(SRCROOT)/MoltenVKSPIRVToMSLConverter/SPIRV-Tools/source" + "$(SRCROOT)/MoltenVKSPIRVToMSLConverter/SPIRV-Tools/build" + "$(SRCROOT)/MoltenVKSPIRVToMSLConverter/SPIRV-Headers/include" + +5. ***(Optional)*** If you want *Xcode* to reference the added files through symlinks (to increase + portability) instead of resolving them, perform the following steps: + + 1. **Create a backup of your project!** This is an intrusive and dangerous operation! + 2. In the *Finder*, right-click your `MyApp.xcodeproj` file and select *Show Package Contents*. + 3. Open the `project.pbxproj` file in a text editor. + 4. Replace all occurrences of the `path-to-SPIRV-Tools-repo-folder` (as defined by the symlink added + [above](#install_spirv)) with simply `SPIRV-Tools` (the name of the symlink). Be sure you only + replace the part of the path that matches the `path-to-SPIRV-Tools-repo-folder`. Do not replace + any part of the path that indicates a subfolder within that repository folder. + + + + +Using the *glslang* library with **MoltenVKShaderConverter** +------------------------------------------------------------ + +**MoltenVKShaderConverter** uses `glslang`, the Khronos *GLSL* reference compiler, to parse *GLSL* source code +and convert it to *SPIR-V*. + +If you used the `--recursive` option when cloning the `MoltenVK` repository, you should already have +the `glslang` submodule. If you did **_not_** use the `--recursive` option when cloning the +`MoltenVK` repository, retrieve the `glslang` submodule into the `External` directory as follows, +from within the `MoltenVK` repository directory: + + git submodule update --init External/glslang + + + + +Updating the *glslang* library version +------------------------------------------ + +If you are developing enhancements to **MoltenVKShaderConverter**, you can update the version of +`glslang` used by **MoltenVKShaderConverter**, as follows: + + cd External + rm -rf glslang + git clone https://github.com/KhronosGroup/glslang.git + ./makeglslang + +The updated version will then be "locked in" the next time the `MoltenVK` repository is committed to `git`. + +>***Note:*** If after updating to a new verions of `glslang`, you encounter build errors when +>building **MoltenVKShaderConverter**, review the [instructions below](#add_glslang) to ensure +>all necessary `glslang` files are included in the **MoltenVKShaderConverter** builds. + + + + +Adding the *glslang* library to a new *Xcode* project +----------------------------------------------------- + +The `MoltenVKShaderConverter` project is already configured to use the `glslang` library. +However, to add the `glslang` library to a new *Xcode* project: + +1. Follow the [instructions above](#install_glslang) to create a symlink from your project + to the location of your local clone of the `glslang` repository, and make the required + modifications to the `glslang` code. + +2. In the project navigator, add a new *Group* named `glslang`. + +3. Add the following folders from the `glslang` file folder to the `glslang` *Group* in + the *Project Navigator* panel: + + glslang + OGLCompilersDLL + SPIRV + + In the ***Choose options for adding these files*** dialog that opens, select the + ***Create groups*** option, add the files to *both* the `MoltenVKGLSLToSPIRVConverter-iOS` + and `MoltenVKGLSLToSPIRVConverter-macOS` targets, and click the ***Finish*** button. + +4. In the *Project Navigator* panel, remove the references to the following files and folders: + + glslang/glslang/MachineIndependant/glslang.y + glslang/glslang/OSDependent/Windows + +5. ***(Optional)*** If you want *Xcode* to reference the added files through symlinks (to increase + portability) instead of resolving them, perform the following steps: + + 1. **Create a backup of your project!** This is an intrusive and dangerous operation! + 2. In the *Finder*, right-click your `MyApp.xcodeproj` file and select *Show Package Contents*. + 3. Open the `project.pbxproj` file in a text editor. + 4. Replace all occurrences of the `path-to-glslang-repo-folder` (as defined by the symlink added + [above](#install_glslang)) with simply `glslang` (the name of the symlink). Be sure you only + replace the part of the path that matches the `path-to-glslang-repo-folder`. Do not replace + any part of the path that indicates a subfolder within that repository folder. diff --git a/README.md b/README.md new file mode 100755 index 00000000..8a3bef0b --- /dev/null +++ b/README.md @@ -0,0 +1,196 @@ + + + + +MoltenVK +======== + +Copyright (c) 2014-2017 [The Brenwill Workshop Ltd.](http://www.brenwill.com) + +*This document is written in [Markdown](http://en.wikipedia.org/wiki/Markdown) format. +For best results, use a Markdown reader.* + + + +Table of Contents +----------------- + +- [About This Document](#about_this) +- [Introduction](#intro) +- [Installing **MoltenVK**](#install) +- [Third-Party Components](#third-party) +- [Building **MoltenVK**](#building) +- [Using **MoltenVK** in Your Application](#using) +- [Third-Party Credits](#credits) + + + + +About This Document +------------------- + +This document describes how to use the **MoltenVK** open-source repository to build a **MoltenVK** +runtime distribution package. + +To learn how to integrate the **MoltenVK** runtime into a game or application, see the +[**MoltenVK Runtime User Guide**](Docs/MoltenVK_Runtime_UserGuide.md) document in the `Docs` directory. + + + + +Introduction +------------ + +**MoltenVK** contains two products: + +- **MoltenVK** is an implementation of the [*Vulkan*](https://www.khronos.org/vulkan) + graphics and compute API, that runs on Apple's [*Metal*](https://developer.apple.com/metal) + graphics and compute framework on both *iOS* and *macOS*. + +- **MoltenVKShaderConverter** converts *SPIR-V* shader code to *Metal Shading Language (MSL)* + shader source code, and converts *GLSL* shader source code to *SPIR-V* shader code and/or + *Metal Shading Language (MSL)* shader source code, for use with **MoltenVK**. The converter + can run at runtime as a component of the *MoltenVK* runtime, or it can be packaged into a + stand-alone command-line *macOS* tool. The *Xcode* project contains several targets, + reflecting this multi-use capability. + + + + +Installing **MoltenVK** +----------------------- + +**MoltenVK** relies on several third-party open-source libraries, which are described in the +[next section](#third-party). The easiest way to install **MoltenVK** is to recursively clone +this `MoltenVK` repository, and then run the `External/makeAll` script to create necessary +components within the third-party libraries. + +1. Ensure you have `python3` and `asciidoctor` installed: + + brew install python3 + sudo gem install asciidoctor + +2. Recursively clone the `MoltenVK` repository: + + git clone --recursive https://github.com/KhronosGroup/MoltenVK.git + +3. Run the third-party build script: + + cd MoltenVK/External + ./makeAll + +See the [next section](#third-party) for more information about the third-party libraries, +and how to work with them within the **MoltenVK** development environment. + + + +Third-Party Components +---------------------- + +**MoltenVK** makes use of several third-party open-source libraries. +Development of some of these components is managed separately. + +If you used the `--recursive` option when cloning this repository, as described +[above](#install), all third party libraries will have been retrieved. + +If you did not use the `--recursive` option when cloning this repository, you can retrieve +and install these libraries into your `MoltenVK` repository environment as follows from within +the `MoltenVK` repository: + + git submodule update --init --recursive + cd External + ./makeAll + +>***Note:*** Some of these libraries are maintained and updated regularly with new features. +> See the documents listed just below here to learn how to update the version of each library as needed. + +**MoltenVK** uses the third-party libraries described in the following documents: + +- [`MoltenVK/ThirdPartyConfig.md`](MoltenVK/ThirdPartyConfig.md) +- [`MoltenVKShaderConverter/ThirdPartyConfig.md`](MoltenVKShaderConverter/ThirdPartyConfig.md) + + + + +Building **MoltenVK** +------------------- + +>***Note:*** Before attempting to build **MoltenVK**, be sure you have followed the +instructions in the [*Third-Party Components*](#third-party) section above to retrieve +and install the required third-party components. + +>***Note:*** At runtime, **MoltenVK** can run on *iOS 9* and *macOS 11.0* devices, +>but it does reference advanced OS frameworks during building. *Xcode 9* +>or above is required to build **MoltenVK**, and build and link **MoltenVK** projects. + +>***Note for Xcode 9 on macOS Sierra (10.12):*** There is currently an issue with *Xcode 9* when building +**MoltenVK** on *macOS Sierra (10.12)*. When creating the `libMoltenVK.dylib` file for *macOS*, `libtool` +is unable to link to the file `/usr/lib/system/libsystem_darwin.dylib` that *Xcode 9* indicates is required, +because that file does not exist on *macOS Sierra (10.12)*. To work around this issue, add the following symlink: +> + ln -sfn /usr/lib/system/libsystem_coretls.dylib /usr/local/lib/libsystem_darwin.dylib + + +The `MoltenVKPackaging.xcodeproj` *Xcode* project contains targets and schemes to build +and package the entire **MoltenVK** runtime distribution package, or to build individual +**MoltenVK** or **MoltenVKShaderConverter** components. + +To build a **MoltenVK** runtime distribution package, suitable for testing and integrating into an app, +open `MoltenVKPackaging.xcodeproj` in *Xcode*, and use one of the following *Xcode Schemes*: + +- **MoltenVK (Release)** - build the entire **MoltenVK** runtime distribution package using the + *Release* configuration. +- **MoltenVK (Debug)** - build the entire **MoltenVK** runtime distribution package using the + *Debug* configuration. + +Each of these`MoltenVKPackaging.xcodeproj` *Xcode* project *Schemes* puts the resulting packages in the +`Package` directory, creating it if necessary. This directory contains separate `Release` and `Debug` +directories, holding the most recent **Release** and **Debug** builds, respectively. + +A separate `Latest` directory links to the most recent build, regardless of whether it was a **Release** +or **Debug** build. Effectively, the `Package/Latest` directory points to whichever of the `Package/Release` +or `Package/Debug` directories was most recently updated. + +With this packaging structure, you can follow the [instructions below](#using) to link your application +to the **MoltenVK** frameworks in the `Package/Latest` directory, to provide the flexibility to test your +app with either a **Debug** build, or a higher-performance **Release** build. + +Once you have built the **MoltenVK** runtime distribution package, the **MoltenVK** demo apps can be +accessed from the `Demos/Demos.xcworkspace` *Xcode* workspace. This is the same workspace that is +included in the **MoltenVK** runtime distribution package, and you can use it to build and run the +**MoltenVK** demo apps, or to add new demos to this **MoltenVK** repository. + + + + +Using **MoltenVK** in Your Application +-------------------------------------- + +Once you have compiled and built the **MoltenVK** runtime distribution package from this **MoltenVK** +repository, as described in the [previous section](#building), follow the instructions in the installation +section of the [**MoltenVK Runtime User Guide**](Docs/MoltenVK_Runtime_UserGuide.md#install) document in the +`Docs` directory of the **MoltenVK** runtime distribution package found in the `Package/Latest` directory, +to link the **MoltenVK** frameworks and libraries in the `Package/Latest` directory to your application. + +The runtime distribution package in the `Package/Latest` directory is a stand-alone package, and you can copy +the contents of that directory out of this **MoltenVK** repository into your own application building environment. + + + + +Third-Party Credits +------------------- + +**MoltenVK** uses technology from the following open-source frameworks: + +- [*Vulkan-Hpp*](https://github.com/KhronosGroup/Vulkan-Hpp) +- [*Vulkan-Docs*](https://github.com/KhronosGroup/Vulkan-Docs) +- [*tinyxml2*](https://github.com/leethomason/tinyxml2) + +**MoltenVKShaderConverter** uses technology from the following open-source frameworks: + +- [*SPIRV-Cross*](https://github.com/KhronosGroup/SPIRV-Cross) +- [*SPIRV-Tools*](https://github.com/KhronosGroup/SPIRV-Tools) +- [*glslang*](https://github.com/KhronosGroup/glslang)