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);
-
}