diff --git a/Docs/ThirdPartyConfig.md b/Docs/ThirdPartyConfig.md index d179f09d..43305796 100644 --- a/Docs/ThirdPartyConfig.md +++ b/Docs/ThirdPartyConfig.md @@ -151,39 +151,24 @@ The updated version will then be "locked in" the next time the `MoltenVK` reposi ### 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: +If you make changes to the `SPIRV-Cross` submodule, you can regression test your changes +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: +1. Load and build the versions of `SPRIV-Tools` and `glslang` that are used by the `SPIRV-Cross` tests: cd External/SPIRV-Cross - make + ./checkout_glslang_spirv_tools.sh -4. Run the regression tests: +2. Run the regression tests: ./test_shaders.sh -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. +3. If your changes result in different expected output for a reference shader, and the new results + are correct, you can update the reference shader for a particular regression test by deleting + that reference shader, in either `External/SPIRV-Cross/reference/shaders-msl` or + `External/SPIRV-Cross/reference/opt/shaders-msl`, and running the test again. The test will + replace the deleted reference shader. + diff --git a/External/SPIRV-Cross b/External/SPIRV-Cross index 95910ddd..27d4af75 160000 --- a/External/SPIRV-Cross +++ b/External/SPIRV-Cross @@ -1 +1 @@ -Subproject commit 95910ddd5aa03cbd7188fc7c107f9cc893136f10 +Subproject commit 27d4af75a0736849c046c5f068ecc80f17c1ed7e diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm index 021e40aa..327fee41 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm @@ -20,6 +20,7 @@ #include "MVKCommandBuffer.h" #include "MVKBuffer.h" #include "MVKFoundation.h" +#include "MVKLogging.h" #include using namespace std; @@ -612,6 +613,7 @@ VkResult MVKDescriptorPool::allocateDescriptorSets(uint32_t count, lock_guard lock(_lock); if (_allocatedSetCount + count > _maxSets) { +// MVKLogDebug("Pool %p could not allocate %d descriptor sets. There are alreay %d sets, and the maximum for this pool is %d.", this, count, _allocatedSetCount, _maxSets); return mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "The maximum number of descriptor sets that can be allocated by this descriptor pool is %d.", _maxSets); } @@ -622,6 +624,8 @@ VkResult MVKDescriptorPool::allocateDescriptorSets(uint32_t count, pDescriptorSets[dsIdx] = (VkDescriptorSet)mvkDescSet; _allocatedSetCount++; } + +// MVKLogDebug("Pool %p allocating %d descriptor sets for a new total of %d sets.", this, count, _allocatedSetCount); return VK_SUCCESS; } @@ -634,12 +638,15 @@ VkResult MVKDescriptorPool::freeDescriptorSets(uint32_t count, const VkDescripto _allocatedSetCount--; delete mvkDS; } + +// MVKLogDebug("Pool %p freed %d descriptor sets for a new total of %d sets.", this, count, _allocatedSetCount); return VK_SUCCESS; } VkResult MVKDescriptorPool::reset(VkDescriptorPoolResetFlags flags) { lock_guard lock(_lock); +// MVKLogDebug("Pool %p resetting and freeing remaining %d descriptor sets.", this, _allocatedSetCount); mvkDestroyContainerContents(_allocatedSets); _allocatedSetCount = 0; return VK_SUCCESS; @@ -647,13 +654,17 @@ VkResult MVKDescriptorPool::reset(VkDescriptorPoolResetFlags flags) { MVKDescriptorPool::MVKDescriptorPool(MVKDevice* device, const VkDescriptorPoolCreateInfo* pCreateInfo) : MVKBaseDeviceObject(device) { +// MVKLogDebug("Pool %p created.", this); _maxSets = pCreateInfo->maxSets; _allocatedSetCount = 0; } // TODO: Destroying a descriptor pool implicitly destroys all descriptor sets created from it. -MVKDescriptorPool::~MVKDescriptorPool() { reset(0); } +MVKDescriptorPool::~MVKDescriptorPool() { +// MVKLogDebug("Pool %p destroyed with %d descriptor sets.", this, _allocatedSetCount); + reset(0); +} #pragma mark - diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverterTool/FileSupport.h b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/FileSupport.h similarity index 77% rename from MoltenVKShaderConverter/MoltenVKShaderConverterTool/FileSupport.h rename to MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/FileSupport.h index affa71e1..d3bd57c2 100644 --- a/MoltenVKShaderConverter/MoltenVKShaderConverterTool/FileSupport.h +++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/FileSupport.h @@ -72,20 +72,4 @@ namespace mvk { */ 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/MoltenVKSPIRVToMSLConverter/FileSupport.mm similarity index 75% rename from MoltenVKShaderConverter/MoltenVKShaderConverterTool/FileSupport.mm rename to MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/FileSupport.mm index 9a6a3904..fb8acf7e 100644 --- a/MoltenVKShaderConverter/MoltenVKShaderConverterTool/FileSupport.mm +++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/FileSupport.mm @@ -17,7 +17,6 @@ */ #include "FileSupport.h" -#include "MoltenVKShaderConverterTool.h" #include #import @@ -139,37 +138,3 @@ bool mvk::writeFile(const string& path, const vector& contents, string& er 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/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp index ebb76263..828c3550 100644 --- a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp +++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp @@ -19,9 +19,11 @@ #include "SPIRVToMSLConverter.h" #include "MVKCommonEnvironment.h" #include "MVKStrings.h" +#include "FileSupport.h" #include "spirv_msl.hpp" #include "spirv_glsl.hpp" #include +#import using namespace mvk; using namespace std; @@ -261,6 +263,27 @@ void SPIRVToMSLConverter::logSPIRV(const char* opDesc) { _resultLog += " SPIR-V:\n"; _resultLog += spvLog; _resultLog += "\nEnd SPIR-V\n\n"; + + // Uncomment one or both of the following lines to get additional debugging and tracability capabilities. + // The SPIR-V can be written in binary form to a file, and/or logged in human readable form to the console. + // These can be helpful if errors occur during conversion of SPIR-V to MSL. +// writeSPIRVToFile("spvout.spv"); +// printf("\n%s\n", getResultLog().c_str()); +} + +/** + * Writes the SPIR-V code to a file. This can be useful for debugging + * when the SPRIR-V did not originally come from a known file + */ +void SPIRVToMSLConverter::writeSPIRVToFile(string spvFilepath) { + vector fileContents; + spirvToBytes(_spirv, fileContents); + string errMsg; + if (writeFile(spvFilepath, fileContents, errMsg)) { + _resultLog += "Saved SPIR-V to file: " + absolutePath(spvFilepath) + "\n\n"; + } else { + _resultLog += "Could not write SPIR-V file. " + errMsg + "\n\n"; + } } /** Validates that the SPIR-V code will disassemble during logging. */ @@ -323,4 +346,33 @@ MVK_PUBLIC_SYMBOL void mvk::logSPIRV(vector& spirv, string& spvLog) { spvTextDestroy(text); } +MVK_PUBLIC_SYMBOL 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); +} + +MVK_PUBLIC_SYMBOL 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); +} + +MVK_PUBLIC_SYMBOL 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/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h index c14b4207..f1023b85 100644 --- a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h +++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h @@ -218,6 +218,7 @@ namespace mvk { bool logError(const char* errMsg); void logSPIRV(const char* opDesc); bool validateSPIRV(); + void writeSPIRVToFile(std::string spvFilepath); void logSource(std::string& src, const char* srcLang, const char* opDesc); std::vector _spirv; @@ -233,5 +234,24 @@ namespace mvk { /** Appends the SPIR-V in human-readable form to the specified log string. */ void logSPIRV(std::vector& spirv, std::string& spvLog); + /** 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); + } #endif diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj index 746ff547..42caa103 100644 --- a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj +++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj @@ -23,7 +23,6 @@ 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, ); }; }; @@ -458,6 +457,9 @@ A92C04471FF2A39A00BAAE59 /* validation_state.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A92C029C1FF2A39A00BAAE59 /* validation_state.cpp */; }; A92C04481FF2A39A00BAAE59 /* decoration.h in Headers */ = {isa = PBXBuildFile; fileRef = A92C029D1FF2A39A00BAAE59 /* decoration.h */; }; A92C04491FF2A39A00BAAE59 /* decoration.h in Headers */ = {isa = PBXBuildFile; fileRef = A92C029D1FF2A39A00BAAE59 /* decoration.h */; }; + A95096BB2003D00300F10950 /* FileSupport.mm in Sources */ = {isa = PBXBuildFile; fileRef = A925B70A1C7754B2006E7ECD /* FileSupport.mm */; }; + A95096BC2003D00300F10950 /* FileSupport.mm in Sources */ = {isa = PBXBuildFile; fileRef = A925B70A1C7754B2006E7ECD /* FileSupport.mm */; }; + A95096BF2003D32400F10950 /* DirectorySupport.mm in Sources */ = {isa = PBXBuildFile; fileRef = A95096BD2003D32400F10950 /* DirectorySupport.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 */; }; @@ -912,6 +914,8 @@ A92C029D1FF2A39A00BAAE59 /* decoration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = decoration.h; 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; }; + A95096BD2003D32400F10950 /* DirectorySupport.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DirectorySupport.mm; sourceTree = ""; }; + A95096BE2003D32400F10950 /* DirectorySupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirectorySupport.h; sourceTree = ""; }; 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; }; @@ -1037,6 +1041,8 @@ children = ( A9FE521B1D440E17001DD573 /* SPIRV-Tools */, A9381E5C1CB55F4600287A79 /* SPIRV-Cross */, + A925B70B1C7754B2006E7ECD /* FileSupport.h */, + A925B70A1C7754B2006E7ECD /* FileSupport.mm */, A928C9171D0488DC00071B88 /* SPIRVConversion.h */, A928C9181D0488DC00071B88 /* SPIRVConversion.mm */, A9093F5A1C58013E0094110D /* SPIRVToMSLConverter.cpp */, @@ -1380,8 +1386,8 @@ A97CC73C1C7527F3004A5C7E /* MoltenVKShaderConverterTool */ = { isa = PBXGroup; children = ( - A925B70A1C7754B2006E7ECD /* FileSupport.mm */, - A925B70B1C7754B2006E7ECD /* FileSupport.h */, + A95096BE2003D32400F10950 /* DirectorySupport.h */, + A95096BD2003D32400F10950 /* DirectorySupport.mm */, A97CC73D1C7527F3004A5C7E /* main.cpp */, A97CC73E1C7527F3004A5C7E /* MoltenVKShaderConverterTool.cpp */, A97CC73F1C7527F3004A5C7E /* MoltenVKShaderConverterTool.h */, @@ -2089,7 +2095,7 @@ buildActionMask = 2147483647; files = ( A97CC7411C7527F3004A5C7E /* MoltenVKShaderConverterTool.cpp in Sources */, - A925B70C1C7754B2006E7ECD /* FileSupport.mm in Sources */, + A95096BF2003D32400F10950 /* DirectorySupport.mm in Sources */, A97CC7401C7527F3004A5C7E /* main.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2247,6 +2253,7 @@ A92C038C1FF2A39A00BAAE59 /* ccp_pass.cpp in Sources */, A92C03061FF2A39A00BAAE59 /* parsed_operand.cpp in Sources */, A92C03681FF2A39A00BAAE59 /* remove_duplicates_pass.cpp in Sources */, + A95096BB2003D00300F10950 /* FileSupport.mm in Sources */, A92C02DC1FF2A39A00BAAE59 /* spirv_validator_options.cpp in Sources */, A92C03741FF2A39A00BAAE59 /* type_manager.cpp in Sources */, A92C03161FF2A39A00BAAE59 /* software_version.cpp in Sources */, @@ -2366,6 +2373,7 @@ A92C038D1FF2A39A00BAAE59 /* ccp_pass.cpp in Sources */, A92C03071FF2A39A00BAAE59 /* parsed_operand.cpp in Sources */, A92C03691FF2A39A00BAAE59 /* remove_duplicates_pass.cpp in Sources */, + A95096BC2003D00300F10950 /* FileSupport.mm in Sources */, A92C02DD1FF2A39A00BAAE59 /* spirv_validator_options.cpp in Sources */, A92C03751FF2A39A00BAAE59 /* type_manager.cpp in Sources */, A92C03171FF2A39A00BAAE59 /* software_version.cpp in Sources */, diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKShaderConverter.xcscheme b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKShaderConverter.xcscheme index bbf45bd2..c86fbaf4 100644 --- a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKShaderConverter.xcscheme +++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKShaderConverter.xcscheme @@ -77,7 +77,7 @@ isEnabled = "NO"> + + +namespace mvk { + + /** + * 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/DirectorySupport.mm b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/DirectorySupport.mm new file mode 100644 index 00000000..0bcbd97c --- /dev/null +++ b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/DirectorySupport.mm @@ -0,0 +1,61 @@ +/* + * DirectorySupport.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 "DirectorySupport.h" +#include "FileSupport.h" +#include "MoltenVKShaderConverterTool.h" + +#import + +using namespace std; +using namespace mvk; + +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 index 82bd8f24..b100132a 100644 --- a/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.cpp +++ b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.cpp @@ -18,9 +18,9 @@ #include "MoltenVKShaderConverterTool.h" #include "FileSupport.h" +#include "DirectorySupport.h" #include "GLSLToSPIRVConverter.h" #include "SPIRVToMSLConverter.h" -#import using namespace std; using namespace mvk; @@ -526,33 +526,3 @@ bool mvk::equal(string const& a, string const& b, bool checkCase) { 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 index 2da2ad33..3733b0a0 100644 --- a/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.h +++ b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.h @@ -111,23 +111,4 @@ namespace mvk { /** 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); - }