Separate SPIRVToMSLConverterContext into input config and output results.

Refactor SPIRVToMSLConverterContext into distinct SPIRVToMSLConversionConfiguration
and SPIRVToMSLConversionResults for conversion input and output, respectively.
Update to latest SPIRV-Cross version.
Update What's New document.
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index 024ee5a..e7f548f 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -20,9 +20,23 @@
 
 - On iOS GPU family 2 and earlier, support immutable depth-compare samplers 
   as constexpr samplers hardcoded in MSL.
-- Add MTLCommandBuffer completion timing performance tracking option.
+- Add `MTLCommandBuffer` completion timing performance tracking option.
 - Expand `MVK_CONFIG_TRACE_VULKAN_CALLS` to optionally log Vulkan call timings.
 - Skip `SPIRV-Tools` build in Travis because Travis does not support the required Python 3.
+- Separate `SPIRVToMSLConverterContext` into input config and output results.
+- Fix pipeline cache lookups.
+- Update to latest SPIRV-Cross version:
+	- MSL: Add support for `SubgroupSize` / `SubgroupInvocationID` in fragment.
+	- MSL: Support `OpImageQueryLod`.
+	- MSL: Support `MinLod` operand.
+	- MSL: Support `PrimitiveID` in fragment and barycentrics.
+	- MSL: Support 64-bit integers.
+	- MSL: New SDK errors out on cull distance.
+	- MSL: Conditionally validate MSL 2.2 shaders.
+	- MSL: Rewrite how resource indices are fallback-assigned.
+	- MSL: Support custom bindings for argument buffers.
+	- MSL: Fix sampling with FP16 coordinates.
+	- MSL: Deal with scalar input values for distance/length/normalize.
 
 
 
@@ -570,7 +584,7 @@
 Released 2018/07/31
 
 - Disable rasterization and return void from vertex shaders that write to resources.
-- Add SPIRVToMSLConverterOptions::isRasterizationDisabled to allow pipeline and 
+- Add SPIRVToMSLConversionOptions::isRasterizationDisabled to allow pipeline and 
   vertex shader to communicate rasterization status.
 - Track layered rendering capability.    
 - Add MVKPhysicalDeviceMetalFeatures::layeredRendering.
diff --git a/ExternalRevisions/SPIRV-Cross_repo_revision b/ExternalRevisions/SPIRV-Cross_repo_revision
index 30297b1..a10b1b1 100644
--- a/ExternalRevisions/SPIRV-Cross_repo_revision
+++ b/ExternalRevisions/SPIRV-Cross_repo_revision
@@ -1 +1 @@
-4104e363005a079acc215f0920743a8affb31278
+8ee8e60f70f937c72379ab1fc404a1c36d660a31
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
index 755199e..90cf292 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
@@ -93,7 +93,7 @@
               MVKShaderResourceBinding& dslMTLRezIdxOffsets);
 
 	/** Populates the specified shader converter context, at the specified descriptor set binding. */
-	void populateShaderConverterContext(mvk::SPIRVToMSLConverterContext& context,
+	void populateShaderConverterContext(mvk::SPIRVToMSLConversionConfiguration& context,
                                         MVKShaderResourceBinding& dslMTLRezIdxOffsets,
                                         uint32_t dslIndex);
 
@@ -160,7 +160,7 @@
 
 
 	/** Populates the specified shader converter context, at the specified DSL index. */
-	void populateShaderConverterContext(mvk::SPIRVToMSLConverterContext& context,
+	void populateShaderConverterContext(mvk::SPIRVToMSLConversionConfiguration& context,
                                         MVKShaderResourceBinding& dslMTLRezIdxOffsets,
                                         uint32_t dslIndex);
 
@@ -427,7 +427,7 @@
  * If the shader stage binding has a binding defined for the specified stage, populates
  * the context at the descriptor set binding from the shader stage resource binding.
  */
-void mvkPopulateShaderConverterContext(mvk::SPIRVToMSLConverterContext& context,
+void mvkPopulateShaderConverterContext(mvk::SPIRVToMSLConversionConfiguration& context,
 									   MVKShaderStageResourceBinding& ssRB,
 									   spv::ExecutionModel stage,
 									   uint32_t descriptorSetIndex,
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
index 93ab0ae..832f460 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
@@ -375,7 +375,7 @@
 	return true;
 }
 
-void MVKDescriptorSetLayoutBinding::populateShaderConverterContext(SPIRVToMSLConverterContext& context,
+void MVKDescriptorSetLayoutBinding::populateShaderConverterContext(SPIRVToMSLConversionConfiguration& context,
                                                                    MVKShaderResourceBinding& dslMTLRezIdxOffsets,
                                                                    uint32_t dslIndex) {
 
@@ -632,7 +632,7 @@
     }
 }
 
-void MVKDescriptorSetLayout::populateShaderConverterContext(SPIRVToMSLConverterContext& context,
+void MVKDescriptorSetLayout::populateShaderConverterContext(SPIRVToMSLConversionConfiguration& context,
                                                             MVKShaderResourceBinding& dslMTLRezIdxOffsets,
 															uint32_t dslIndex) {
 	uint32_t bindCnt = (uint32_t)_bindings.size();
@@ -1142,7 +1142,7 @@
 	}
 }
 
-void mvkPopulateShaderConverterContext(SPIRVToMSLConverterContext& context,
+void mvkPopulateShaderConverterContext(SPIRVToMSLConversionConfiguration& context,
 									   MVKShaderStageResourceBinding& ssRB,
 									   spv::ExecutionModel stage,
 									   uint32_t descriptorSetIndex,
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index fd0ab2e..58647c0 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -1568,7 +1568,7 @@
 
 	MVKLogInfo(logMsg.c_str(), _properties.deviceName, devTypeStr.c_str(), _properties.vendorID, _properties.deviceID,
 			   [[[NSUUID alloc] initWithUUIDBytes: _properties.pipelineCacheUUID] autorelease].UUIDString.UTF8String,
-			   SPIRVToMSLConverterOptions::printMSLVersion(_metalFeatures.mslVersion).c_str());
+			   SPIRVToMSLConversionOptions::printMSLVersion(_metalFeatures.mslVersion).c_str());
 }
 
 MVKPhysicalDevice::~MVKPhysicalDevice() {
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
index ebbaa35..a6ecc88 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
@@ -59,7 +59,7 @@
                             MVKVector<uint32_t>& dynamicOffsets);
 
 	/** Populates the specified shader converter context. */
-	void populateShaderConverterContext(SPIRVToMSLConverterContext& context);
+	void populateShaderConverterContext(SPIRVToMSLConversionConfiguration& context);
 
 	/** Updates a descriptor set in a command encoder. */
 	void pushDescriptorSet(MVKCommandEncoder* cmdEncoder,
@@ -232,22 +232,18 @@
     id<MTLRenderPipelineState> getOrCompilePipeline(MTLRenderPipelineDescriptor* plDesc, id<MTLRenderPipelineState>& plState);
     id<MTLComputePipelineState> getOrCompilePipeline(MTLComputePipelineDescriptor* plDesc, id<MTLComputePipelineState>& plState, const char* compilerType);
     void initMTLRenderPipelineState(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData);
-    void initMVKShaderConverterContext(SPIRVToMSLConverterContext& _shaderContext,
-                                       const VkGraphicsPipelineCreateInfo* pCreateInfo,
-                                       const SPIRVTessReflectionData& reflectData);
-    void addVertexInputToShaderConverterContext(SPIRVToMSLConverterContext& shaderContext,
-                                                const VkGraphicsPipelineCreateInfo* pCreateInfo);
-    void addPrevStageOutputToShaderConverterContext(SPIRVToMSLConverterContext& shaderContext,
-                                                    std::vector<SPIRVShaderOutput>& outputs);
+    void initMVKShaderConverterContext(SPIRVToMSLConversionConfiguration& _shaderContext, const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData);
+    void addVertexInputToShaderConverterContext(SPIRVToMSLConversionConfiguration& shaderContext, const VkGraphicsPipelineCreateInfo* pCreateInfo);
+    void addPrevStageOutputToShaderConverterContext(SPIRVToMSLConversionConfiguration& shaderContext, std::vector<SPIRVShaderOutput>& outputs);
     MTLRenderPipelineDescriptor* getMTLRenderPipelineDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData);
-    MTLRenderPipelineDescriptor* getMTLTessVertexStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConverterContext& shaderContext);
-    MTLComputePipelineDescriptor* getMTLTessControlStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConverterContext& shaderContext);
-    MTLRenderPipelineDescriptor* getMTLTessRasterStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConverterContext& shaderContext);
-    bool addVertexShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConverterContext& shaderContext);
-    bool addTessCtlShaderToPipeline(MTLComputePipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConverterContext& shaderContext, std::vector<SPIRVShaderOutput>& prevOutput);
-    bool addTessEvalShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConverterContext& shaderContext, std::vector<SPIRVShaderOutput>& prevOutput);
-    bool addFragmentShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConverterContext& shaderContext);
-    bool addVertexInputToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkPipelineVertexInputStateCreateInfo* pVI, const SPIRVToMSLConverterContext& shaderContext);
+    MTLRenderPipelineDescriptor* getMTLTessVertexStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderContext);
+	MTLComputePipelineDescriptor* getMTLTessControlStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderContext);
+	MTLRenderPipelineDescriptor* getMTLTessRasterStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderContext);
+	bool addVertexShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderContext);
+	bool addTessCtlShaderToPipeline(MTLComputePipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderContext, std::vector<SPIRVShaderOutput>& prevOutput);
+	bool addTessEvalShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderContext, std::vector<SPIRVShaderOutput>& prevOutput);
+    bool addFragmentShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderContext);
+	bool addVertexInputToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkPipelineVertexInputStateCreateInfo* pVI, const SPIRVToMSLConversionConfiguration& shaderContext);
     void addTessellationToPipeline(MTLRenderPipelineDescriptor* plDesc, const SPIRVTessReflectionData& reflectData, const VkPipelineTessellationStateCreateInfo* pTS);
     void addFragmentOutputToPipeline(MTLRenderPipelineDescriptor* plDesc, const SPIRVTessReflectionData& reflectData, const VkGraphicsPipelineCreateInfo* pCreateInfo, bool isTessellationVertexPipeline = false);
     bool isRenderingPoints(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData);
