From 20c98e5e464e2d80881454cde3d9d5356aaccc25 Mon Sep 17 00:00:00 2001 From: Bill Hollings Date: Fri, 30 Mar 2018 12:13:50 -0400 Subject: [PATCH] Add support for caching converted MSL shader code offline from pipeline cache via vkGetPipelineCacheData(), vkCreatePipelineCache() & vkMergePipelineCaches(). Add the cereal serialization framework as a dependency. Update documentation. Update project settings to Xcode 9.3. --- .gitignore | 1 + Common/MVKStrings.h | 21 ++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../API-Samples.xcodeproj/project.pbxproj | 2 +- .../xcschemes/API-Samples-iOS.xcscheme | 4 +- .../xcschemes/API-Samples-macOS.xcscheme | 4 +- .../Demos/Demos.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Cube-iOS.xcscheme | 4 +- .../xcschemes/Cube-macOS.xcscheme | 4 +- .../Hologram.xcodeproj/project.pbxproj | 2 +- .../xcschemes/Hologram-iOS.xcscheme | 4 +- .../xcschemes/Hologram-macOS.xcscheme | 4 +- Docs/MoltenVK_Runtime_UserGuide.md | 32 +- External/README.md | 39 ++- External/cereal_repo_revision | 1 + External/fetchDependencies | 10 + MoltenVK/MoltenVK.xcodeproj/project.pbxproj | 4 +- .../xcschemes/MoltenVK-iOS.xcscheme | 4 +- .../xcschemes/MoltenVK-macOS.xcscheme | 4 +- MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h | 3 + MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm | 28 +- MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h | 25 +- MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm | 287 +++++++++++++++++- .../MoltenVK/GPUObjects/MVKShaderModule.h | 66 +++- .../MoltenVK/GPUObjects/MVKShaderModule.mm | 52 ++-- MoltenVK/MoltenVK/Vulkan/vulkan.mm | 2 +- MoltenVKPackaging.xcodeproj/project.pbxproj | 4 +- .../xcschemes/MoltenVK (Debug).xcscheme | 4 +- .../xcschemes/MoltenVK (Release).xcscheme | 4 +- .../project.pbxproj | 2 +- .../MoltenVKGLSLToSPIRVConverter-iOS.xcscheme | 4 +- ...oltenVKGLSLToSPIRVConverter-macOS.xcscheme | 4 +- .../MoltenVKSPIRVToMSLConverter-iOS.xcscheme | 4 +- ...MoltenVKSPIRVToMSLConverter-macOS.xcscheme | 4 +- .../MoltenVKShaderConverter.xcscheme | 16 +- 35 files changed, 535 insertions(+), 128 deletions(-) create mode 100644 Demos/Demos.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 External/cereal_repo_revision diff --git a/.gitignore b/.gitignore index 1626c97d..b9e49068 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ External/SPIRV-Tools External/SPIRV-Headers External/SPIRV-Cross External/VulkanSamples +External/cereal # Other source repository archive directories (protects when importing) .hg diff --git a/Common/MVKStrings.h b/Common/MVKStrings.h index 30253442..0e1f9d5a 100644 --- a/Common/MVKStrings.h +++ b/Common/MVKStrings.h @@ -20,6 +20,7 @@ #define __MVKStrings_h_ 1 #include +#include namespace mvk { @@ -44,6 +45,26 @@ namespace mvk { return ( (startPos != std::string::npos) && (endPos != std::string::npos) ) ? s.substr(startPos, endPos + 1) : ""; } + /** A memory-based stream buffer. */ + class membuf : public std::streambuf { + public: + membuf(char* p, size_t n) { + setg(p, p, p + n); + setp(p, p + n); + } + }; + + /** A character counting stream buffer. */ + class countbuf : public std::streambuf { + public: + size_t buffSize = 0; + private: + std::streamsize xsputn (const char* /* s */, std::streamsize n) override { + buffSize += n; + return n; + } + }; + } #endif diff --git a/Demos/Demos.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Demos/Demos.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/Demos/Demos.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/project.pbxproj b/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/project.pbxproj index 1d89a931..08a0dcc5 100644 --- a/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/project.pbxproj +++ b/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/project.pbxproj @@ -623,7 +623,7 @@ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0920; + LastUpgradeCheck = 0930; TargetAttributes = { A977BCBD1B66BB010067E5BF = { DevelopmentTeam = VU3TCKU48B; 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 index f3edd6cd..248af895 100644 --- 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 @@ -1,6 +1,6 @@ @@ -46,7 +45,6 @@ buildConfiguration = "Release" selectedDebuggerIdentifier = "" selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" 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 index 2711187b..46951ff2 100644 --- 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 @@ -1,6 +1,6 @@ @@ -46,7 +45,6 @@ buildConfiguration = "Release" selectedDebuggerIdentifier = "" selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/Demos/LunarG-VulkanSamples/Demos/Demos.xcodeproj/project.pbxproj b/Demos/LunarG-VulkanSamples/Demos/Demos.xcodeproj/project.pbxproj index 7ec96e10..8258d687 100644 --- a/Demos/LunarG-VulkanSamples/Demos/Demos.xcodeproj/project.pbxproj +++ b/Demos/LunarG-VulkanSamples/Demos/Demos.xcodeproj/project.pbxproj @@ -266,7 +266,7 @@ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0920; + LastUpgradeCheck = 0930; TargetAttributes = { A9B53B0F1C3AC0BE00ABC6F6 = { DevelopmentTeam = VU3TCKU48B; 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 index 9a76db66..e38307a6 100644 --- a/Demos/LunarG-VulkanSamples/Demos/Demos.xcodeproj/xcshareddata/xcschemes/Cube-iOS.xcscheme +++ b/Demos/LunarG-VulkanSamples/Demos/Demos.xcodeproj/xcshareddata/xcschemes/Cube-iOS.xcscheme @@ -1,6 +1,6 @@ @@ -46,7 +45,6 @@ buildConfiguration = "Release" selectedDebuggerIdentifier = "" selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" 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 index c08a7e80..267e7fcb 100644 --- a/Demos/LunarG-VulkanSamples/Demos/Demos.xcodeproj/xcshareddata/xcschemes/Cube-macOS.xcscheme +++ b/Demos/LunarG-VulkanSamples/Demos/Demos.xcodeproj/xcshareddata/xcschemes/Cube-macOS.xcscheme @@ -1,6 +1,6 @@ @@ -46,7 +45,6 @@ buildConfiguration = "Release" selectedDebuggerIdentifier = "" selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/project.pbxproj b/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/project.pbxproj index 9b808c94..8c1f6590 100644 --- a/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/project.pbxproj +++ b/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/project.pbxproj @@ -355,7 +355,7 @@ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0920; + LastUpgradeCheck = 0930; TargetAttributes = { A977BCBD1B66BB010067E5BF = { DevelopmentTeam = VU3TCKU48B; 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 index c33f730a..b43b7b40 100644 --- a/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/xcshareddata/xcschemes/Hologram-iOS.xcscheme +++ b/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/xcshareddata/xcschemes/Hologram-iOS.xcscheme @@ -1,6 +1,6 @@ @@ -46,7 +45,6 @@ buildConfiguration = "Release" selectedDebuggerIdentifier = "" selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" 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 index 86a0df37..4561f139 100644 --- a/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/xcshareddata/xcschemes/Hologram-macOS.xcscheme +++ b/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/xcshareddata/xcschemes/Hologram-macOS.xcscheme @@ -1,6 +1,6 @@ @@ -46,7 +45,6 @@ buildConfiguration = "Release" selectedDebuggerIdentifier = "" selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/Docs/MoltenVK_Runtime_UserGuide.md b/Docs/MoltenVK_Runtime_UserGuide.md index 80979c29..a3a1aa80 100644 --- a/Docs/MoltenVK_Runtime_UserGuide.md +++ b/Docs/MoltenVK_Runtime_UserGuide.md @@ -388,11 +388,33 @@ This section discusses various options for improving performance when using **Mo ### 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**. +A number of steps is require to load and compile *SPIR-V* shaders into a form that *Metal* can use. +Although the overall process is fast, the slowest step involves converting shaders from *SPIR-V* to +*MSL* source code format. + +If you have a lot of shaders, you can dramatically improve shader loading time by using the standard +*Vulkan pipeline cache* feature, to serialize shaders and store them in *MSL* form offline. +Loading *MSL* shaders via the pipeline cache serializing mechanism can be significantly faster than +converting from *SPIR-V* to *MSL* each time. + +In *Vulkan*, pipeline cache serialization for offline storage is available through the +`vkGetPipelineCacheData()` and `vkCreatePipelineCache()` functions. Loading the pipeline cache +from offline storage at app start-up time can dramatically improve both shader loading performance, +and performance glitches and hiccups during runtime code if shader loading is performed then. + +When using pipeline caching, nothing changes about how you load *SPIR-V* shader code. **MoltenVK** +automatically detects that the *SPIR-V* was previously converted to *MSL*, and stored offline via +the *Vulkan* pipeline cache serialization mechanism, and does not invoke the relatively expensive +step of converting the *SPIR-V* to *MSL* again. + +As a second shader loading performance option, *Metal* also 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**. This behaviour is not standard *Vulkan* behaviour, and does not +improve performance significantly. Your first choice should be to use offline storage of pipeline +cache contents as described in the previous paragraphs. diff --git a/External/README.md b/External/README.md index 7d2bb009..fcd48910 100644 --- a/External/README.md +++ b/External/README.md @@ -20,6 +20,7 @@ Table of Contents - [Adding the *SPIRV-Cross* Library to the *MoltenVKShaderConverter Xcode* Project](#add_spirv-cross) - [Adding the *SPIRV-Tools* Library to the *MoltenVKShaderConverter Xcode* Project](#add_spirv-tools) - [Adding the *glslang* Library to the *MoltenVKShaderConverter Xcode* Project](#add_glslang) +- [Adding the *cereal* Library to the *MoltenVK Xcode* Project](#add_cereal) @@ -35,6 +36,7 @@ Fetching External Libraries - [*SPIRV-Tools*](https://github.com/KhronosGroup/SPIRV-Tools) - [*SPIRV-Headers*](https://github.com/KhronosGroup/SPIRV-Headers) - [*VulkanSamples*](https://github.com/brenwill/VulkanSamples) +- [*cereal*](https://github.com/USCiLab/cereal) These external open-source libraries are maintained in the `External` directory. To retrieve these libraries from their sources, run the `fetchDependencies` @@ -75,9 +77,12 @@ determined as follows: - **_SPIRV-Headers_**: automatically retrieved by the *glslang* repository. -You can update which version of the *SPIRV-Cross*, *VulkanSamples*, and -*Vulkan-LoaderAndValidationLayers* libraries are retrieved, by changing the -value held in the corresponding `*_repo_revision` file listed above. +- **_cereal_**: a GitHub repository commit identifier found in the + `External/cereal_repo_revision` file. + +You can update which versions of the *SPIRV-Cross*, *VulkanSamples*, +*Vulkan-LoaderAndValidationLayers*, or *cereal* libraries are retrieved, +by changing the value held in the corresponding `*_repo_revision` file listed above. The version of the *glslang*, *SPIRV-Tools*, and *SPIRV-Headers* libraries is automatically determined by the version of the *Vulkan-LoaderAndValidationLayers* @@ -97,7 +102,7 @@ Adding the *SPIRV-Cross* Library to the *MoltenVKShaderConverter Xcode* Project The `MoltenVKShaderConverter` *Xcode* project is already configured to use the *SPIRV-Cross* library. However, after updating the version of *SPIRV-Cross*, as described [above](#updating), -if you encounter any linking errors, may need to re-add the *SPIRV-Cross* library to the +if you encounter any building errors, you may need to re-add the *SPIRV-Cross* library to the `MoltenVKShaderConverter` *Xcode* project as follows: 1. In the *Project Navigator*, remove all of the files under the *Group* named @@ -158,7 +163,7 @@ Adding the *SPIRV-Tools* Library to the *MoltenVKShaderConverter Xcode* Project The `MoltenVKShaderConverter` *Xcode* project is already configured to use the *SPIRV-Tools* library. However, after updating the version of *SPIRV-Tools*, as described [above](#updating), -if you encounter any linking errors, may need to re-add the *SPIRV-Tools* library to the +if you encounter any building errors, you may need to re-add the *SPIRV-Tools* library to the `MoltenVKShaderConverter` *Xcode* project as follows: 1. In the *Project Navigator*, remove the *Group* named `source` from under the *Group* named @@ -169,15 +174,17 @@ if you encounter any linking errors, may need to re-add the *SPIRV-Tools* librar _**Create groups**_ option, add the files to *both* the `MoltenVKSPIRVToMSLConverter-iOS` and `MoltenVKSPIRVToMSLConverter-macOS` targets, and click the ***Finish*** button. -3. 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: +3. In the *Project Navigator* panel, select the `MoltenVKShaderConverter` *Xcode* project, then select the + `MoltenVKSPIRVToMSLConverter-macOS` target, and open the *Build Settings* tab. Locate the build setting + entry **Header Search Paths** (`HEADER_SEARCH_PATHS`) and add the following paths: "$(SRCROOT)/../External/glslang/External/spirv-tools/include" "$(SRCROOT)/../External/glslang/External/spirv-tools/source" "$(SRCROOT)/../External/glslang/External/spirv-tools/external/spirv-headers/include" "$(SRCROOT)/../External/glslang/build/External/spirv-tools" +4. Repeat *Step 3* for the `MoltenVKSPIRVToMSLConverter-iOS` target within the `MoltenVKShaderConverter` *Xcode* project + 5. ***(Optional)*** To simplify the paths used within *Xcode* to reference the added files, perform the following steps: @@ -196,7 +203,7 @@ Adding the *glslang* Library to the *MoltenVKShaderConverter Xcode* Project The `MoltenVKShaderConverter` *Xcode* project is already configured to use the *glslang* library. However, after updating the version of *glslang*, as described [above](#updating), -if you encounter any linking errors, may need to re-add the *glslang* library to the +if you encounter any building errors, you may need to re-add the *glslang* library to the `MoltenVKShaderConverter` *Xcode* project as follows: 1. In the *Project Navigator*, remove all *Groups* from under the *Group* named @@ -229,3 +236,17 @@ if you encounter any linking errors, may need to re-add the *glslang* library to `glslang`, `OGLCompilersDLL`, and `SPIRV` directories added above. + + +Adding the *cereal* Library to the *MoltenVK Xcode* Project +----------------------------------------------------------- + +The `MoltenVK` *Xcode* project is already configured to use the *cereal* library. However, after +updating the version of *cereal*, as described [above](#updating), if you encounter any building +errors, you may need to re-add the *cereal* library to the `MoltenVK` *Xcode* project as follows: + +1. In the *Project Navigator* panel, select the `MoltenVK` *Xcode* project, then the `MoltenVK` + project target, and open the *Build Settings* tab. Locate the build setting entry + **Header Search Paths** (`HEADER_SEARCH_PATHS`) and add the following paths: + + "$(SRCROOT)/../External/cereal/include" diff --git a/External/cereal_repo_revision b/External/cereal_repo_revision new file mode 100644 index 00000000..e54806e3 --- /dev/null +++ b/External/cereal_repo_revision @@ -0,0 +1 @@ +51cbda5f30e56c801c07fe3d3aba5d7fb9e6cca4 diff --git a/External/fetchDependencies b/External/fetchDependencies index 10b72a64..2e6d07aa 100755 --- a/External/fetchDependencies +++ b/External/fetchDependencies @@ -48,6 +48,16 @@ echo V_LVL_NAME=Vulkan-LoaderAndValidationLayers GLSLANG_NAME=glslang + +# ----------------- Cereal ------------------- + +REPO_NAME=cereal +REPO_URL="https://github.com/USCiLab/${REPO_NAME}.git" +REPO_REV=$(cat "./${REPO_NAME}_repo_revision") + +clone_repo ${REPO_NAME} ${REPO_URL} ${REPO_REV} + + # ----------------- SPIRV-Cross ------------------- REPO_NAME=SPIRV-Cross diff --git a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj index daf0437e..c267980a 100644 --- a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj +++ b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj @@ -679,7 +679,7 @@ A9F55D25198BE6A7004EC31B /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0920; + LastUpgradeCheck = 0930; ORGANIZATIONNAME = "The Brenwill Workshop Ltd."; TargetAttributes = { A9B8EE091A98D796009C5A02 = { @@ -969,6 +969,7 @@ HEADER_SEARCH_PATHS = ( "\"$(SRCROOT)/include\"", "\"$(SRCROOT)/../MoltenVKShaderConverter\"", + "\"$(SRCROOT)/../External/cereal/include\"", ); IPHONEOS_DEPLOYMENT_TARGET = 9.0; MACH_O_TYPE = staticlib; @@ -1026,6 +1027,7 @@ HEADER_SEARCH_PATHS = ( "\"$(SRCROOT)/include\"", "\"$(SRCROOT)/../MoltenVKShaderConverter\"", + "\"$(SRCROOT)/../External/cereal/include\"", ); IPHONEOS_DEPLOYMENT_TARGET = 9.0; MACH_O_TYPE = staticlib; diff --git a/MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-iOS.xcscheme b/MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-iOS.xcscheme index eb093f8f..0337c065 100644 --- a/MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-iOS.xcscheme +++ b/MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-iOS.xcscheme @@ -1,6 +1,6 @@ @@ -37,7 +36,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-macOS.xcscheme b/MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-macOS.xcscheme index fa9927b9..0c6fbd04 100644 --- a/MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-macOS.xcscheme +++ b/MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-macOS.xcscheme @@ -1,6 +1,6 @@ @@ -37,7 +36,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h index 4eedd438..db36df58 100644 --- a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h +++ b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h @@ -113,6 +113,9 @@ typedef struct { MVKShaderCompilationEventPerformance functionRetrieval; /** Retrieve a MTLFunction from a MTLLibrary. */ MVKShaderCompilationEventPerformance functionSpecialization; /** Specialize a retrieved MTLFunction. */ MVKShaderCompilationEventPerformance pipelineCompile; /** Compile MTLFunctions into a pipeline. */ + MVKShaderCompilationEventPerformance sizePipelineCache; /** Calculate the size of cache data required to write MSL to pipeline cache data stream. */ + MVKShaderCompilationEventPerformance writePipelineCache; /** Write MSL to pipeline cache data stream. */ + MVKShaderCompilationEventPerformance readPipelineCache; /** Read MSL from pipeline cache data stream. */ } MVKShaderCompilationPerformance; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm index 9c89fb69..6595cafc 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm @@ -1328,7 +1328,7 @@ void MVKDevice::addShaderCompilationEventPerformanceImpl(MVKShaderCompilationEve double totalInterval = (shaderCompilationEvent.averageDuration * shaderCompilationEvent.count++) + currInterval; shaderCompilationEvent.averageDuration = totalInterval / shaderCompilationEvent.count; - MVKLogDebug("%s performance curr: %.3f ms, avg: %.3f ms, min: %.3f ms, max: %.3f ms, count: %d", + MVKLogDebug("Shader building performance to %s curr: %.3f ms, avg: %.3f ms, min: %.3f ms, max: %.3f ms, count: %d", getShaderCompilationEventName(shaderCompilationEvent), currInterval, shaderCompilationEvent.averageDuration, @@ -1338,14 +1338,17 @@ void MVKDevice::addShaderCompilationEventPerformanceImpl(MVKShaderCompilationEve } const char* MVKDevice::getShaderCompilationEventName(MVKShaderCompilationEventPerformance& shaderCompilationEvent) { - if (&shaderCompilationEvent == &_shaderCompilationPerformance.hashShaderCode) { return "Hash shader code"; } - 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.shaderLibraryFromCache) { return "Retrieve shader library from the cache."; } - 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"; } + if (&shaderCompilationEvent == &_shaderCompilationPerformance.hashShaderCode) { return "hash shader code"; } + 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.shaderLibraryFromCache) { return "retrieve shader library from the cache."; } + 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"; } + if (&shaderCompilationEvent == &_shaderCompilationPerformance.sizePipelineCache) { return "calculate cache size required to write MSL to pipeline cache"; } + if (&shaderCompilationEvent == &_shaderCompilationPerformance.writePipelineCache) { return "write MSL to pipeline cache"; } + if (&shaderCompilationEvent == &_shaderCompilationPerformance.readPipelineCache) { return "read MSL from pipeline cache"; } return "Unknown shader compile event"; } @@ -1406,6 +1409,8 @@ uint32_t MVKDevice::expandVisibilityResultMTLBuffer(uint32_t queryCount) { MVKDevice::MVKDevice(MVKPhysicalDevice* physicalDevice, const VkDeviceCreateInfo* pCreateInfo) : _mvkConfig() { // MVKLogDebug("Creating MVKDevice. Elapsed time: %.6f ms.", mvkGetElapsedMilliseconds()); + initPerformanceTracking(); + _physicalDevice = physicalDevice; _pFeatures = &_physicalDevice->_features; _pMetalFeatures = &_physicalDevice->_metalFeatures; @@ -1454,8 +1459,6 @@ MVKDevice::MVKDevice(MVKPhysicalDevice* physicalDevice, const VkDeviceCreateInfo } } - initPerformanceTracking(); - MVKLogInfo("Created VkDevice to run on GPU %s", _pProperties->deviceName); } @@ -1474,6 +1477,9 @@ void MVKDevice::initPerformanceTracking() { _shaderCompilationPerformance.functionRetrieval = initPerf; _shaderCompilationPerformance.functionSpecialization = initPerf; _shaderCompilationPerformance.pipelineCompile = initPerf; + _shaderCompilationPerformance.sizePipelineCache = initPerf; + _shaderCompilationPerformance.writePipelineCache = initPerf; + _shaderCompilationPerformance.readPipelineCache = initPerf; } MVKDevice::~MVKDevice() { diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h index d7a105a1..138608ad 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h @@ -24,6 +24,7 @@ #include #include #include +#include #import @@ -164,30 +165,32 @@ 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 + * 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 this pipeline cache. */ - inline VkResult getData(size_t* pDataSize, void* pData) { - *pDataSize = 0; - return VK_SUCCESS; - } + VkResult writeData(size_t* pDataSize, void* pData); /** Return a shader library from the specified shader context sourced from the specified shader module. */ MVKShaderLibrary* getShaderLibrary(SPIRVToMSLConverterContext* pContext, MVKShaderModule* shaderModule); /** 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; } + VkResult mergePipelineCaches(uint32_t srcCacheCount, const VkPipelineCache* pSrcCaches); #pragma mark Construction /** Constructs an instance for the specified device. */ - MVKPipelineCache(MVKDevice* device, const VkPipelineCacheCreateInfo* pCreateInfo) : MVKBaseDeviceObject(device) {} + MVKPipelineCache(MVKDevice* device, const VkPipelineCacheCreateInfo* pCreateInfo); ~MVKPipelineCache() override; protected: - std::unordered_map _shaderCacheByModuleHash; - std::mutex _shaderCacheLock; + MVKShaderLibraryCache* getShaderLibraryCache(MVKShaderModuleKey smKey); + void readData(const VkPipelineCacheCreateInfo* pCreateInfo); + void writeData(std::ostream& outstream, bool isCounting = false); + void markDirty(); + std::unordered_map _shaderCache; + size_t _dataSize = 0; + std::mutex _shaderCacheLock; }; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm index d9682213..a31e55ef 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm @@ -17,11 +17,18 @@ */ #include "MVKPipeline.h" +#include #include "MVKRenderPass.h" #include "MVKCommandBuffer.h" #include "MVKFoundation.h" +#include "MVKOSExtensions.h" +#include "MVKStrings.h" #include "mvk_datatypes.h" +#include +#include +#include + using namespace std; @@ -453,20 +460,288 @@ MVKComputePipeline::~MVKComputePipeline() { MVKShaderLibrary* MVKPipelineCache::getShaderLibrary(SPIRVToMSLConverterContext* pContext, MVKShaderModule* shaderModule) { lock_guard lock(_shaderCacheLock); - size_t smKey = shaderModule->getKey(); - MVKShaderLibraryCache* slCache = _shaderCacheByModuleHash[smKey]; + bool wasAdded = false; + MVKShaderLibraryCache* slCache = getShaderLibraryCache(shaderModule->getKey()); + MVKShaderLibrary* shLib = slCache->getShaderLibrary(pContext, shaderModule, &wasAdded); + if (wasAdded) { markDirty(); } + return shLib; +} + +// Returns a shader library cache for the specified shader module key, creating it if necessary. +MVKShaderLibraryCache* MVKPipelineCache::getShaderLibraryCache(MVKShaderModuleKey smKey) { + MVKShaderLibraryCache* slCache = _shaderCache[smKey]; if ( !slCache ) { slCache = new MVKShaderLibraryCache(_device); - _shaderCacheByModuleHash[smKey] = slCache; + _shaderCache[smKey] = slCache; + } + return slCache; +} + + +#pragma mark Streaming pipeline cache to and from offline memory + +static uint32_t kDataHeaderSize = (sizeof(uint32_t) * 4) + VK_UUID_SIZE; + +// Entry type markers to be inserted into data stream +typedef enum { + MVKPipelineCacheEntryTypeEOF = 0, + MVKPipelineCacheEntryTypeShaderLibrary = 1, +} MVKPipelineCacheEntryType; + +// Ceral archive definitions +namespace mvk { + + template + void serialize(Archive & archive, SPIRVEntryPoint& ep) { + archive(ep.mtlFunctionName, + ep.workgroupSize.width, + ep.workgroupSize.height, + ep.workgroupSize.depth, + ep.workgroupSizeId.width, + ep.workgroupSizeId.height, + ep.workgroupSizeId.depth, + ep.workgroupSizeId.constant); + } + + template + void serialize(Archive & archive, SPIRVToMSLConverterOptions& opt) { + archive(opt.entryPointName, + opt.entryPointStage, + opt.mslVersion, + opt.shouldFlipVertexY, + opt.isRenderingPoints); + } + + template + void serialize(Archive & archive, MSLVertexAttribute& va) { + archive(va.location, + va.mslBuffer, + va.mslOffset, + va.mslStride, + va.isPerInstance, + va.isUsedByShader); + } + + template + void serialize(Archive & archive, MSLResourceBinding& rb) { + archive(rb.stage, + rb.descriptorSet, + rb.binding, + rb.mslBuffer, + rb.mslTexture, + rb.mslSampler, + rb.isUsedByShader); + } + + template + void serialize(Archive & archive, SPIRVToMSLConverterContext& ctx) { + archive(ctx.options, ctx.vertexAttributes, ctx.resourceBindings); } - return slCache->getShaderLibrary(pContext, shaderModule); } +template +void serialize(Archive & archive, MVKShaderModuleKey& k) { + archive(k.codeSize, k.codeHash); +} + +// Helper class to iterate through the shader libraries in a shader library cache in order to serialize them. +// Needs to support input of null shader library cache. +class MVKShaderCacheIterator : MVKBaseObject { +protected: + friend MVKPipelineCache; + + bool next() { return (++_index < (_pSLCache ? _pSLCache->_shaderLibraries.size() : 0)); } + SPIRVToMSLConverterContext& getShaderContext() { return _pSLCache->_shaderLibraries[_index].first; } + std::string& getMSL() { return _pSLCache->_shaderLibraries[_index].second->_msl; } + SPIRVEntryPoint& getEntryPoint() { return _pSLCache->_shaderLibraries[_index].second->_entryPoint; } + MVKShaderCacheIterator(MVKShaderLibraryCache* pSLCache) : _pSLCache(pSLCache) {} + + MVKShaderLibraryCache* _pSLCache; + size_t _count = 0; + int32_t _index = -1; +}; + +// 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 this pipeline cache. +// This is the compliment of the readData() function. The two must be kept aligned. +VkResult MVKPipelineCache::writeData(size_t* pDataSize, void* pData) { + lock_guard lock(_shaderCacheLock); + + try { + + if ( !pDataSize ) { return VK_SUCCESS; } + + if (pData) { + if (*pDataSize >= _dataSize) { + mvk::membuf mb((char*)pData, _dataSize); + ostream outStream(&mb); + writeData(outStream); + *pDataSize = _dataSize; + return VK_SUCCESS; + } else { + *pDataSize = 0; + return VK_INCOMPLETE; + } + } else { + if (_dataSize == 0) { + mvk::countbuf cb; + ostream outStream(&cb); + writeData(outStream, true); + _dataSize = cb.buffSize; + } + *pDataSize = _dataSize; + return VK_SUCCESS; + } + + } catch (cereal::Exception& ex) { + *pDataSize = 0; + return mvkNotifyErrorWithText(VK_INCOMPLETE, "Error writing pipeline cache data: %s", ex.what()); + } +} + +// Serializes the data in this cache to a stream +void MVKPipelineCache::writeData(ostream& outstream, bool isCounting) { + + MVKShaderCompilationEventPerformance& shaderCompilationEvent = isCounting + ? _device->_shaderCompilationPerformance.sizePipelineCache + : _device->_shaderCompilationPerformance.writePipelineCache; + + uint32_t cacheEntryType; + cereal::BinaryOutputArchive writer(outstream); + + // Write the data header...after ensuring correct byte-order. + const VkPhysicalDeviceProperties* pDevProps = _device->_pProperties; + writer(NSSwapHostIntToLittle(kDataHeaderSize)); + writer(NSSwapHostIntToLittle(VK_PIPELINE_CACHE_HEADER_VERSION_ONE)); + writer(NSSwapHostIntToLittle(pDevProps->vendorID)); + writer(NSSwapHostIntToLittle(pDevProps->deviceID)); + writer(pDevProps->pipelineCacheUUID); + + // Shader libraries + // Output a cache entry for each shader library, including the shader module key in each entry. + cacheEntryType = MVKPipelineCacheEntryTypeShaderLibrary; + for (auto& scPair : _shaderCache) { + MVKShaderModuleKey smKey = scPair.first; + MVKShaderCacheIterator cacheIter(scPair.second); + while (cacheIter.next()) { + uint64_t startTime = _device->getPerformanceTimestamp(); + writer(cacheEntryType); + writer(smKey); + writer(cacheIter.getShaderContext()); + writer(cacheIter.getEntryPoint()); + writer(cacheIter.getMSL()); + _device->addShaderCompilationEventPerformance(shaderCompilationEvent, startTime); + } + } + + // Mark the end of the archive + cacheEntryType = MVKPipelineCacheEntryTypeEOF; + writer(cacheEntryType); +} + +// Loads any data indicated by the creation info. +// This is the compliment of the writeData() function. The two must be kept aligned. +void MVKPipelineCache::readData(const VkPipelineCacheCreateInfo* pCreateInfo) { + try { + + size_t byteCount = pCreateInfo->initialDataSize; + uint32_t cacheEntryType; + + // Must be able to read the header and at least one cache entry type. + if (byteCount < kDataHeaderSize + sizeof(cacheEntryType)) { return; } + + mvk::membuf mb((char*)pCreateInfo->pInitialData, byteCount); + istream inStream(&mb); + cereal::BinaryInputArchive reader(inStream); + + // Read the data header...and ensure correct byte-order. + uint32_t hdrComponent; + uint8_t pcUUID[VK_UUID_SIZE]; + const VkPhysicalDeviceProperties* pDevProps = _device->_pProperties; + + reader(hdrComponent); // Header size + if (NSSwapLittleIntToHost(hdrComponent) != kDataHeaderSize) { return; } + + reader(hdrComponent); // Header version + if (NSSwapLittleIntToHost(hdrComponent) != VK_PIPELINE_CACHE_HEADER_VERSION_ONE) { return; } + + reader(hdrComponent); // Vendor ID + if (NSSwapLittleIntToHost(hdrComponent) != pDevProps->vendorID) { return; } + + reader(hdrComponent); // Device ID + if (NSSwapLittleIntToHost(hdrComponent) != pDevProps->deviceID) { return; } + + reader(pcUUID); // Pipeline cache UUID + if (memcmp(pcUUID, pDevProps->pipelineCacheUUID, VK_UUID_SIZE) != 0) { return; } + + bool done = false; + while ( !done ) { + reader(cacheEntryType); + switch (cacheEntryType) { + case MVKPipelineCacheEntryTypeShaderLibrary: { + uint64_t startTime = _device->getPerformanceTimestamp(); + + MVKShaderModuleKey smKey; + reader(smKey); + + SPIRVToMSLConverterContext shaderContext; + reader(shaderContext); + + SPIRVEntryPoint entryPoint; + reader(entryPoint); + + string msl; + reader(msl); + + // Add the shader library to the staging cache. + MVKShaderLibraryCache* slCache = getShaderLibraryCache(smKey); + _device->addShaderCompilationEventPerformance(_device->_shaderCompilationPerformance.readPipelineCache, startTime); + slCache->addShaderLibrary(&shaderContext, msl, entryPoint); + + break; + } + + default: { + done = true; + break; + } + } + } + + } catch (cereal::Exception& ex) { + setConfigurationResult(mvkNotifyErrorWithText(VK_SUCCESS, "Error reading pipeline cache data: %s", ex.what())); + } +} + +// Mark the cache as dirty, so that existing streaming info is released +void MVKPipelineCache::markDirty() { + _dataSize = 0; +} + +VkResult MVKPipelineCache::mergePipelineCaches(uint32_t srcCacheCount, const VkPipelineCache* pSrcCaches) { + for (uint32_t srcIdx = 0; srcIdx < srcCacheCount; srcIdx++) { + MVKPipelineCache* srcPLC = (MVKPipelineCache*)pSrcCaches[srcIdx]; + for (auto& srcPair : srcPLC->_shaderCache) { + getShaderLibraryCache(srcPair.first)->merge(srcPair.second); + } + } + markDirty(); + + return VK_SUCCESS; +} + + +#pragma mark Construction + +MVKPipelineCache::MVKPipelineCache(MVKDevice* device, const VkPipelineCacheCreateInfo* pCreateInfo) : MVKBaseDeviceObject(device) { + readData(pCreateInfo); +} MVKPipelineCache::~MVKPipelineCache() { - for (auto& pair : _shaderCacheByModuleHash) { delete pair.second; } - _shaderCacheByModuleHash.clear(); + for (auto& pair : _shaderCache) { delete pair.second; } + _shaderCache.clear(); } diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h index 2ab4e5f7..2e0e4a3e 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h @@ -26,6 +26,7 @@ #import class MVKPipelineCache; +class MVKShaderCacheIterator; using namespace mvk; @@ -50,7 +51,7 @@ public: MVKMTLFunction getMTLFunction(const VkSpecializationInfo* pSpecializationInfo); /** Constructs an instance from the specified MSL source code. */ - MVKShaderLibrary(MVKDevice* device, const char* mslSourceCode, const SPIRVEntryPoint& entryPoint); + MVKShaderLibrary(MVKDevice* device, const std::string& mslSourceCode, const SPIRVEntryPoint& entryPoint); /** Constructs an instance from the specified compiled MSL code data. */ MVKShaderLibrary(MVKDevice* device, @@ -60,11 +61,14 @@ public: ~MVKShaderLibrary() override; protected: - void handleCompilationError(NSError* err, const char* opDesc); + friend MVKShaderCacheIterator; + + void handleCompilationError(NSError* err, const char* opDesc); MTLFunctionConstant* getFunctionConstant(NSArray* mtlFCs, NSUInteger mtlFCID); id _mtlLibrary; SPIRVEntryPoint _entryPoint; + std::string _msl; }; @@ -76,21 +80,32 @@ class MVKShaderLibraryCache : public MVKBaseDeviceObject { public: - /** Return a shader library from the specified shader context sourced from the specified shader module. */ - MVKShaderLibrary* getShaderLibrary(SPIRVToMSLConverterContext* pContext, MVKShaderModule* shaderModule); + /** + * Returns a shader library from the specified shader context sourced from the specified shader module, + * lazily creating the shader library from source code in the shader module, if needed. + * + * If pWasAdded is not nil, this function will set it to true if a new shader library was created, + * and to false if an existing shader library was found and returned. + */ + MVKShaderLibrary* getShaderLibrary(SPIRVToMSLConverterContext* pContext, + MVKShaderModule* shaderModule, + bool* pWasAdded = nullptr); MVKShaderLibraryCache(MVKDevice* device) : MVKBaseDeviceObject(device) {}; ~MVKShaderLibraryCache() override; protected: + friend MVKShaderCacheIterator; + friend MVKPipelineCache; + MVKShaderLibrary* findShaderLibrary(SPIRVToMSLConverterContext* pContext); MVKShaderLibrary* addShaderLibrary(SPIRVToMSLConverterContext* pContext, - const char* mslSourceCode, + const std::string& mslSourceCode, const SPIRVEntryPoint& entryPoint); + void merge(MVKShaderLibraryCache* other); std::mutex _accessLock; - std::size_t _shaderModuleHash; std::vector> _shaderLibraries; }; @@ -98,6 +113,30 @@ protected: #pragma mark - #pragma mark MVKShaderModule +typedef struct MVKShaderModuleKey_t { + std::size_t codeSize; + std::size_t codeHash; + + bool operator==(const MVKShaderModuleKey_t& rhs) const { + return ((codeSize == rhs.codeSize) && (codeHash == rhs.codeHash)); + } + MVKShaderModuleKey_t(std::size_t codeSize, std::size_t codeHash) : codeSize(codeSize), codeHash(codeHash) {} + MVKShaderModuleKey_t() : MVKShaderModuleKey_t(0, 0) {} +} MVKShaderModuleKey; + +/** + * Hash structure implementation for MVKShaderModuleKey in std namespace, + * so MVKShaderModuleKey can be used as a key in a std::map and std::unordered_map. + */ +namespace std { + template <> + struct hash { + std::size_t operator()(const MVKShaderModuleKey& k) const { + return k.codeHash; + } + }; +} + /** Represents a Vulkan shader module. */ class MVKShaderModule : public MVKBaseDeviceObject { @@ -111,25 +150,30 @@ public: bool convert(SPIRVToMSLConverterContext* pContext); /** - * Returns the Metal Shading Language source code most recently converted - * by the convert() function, or set directly using the setMSL() function. + * Returns the Metal Shading Language source code as converted by the most recent + * call to convert() function, or set directly using the setMSL() function. */ inline const std::string& getMSL() { return _converter.getMSL(); } - /** Returns information about the shader entry point. */ + /** + * Returns information about the shader entry point as converted by the most recent + * call to convert() function, or set directly using the setMSL() function. + */ inline const SPIRVEntryPoint& getEntryPoint() { return _converter.getEntryPoint(); } /** Returns a key as a means of identifying this shader module in a pipeline cache. */ - inline std::size_t getKey() { return _key; } + inline MVKShaderModuleKey getKey() { return _key; } MVKShaderModule(MVKDevice* device, const VkShaderModuleCreateInfo* pCreateInfo); ~MVKShaderModule() override; protected: + friend MVKShaderCacheIterator; + MVKShaderLibraryCache _shaderLibraryCache; SPIRVToMSLConverter _converter; MVKShaderLibrary* _defaultLibrary; - std::size_t _key; + MVKShaderModuleKey _key; std::mutex _accessLock; }; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm index d36389e2..0b9120bd 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm @@ -120,12 +120,12 @@ MTLFunctionConstant* MVKShaderLibrary::getFunctionConstant(NSArraygetPerformanceTimestamp(); @autoreleasepool { MTLCompileOptions* options = [[MTLCompileOptions new] autorelease]; // TODO: what compile options apply? NSError* err = nil; - _mtlLibrary = [getMTLDevice() newLibraryWithSource: @(mslSourceCode) + _mtlLibrary = [getMTLDevice() newLibraryWithSource: @(mslSourceCode.c_str()) options: options error: &err]; // retained handleCompilationError(err, "Shader module compilation"); @@ -133,6 +133,7 @@ MVKShaderLibrary::MVKShaderLibrary(MVKDevice* device, const char* mslSourceCode, _device->addShaderCompilationEventPerformance(_device->_shaderCompilationPerformance.mslCompile, startTime); _entryPoint = entryPoint; + _msl = mslSourceCode; } MVKShaderLibrary::MVKShaderLibrary(MVKDevice* device, @@ -178,13 +179,19 @@ MVKShaderLibrary::~MVKShaderLibrary() { #pragma mark MVKShaderLibraryCache MVKShaderLibrary* MVKShaderLibraryCache::getShaderLibrary(SPIRVToMSLConverterContext* pContext, - MVKShaderModule* shaderModule) { + MVKShaderModule* shaderModule, + bool* pWasAdded) { + bool wasAdded = false; MVKShaderLibrary* shLib = findShaderLibrary(pContext); if ( !shLib ) { if (shaderModule->convert(pContext)) { - shLib = addShaderLibrary(pContext, shaderModule->getMSL().c_str(), shaderModule->getEntryPoint()); + shLib = addShaderLibrary(pContext, shaderModule->getMSL(), shaderModule->getEntryPoint()); + wasAdded = true; } } + + if (pWasAdded) { *pWasAdded = wasAdded; } + return shLib; } @@ -200,15 +207,25 @@ MVKShaderLibrary* MVKShaderLibraryCache::findShaderLibrary(SPIRVToMSLConverterCo return NULL; } -/** Adds and returns a new shader library configured from the specified context. */ +// Adds and returns a new shader library configured from the specified context. MVKShaderLibrary* MVKShaderLibraryCache::addShaderLibrary(SPIRVToMSLConverterContext* pContext, - const char* mslSourceCode, + const string& mslSourceCode, const SPIRVEntryPoint& entryPoint) { MVKShaderLibrary* shLib = new MVKShaderLibrary(_device, mslSourceCode, entryPoint); _shaderLibraries.push_back(pair(*pContext, shLib)); return shLib; } +// Merge another shader library cache with this one. Handle null input. +void MVKShaderLibraryCache::merge(MVKShaderLibraryCache* other) { + if ( !other ) { return; } + for (auto& otherPair : other->_shaderLibraries) { + if ( !findShaderLibrary(&otherPair.first) ) { + _shaderLibraries.push_back(otherPair); + } + } +} + MVKShaderLibraryCache::~MVKShaderLibraryCache() { for (auto& slPair : _shaderLibraries) { delete slPair.second; } } @@ -225,10 +242,8 @@ MVKMTLFunction MVKShaderModule::getMTLFunction(SPIRVToMSLConverterContext* pCont if ( !mvkLib ) { uint64_t startTime = _device->getPerformanceTimestamp(); if (pipelineCache) { - MVKLogDebug("Retrieving shader from pipeline cache."); mvkLib = pipelineCache->getShaderLibrary(pContext, this); } else { - MVKLogDebug("Retrieving shader from shader module."); mvkLib = _shaderLibraryCache.getShaderLibrary(pContext, this); } _device->addShaderCompilationEventPerformance(_device->_shaderCompilationPerformance.shaderLibraryFromCache, startTime); @@ -255,9 +270,10 @@ bool MVKShaderModule::convert(SPIRVToMSLConverterContext* pContext) { #pragma mark Construction MVKShaderModule::MVKShaderModule(MVKDevice* device, - const VkShaderModuleCreateInfo* pCreateInfo) : MVKBaseDeviceObject(device), - _shaderLibraryCache(device) { - _defaultLibrary = nullptr; + const VkShaderModuleCreateInfo* pCreateInfo) : MVKBaseDeviceObject(device), _shaderLibraryCache(device) { + + _defaultLibrary = nullptr; + size_t codeSize = pCreateInfo->codeSize; @@ -267,7 +283,7 @@ MVKShaderModule::MVKShaderModule(MVKDevice* device, return; } - _key = mvkHash(&pCreateInfo->codeSize, 1); + size_t codeHash = 0; // Retrieve the magic number to determine what type of shader code has been loaded. uint32_t magicNum = *pCreateInfo->pCode; @@ -276,7 +292,7 @@ MVKShaderModule::MVKShaderModule(MVKDevice* device, size_t spvCount = (pCreateInfo->codeSize + 3) >> 2; // Round up if byte length not exactly on uint32_t boundary uint64_t startTime = _device->getPerformanceTimestamp(); - _key = mvkHash(pCreateInfo->pCode, spvCount, _key); + codeHash = mvkHash(pCreateInfo->pCode, spvCount); _device->addShaderCompilationEventPerformance(_device->_shaderCompilationPerformance.hashShaderCode, startTime); _converter.setSPIRV(pCreateInfo->pCode, spvCount); @@ -289,8 +305,8 @@ MVKShaderModule::MVKShaderModule(MVKDevice* device, size_t mslCodeLen = pCreateInfo->codeSize - hdrSize; uint64_t startTime = _device->getPerformanceTimestamp(); - _key = mvkHash(&magicNum, 1, _key); - _key = mvkHash(pMSLCode, mslCodeLen, _key); + codeHash = mvkHash(&magicNum, 1); + codeHash = mvkHash(pMSLCode, mslCodeLen, codeHash); _device->addShaderCompilationEventPerformance(_device->_shaderCompilationPerformance.hashShaderCode, startTime); _converter.setMSL(pMSLCode, nullptr); @@ -304,8 +320,8 @@ MVKShaderModule::MVKShaderModule(MVKDevice* device, size_t mslCodeLen = pCreateInfo->codeSize - hdrSize; uint64_t startTime = _device->getPerformanceTimestamp(); - _key = mvkHash(&magicNum, 1, _key); - _key = mvkHash(pMSLCode, mslCodeLen, _key); + codeHash = mvkHash(&magicNum, 1); + codeHash = mvkHash(pMSLCode, mslCodeLen, codeHash); _device->addShaderCompilationEventPerformance(_device->_shaderCompilationPerformance.hashShaderCode, startTime); _defaultLibrary = new MVKShaderLibrary(_device, (void*)(pMSLCode), mslCodeLen); @@ -316,6 +332,8 @@ MVKShaderModule::MVKShaderModule(MVKDevice* device, setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FORMAT_NOT_SUPPORTED, "SPIR-V contains invalid magic number %x.", magicNum)); break; } + + _key = MVKShaderModuleKey(codeSize, codeHash); } MVKShaderModule::~MVKShaderModule() { diff --git a/MoltenVK/MoltenVK/Vulkan/vulkan.mm b/MoltenVK/MoltenVK/Vulkan/vulkan.mm index e1dce857..62a03b78 100644 --- a/MoltenVK/MoltenVK/Vulkan/vulkan.mm +++ b/MoltenVK/MoltenVK/Vulkan/vulkan.mm @@ -674,7 +674,7 @@ MVK_PUBLIC_SYMBOL VkResult vkGetPipelineCacheData( void* pData) { MVKPipelineCache* mvkPLC = (MVKPipelineCache*)pipelineCache; - return mvkPLC->getData(pDataSize, pData); + return mvkPLC->writeData(pDataSize, pData); } MVK_PUBLIC_SYMBOL VkResult vkMergePipelineCaches( diff --git a/MoltenVKPackaging.xcodeproj/project.pbxproj b/MoltenVKPackaging.xcodeproj/project.pbxproj index e04422d6..74e762bc 100644 --- a/MoltenVKPackaging.xcodeproj/project.pbxproj +++ b/MoltenVKPackaging.xcodeproj/project.pbxproj @@ -143,6 +143,7 @@ A9AD67D22054E2D700ED3C08 /* Vulkan-LoaderAndValidationLayers_repo_revision */ = {isa = PBXFileReference; lastKnownFileType = text; path = "Vulkan-LoaderAndValidationLayers_repo_revision"; sourceTree = ""; }; A9AD67D32054E2D700ED3C08 /* SPIRV-Cross_repo_revision */ = {isa = PBXFileReference; lastKnownFileType = text; path = "SPIRV-Cross_repo_revision"; sourceTree = ""; }; A9AD67E92055D8A600ED3C08 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + A9EE46412065766C00193200 /* cereal_repo_revision */ = {isa = PBXFileReference; lastKnownFileType = text; path = cereal_repo_revision; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXGroup section */ @@ -181,6 +182,7 @@ children = ( A9AD67E92055D8A600ED3C08 /* README.md */, A943100220546CDD00F5CF87 /* fetchDependencies */, + A9EE46412065766C00193200 /* cereal_repo_revision */, A9AD67D32054E2D700ED3C08 /* SPIRV-Cross_repo_revision */, A9AD67D22054E2D700ED3C08 /* Vulkan-LoaderAndValidationLayers_repo_revision */, A9AD67D12054E2D700ED3C08 /* VulkanSamples_repo_revision */, @@ -206,7 +208,7 @@ A90B2B1D1A9B6170008EE819 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0920; + LastUpgradeCheck = 0930; TargetAttributes = { A9FEADBC1F3517480010240E = { DevelopmentTeam = VU3TCKU48B; diff --git a/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK (Debug).xcscheme b/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK (Debug).xcscheme index d3c28dd6..55041295 100644 --- a/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK (Debug).xcscheme +++ b/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK (Debug).xcscheme @@ -1,6 +1,6 @@ @@ -38,7 +37,6 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" disableMainThreadChecker = "YES" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK (Release).xcscheme b/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK (Release).xcscheme index 37369e10..fd1beeb4 100644 --- a/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK (Release).xcscheme +++ b/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK (Release).xcscheme @@ -1,6 +1,6 @@ @@ -38,7 +37,6 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" disableMainThreadChecker = "YES" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj index 00d97ff5..fbdeda15 100644 --- a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj +++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj @@ -2209,7 +2209,7 @@ A9F55D25198BE6A7004EC31B /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0920; + LastUpgradeCheck = 0930; ORGANIZATIONNAME = "The Brenwill Workshop Ltd."; TargetAttributes = { A9092A8C1A81717B00051823 = { diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-iOS.xcscheme b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-iOS.xcscheme index 3ef3e9de..44aef331 100644 --- a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-iOS.xcscheme +++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-iOS.xcscheme @@ -1,6 +1,6 @@ @@ -37,7 +36,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-macOS.xcscheme b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-macOS.xcscheme index 22040b26..0d12024d 100644 --- a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-macOS.xcscheme +++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-macOS.xcscheme @@ -1,6 +1,6 @@ @@ -37,7 +36,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-iOS.xcscheme b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-iOS.xcscheme index 4173dc87..b883f48a 100644 --- a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-iOS.xcscheme +++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-iOS.xcscheme @@ -1,6 +1,6 @@ @@ -37,7 +36,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-macOS.xcscheme b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-macOS.xcscheme index 94403cc6..5752099a 100644 --- a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-macOS.xcscheme +++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-macOS.xcscheme @@ -1,6 +1,6 @@ @@ -37,7 +36,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKShaderConverter.xcscheme b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKShaderConverter.xcscheme index de178f06..45a5b0ed 100644 --- a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKShaderConverter.xcscheme +++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKShaderConverter.xcscheme @@ -1,6 +1,6 @@ @@ -46,7 +45,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" @@ -74,19 +72,19 @@ + isEnabled = "NO"> + argument = "/Users/bill/Documents/Dev/iOSProjects/Molten/Support/2018/MVK_Issue_112/bad_attrs.vert" + isEnabled = "NO"> + isEnabled = "YES"> + argument = "/Users/bill/Documents/Dev/iOSProjects/Molten/Support/2018/MVK_Issue_112/second/vert_bin.spv" + isEnabled = "YES">