Use of MVKSmallVector between MVKPipeline and SPIRVReflection.
Make SPIRVReflection global functions inline and templated.
Remove SPIRVReflection.cpp.
Allow MVKSmallVector to be sorted by supporting
random access from MVKSmallVector::iterator.
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
index 78fff51..8dd5e2c 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
@@ -239,19 +239,21 @@
~MVKGraphicsPipeline() override;
protected:
+ typedef MVKSmallVector<SPIRVShaderOutput, 32> SPIRVShaderOutputs;
+
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(SPIRVToMSLConversionConfiguration& _shaderContext, const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData);
void addVertexInputToShaderConverterContext(SPIRVToMSLConversionConfiguration& shaderContext, const VkGraphicsPipelineCreateInfo* pCreateInfo);
- void addPrevStageOutputToShaderConverterContext(SPIRVToMSLConversionConfiguration& shaderContext, std::vector<SPIRVShaderOutput>& outputs);
+ void addPrevStageOutputToShaderConverterContext(SPIRVToMSLConversionConfiguration& shaderContext, SPIRVShaderOutputs& outputs);
MTLRenderPipelineDescriptor* newMTLRenderPipelineDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData);
MTLRenderPipelineDescriptor* newMTLTessVertexStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderContext);
MTLComputePipelineDescriptor* newMTLTessControlStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderContext);
MTLRenderPipelineDescriptor* newMTLTessRasterStageDescriptor(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 addTessCtlShaderToPipeline(MTLComputePipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderContext, SPIRVShaderOutputs& prevOutput);
+ bool addTessEvalShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderContext, SPIRVShaderOutputs& 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);
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
index 89d30be..781681a 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
@@ -606,9 +606,8 @@
SPIRVToMSLConversionConfiguration& shaderContext) {
MTLComputePipelineDescriptor* plDesc = [MTLComputePipelineDescriptor new]; // retained
- std::vector<SPIRVShaderOutput> vtxOutputs;
+ SPIRVShaderOutputs vtxOutputs;
std::string errorLog;
- // Unfortunately, MoltenVKShaderConverter doesn't know about MVKSmallVector, so we can't use that here.
if (!getShaderOutputs(((MVKShaderModule*)_pVertexSS->module)->getSPIRV(), spv::ExecutionModelVertex, _pVertexSS->pName, vtxOutputs, errorLog) ) {
setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Failed to get vertex outputs: %s", errorLog.c_str()));
return nil;
@@ -654,7 +653,7 @@
SPIRVToMSLConversionConfiguration& shaderContext) {
MTLRenderPipelineDescriptor* plDesc = [MTLRenderPipelineDescriptor new]; // retained
- std::vector<SPIRVShaderOutput> tcOutputs;
+ SPIRVShaderOutputs tcOutputs;
std::string errorLog;
if (!getShaderOutputs(((MVKShaderModule*)_pTessCtlSS->module)->getSPIRV(), spv::ExecutionModelTessellationControl, _pTessCtlSS->pName, tcOutputs, errorLog) ) {
setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Failed to get tessellation control outputs: %s", errorLog.c_str()));
@@ -823,7 +822,7 @@
bool MVKGraphicsPipeline::addTessCtlShaderToPipeline(MTLComputePipelineDescriptor* plDesc,
const VkGraphicsPipelineCreateInfo* pCreateInfo,
SPIRVToMSLConversionConfiguration& shaderContext,
- std::vector<SPIRVShaderOutput>& vtxOutputs) {
+ SPIRVShaderOutputs& vtxOutputs) {
shaderContext.options.entryPointStage = spv::ExecutionModelTessellationControl;
shaderContext.options.entryPointName = _pTessCtlSS->pName;
shaderContext.options.mslOptions.swizzle_buffer_index = _swizzleBufferIndex.stages[kMVKShaderStageTessCtl];
@@ -876,7 +875,7 @@
bool MVKGraphicsPipeline::addTessEvalShaderToPipeline(MTLRenderPipelineDescriptor* plDesc,
const VkGraphicsPipelineCreateInfo* pCreateInfo,
SPIRVToMSLConversionConfiguration& shaderContext,
- std::vector<SPIRVShaderOutput>& tcOutputs) {
+ SPIRVShaderOutputs& tcOutputs) {
shaderContext.options.entryPointStage = spv::ExecutionModelTessellationEvaluation;
shaderContext.options.entryPointName = _pTessEvalSS->pName;
shaderContext.options.mslOptions.swizzle_buffer_index = _swizzleBufferIndex.stages[kMVKShaderStageTessEval];
@@ -1241,7 +1240,7 @@
// Initializes the vertex attributes in a shader converter context from the previous stage output.
void MVKGraphicsPipeline::addPrevStageOutputToShaderConverterContext(SPIRVToMSLConversionConfiguration& shaderContext,
- std::vector<SPIRVShaderOutput>& shaderOutputs) {
+ SPIRVShaderOutputs& shaderOutputs) {
// Set the shader context vertex attribute information
shaderContext.vertexAttributes.clear();
uint32_t vaCnt = (uint32_t)shaderOutputs.size();
diff --git a/MoltenVK/MoltenVK/Utility/MVKSmallVector.h b/MoltenVK/MoltenVK/Utility/MVKSmallVector.h
index 39dd099..1d1612b 100755
--- a/MoltenVK/MoltenVK/Utility/MVKSmallVector.h
+++ b/MoltenVK/MoltenVK/Utility/MVKSmallVector.h
@@ -62,13 +62,15 @@
Allocator alc;
public:
- class iterator : public std::iterator<std::forward_iterator_tag, Type>
+ class iterator : public std::iterator<std::random_access_iterator_tag, Type>
{
const MVKSmallVectorImpl *vector;
size_t index;
public:
- iterator() = delete;
+ typedef typename std::iterator_traits<iterator>::difference_type diff_type;
+
+ iterator() : vector{ nullptr }, index{ 0 } { }
iterator( const size_t _index, const MVKSmallVectorImpl &_vector ) : vector{ &_vector }, index{ _index } { }
iterator &operator=( const iterator &it )
@@ -87,6 +89,23 @@
iterator& operator++() { ++index; return *this; }
iterator operator++( int ) { auto t = *this; ++index; return t; }
+ iterator& operator--() { --index; return *this; }
+ iterator operator--( int ) { auto t = *this; --index; return t; }
+
+ iterator operator+ (const diff_type n) { return iterator( index + n, *vector ); }
+ iterator& operator+= (const diff_type n) { index += n; return *this; }
+ iterator operator- (const diff_type n) { return iterator( index - n, *vector ); }
+ iterator& operator-= (const diff_type n) { index -= n; return *this; }
+
+ diff_type operator- (const iterator& it) { return index - it.index; }
+
+ bool operator< (const iterator& it) { return index < it.index; }
+ bool operator<= (const iterator& it) { return index <= it.index; }
+ bool operator> (const iterator& it) { return index > it.index; }
+ bool operator>= (const iterator& it) { return index >= it.index; }
+
+ const Type &operator[]( const diff_type i ) const { return vector->alc.ptr[index + i]; }
+ Type &operator[]( const diff_type i ) { return vector->alc.ptr[index + i]; }
bool is_valid() const { return index < vector->alc.size(); }
size_t get_position() const { return index; }
@@ -497,13 +516,15 @@
Allocator alc;
public:
- class iterator : public std::iterator<std::forward_iterator_tag, Type*>
+ class iterator : public std::iterator<std::random_access_iterator_tag, Type*>
{
MVKSmallVectorImpl *vector;
size_t index;
public:
- iterator() = delete;
+ typedef typename std::iterator_traits<iterator>::difference_type diff_type;
+
+ iterator() : vector{ nullptr }, index{ 0 } { }
iterator( const size_t _index, MVKSmallVectorImpl &_vector ) : vector{ &_vector }, index{ _index } { }
iterator &operator=( const iterator &it )
@@ -518,8 +539,25 @@
bool operator==( const iterator &it ) const { return vector == it.vector && index == it.index; }
bool operator!=( const iterator &it ) const { return vector != it.vector || index != it.index; }
- iterator& operator++() { ++index; return *this; }
+ iterator& operator++() { ++index; return *this; }
iterator operator++( int ) { auto t = *this; ++index; return t; }
+ iterator& operator--() { --index; return *this; }
+ iterator operator--( int ) { auto t = *this; --index; return t; }
+
+ iterator operator+ (const diff_type n) { return iterator( index + n, *vector ); }
+ iterator& operator+= (const diff_type n) { index += n; return *this; }
+ iterator operator- (const diff_type n) { return iterator( index - n, *vector ); }
+ iterator& operator-= (const diff_type n) { index -= n; return *this; }
+
+ diff_type operator- (const iterator& it) { return index - it.index; }
+
+ bool operator< (const iterator& it) { return index < it.index; }
+ bool operator<= (const iterator& it) { return index <= it.index; }
+ bool operator> (const iterator& it) { return index > it.index; }
+ bool operator>= (const iterator& it) { return index >= it.index; }
+
+ const Type &operator[]( const diff_type i ) const { return vector->alc.ptr[index + i]; }
+ Type &operator[]( const diff_type i ) { return vector->alc.ptr[index + i]; }
bool is_valid() const { return index < vector->alc.size(); }
size_t get_position() const { return index; }
diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVReflection.cpp b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVReflection.cpp
deleted file mode 100644
index 213b3ad..0000000
--- a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVReflection.cpp
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * SPIRVReflection.cpp
- *
- * Copyright (c) 2019-2020 Chip Davis for Codeweavers
- *
- * 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 "SPIRVReflection.h"
-#include <SPIRV-Cross/spirv_parser.hpp>
-#include <SPIRV-Cross/spirv_reflect.hpp>
-
-namespace mvk {
-
-static const char missingPatchInputErr[] = "Neither tessellation shader specifies a patch input mode (Triangles, Quads, or Isolines).";
-static const char missingWindingErr[] = "Neither tessellation shader specifies a winding order mode (VertexOrderCw or VertexOrderCcw).";
-static const char missingPartitionErr[] = "Neither tessellation shader specifies a partition mode (SpacingEqual, SpacingFractionalOdd, or SpacingFractionalEven).";
-static const char missingOutputVerticesErr[] = "Neither tessellation shader specifies the number of output control points.";
-
-/** Given a tessellation control shader and a tessellation evaluation shader, both in SPIR-V format, returns tessellation reflection data. */
-bool getTessReflectionData(const std::vector<uint32_t>& tesc, const std::string& tescEntryName, const std::vector<uint32_t>& tese, const std::string& teseEntryName, SPIRVTessReflectionData& reflectData, std::string& errorLog) {
-#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
- try {
-#endif
- SPIRV_CROSS_NAMESPACE::CompilerReflection tescReflect(tesc);
- SPIRV_CROSS_NAMESPACE::CompilerReflection teseReflect(tese);
-
- if (!tescEntryName.empty()) {
- tescReflect.set_entry_point(tescEntryName, spv::ExecutionModelTessellationControl);
- }
- if (!teseEntryName.empty()) {
- teseReflect.set_entry_point(teseEntryName, spv::ExecutionModelTessellationEvaluation);
- }
-
- tescReflect.compile();
- teseReflect.compile();
-
- const SPIRV_CROSS_NAMESPACE::Bitset& tescModes = tescReflect.get_execution_mode_bitset();
- const SPIRV_CROSS_NAMESPACE::Bitset& teseModes = teseReflect.get_execution_mode_bitset();
-
- // Extract the parameters from the shaders.
- if (tescModes.get(spv::ExecutionModeTriangles)) {
- reflectData.patchKind = spv::ExecutionModeTriangles;
- } else if (tescModes.get(spv::ExecutionModeQuads)) {
- reflectData.patchKind = spv::ExecutionModeQuads;
- } else if (tescModes.get(spv::ExecutionModeIsolines)) {
- reflectData.patchKind = spv::ExecutionModeIsolines;
- } else if (teseModes.get(spv::ExecutionModeTriangles)) {
- reflectData.patchKind = spv::ExecutionModeTriangles;
- } else if (teseModes.get(spv::ExecutionModeQuads)) {
- reflectData.patchKind = spv::ExecutionModeQuads;
- } else if (teseModes.get(spv::ExecutionModeIsolines)) {
- reflectData.patchKind = spv::ExecutionModeIsolines;
- } else {
- errorLog = missingPatchInputErr;
- return false;
- }
-
- if (tescModes.get(spv::ExecutionModeVertexOrderCw)) {
- reflectData.windingOrder = spv::ExecutionModeVertexOrderCw;
- } else if (tescModes.get(spv::ExecutionModeVertexOrderCcw)) {
- reflectData.windingOrder = spv::ExecutionModeVertexOrderCcw;
- } else if (teseModes.get(spv::ExecutionModeVertexOrderCw)) {
- reflectData.windingOrder = spv::ExecutionModeVertexOrderCw;
- } else if (teseModes.get(spv::ExecutionModeVertexOrderCcw)) {
- reflectData.windingOrder = spv::ExecutionModeVertexOrderCcw;
- } else {
- errorLog = missingWindingErr;
- return false;
- }
-
- reflectData.pointMode = tescModes.get(spv::ExecutionModePointMode) || teseModes.get(spv::ExecutionModePointMode);
-
- if (tescModes.get(spv::ExecutionModeSpacingEqual)) {
- reflectData.partitionMode = spv::ExecutionModeSpacingEqual;
- } else if (tescModes.get(spv::ExecutionModeSpacingFractionalEven)) {
- reflectData.partitionMode = spv::ExecutionModeSpacingFractionalEven;
- } else if (tescModes.get(spv::ExecutionModeSpacingFractionalOdd)) {
- reflectData.partitionMode = spv::ExecutionModeSpacingFractionalOdd;
- } else if (teseModes.get(spv::ExecutionModeSpacingEqual)) {
- reflectData.partitionMode = spv::ExecutionModeSpacingEqual;
- } else if (teseModes.get(spv::ExecutionModeSpacingFractionalEven)) {
- reflectData.partitionMode = spv::ExecutionModeSpacingFractionalEven;
- } else if (teseModes.get(spv::ExecutionModeSpacingFractionalOdd)) {
- reflectData.partitionMode = spv::ExecutionModeSpacingFractionalOdd;
- } else {
- errorLog = missingPartitionErr;
- return false;
- }
-
- if (tescModes.get(spv::ExecutionModeOutputVertices)) {
- reflectData.numControlPoints = tescReflect.get_execution_mode_argument(spv::ExecutionModeOutputVertices);
- } else if (teseModes.get(spv::ExecutionModeOutputVertices)) {
- reflectData.numControlPoints = teseReflect.get_execution_mode_argument(spv::ExecutionModeOutputVertices);
- } else {
- errorLog = missingOutputVerticesErr;
- return false;
- }
-
- return true;
-
-#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
- } catch (SPIRV_CROSS_NAMESPACE::CompilerError& ex) {
- errorLog = ex.what();
- return false;
- }
-#endif
-}
-
-/** Given a shader in SPIR-V format, returns output reflection data. */
-bool getShaderOutputs(const std::vector<uint32_t>& spirv, spv::ExecutionModel model, const std::string& entryName, std::vector<SPIRVShaderOutput>& outputs, std::string& errorLog) {
-#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
- try {
-#endif
- SPIRV_CROSS_NAMESPACE::Parser parser(spirv);
- parser.parse();
- SPIRV_CROSS_NAMESPACE::CompilerReflection reflect(parser.get_parsed_ir());
- if (!entryName.empty()) {
- reflect.set_entry_point(entryName, model);
- }
- reflect.compile();
- reflect.update_active_builtins();
-
- outputs.clear();
-
- auto addSat = [](uint32_t a, uint32_t b) { return a == uint32_t(-1) ? a : a + b; };
- parser.get_parsed_ir().for_each_typed_id<SPIRV_CROSS_NAMESPACE::SPIRVariable>([&reflect, &outputs, model, addSat](uint32_t varID, const SPIRV_CROSS_NAMESPACE::SPIRVariable& var) {
- if (var.storage != spv::StorageClassOutput) { return; }
-
- bool isUsed = true;
- const auto* type = &reflect.get_type(reflect.get_type_from_variable(varID).parent_type);
- bool patch = reflect.has_decoration(varID, spv::DecorationPatch);
- auto biType = spv::BuiltInMax;
- if (reflect.has_decoration(varID, spv::DecorationBuiltIn)) {
- biType = (spv::BuiltIn)reflect.get_decoration(varID, spv::DecorationBuiltIn);
- isUsed = reflect.has_active_builtin(biType, var.storage);
- }
- uint32_t loc = -1;
- if (reflect.has_decoration(varID, spv::DecorationLocation)) {
- loc = reflect.get_decoration(varID, spv::DecorationLocation);
- }
- if (model == spv::ExecutionModelTessellationControl && !patch)
- type = &reflect.get_type(type->parent_type);
-
- if (type->basetype == SPIRV_CROSS_NAMESPACE::SPIRType::Struct) {
- for (uint32_t i = 0; i < type->member_types.size(); i++) {
- // Each member may have a location decoration. If not, each member
- // gets an incrementing location.
- uint32_t memberLoc = addSat(loc, i);
- if (reflect.has_member_decoration(type->self, i, spv::DecorationLocation)) {
- memberLoc = reflect.get_member_decoration(type->self, i, spv::DecorationLocation);
- }
- patch = reflect.has_member_decoration(type->self, i, spv::DecorationPatch);
- if (reflect.has_member_decoration(type->self, i, spv::DecorationBuiltIn)) {
- biType = (spv::BuiltIn)reflect.get_member_decoration(type->self, i, spv::DecorationBuiltIn);
- isUsed = reflect.has_active_builtin(biType, var.storage);
- }
- const SPIRV_CROSS_NAMESPACE::SPIRType& memberType = reflect.get_type(type->member_types[i]);
- if (memberType.columns > 1) {
- for (uint32_t i = 0; i < memberType.columns; i++) {
- outputs.push_back({memberType.basetype, memberType.vecsize, addSat(memberLoc, i), biType, patch, isUsed});
- }
- } else if (!memberType.array.empty()) {
- for (uint32_t i = 0; i < memberType.array[0]; i++) {
- outputs.push_back({memberType.basetype, memberType.vecsize, addSat(memberLoc, i), biType, patch, isUsed});
- }
- } else {
- outputs.push_back({memberType.basetype, memberType.vecsize, memberLoc, biType, patch, isUsed});
- }
- }
- } else if (type->columns > 1) {
- for (uint32_t i = 0; i < type->columns; i++) {
- outputs.push_back({type->basetype, type->vecsize, addSat(loc, i), biType, patch, isUsed});
- }
- } else if (!type->array.empty()) {
- for (uint32_t i = 0; i < type->array[0]; i++) {
- outputs.push_back({type->basetype, type->vecsize, addSat(loc, i), biType, patch, isUsed});
- }
- } else {
- outputs.push_back({type->basetype, type->vecsize, loc, biType, patch, isUsed});
- }
- });
- // Sort outputs by ascending location.
- std::stable_sort(outputs.begin(), outputs.end(), [](const SPIRVShaderOutput& a, const SPIRVShaderOutput& b) {
- return a.location < b.location;
- });
- // Assign locations to outputs that don't have one.
- uint32_t loc = -1;
- for (SPIRVShaderOutput& out : outputs) {
- if (out.location == uint32_t(-1)) { out.location = loc + 1; }
- loc = out.location;
- }
- return true;
-#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
- } catch (SPIRV_CROSS_NAMESPACE::CompilerError& ex) {
- errorLog = ex.what();
- return false;
- }
-#endif
-}
-
-}
diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVReflection.h b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVReflection.h
index 30d4dbf..b17fe79 100644
--- a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVReflection.h
+++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVReflection.h
@@ -21,6 +21,8 @@
#include <SPIRV-Cross/spirv.hpp>
#include <SPIRV-Cross/spirv_common.hpp>
+#include <SPIRV-Cross/spirv_parser.hpp>
+#include <SPIRV-Cross/spirv_reflect.hpp>
#include <string>
#include <vector>
@@ -75,10 +77,191 @@
#pragma mark Functions
/** Given a tessellation control shader and a tessellation evaluation shader, both in SPIR-V format, returns tessellation reflection data. */
- bool getTessReflectionData(const std::vector<uint32_t>& tesc, const std::string& tescEntryName, const std::vector<uint32_t>& tese, const std::string& teseEntryName, SPIRVTessReflectionData& reflectData, std::string& errorLog);
+ template<typename Vs>
+ static inline bool getTessReflectionData(const Vs& tesc, const std::string& tescEntryName,
+ const Vs& tese, const std::string& teseEntryName,
+ SPIRVTessReflectionData& reflectData, std::string& errorLog) {
+#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
+ try {
+#endif
+ SPIRV_CROSS_NAMESPACE::CompilerReflection tescReflect(tesc);
+ SPIRV_CROSS_NAMESPACE::CompilerReflection teseReflect(tese);
+
+ if (!tescEntryName.empty()) {
+ tescReflect.set_entry_point(tescEntryName, spv::ExecutionModelTessellationControl);
+ }
+ if (!teseEntryName.empty()) {
+ teseReflect.set_entry_point(teseEntryName, spv::ExecutionModelTessellationEvaluation);
+ }
+
+ tescReflect.compile();
+ teseReflect.compile();
+
+ const SPIRV_CROSS_NAMESPACE::Bitset& tescModes = tescReflect.get_execution_mode_bitset();
+ const SPIRV_CROSS_NAMESPACE::Bitset& teseModes = teseReflect.get_execution_mode_bitset();
+
+ // Extract the parameters from the shaders.
+ if (tescModes.get(spv::ExecutionModeTriangles)) {
+ reflectData.patchKind = spv::ExecutionModeTriangles;
+ } else if (tescModes.get(spv::ExecutionModeQuads)) {
+ reflectData.patchKind = spv::ExecutionModeQuads;
+ } else if (tescModes.get(spv::ExecutionModeIsolines)) {
+ reflectData.patchKind = spv::ExecutionModeIsolines;
+ } else if (teseModes.get(spv::ExecutionModeTriangles)) {
+ reflectData.patchKind = spv::ExecutionModeTriangles;
+ } else if (teseModes.get(spv::ExecutionModeQuads)) {
+ reflectData.patchKind = spv::ExecutionModeQuads;
+ } else if (teseModes.get(spv::ExecutionModeIsolines)) {
+ reflectData.patchKind = spv::ExecutionModeIsolines;
+ } else {
+ errorLog = "Neither tessellation shader specifies a patch input mode (Triangles, Quads, or Isolines).";
+ return false;
+ }
+
+ if (tescModes.get(spv::ExecutionModeVertexOrderCw)) {
+ reflectData.windingOrder = spv::ExecutionModeVertexOrderCw;
+ } else if (tescModes.get(spv::ExecutionModeVertexOrderCcw)) {
+ reflectData.windingOrder = spv::ExecutionModeVertexOrderCcw;
+ } else if (teseModes.get(spv::ExecutionModeVertexOrderCw)) {
+ reflectData.windingOrder = spv::ExecutionModeVertexOrderCw;
+ } else if (teseModes.get(spv::ExecutionModeVertexOrderCcw)) {
+ reflectData.windingOrder = spv::ExecutionModeVertexOrderCcw;
+ } else {
+ errorLog = "Neither tessellation shader specifies a winding order mode (VertexOrderCw or VertexOrderCcw).";
+ return false;
+ }
+
+ reflectData.pointMode = tescModes.get(spv::ExecutionModePointMode) || teseModes.get(spv::ExecutionModePointMode);
+
+ if (tescModes.get(spv::ExecutionModeSpacingEqual)) {
+ reflectData.partitionMode = spv::ExecutionModeSpacingEqual;
+ } else if (tescModes.get(spv::ExecutionModeSpacingFractionalEven)) {
+ reflectData.partitionMode = spv::ExecutionModeSpacingFractionalEven;
+ } else if (tescModes.get(spv::ExecutionModeSpacingFractionalOdd)) {
+ reflectData.partitionMode = spv::ExecutionModeSpacingFractionalOdd;
+ } else if (teseModes.get(spv::ExecutionModeSpacingEqual)) {
+ reflectData.partitionMode = spv::ExecutionModeSpacingEqual;
+ } else if (teseModes.get(spv::ExecutionModeSpacingFractionalEven)) {
+ reflectData.partitionMode = spv::ExecutionModeSpacingFractionalEven;
+ } else if (teseModes.get(spv::ExecutionModeSpacingFractionalOdd)) {
+ reflectData.partitionMode = spv::ExecutionModeSpacingFractionalOdd;
+ } else {
+ errorLog = "Neither tessellation shader specifies a partition mode (SpacingEqual, SpacingFractionalOdd, or SpacingFractionalEven).";
+ return false;
+ }
+
+ if (tescModes.get(spv::ExecutionModeOutputVertices)) {
+ reflectData.numControlPoints = tescReflect.get_execution_mode_argument(spv::ExecutionModeOutputVertices);
+ } else if (teseModes.get(spv::ExecutionModeOutputVertices)) {
+ reflectData.numControlPoints = teseReflect.get_execution_mode_argument(spv::ExecutionModeOutputVertices);
+ } else {
+ errorLog = "Neither tessellation shader specifies the number of output control points.";
+ return false;
+ }
+
+ return true;
+
+#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
+ } catch (SPIRV_CROSS_NAMESPACE::CompilerError& ex) {
+ errorLog = ex.what();
+ return false;
+ }
+#endif
+ }
/** Given a shader in SPIR-V format, returns output reflection data. */
- bool getShaderOutputs(const std::vector<uint32_t>& spirv, spv::ExecutionModel model, const std::string& entryName, std::vector<SPIRVShaderOutput>& outputs, std::string& errorLog);
+ template<typename Vs, typename Vo>
+ static inline bool getShaderOutputs(const Vs& spirv, spv::ExecutionModel model, const std::string& entryName,
+ Vo& outputs, std::string& errorLog) {
+#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
+ try {
+#endif
+ SPIRV_CROSS_NAMESPACE::Parser parser(spirv);
+ parser.parse();
+ SPIRV_CROSS_NAMESPACE::CompilerReflection reflect(parser.get_parsed_ir());
+ if (!entryName.empty()) {
+ reflect.set_entry_point(entryName, model);
+ }
+ reflect.compile();
+ reflect.update_active_builtins();
+
+ outputs.clear();
+
+ auto addSat = [](uint32_t a, uint32_t b) { return a == uint32_t(-1) ? a : a + b; };
+ parser.get_parsed_ir().for_each_typed_id<SPIRV_CROSS_NAMESPACE::SPIRVariable>([&reflect, &outputs, model, addSat](uint32_t varID, const SPIRV_CROSS_NAMESPACE::SPIRVariable& var) {
+ if (var.storage != spv::StorageClassOutput) { return; }
+
+ bool isUsed = true;
+ const auto* type = &reflect.get_type(reflect.get_type_from_variable(varID).parent_type);
+ bool patch = reflect.has_decoration(varID, spv::DecorationPatch);
+ auto biType = spv::BuiltInMax;
+ if (reflect.has_decoration(varID, spv::DecorationBuiltIn)) {
+ biType = (spv::BuiltIn)reflect.get_decoration(varID, spv::DecorationBuiltIn);
+ isUsed = reflect.has_active_builtin(biType, var.storage);
+ }
+ uint32_t loc = -1;
+ if (reflect.has_decoration(varID, spv::DecorationLocation)) {
+ loc = reflect.get_decoration(varID, spv::DecorationLocation);
+ }
+ if (model == spv::ExecutionModelTessellationControl && !patch)
+ type = &reflect.get_type(type->parent_type);
+
+ if (type->basetype == SPIRV_CROSS_NAMESPACE::SPIRType::Struct) {
+ for (uint32_t idx = 0; idx < type->member_types.size(); idx++) {
+ // Each member may have a location decoration. If not, each member
+ // gets an incrementing location.
+ uint32_t memberLoc = addSat(loc, idx);
+ if (reflect.has_member_decoration(type->self, idx, spv::DecorationLocation)) {
+ memberLoc = reflect.get_member_decoration(type->self, idx, spv::DecorationLocation);
+ }
+ patch = reflect.has_member_decoration(type->self, idx, spv::DecorationPatch);
+ if (reflect.has_member_decoration(type->self, idx, spv::DecorationBuiltIn)) {
+ biType = (spv::BuiltIn)reflect.get_member_decoration(type->self, idx, spv::DecorationBuiltIn);
+ isUsed = reflect.has_active_builtin(biType, var.storage);
+ }
+ const SPIRV_CROSS_NAMESPACE::SPIRType& memberType = reflect.get_type(type->member_types[idx]);
+ if (memberType.columns > 1) {
+ for (uint32_t i = 0; i < memberType.columns; i++) {
+ outputs.push_back({memberType.basetype, memberType.vecsize, addSat(memberLoc, i), biType, patch, isUsed});
+ }
+ } else if (!memberType.array.empty()) {
+ for (uint32_t i = 0; i < memberType.array[0]; i++) {
+ outputs.push_back({memberType.basetype, memberType.vecsize, addSat(memberLoc, i), biType, patch, isUsed});
+ }
+ } else {
+ outputs.push_back({memberType.basetype, memberType.vecsize, memberLoc, biType, patch, isUsed});
+ }
+ }
+ } else if (type->columns > 1) {
+ for (uint32_t i = 0; i < type->columns; i++) {
+ outputs.push_back({type->basetype, type->vecsize, addSat(loc, i), biType, patch, isUsed});
+ }
+ } else if (!type->array.empty()) {
+ for (uint32_t i = 0; i < type->array[0]; i++) {
+ outputs.push_back({type->basetype, type->vecsize, addSat(loc, i), biType, patch, isUsed});
+ }
+ } else {
+ outputs.push_back({type->basetype, type->vecsize, loc, biType, patch, isUsed});
+ }
+ });
+ // Sort outputs by ascending location.
+ std::stable_sort(outputs.begin(), outputs.end(), [](const SPIRVShaderOutput& a, const SPIRVShaderOutput& b) {
+ return a.location < b.location;
+ });
+ // Assign locations to outputs that don't have one.
+ uint32_t loc = -1;
+ for (SPIRVShaderOutput& out : outputs) {
+ if (out.location == uint32_t(-1)) { out.location = loc + 1; }
+ loc = out.location;
+ }
+ return true;
+#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
+ } catch (SPIRV_CROSS_NAMESPACE::CompilerError& ex) {
+ errorLog = ex.what();
+ return false;
+ }
+#endif
+ }
}
#endif
diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj
index e19c797..4de848f 100644
--- a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj
+++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj
@@ -7,8 +7,6 @@
objects = {
/* Begin PBXBuildFile section */
- 450A4F5F220CB180007203D7 /* SPIRVReflection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 450A4F5D220CB180007203D7 /* SPIRVReflection.cpp */; };
- 450A4F60220CB180007203D7 /* SPIRVReflection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 450A4F5D220CB180007203D7 /* SPIRVReflection.cpp */; };
450A4F61220CB180007203D7 /* SPIRVReflection.h in Headers */ = {isa = PBXBuildFile; fileRef = 450A4F5E220CB180007203D7 /* SPIRVReflection.h */; };
450A4F62220CB180007203D7 /* SPIRVReflection.h in Headers */ = {isa = PBXBuildFile; fileRef = 450A4F5E220CB180007203D7 /* SPIRVReflection.h */; };
A909408A1C58013E0094110D /* SPIRVToMSLConverter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9093F5A1C58013E0094110D /* SPIRVToMSLConverter.cpp */; };
@@ -81,7 +79,6 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
- 450A4F5D220CB180007203D7 /* SPIRVReflection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SPIRVReflection.cpp; sourceTree = "<group>"; };
450A4F5E220CB180007203D7 /* SPIRVReflection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPIRVReflection.h; sourceTree = "<group>"; };
A9093F5A1C58013E0094110D /* SPIRVToMSLConverter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SPIRVToMSLConverter.cpp; sourceTree = "<group>"; };
A9093F5B1C58013E0094110D /* SPIRVToMSLConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPIRVToMSLConverter.h; sourceTree = "<group>"; };
@@ -175,7 +172,6 @@
A925B70A1C7754B2006E7ECD /* FileSupport.mm */,
A928C9171D0488DC00071B88 /* SPIRVConversion.h */,
A928C9181D0488DC00071B88 /* SPIRVConversion.mm */,
- 450A4F5D220CB180007203D7 /* SPIRVReflection.cpp */,
450A4F5E220CB180007203D7 /* SPIRVReflection.h */,
A9093F5A1C58013E0094110D /* SPIRVToMSLConverter.cpp */,
A9093F5B1C58013E0094110D /* SPIRVToMSLConverter.h */,
@@ -648,7 +644,6 @@
A909408A1C58013E0094110D /* SPIRVToMSLConverter.cpp in Sources */,
A9C70F66221B321700FBA31A /* SPIRVSupport.cpp in Sources */,
A928C91B1D0488DC00071B88 /* SPIRVConversion.mm in Sources */,
- 450A4F5F220CB180007203D7 /* SPIRVReflection.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -660,7 +655,6 @@
A909408B1C58013E0094110D /* SPIRVToMSLConverter.cpp in Sources */,
A9C70F67221B321700FBA31A /* SPIRVSupport.cpp in Sources */,
A928C91C1D0488DC00071B88 /* SPIRVConversion.mm in Sources */,
- 450A4F60220CB180007203D7 /* SPIRVReflection.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};