@@ -356,7 +352,7 @@
 	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);
+	MVKShaderLibrary* getShaderLibrary(SPIRVToMSLConversionConfiguration* pContext, MVKShaderModule* shaderModule);
 
 	/** Merges the contents of the specified number of pipeline caches into this cache. */
 	VkResult mergePipelineCaches(uint32_t srcCacheCount, const VkPipelineCache* pSrcCaches);
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
index 0d31459..237b1b0 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
@@ -93,7 +93,7 @@
 	}
 }
 
-void MVKPipelineLayout::populateShaderConverterContext(SPIRVToMSLConverterContext& context) {
+void MVKPipelineLayout::populateShaderConverterContext(SPIRVToMSLConversionConfiguration& context) {
 	context.resourceBindings.clear();
 
     // Add resource bindings defined in the descriptor set layouts
@@ -432,8 +432,9 @@
 		// In this case, we need to create three render pipelines. But, the way Metal handles
 		// index buffers for compute stage-in means we have to defer creation of stage 2 until
 		// draw time. In the meantime, we'll create and retain a descriptor for it.
-		SPIRVToMSLConverterContext shaderContext;
+		SPIRVToMSLConversionConfiguration shaderContext;
 		initMVKShaderConverterContext(shaderContext, pCreateInfo, reflectData);
+
 		MTLRenderPipelineDescriptor* vtxPLDesc = getMTLTessVertexStageDescriptor(pCreateInfo, reflectData, shaderContext);
 		_mtlTessControlStageDesc = getMTLTessControlStageDescriptor(pCreateInfo, reflectData, shaderContext);	// retained
 		MTLRenderPipelineDescriptor* rastPLDesc = getMTLTessRasterStageDescriptor(pCreateInfo, reflectData, shaderContext);
@@ -446,8 +447,9 @@
 }
 
 // Returns a MTLRenderPipelineDescriptor constructed from this instance, or nil if an error occurs.
-MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLRenderPipelineDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData) {
-	SPIRVToMSLConverterContext shaderContext;
+MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLRenderPipelineDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo,
+																				 const SPIRVTessReflectionData& reflectData) {
+	SPIRVToMSLConversionConfiguration shaderContext;
 	initMVKShaderConverterContext(shaderContext, pCreateInfo, reflectData);
 
 	MTLRenderPipelineDescriptor* plDesc = [[MTLRenderPipelineDescriptor new] autorelease];
@@ -473,7 +475,9 @@
 }
 
 // Returns a MTLRenderPipelineDescriptor for the vertex stage of a tessellated draw constructed from this instance, or nil if an error occurs.
-MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLTessVertexStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConverterContext& shaderContext) {
+MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLTessVertexStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo,
+																				  const SPIRVTessReflectionData& reflectData,
+																				  SPIRVToMSLConversionConfiguration& shaderContext) {
 	MTLRenderPipelineDescriptor* plDesc = [[MTLRenderPipelineDescriptor new] autorelease];
 
 	// Add shader stages.
@@ -582,7 +586,9 @@
 }
 
 // Returns a MTLComputePipelineDescriptor for the tess. control stage of a tessellated draw constructed from this instance, or nil if an error occurs.
-MTLComputePipelineDescriptor* MVKGraphicsPipeline::getMTLTessControlStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConverterContext& shaderContext) {
+MTLComputePipelineDescriptor* MVKGraphicsPipeline::getMTLTessControlStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo,
+																					const SPIRVTessReflectionData& reflectData,
+																					SPIRVToMSLConversionConfiguration& shaderContext) {
 	MTLComputePipelineDescriptor* plDesc = [MTLComputePipelineDescriptor new];
 
 	std::vector<SPIRVShaderOutput> vtxOutputs;
@@ -627,7 +633,9 @@
 }
 
 // Returns a MTLRenderPipelineDescriptor for the last stage of a tessellated draw constructed from this instance, or nil if an error occurs.
-MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLTessRasterStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConverterContext& shaderContext) {
+MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLTessRasterStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo,
+																				  const SPIRVTessReflectionData& reflectData,
+																				  SPIRVToMSLConversionConfiguration& shaderContext) {
 	MTLRenderPipelineDescriptor* plDesc = [[MTLRenderPipelineDescriptor new] autorelease];
 
 	std::vector<SPIRVShaderOutput> tcOutputs;
@@ -750,7 +758,9 @@
 }
 
 // Adds a vertex shader to the pipeline description.
-bool MVKGraphicsPipeline::addVertexShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConverterContext& shaderContext) {
+bool MVKGraphicsPipeline::addVertexShaderToPipeline(MTLRenderPipelineDescriptor* plDesc,
+													const VkGraphicsPipelineCreateInfo* pCreateInfo,
+													SPIRVToMSLConversionConfiguration& shaderContext) {
 	uint32_t vbCnt = pCreateInfo->pVertexInputState->vertexBindingDescriptionCount;
 	shaderContext.options.entryPointStage = spv::ExecutionModelVertex;
 	shaderContext.options.entryPointName = _pVertexSS->pName;
@@ -761,16 +771,20 @@
 	shaderContext.options.mslOptions.capture_output_to_buffer = isTessellationPipeline();
 	shaderContext.options.mslOptions.disable_rasterization = isTessellationPipeline() || (pCreateInfo->pRasterizationState && (pCreateInfo->pRasterizationState->rasterizerDiscardEnable));
     addVertexInputToShaderConverterContext(shaderContext, pCreateInfo);
-	id<MTLFunction> mtlFunction = ((MVKShaderModule*)_pVertexSS->module)->getMTLFunction(&shaderContext, _pVertexSS->pSpecializationInfo, _pipelineCache).mtlFunction;
-	if ( !mtlFunction ) {
+
+	MVKMTLFunction func = ((MVKShaderModule*)_pVertexSS->module)->getMTLFunction(&shaderContext, _pVertexSS->pSpecializationInfo, _pipelineCache);
+	if ( !func.mtlFunction ) {
 		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Vertex shader function could not be compiled into pipeline. See previous logged error."));
 		return false;
 	}
-	plDesc.vertexFunction = mtlFunction;
-	plDesc.rasterizationEnabled = !shaderContext.options.mslOptions.disable_rasterization;
-	_needsVertexSwizzleBuffer = shaderContext.options.needsSwizzleBuffer;
-	_needsVertexBufferSizeBuffer = shaderContext.options.needsBufferSizeBuffer;
-	_needsVertexOutputBuffer = shaderContext.options.needsOutputBuffer;
+	plDesc.vertexFunction = func.mtlFunction;
+
+	auto& funcRslts = func.shaderConversionResults;
+	plDesc.rasterizationEnabled = !funcRslts.isRasterizationDisabled;
+	_needsVertexSwizzleBuffer = funcRslts.needsSwizzleBuffer;
+	_needsVertexBufferSizeBuffer = funcRslts.needsBufferSizeBuffer;
+	_needsVertexOutputBuffer = funcRslts.needsOutputBuffer;
+
 	// If we need the swizzle buffer and there's no place to put it, we're in serious trouble.
 	if (!verifyImplicitBuffer(_needsVertexSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageVertex, "swizzle", vbCnt)) {
 		return false;
@@ -791,7 +805,7 @@
 
 bool MVKGraphicsPipeline::addTessCtlShaderToPipeline(MTLComputePipelineDescriptor* plDesc,
 													 const VkGraphicsPipelineCreateInfo* pCreateInfo,
-													 SPIRVToMSLConverterContext& shaderContext,
+													 SPIRVToMSLConversionConfiguration& shaderContext,
 													 std::vector<SPIRVShaderOutput>& vtxOutputs) {
 	shaderContext.options.entryPointStage = spv::ExecutionModelTessellationControl;
 	shaderContext.options.entryPointName = _pTessCtlSS->pName;
@@ -803,17 +817,21 @@
 	shaderContext.options.mslOptions.buffer_size_buffer_index = _bufferSizeBufferIndex.stages[kMVKShaderStageTessCtl];
 	shaderContext.options.mslOptions.capture_output_to_buffer = true;
 	addPrevStageOutputToShaderConverterContext(shaderContext, vtxOutputs);
-	id<MTLFunction> mtlFunction = ((MVKShaderModule*)_pTessCtlSS->module)->getMTLFunction(&shaderContext, _pTessCtlSS->pSpecializationInfo, _pipelineCache).mtlFunction;
-	if ( !mtlFunction ) {
+
+	MVKMTLFunction func = ((MVKShaderModule*)_pTessCtlSS->module)->getMTLFunction(&shaderContext, _pTessCtlSS->pSpecializationInfo, _pipelineCache);
+	if ( !func.mtlFunction ) {
 		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Tessellation control shader function could not be compiled into pipeline. See previous logged error."));
 		return false;
 	}
-	plDesc.computeFunction = mtlFunction;
-	_needsTessCtlSwizzleBuffer = shaderContext.options.needsSwizzleBuffer;
-	_needsTessCtlBufferSizeBuffer = shaderContext.options.needsBufferSizeBuffer;
-	_needsTessCtlOutputBuffer = shaderContext.options.needsOutputBuffer;
-	_needsTessCtlPatchOutputBuffer = shaderContext.options.needsPatchOutputBuffer;
-	_needsTessCtlInput = shaderContext.options.needsInputThreadgroupMem;
+	plDesc.computeFunction = func.mtlFunction;
+
+	auto& funcRslts = func.shaderConversionResults;
+	_needsTessCtlSwizzleBuffer = funcRslts.needsSwizzleBuffer;
+	_needsTessCtlBufferSizeBuffer = funcRslts.needsBufferSizeBuffer;
+	_needsTessCtlOutputBuffer = funcRslts.needsOutputBuffer;
+	_needsTessCtlPatchOutputBuffer = funcRslts.needsPatchOutputBuffer;
+	_needsTessCtlInput = funcRslts.needsInputThreadgroupMem;
+
 	if (!verifyImplicitBuffer(_needsTessCtlSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageTessCtl, "swizzle", kMVKTessCtlNumReservedBuffers)) {
 		return false;
 	}
@@ -839,7 +857,7 @@
 
 bool MVKGraphicsPipeline::addTessEvalShaderToPipeline(MTLRenderPipelineDescriptor* plDesc,
 													  const VkGraphicsPipelineCreateInfo* pCreateInfo,
-													  SPIRVToMSLConverterContext& shaderContext,
+													  SPIRVToMSLConversionConfiguration& shaderContext,
 													  std::vector<SPIRVShaderOutput>& tcOutputs) {
 	shaderContext.options.entryPointStage = spv::ExecutionModelTessellationEvaluation;
 	shaderContext.options.entryPointName = _pTessEvalSS->pName;
@@ -848,16 +866,20 @@
 	shaderContext.options.mslOptions.capture_output_to_buffer = false;
 	shaderContext.options.mslOptions.disable_rasterization = (pCreateInfo->pRasterizationState && (pCreateInfo->pRasterizationState->rasterizerDiscardEnable));
 	addPrevStageOutputToShaderConverterContext(shaderContext, tcOutputs);
-	id<MTLFunction> mtlFunction = ((MVKShaderModule*)_pTessEvalSS->module)->getMTLFunction(&shaderContext, _pTessEvalSS->pSpecializationInfo, _pipelineCache).mtlFunction;
-	if ( !mtlFunction ) {
+
+	MVKMTLFunction func = ((MVKShaderModule*)_pTessEvalSS->module)->getMTLFunction(&shaderContext, _pTessEvalSS->pSpecializationInfo, _pipelineCache);
+	if ( !func.mtlFunction ) {
 		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Tessellation evaluation shader function could not be compiled into pipeline. See previous logged error."));
 		return false;
 	}
 	// Yeah, you read that right. Tess. eval functions are a kind of vertex function in Metal.
-	plDesc.vertexFunction = mtlFunction;
-	plDesc.rasterizationEnabled = !shaderContext.options.mslOptions.disable_rasterization;
-	_needsTessEvalSwizzleBuffer = shaderContext.options.needsSwizzleBuffer;
-	_needsTessEvalBufferSizeBuffer = shaderContext.options.needsBufferSizeBuffer;
+	plDesc.vertexFunction = func.mtlFunction;
+
+	auto& funcRslts = func.shaderConversionResults;
+	plDesc.rasterizationEnabled = !funcRslts.isRasterizationDisabled;
+	_needsTessEvalSwizzleBuffer = funcRslts.needsSwizzleBuffer;
+	_needsTessEvalBufferSizeBuffer = funcRslts.needsBufferSizeBuffer;
+
 	if (!verifyImplicitBuffer(_needsTessEvalSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageTessEval, "swizzle", kMVKTessEvalNumReservedBuffers)) {
 		return false;
 	}
@@ -869,21 +891,24 @@
 
 bool MVKGraphicsPipeline::addFragmentShaderToPipeline(MTLRenderPipelineDescriptor* plDesc,
 													  const VkGraphicsPipelineCreateInfo* pCreateInfo,
-													  SPIRVToMSLConverterContext& shaderContext) {
+													  SPIRVToMSLConversionConfiguration& shaderContext) {
 	if (_pFragmentSS) {
 		shaderContext.options.entryPointStage = spv::ExecutionModelFragment;
 		shaderContext.options.mslOptions.swizzle_buffer_index = _swizzleBufferIndex.stages[kMVKShaderStageFragment];
 		shaderContext.options.mslOptions.buffer_size_buffer_index = _bufferSizeBufferIndex.stages[kMVKShaderStageFragment];
 		shaderContext.options.entryPointName = _pFragmentSS->pName;
 		shaderContext.options.mslOptions.capture_output_to_buffer = false;
-		id<MTLFunction> mtlFunction = ((MVKShaderModule*)_pFragmentSS->module)->getMTLFunction(&shaderContext, _pFragmentSS->pSpecializationInfo, _pipelineCache).mtlFunction;
-		if ( !mtlFunction ) {
+
+		MVKMTLFunction func = ((MVKShaderModule*)_pFragmentSS->module)->getMTLFunction(&shaderContext, _pFragmentSS->pSpecializationInfo, _pipelineCache);
+		if ( !func.mtlFunction ) {
 			setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Fragment shader function could not be compiled into pipeline. See previous logged error."));
 			return false;
 		}
-		plDesc.fragmentFunction = mtlFunction;
-		_needsFragmentSwizzleBuffer = shaderContext.options.needsSwizzleBuffer;
-		_needsFragmentBufferSizeBuffer = shaderContext.options.needsBufferSizeBuffer;
+		plDesc.fragmentFunction = func.mtlFunction;
+
+		auto& funcRslts = func.shaderConversionResults;
+		_needsFragmentSwizzleBuffer = funcRslts.needsSwizzleBuffer;
+		_needsFragmentBufferSizeBuffer = funcRslts.needsBufferSizeBuffer;
 		if (!verifyImplicitBuffer(_needsFragmentSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageFragment, "swizzle", 0)) {
 			return false;
 		}
@@ -896,7 +921,7 @@
 
 bool MVKGraphicsPipeline::addVertexInputToPipeline(MTLRenderPipelineDescriptor* plDesc,
 												   const VkPipelineVertexInputStateCreateInfo* pVI,
-												   const SPIRVToMSLConverterContext& shaderContext) {
+												   const SPIRVToMSLConversionConfiguration& shaderContext) {
     // Collect extension structures
     VkPipelineVertexInputDivisorStateCreateInfoEXT* pVertexInputDivisorState = nullptr;
     auto* next = (MVKVkAPIStructHeader*)pVI->pNext;
@@ -1080,7 +1105,7 @@
 }
 
 // Initializes the context used to prepare the MSL library used by this pipeline.
-void MVKGraphicsPipeline::initMVKShaderConverterContext(SPIRVToMSLConverterContext& shaderContext,
+void MVKGraphicsPipeline::initMVKShaderConverterContext(SPIRVToMSLConversionConfiguration& shaderContext,
                                                         const VkGraphicsPipelineCreateInfo* pCreateInfo,
                                                         const SPIRVTessReflectionData& reflectData) {
 
@@ -1122,7 +1147,7 @@
 }
 
 // Initializes the vertex attributes in a shader converter context.
-void MVKGraphicsPipeline::addVertexInputToShaderConverterContext(SPIRVToMSLConverterContext& shaderContext,
+void MVKGraphicsPipeline::addVertexInputToShaderConverterContext(SPIRVToMSLConversionConfiguration& shaderContext,
                                                                  const VkGraphicsPipelineCreateInfo* pCreateInfo) {
     // Set the shader context vertex attribute information
     shaderContext.vertexAttributes.clear();
@@ -1186,7 +1211,7 @@
 }
 
 // Initializes the vertex attributes in a shader converter context from the previous stage output.
-void MVKGraphicsPipeline::addPrevStageOutputToShaderConverterContext(SPIRVToMSLConverterContext& shaderContext,
+void MVKGraphicsPipeline::addPrevStageOutputToShaderConverterContext(SPIRVToMSLConversionConfiguration& shaderContext,
                                                                      std::vector<SPIRVShaderOutput>& shaderOutputs) {
     // Set the shader context vertex attribute information
     shaderContext.vertexAttributes.clear();
@@ -1286,7 +1311,7 @@
     const VkPipelineShaderStageCreateInfo* pSS = &pCreateInfo->stage;
     if ( !mvkAreFlagsEnabled(pSS->stage, VK_SHADER_STAGE_COMPUTE_BIT) ) { return MVKMTLFunctionNull; }
 
-    SPIRVToMSLConverterContext shaderContext;
+    SPIRVToMSLConversionConfiguration shaderContext;
 	shaderContext.options.entryPointName = pCreateInfo->stage.pName;
 	shaderContext.options.entryPointStage = spv::ExecutionModelGLCompute;
     shaderContext.options.mslOptions.msl_version = _device->_pMetalFeatures->mslVersion;
@@ -1300,11 +1325,13 @@
     shaderContext.options.mslOptions.swizzle_buffer_index = _swizzleBufferIndex.stages[kMVKShaderStageCompute];
     shaderContext.options.mslOptions.buffer_size_buffer_index = _bufferSizeBufferIndex.stages[kMVKShaderStageCompute];
 
-    MVKShaderModule* mvkShdrMod = (MVKShaderModule*)pSS->module;
-    MVKMTLFunction func = mvkShdrMod->getMTLFunction(&shaderContext, pSS->pSpecializationInfo, _pipelineCache);
-    _needsSwizzleBuffer = shaderContext.options.needsSwizzleBuffer;
-    _needsBufferSizeBuffer = shaderContext.options.needsBufferSizeBuffer;
-    return func;
+    MVKMTLFunction func = ((MVKShaderModule*)pSS->module)->getMTLFunction(&shaderContext, pSS->pSpecializationInfo, _pipelineCache);
+
+	auto& funcRslts = func.shaderConversionResults;
+	_needsSwizzleBuffer = funcRslts.needsSwizzleBuffer;
+    _needsBufferSizeBuffer = funcRslts.needsBufferSizeBuffer;
+
+	return func;
 }
 
 MVKComputePipeline::~MVKComputePipeline() {
@@ -1316,7 +1343,7 @@
 #pragma mark MVKPipelineCache
 
 // Return a shader library from the specified shader context sourced from the specified shader module.
-MVKShaderLibrary* MVKPipelineCache::getShaderLibrary(SPIRVToMSLConverterContext* pContext, MVKShaderModule* shaderModule) {
+MVKShaderLibrary* MVKPipelineCache::getShaderLibrary(SPIRVToMSLConversionConfiguration* pContext, MVKShaderModule* shaderModule) {
 	lock_guard<mutex> lock(_shaderCacheLock);
 
 	bool wasAdded = false;
@@ -1358,9 +1385,9 @@
 	friend MVKPipelineCache;
 
 	bool next() { return (++_index < (_pSLCache ? _pSLCache->_shaderLibraries.size() : 0)); }
-	SPIRVToMSLConverterContext& getShaderContext() { return _pSLCache->_shaderLibraries[_index].first; }
+	SPIRVToMSLConversionConfiguration& getShaderConversionConfig() { return _pSLCache->_shaderLibraries[_index].first; }
 	std::string& getMSL() { return _pSLCache->_shaderLibraries[_index].second->_msl; }
-	SPIRVEntryPoint& getEntryPoint() { return _pSLCache->_shaderLibraries[_index].second->_entryPoint; }
+	SPIRVToMSLConversionResults& getShaderConversionResults() { return _pSLCache->_shaderLibraries[_index].second->_shaderConversionResults; }
 	MVKShaderCacheIterator(MVKShaderLibraryCache* pSLCache) : _pSLCache(pSLCache) {}
 
 	MVKShaderLibraryCache* _pSLCache;
@@ -1435,8 +1462,8 @@
 			uint64_t startTime = _device->getPerformanceTimestamp();
 			writer(cacheEntryType);
 			writer(smKey);
-			writer(cacheIter.getShaderContext());
-			writer(cacheIter.getEntryPoint());
+			writer(cacheIter.getShaderConversionConfig());
+			writer(cacheIter.getShaderConversionResults());
 			writer(cacheIter.getMSL());
 			_device->addActivityPerformance(activityTracker, startTime);
 		}
@@ -1492,11 +1519,11 @@
 					MVKShaderModuleKey smKey;
 					reader(smKey);
 
-					SPIRVToMSLConverterContext shaderContext;
-					reader(shaderContext);
+					SPIRVToMSLConversionConfiguration shaderConversionConfig;
+					reader(shaderConversionConfig);
 
-					SPIRVEntryPoint entryPoint;
-					reader(entryPoint);
+					SPIRVToMSLConversionResults shaderConversionResults;
+					reader(shaderConversionResults);
 
 					string msl;
 					reader(msl);
@@ -1504,7 +1531,7 @@
 					// Add the shader library to the staging cache.
 					MVKShaderLibraryCache* slCache = getShaderLibraryCache(smKey);
 					_device->addActivityPerformance(_device->_performanceStatistics.pipelineCache.readPipelineCache, startTime);
-					slCache->addShaderLibrary(&shaderContext, msl, entryPoint);
+					slCache->addShaderLibrary(&shaderConversionConfig, msl, shaderConversionResults);
 
 					break;
 				}
@@ -1625,18 +1652,13 @@
 	}
 
 	template<class Archive>
-	void serialize(Archive & archive, SPIRVToMSLConverterOptions& opt) {
+	void serialize(Archive & archive, SPIRVToMSLConversionOptions& opt) {
 		archive(opt.mslOptions,
 				opt.entryPointName,
 				opt.entryPointStage,
 				opt.tessPatchKind,
 				opt.numTessControlPoints,
-				opt.shouldFlipVertexY,
-				opt.needsSwizzleBuffer,
-				opt.needsOutputBuffer,
-				opt.needsPatchOutputBuffer,
-				opt.needsBufferSizeBuffer,
-				opt.needsInputThreadgroupMem);
+				opt.shouldFlipVertexY);
 	}
 
 	template<class Archive>
@@ -1654,10 +1676,21 @@
 	}
 
 	template<class Archive>
-	void serialize(Archive & archive, SPIRVToMSLConverterContext& ctx) {
+	void serialize(Archive & archive, SPIRVToMSLConversionConfiguration& ctx) {
 		archive(ctx.options, ctx.vertexAttributes, ctx.resourceBindings);
 	}
 
+	template<class Archive>
+	void serialize(Archive & archive, SPIRVToMSLConversionResults& scr) {
+		archive(scr.entryPoint,
+				scr.isRasterizationDisabled,
+				scr.needsSwizzleBuffer,
+				scr.needsOutputBuffer,
+				scr.needsPatchOutputBuffer,
+				scr.needsBufferSizeBuffer,
+				scr.needsInputThreadgroupMem);
+	}
+
 }
 
 template<class Archive>
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
index d53a4ec..01dedda 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
@@ -38,10 +38,11 @@
 #pragma mark -
 #pragma mark MVKShaderLibrary
 
-/** Specifies the SPIRV LocalSize, which is the number of threads in a compute shader workgroup. */
+/** A MTLFunction and corresponding result information resulting from a shader conversion. */
 typedef struct {
-    id<MTLFunction> mtlFunction;
-    MTLSize threadGroupSize;
+	id<MTLFunction> mtlFunction;
+	const SPIRVToMSLConversionResults shaderConversionResults;
+	MTLSize threadGroupSize;
 } MVKMTLFunction;
 
 /** A MVKMTLFunction indicating an invalid MTLFunction. The mtlFunction member is nil. */
@@ -61,7 +62,7 @@
 	/** Constructs an instance from the specified MSL source code. */
 	MVKShaderLibrary(MVKVulkanAPIDeviceObject* owner,
 					 const std::string& mslSourceCode,
-					 const SPIRVEntryPoint& entryPoint);
+					 const SPIRVToMSLConversionResults& shaderConversionResults);
 
 	/** Constructs an instance from the specified compiled MSL code data. */
 	MVKShaderLibrary(MVKVulkanAPIDeviceObject* owner,
@@ -84,7 +85,7 @@
 
 	MVKVulkanAPIDeviceObject* _owner;
 	id<MTLLibrary> _mtlLibrary;
-	SPIRVEntryPoint _entryPoint;
+	SPIRVToMSLConversionResults _shaderConversionResults;
 	std::string _msl;
 };
 
@@ -107,7 +108,7 @@
 	 * 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,
+	MVKShaderLibrary* getShaderLibrary(SPIRVToMSLConversionConfiguration* pContext,
 									   MVKShaderModule* shaderModule,
 									   bool* pWasAdded = nullptr);
 
@@ -120,14 +121,14 @@
 	friend MVKPipelineCache;
 	friend MVKShaderModule;
 
-	MVKShaderLibrary* findShaderLibrary(SPIRVToMSLConverterContext* pContext);
-	MVKShaderLibrary* addShaderLibrary(SPIRVToMSLConverterContext* pContext,
+	MVKShaderLibrary* findShaderLibrary(SPIRVToMSLConversionConfiguration* pContext);
+	MVKShaderLibrary* addShaderLibrary(SPIRVToMSLConversionConfiguration* pContext,
 									   const std::string& mslSourceCode,
-									   const SPIRVEntryPoint& entryPoint);
+									   const SPIRVToMSLConversionResults& shaderConversionResults);
 	void merge(MVKShaderLibraryCache* other);
 
 	MVKVulkanAPIDeviceObject* _owner;
-	std::vector<std::pair<SPIRVToMSLConverterContext, MVKShaderLibrary*>> _shaderLibraries;
+	std::vector<std::pair<SPIRVToMSLConversionConfiguration, MVKShaderLibrary*>> _shaderLibraries;
 };
 
 
@@ -168,12 +169,12 @@
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT; }
 
 	/** Returns the Metal shader function, possibly specialized. */
-	MVKMTLFunction getMTLFunction(SPIRVToMSLConverterContext* pContext,
+	MVKMTLFunction getMTLFunction(SPIRVToMSLConversionConfiguration* pContext,
 								  const VkSpecializationInfo* pSpecializationInfo,
 								  MVKPipelineCache* pipelineCache);
 
 	/** Convert the SPIR-V to MSL, using the specified shader conversion context. */
-	bool convert(SPIRVToMSLConverterContext* pContext);
+	bool convert(SPIRVToMSLConversionConfiguration* pContext);
 
 	/** Returns the original SPIR-V code that was specified when this object was created. */
 	const std::vector<uint32_t>& getSPIRV() { return _spvConverter.getSPIRV(); }
@@ -184,12 +185,9 @@
 	 */
 	const std::string& getMSL() { return _spvConverter.getMSL(); }
 
-	/**
-	 * Returns information about the shader entry point as converted by the most recent
-	 * call to convert() function, or set directly using the setMSL() function.
-	 */
-	const SPIRVEntryPoint& getEntryPoint() { return _spvConverter.getEntryPoint(); }
-    
+	/** Returns information about the shader conversion results. */
+	const SPIRVToMSLConversionResults& getConversionResults() { return _spvConverter.getConversionResults(); }
+
     /** Sets the number of threads in a single compute kernel workgroup, per dimension. */
     void setWorkgroupSize(uint32_t x, uint32_t y, uint32_t z);
     
@@ -204,7 +202,7 @@
 	friend MVKShaderCacheIterator;
 
 	void propogateDebugName() override {}
-	MVKGLSLConversionShaderStage getMVKGLSLConversionShaderStage(SPIRVToMSLConverterContext* pContext);
+	MVKGLSLConversionShaderStage getMVKGLSLConversionShaderStage(SPIRVToMSLConversionConfiguration* pContext);
 
 	MVKShaderLibraryCache _shaderLibraryCache;
 	SPIRVToMSLConverter _spvConverter;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
index 426670a..6c7adb5 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
@@ -26,7 +26,7 @@
 using namespace std;
 
 
-const MVKMTLFunction MVKMTLFunctionNull = { nil, MTLSizeMake(1, 1, 1) };
+const MVKMTLFunction MVKMTLFunctionNull = { nil, SPIRVToMSLConversionResults(), MTLSizeMake(1, 1, 1) };
 
 #pragma mark -
 #pragma mark MVKShaderLibrary
@@ -42,7 +42,6 @@
 			}
 		}
 	}
-
 	return wgDim.size;
 }
 
@@ -50,7 +49,7 @@
 
     if ( !_mtlLibrary ) { return MVKMTLFunctionNull; }
 
-    NSString* mtlFuncName = @(_entryPoint.mtlFunctionName.c_str());
+    NSString* mtlFuncName = @(_shaderConversionResults.entryPoint.mtlFunctionName.c_str());
 	MVKDevice* mvkDev = _owner->getDevice();
     uint64_t startTime = mvkDev->getPerformanceTimestamp();
     id<MTLFunction> mtlFunc = [[_mtlLibrary newFunctionWithName: mtlFuncName] autorelease];
@@ -96,10 +95,11 @@
 	if ( !dbName ) { dbName = _owner-> getDebugName(); }
 	setLabelIfNotNil(mtlFunc, dbName);
 
-	return { mtlFunc, MTLSizeMake(getWorkgroupDimensionSize(_entryPoint.workgroupSize.width, pSpecializationInfo),
-								  getWorkgroupDimensionSize(_entryPoint.workgroupSize.height, pSpecializationInfo),
-								  getWorkgroupDimensionSize(_entryPoint.workgroupSize.depth, pSpecializationInfo)) };
 
+	auto& wgSize = _shaderConversionResults.entryPoint.workgroupSize;
+	return { mtlFunc, _shaderConversionResults, MTLSizeMake(getWorkgroupDimensionSize(wgSize.width, pSpecializationInfo),
+															getWorkgroupDimensionSize(wgSize.height, pSpecializationInfo),
+															getWorkgroupDimensionSize(wgSize.depth, pSpecializationInfo))};
 }
 
 // Returns the MTLFunctionConstant with the specified ID from the specified array of function constants.
@@ -109,12 +109,14 @@
     return nil;
 }
 
-MVKShaderLibrary::MVKShaderLibrary(MVKVulkanAPIDeviceObject* owner, const string& mslSourceCode, const SPIRVEntryPoint& entryPoint) : _owner(owner) {
+MVKShaderLibrary::MVKShaderLibrary(MVKVulkanAPIDeviceObject* owner,
+								   const string& mslSourceCode,
+								   const SPIRVToMSLConversionResults& shaderConversionResults) : _owner(owner) {
 	MVKShaderLibraryCompiler* slc = new MVKShaderLibraryCompiler(_owner);
 	_mtlLibrary = slc->newMTLLibrary(@(mslSourceCode.c_str()));	// retained
 	slc->destroy();
 
-	_entryPoint = entryPoint;
+	_shaderConversionResults = shaderConversionResults;
 	_msl = mslSourceCode;
 }
 
@@ -138,7 +140,7 @@
 
 MVKShaderLibrary::MVKShaderLibrary(MVKShaderLibrary& other) : _owner(other._owner) {
 	_mtlLibrary = [other._mtlLibrary retain];
-	_entryPoint = other._entryPoint;
+	_shaderConversionResults = other._shaderConversionResults;
 	_msl = other._msl;
 }
 
@@ -159,9 +161,10 @@
 }
 
 void MVKShaderLibrary::setWorkgroupSize(uint32_t x, uint32_t y, uint32_t z) {
-    _entryPoint.workgroupSize.width.size = x;
-    _entryPoint.workgroupSize.height.size = y;
-    _entryPoint.workgroupSize.depth.size = z;
+	auto& wgSize = _shaderConversionResults.entryPoint.workgroupSize;
+    wgSize.width.size = x;
+    wgSize.height.size = y;
+    wgSize.depth.size = z;
 }
 
 MVKShaderLibrary::~MVKShaderLibrary() {
@@ -172,14 +175,14 @@
 #pragma mark -
 #pragma mark MVKShaderLibraryCache
 
-MVKShaderLibrary* MVKShaderLibraryCache::getShaderLibrary(SPIRVToMSLConverterContext* pContext,
+MVKShaderLibrary* MVKShaderLibraryCache::getShaderLibrary(SPIRVToMSLConversionConfiguration* pContext,
 														  MVKShaderModule* shaderModule,
 														  bool* pWasAdded) {
 	bool wasAdded = false;
 	MVKShaderLibrary* shLib = findShaderLibrary(pContext);
 	if ( !shLib ) {
 		if (shaderModule->convert(pContext)) {
-			shLib = addShaderLibrary(pContext, shaderModule->getMSL(), shaderModule->getEntryPoint());
+			shLib = addShaderLibrary(pContext, shaderModule->getMSL(), shaderModule->getConversionResults());
 			wasAdded = true;
 		}
 	}
@@ -191,7 +194,7 @@
 
 // Finds and returns a shader library matching the specified context, or returns nullptr if it doesn't exist.
 // If a match is found, the specified context is aligned with the context of the matching library.
-MVKShaderLibrary* MVKShaderLibraryCache::findShaderLibrary(SPIRVToMSLConverterContext* pContext) {
+MVKShaderLibrary* MVKShaderLibraryCache::findShaderLibrary(SPIRVToMSLConversionConfiguration* pContext) {
 	for (auto& slPair : _shaderLibraries) {
 		if (slPair.first.matches(*pContext)) {
 			pContext->alignWith(slPair.first);
@@ -202,10 +205,10 @@
 }
 
 // Adds and returns a new shader library configured from the specified context.
-MVKShaderLibrary* MVKShaderLibraryCache::addShaderLibrary(SPIRVToMSLConverterContext* pContext,
+MVKShaderLibrary* MVKShaderLibraryCache::addShaderLibrary(SPIRVToMSLConversionConfiguration* pContext,
 														  const string& mslSourceCode,
-														  const SPIRVEntryPoint& entryPoint) {
-	MVKShaderLibrary* shLib = new MVKShaderLibrary(_owner, mslSourceCode, entryPoint);
+														  const SPIRVToMSLConversionResults& shaderConversionResults) {
+	MVKShaderLibrary* shLib = new MVKShaderLibrary(_owner, mslSourceCode, shaderConversionResults);
 	_shaderLibraries.emplace_back(*pContext, shLib);
 	return shLib;
 }
@@ -228,7 +231,7 @@
 #pragma mark -
 #pragma mark MVKShaderModule
 
-MVKMTLFunction MVKShaderModule::getMTLFunction(SPIRVToMSLConverterContext* pContext,
+MVKMTLFunction MVKShaderModule::getMTLFunction(SPIRVToMSLConversionConfiguration* pContext,
 											   const VkSpecializationInfo* pSpecializationInfo,
 											   MVKPipelineCache* pipelineCache) {
 	lock_guard<mutex> lock(_accessLock);
@@ -249,7 +252,7 @@
 	return mvkLib ? mvkLib->getMTLFunction(pSpecializationInfo, this) : MVKMTLFunctionNull;
 }
 
-bool MVKShaderModule::convert(SPIRVToMSLConverterContext* pContext) {
+bool MVKShaderModule::convert(SPIRVToMSLConversionConfiguration* pContext) {
 	bool shouldLogCode = _device->_pMVKConfig->debugMode;
 	bool shouldLogEstimatedGLSL = shouldLogCode;
 
@@ -283,7 +286,7 @@
 }
 
 // Returns the MVKGLSLConversionShaderStage corresponding to the shader stage in the SPIR-V converter context.
-MVKGLSLConversionShaderStage MVKShaderModule::getMVKGLSLConversionShaderStage(SPIRVToMSLConverterContext* pContext) {
+MVKGLSLConversionShaderStage MVKShaderModule::getMVKGLSLConversionShaderStage(SPIRVToMSLConversionConfiguration* pContext) {
 	switch (pContext->options.entryPointStage) {
 		case spv::ExecutionModelVertex:						return kMVKGLSLConversionShaderStageVertex;
 		case spv::ExecutionModelTessellationControl:		return kMVKGLSLConversionShaderStageTessControl;
@@ -341,7 +344,7 @@
 			_device->addActivityPerformance(_device->_performanceStatistics.shaderCompilation.hashShaderCode, startTime);
 
 			_spvConverter.setMSL(pMSLCode, nullptr);
-			_defaultLibrary = new MVKShaderLibrary(this, _spvConverter.getMSL().c_str(), _spvConverter.getEntryPoint());
+			_defaultLibrary = new MVKShaderLibrary(this, _spvConverter.getMSL().c_str(), _spvConverter.getConversionResults());
 
 			break;
 		}
diff --git a/MoltenVK/MoltenVK/Utility/MVKFoundation.h b/MoltenVK/MoltenVK/Utility/MVKFoundation.h
index 00b0b85..f6c123b 100644
--- a/MoltenVK/MoltenVK/Utility/MVKFoundation.h
+++ b/MoltenVK/MoltenVK/Utility/MVKFoundation.h
@@ -391,6 +391,13 @@
 }
 #endif
 
+/** Returns whether the container contains an item equal to the value. */
+template<class C, class T>
+bool contains(const C& container, const T& val) {
+	for (const T& cVal : container) { if (cVal == val) { return true; } }
+	return false;
+}
+
 /** Removes the first occurance of the specified value from the specified container. */
 template<class C, class T>
 void mvkRemoveFirstOccurance(C& container, T val) {
diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVConversion.mm b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVConversion.mm
index 1b8af9b..e295c4a 100644
--- a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVConversion.mm
+++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVConversion.mm
@@ -31,7 +31,7 @@
                                             char** pResultLog,
                                             bool shouldLogSPIRV,
                                             bool shouldLogMSL) {
-    SPIRVToMSLConverterContext spvCtx;
+    SPIRVToMSLConversionConfiguration spvCtx;
     SPIRVToMSLConverter spvConverter;
     spvConverter.setSPIRV(spvCode, spvLength);
     bool wasConverted = spvConverter.convert(spvCtx, shouldLogSPIRV, shouldLogMSL);
diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp
index b8409c0..885bb79 100644
--- a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp
+++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp
@@ -29,28 +29,23 @@
 
 
 #pragma mark -
-#pragma mark SPIRVToMSLConverterContext
+#pragma mark SPIRVToMSLConversionConfiguration
 
 // Returns whether the vector contains the value (using a matches(T&) comparison member function). */
 template<class T>
-bool contains(const vector<T>& vec, const T& val) {
+bool containsMatching(const vector<T>& vec, const T& val) {
     for (const T& vecVal : vec) { if (vecVal.matches(val)) { return true; } }
     return false;
 }
 
-MVK_PUBLIC_SYMBOL bool SPIRVToMSLConverterOptions::matches(const SPIRVToMSLConverterOptions& other) const {
+MVK_PUBLIC_SYMBOL bool SPIRVToMSLConversionOptions::matches(const SPIRVToMSLConversionOptions& other) const {
 	if (entryPointStage != other.entryPointStage) { return false; }
 	if (entryPointName != other.entryPointName) { return false; }
 	if (tessPatchKind != other.tessPatchKind) { return false; }
 	if (numTessControlPoints != other.numTessControlPoints) { return false; }
 	if (!!shouldFlipVertexY != !!other.shouldFlipVertexY) { return false; }
-//	if (!!needsSwizzleBuffer != !!other.needsSwizzleBuffer) { return false; }
-//	if (!!needsOutputBuffer != !!other.needsOutputBuffer) { return false; }
-//	if (!!needsPatchOutputBuffer != !!other.needsPatchOutputBuffer) { return false; }
-//	if (!!needsBufferSizeBuffer != !!other.needsBufferSizeBuffer) { return false; }
-//	if (!!needsInputThreadgroupMem != !!other.needsInputThreadgroupMem) { return false; }
 
-//	if (mslOptions.platform != other.mslOptions.platform) { return false; }
+	if (mslOptions.platform != other.mslOptions.platform) { return false; }
 	if (mslOptions.msl_version != other.mslOptions.msl_version) { return false; }
 	if (mslOptions.texel_buffer_texture_width != other.mslOptions.texel_buffer_texture_width) { return false; }
 	if (mslOptions.swizzle_buffer_index != other.mslOptions.swizzle_buffer_index) { return false; }
@@ -61,18 +56,18 @@
 	if (mslOptions.buffer_size_buffer_index != other.mslOptions.buffer_size_buffer_index) { return false; }
 	if (mslOptions.shader_input_wg_index != other.mslOptions.shader_input_wg_index) { return false; }
 	if (!!mslOptions.enable_point_size_builtin != !!other.mslOptions.enable_point_size_builtin) { return false; }
-//	if (!!mslOptions.disable_rasterization != !!other.mslOptions.disable_rasterization) { return false; }
+	if (!!mslOptions.disable_rasterization != !!other.mslOptions.disable_rasterization) { return false; }
 	if (!!mslOptions.capture_output_to_buffer != !!other.mslOptions.capture_output_to_buffer) { return false; }
 	if (!!mslOptions.swizzle_texture_samples != !!other.mslOptions.swizzle_texture_samples) { return false; }
 	if (!!mslOptions.tess_domain_origin_lower_left != !!other.mslOptions.tess_domain_origin_lower_left) { return false; }
-//	if (mslOptions.argument_buffers != other.mslOptions.argument_buffers) { return false; }
-//	if (mslOptions.pad_fragment_output_components != other.mslOptions.pad_fragment_output_components) { return false; }
-//	if (mslOptions.texture_buffer_native != other.mslOptions.texture_buffer_native) { return false; }
+	if (mslOptions.argument_buffers != other.mslOptions.argument_buffers) { return false; }
+	if (mslOptions.pad_fragment_output_components != other.mslOptions.pad_fragment_output_components) { return false; }
+	if (mslOptions.texture_buffer_native != other.mslOptions.texture_buffer_native) { return false; }
 
 	return true;
 }
 
-MVK_PUBLIC_SYMBOL std::string SPIRVToMSLConverterOptions::printMSLVersion(uint32_t mslVersion, bool includePatch) {
+MVK_PUBLIC_SYMBOL std::string SPIRVToMSLConversionOptions::printMSLVersion(uint32_t mslVersion, bool includePatch) {
 	string verStr;
 
 	uint32_t major = mslVersion / 10000;
@@ -91,7 +86,9 @@
 	return verStr;
 }
 
-MVK_PUBLIC_SYMBOL SPIRVToMSLConverterOptions::SPIRVToMSLConverterOptions() {
+MVK_PUBLIC_SYMBOL SPIRVToMSLConversionOptions::SPIRVToMSLConversionOptions() {
+	mslOptions.pad_fragment_output_components = true;
+
 #if MVK_MACOS
 	mslOptions.platform = CompilerMSL::Options::macOS;
 #endif
@@ -143,14 +140,14 @@
 	return true;
 }
 
-MVK_PUBLIC_SYMBOL bool SPIRVToMSLConverterContext::stageSupportsVertexAttributes() const {
+MVK_PUBLIC_SYMBOL bool SPIRVToMSLConversionConfiguration::stageSupportsVertexAttributes() const {
 	return (options.entryPointStage == spv::ExecutionModelVertex ||
 			options.entryPointStage == spv::ExecutionModelTessellationControl ||
 			options.entryPointStage == spv::ExecutionModelTessellationEvaluation);
 }
 
 // Check them all in case inactive VA's duplicate locations used by active VA's.
-MVK_PUBLIC_SYMBOL bool SPIRVToMSLConverterContext::isVertexAttributeLocationUsed(uint32_t location) const {
+MVK_PUBLIC_SYMBOL bool SPIRVToMSLConversionConfiguration::isVertexAttributeLocationUsed(uint32_t location) const {
     for (auto& va : vertexAttributes) {
         if ((va.vertexAttribute.location == location) && va.isUsedByShader) { return true; }
     }
@@ -158,15 +155,14 @@
 }
 
 // Check them all in case inactive VA's duplicate buffers used by active VA's.
-MVK_PUBLIC_SYMBOL bool SPIRVToMSLConverterContext::isVertexBufferUsed(uint32_t mslBuffer) const {
+MVK_PUBLIC_SYMBOL bool SPIRVToMSLConversionConfiguration::isVertexBufferUsed(uint32_t mslBuffer) const {
     for (auto& va : vertexAttributes) {
         if ((va.vertexAttribute.msl_buffer == mslBuffer) && va.isUsedByShader) { return true; }
     }
     return false;
 }
 
-MVK_PUBLIC_SYMBOL void SPIRVToMSLConverterContext::markAllAttributesAndResourcesUsed() {
-
+MVK_PUBLIC_SYMBOL void SPIRVToMSLConversionConfiguration::markAllAttributesAndResourcesUsed() {
 	if (stageSupportsVertexAttributes()) {
 		for (auto& va : vertexAttributes) { va.isUsedByShader = true; }
 	}
@@ -174,31 +170,25 @@
 	for (auto& rb : resourceBindings) { rb.isUsedByShader = true; }
 }
 
-MVK_PUBLIC_SYMBOL bool SPIRVToMSLConverterContext::matches(const SPIRVToMSLConverterContext& other) const {
+MVK_PUBLIC_SYMBOL bool SPIRVToMSLConversionConfiguration::matches(const SPIRVToMSLConversionConfiguration& other) const {
 
     if ( !options.matches(other.options) ) { return false; }
 
 	if (stageSupportsVertexAttributes()) {
 		for (const auto& va : vertexAttributes) {
-			if (va.isUsedByShader && !contains(other.vertexAttributes, va)) { return false; }
+			if (va.isUsedByShader && !containsMatching(other.vertexAttributes, va)) { return false; }
 		}
 	}
 
     for (const auto& rb : resourceBindings) {
-        if (rb.isUsedByShader && !contains(other.resourceBindings, rb)) { return false; }
+        if (rb.isUsedByShader && !containsMatching(other.resourceBindings, rb)) { return false; }
     }
 
     return true;
 }
 
-MVK_PUBLIC_SYMBOL void SPIRVToMSLConverterContext::alignWith(const SPIRVToMSLConverterContext& srcContext) {
 
-	options.mslOptions.disable_rasterization = srcContext.options.mslOptions.disable_rasterization;
-	options.needsSwizzleBuffer = srcContext.options.needsSwizzleBuffer;
-	options.needsOutputBuffer = srcContext.options.needsOutputBuffer;
-	options.needsPatchOutputBuffer = srcContext.options.needsPatchOutputBuffer;
-	options.needsBufferSizeBuffer = srcContext.options.needsBufferSizeBuffer;
-	options.needsInputThreadgroupMem = srcContext.options.needsInputThreadgroupMem;
+MVK_PUBLIC_SYMBOL void SPIRVToMSLConversionConfiguration::alignWith(const SPIRVToMSLConversionConfiguration& srcContext) {
 
 	if (stageSupportsVertexAttributes()) {
 		for (auto& va : vertexAttributes) {
@@ -221,9 +211,6 @@
 #pragma mark -
 #pragma mark SPIRVToMSLConverter
 
-// Populates the entry point with info extracted from the SPRI-V compiler.
-void populateEntryPoint(SPIRVEntryPoint& entryPoint, SPIRV_CROSS_NAMESPACE::Compiler* pCompiler, SPIRVToMSLConverterOptions& options);
-
 MVK_PUBLIC_SYMBOL void SPIRVToMSLConverter::setSPIRV(const uint32_t* spirvCode, size_t length) {
 	_spirv.clear();			// Clear for reuse
 	_spirv.reserve(length);
@@ -232,7 +219,7 @@
 	}
 }
 
-MVK_PUBLIC_SYMBOL bool SPIRVToMSLConverter::convert(SPIRVToMSLConverterContext& context,
+MVK_PUBLIC_SYMBOL bool SPIRVToMSLConverter::convert(SPIRVToMSLConversionConfiguration& context,
 													bool shouldLogSPIRV,
 													bool shouldLogMSL,
                                                     bool shouldLogGLSL) {
@@ -245,6 +232,7 @@
 	_wasConverted = true;
 	_resultLog.clear();
 	_msl.clear();
+	_shaderConversionResults.reset();
 
 	if (shouldLogSPIRV) { logSPIRV("Converting"); }
 
@@ -272,7 +260,6 @@
 
 		// Establish the MSL options for the compiler
 		// This needs to be done in two steps...for CompilerMSL and its superclass.
-		context.options.mslOptions.pad_fragment_output_components = true;
 		pMSLCompiler->set_msl_options(context.options.mslOptions);
 
 		auto scOpts = pMSLCompiler->get_common_options();
@@ -312,15 +299,15 @@
 	}
 #endif
 
-	// Populate the shader context with info from the compilation run, including
-	// which vertex attributes and resource bindings are used by the shader
-	populateEntryPoint(_entryPoint, pMSLCompiler, context.options);
-	context.options.mslOptions.disable_rasterization = pMSLCompiler && pMSLCompiler->get_is_rasterization_disabled();
-	context.options.needsSwizzleBuffer = pMSLCompiler && pMSLCompiler->needs_swizzle_buffer();
-	context.options.needsOutputBuffer = pMSLCompiler && pMSLCompiler->needs_output_buffer();
-	context.options.needsPatchOutputBuffer = pMSLCompiler && pMSLCompiler->needs_patch_output_buffer();
-	context.options.needsBufferSizeBuffer = pMSLCompiler && pMSLCompiler->needs_buffer_size_buffer();
-	context.options.needsInputThreadgroupMem = pMSLCompiler && pMSLCompiler->needs_input_threadgroup_mem();
+	// Populate the shader conversion results with info from the compilation run,
+	// and mark which vertex attributes and resource bindings are used by the shader
+	populateEntryPoint(pMSLCompiler, context.options);
+	_shaderConversionResults.isRasterizationDisabled = pMSLCompiler && pMSLCompiler->get_is_rasterization_disabled();
+	_shaderConversionResults.needsSwizzleBuffer = pMSLCompiler && pMSLCompiler->needs_swizzle_buffer();
+	_shaderConversionResults.needsOutputBuffer = pMSLCompiler && pMSLCompiler->needs_output_buffer();
+	_shaderConversionResults.needsPatchOutputBuffer = pMSLCompiler && pMSLCompiler->needs_patch_output_buffer();
+	_shaderConversionResults.needsBufferSizeBuffer = pMSLCompiler && pMSLCompiler->needs_buffer_size_buffer();
+	_shaderConversionResults.needsInputThreadgroupMem = pMSLCompiler && pMSLCompiler->needs_input_threadgroup_mem();
 
 	if (context.stageSupportsVertexAttributes()) {
 		for (auto& ctxVA : context.vertexAttributes) {
@@ -433,17 +420,17 @@
     _resultLog += "\n\n";
 }
 
-
-#pragma mark Support functions
-
-// Populate a workgroup size dimension.
-void populateWorkgroupDimension(SPIRVWorkgroupSizeDimension& wgDim, uint32_t size, SPIRV_CROSS_NAMESPACE::SpecializationConstant& spvSpecConst) {
+void SPIRVToMSLConverter::populateWorkgroupDimension(SPIRVWorkgroupSizeDimension& wgDim,
+													 uint32_t size,
+													 SPIRV_CROSS_NAMESPACE::SpecializationConstant& spvSpecConst) {
 	wgDim.size = max(size, 1u);
 	wgDim.isSpecialized = (spvSpecConst.id != 0);
 	wgDim.specializationID = spvSpecConst.constant_id;
 }
 
-void populateEntryPoint(SPIRVEntryPoint& entryPoint, SPIRV_CROSS_NAMESPACE::Compiler* pCompiler, SPIRVToMSLConverterOptions& options) {
+// Populates the entry point with info extracted from the SPRI-V compiler.
+void SPIRVToMSLConverter::populateEntryPoint(SPIRV_CROSS_NAMESPACE::Compiler* pCompiler,
+											 SPIRVToMSLConversionOptions& options) {
 
 	if ( !pCompiler ) { return; }
 
@@ -458,11 +445,14 @@
 		}
 	}
 
+	auto& ep = _shaderConversionResults.entryPoint;
+	ep.mtlFunctionName = spvEP.name;
+
 	SPIRV_CROSS_NAMESPACE::SpecializationConstant widthSC, heightSC, depthSC;
 	pCompiler->get_work_group_size_specialization_constants(widthSC, heightSC, depthSC);
 
-	entryPoint.mtlFunctionName = spvEP.name;
-	populateWorkgroupDimension(entryPoint.workgroupSize.width, spvEP.workgroup_size.x, widthSC);
-	populateWorkgroupDimension(entryPoint.workgroupSize.height, spvEP.workgroup_size.y, heightSC);
-	populateWorkgroupDimension(entryPoint.workgroupSize.depth, spvEP.workgroup_size.z, depthSC);
+	auto& wgSize = ep.workgroupSize;
+	populateWorkgroupDimension(wgSize.width, spvEP.workgroup_size.x, widthSC);
+	populateWorkgroupDimension(wgSize.height, spvEP.workgroup_size.y, heightSC);
+	populateWorkgroupDimension(wgSize.depth, spvEP.workgroup_size.z, depthSC);
 }
diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h
index ecaa595..82024cd 100644
--- a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h
+++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h
@@ -27,9 +27,8 @@
 
 namespace mvk {
 
-
 #pragma mark -
-#pragma mark SPIRVToMSLConverterContext
+#pragma mark SPIRVToMSLConversionConfiguration
 
 	/**
 	 * Options for converting SPIR-V to Metal Shading Language
@@ -37,24 +36,19 @@
 	 * THIS STRUCT IS STREAMED OUT AS PART OF THE PIEPLINE CACHE.
 	 * CHANGES TO THIS STRUCT SHOULD BE CAPTURED IN THE STREAMING LOGIC OF THE PIPELINE CACHE.
 	 */
-	typedef struct SPIRVToMSLConverterOptions {
+	typedef struct SPIRVToMSLConversionOptions {
 		SPIRV_CROSS_NAMESPACE::CompilerMSL::Options mslOptions;
 		std::string entryPointName;
 		spv::ExecutionModel entryPointStage = spv::ExecutionModelMax;
 		spv::ExecutionMode tessPatchKind = spv::ExecutionModeMax;
 		uint32_t numTessControlPoints = 0;
 		bool shouldFlipVertexY = true;
-		bool needsSwizzleBuffer = false;
-		bool needsOutputBuffer = false;
-		bool needsPatchOutputBuffer = false;
-		bool needsBufferSizeBuffer = false;
-		bool needsInputThreadgroupMem = false;
 
 		/**
 		 * Returns whether the specified options match this one.
 		 * It does if all corresponding elements are equal.
 		 */
-		bool matches(const SPIRVToMSLConverterOptions& other) const;
+		bool matches(const SPIRVToMSLConversionOptions& other) const;
 
 		bool hasEntryPoint() const {
 			return !entryPointName.empty() && entryPointStage != spv::ExecutionModelMax;
@@ -62,14 +56,17 @@
 
 		static std::string printMSLVersion(uint32_t mslVersion, bool includePatch = false);
 
-		SPIRVToMSLConverterOptions();
+		SPIRVToMSLConversionOptions();
 
-	} SPIRVToMSLConverterOptions;
+	} SPIRVToMSLConversionOptions;
 
 	/**
 	 * Defines MSL characteristics of a vertex attribute at a particular location.
-	 * The isUsedByShader flag is set to true during conversion of SPIR-V to MSL
-	 * if the shader makes use of this vertex attribute.
+	 *
+	 * The isUsedByShader flag is set to true during conversion of SPIR-V to MSL if the shader
+	 * makes use of this vertex attribute. This allows a pipeline to be optimized, and for two
+	 * shader conversion configurations to be compared only against the attributes that are
+	 * actually used by the shader.
 	 *
 	 * THIS STRUCT IS STREAMED OUT AS PART OF THE PIEPLINE CACHE.
 	 * CHANGES TO THIS STRUCT SHOULD BE CAPTURED IN THE STREAMING LOGIC OF THE PIPELINE CACHE.
@@ -98,6 +95,11 @@
 	 * hardcoded into the MSL as a constexpr type, instead of passed in as a runtime-bound variable.
 	 * The content of that constexpr sampler is defined in the constExprSampler parameter.
 	 *
+	 * The isUsedByShader flag is set to true during conversion of SPIR-V to MSL if the shader
+	 * makes use of this resource binding. This allows a pipeline to be optimized, and for two
+	 * shader conversion configurations to be compared only against the resource bindings that
+	 * are actually used by the shader.
+	 *
 	 * THIS STRUCT IS STREAMED OUT AS PART OF THE PIEPLINE CACHE.
 	 * CHANGES TO THIS STRUCT SHOULD BE CAPTURED IN THE STREAMING LOGIC OF THE PIPELINE CACHE.
 	 */
@@ -117,13 +119,13 @@
 	} MSLResourceBinding;
 
 	/**
-	 * Context passed to the SPIRVToMSLConverter to map SPIR-V descriptors to Metal resource indices.
+	 * Configuration passed to the SPIRVToMSLConverter.
 	 *
 	 * THIS STRUCT IS STREAMED OUT AS PART OF THE PIEPLINE CACHE.
 	 * CHANGES TO THIS STRUCT SHOULD BE CAPTURED IN THE STREAMING LOGIC OF THE PIPELINE CACHE.
 	 */
-	typedef struct SPIRVToMSLConverterContext {
-		SPIRVToMSLConverterOptions options;
+	typedef struct SPIRVToMSLConversionConfiguration {
+		SPIRVToMSLConversionOptions options;
 		std::vector<MSLVertexAttribute> vertexAttributes;
 		std::vector<MSLResourceBinding> resourceBindings;
 
@@ -140,17 +142,22 @@
 		void markAllAttributesAndResourcesUsed();
 
         /**
-         * Returns whether this context matches the other context. It does if the respective 
-         * options match and any vertex attributes and resource bindings used by this context
-         * can be found in the other context. Vertex attributes and resource bindings that are
-         * in the other context but are not used by the shader that created this context, are ignored.
+         * Returns whether this configuration matches the other context. It does if the
+		 * respective options match and any vertex attributes and resource bindings used
+		 * by this configuration can be found in the other configuration. Vertex attributes
+		 * and resource bindings that are in the other configuration but are not used by
+		 * the shader that created this configuration, are ignored.
          */
-        bool matches(const SPIRVToMSLConverterContext& other) const;
+        bool matches(const SPIRVToMSLConversionConfiguration& other) const;
 
-        /** Aligns certain aspects of this context with the source context. */
-        void alignWith(const SPIRVToMSLConverterContext& srcContext);
+        /** Aligns certain aspects of this configuration with the source context. */
+        void alignWith(const SPIRVToMSLConversionConfiguration& srcContext);
 
-	} SPIRVToMSLConverterContext;
+	} SPIRVToMSLConversionConfiguration;
+
+
+#pragma mark -
+#pragma mark SPIRVToMSLConversionResults
 
     /**
      * Describes one dimension of the workgroup size of a SPIR-V entry point, including whether
@@ -183,6 +190,25 @@
 		} workgroupSize;
 	} SPIRVEntryPoint;
 
+	/**
+	 * Contains the results of the shader conversion that can be used to populate a pipeline.
+	 *
+	 * THIS STRUCT IS STREAMED OUT AS PART OF THE PIEPLINE CACHE.
+	 * CHANGES TO THIS STRUCT SHOULD BE CAPTURED IN THE STREAMING LOGIC OF THE PIPELINE CACHE.
+	 */
+	typedef struct SPIRVToMSLConversionResults {
+		SPIRVEntryPoint entryPoint;
+		bool isRasterizationDisabled = false;
+		bool needsSwizzleBuffer = false;
+		bool needsOutputBuffer = false;
+		bool needsPatchOutputBuffer = false;
+		bool needsBufferSizeBuffer = false;
+		bool needsInputThreadgroupMem = false;
+
+		void reset() { *this = SPIRVToMSLConversionResults(); }
+
+	} SPIRVToMSLConversionResults;
+
 
 #pragma mark -
 #pragma mark SPIRVToMSLConverter
@@ -214,7 +240,7 @@
          * and optionally, the original GLSL (as converted from the SPIR_V), should be logged 
          * to the result log of this converter. This can be useful during shader debugging.
 		 */
-		bool convert(SPIRVToMSLConverterContext& context,
+		bool convert(SPIRVToMSLConversionConfiguration& context,
                      bool shouldLogSPIRV = false,
                      bool shouldLogMSL = false,
                      bool shouldLogGLSL = false);
@@ -232,14 +258,15 @@
 		 */
 		const std::string& getMSL() { return _msl; }
 
-        /** Returns information about the shader entry point. */
-        const SPIRVEntryPoint& getEntryPoint() { return _entryPoint; }
+		/** Returns information about the shader conversion. */
+		const SPIRVToMSLConversionResults& getConversionResults() { return _shaderConversionResults; }
 
         /** Sets the number of threads in a single compute kernel workgroup, per dimension. */
         void setWorkgroupSize(uint32_t x, uint32_t y, uint32_t z) {
-            _entryPoint.workgroupSize.width.size = x;
-            _entryPoint.workgroupSize.height.size = y;
-            _entryPoint.workgroupSize.depth.size = z;
+			auto& wgSize = _shaderConversionResults.entryPoint.workgroupSize;
+            wgSize.width.size = x;
+            wgSize.height.size = y;
+            wgSize.depth.size = z;
         }
         
 		/**
@@ -249,10 +276,10 @@
 		const std::string& getResultLog() { return _resultLog; }
 
         /** Sets MSL source code. This can be used when MSL is supplied directly. */
-        void setMSL(const std::string& msl, const SPIRVEntryPoint* pEntryPoint) {
-            _msl = msl;
-			if (pEntryPoint) { _entryPoint = *pEntryPoint; }
-        }
+		void setMSL(const std::string& msl, const SPIRVToMSLConversionResults* pShaderConversionResults) {
+			_msl = msl;
+			if (pShaderConversionResults) { _shaderConversionResults = *pShaderConversionResults; }
+		}
 
 	protected:
 		void logMsg(const char* logMsg);
@@ -261,11 +288,13 @@
 		bool validateSPIRV();
 		void writeSPIRVToFile(std::string spvFilepath);
         void logSource(std::string& src, const char* srcLang, const char* opDesc);
+		void populateWorkgroupDimension(SPIRVWorkgroupSizeDimension& wgDim, uint32_t size, SPIRV_CROSS_NAMESPACE::SpecializationConstant& spvSpecConst);
+		void populateEntryPoint(SPIRV_CROSS_NAMESPACE::Compiler* pCompiler, SPIRVToMSLConversionOptions& options);
 
 		std::vector<uint32_t> _spirv;
 		std::string _msl;
 		std::string _resultLog;
-		SPIRVEntryPoint _entryPoint;
+		SPIRVToMSLConversionResults _shaderConversionResults;
 		bool _wasConverted = false;
 	};
 
diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.cpp b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.cpp
index b95ab6b..b7c5552 100644
--- a/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.cpp
+++ b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.cpp
@@ -204,7 +204,7 @@
 	if ( !_shouldWriteMSL ) { return true; }
 
 	// Derive the context under which conversion will occur
-	SPIRVToMSLConverterContext mslContext;
+	SPIRVToMSLConversionConfiguration mslContext;
 	mslContext.options.mslOptions.platform = _mslPlatform;
 	mslContext.options.mslOptions.set_msl_version(_mslVersionMajor, _mslVersionMinor, _mslVersionPatch);
 	mslContext.options.shouldFlipVertexY = _shouldFlipVertexY;
@@ -387,7 +387,7 @@
 	_mslVersionMajor = 2;
 	_mslVersionMinor = 1;
 	_mslVersionPatch = 0;
-	_mslPlatform = SPIRVToMSLConverterOptions().mslOptions.platform;
+	_mslPlatform = SPIRVToMSLConversionOptions().mslOptions.platform;
 
 	_isActive = parseArgs(argc, argv);
 	if ( !_isActive ) { showUsage(); }