diff --git a/include/vsg/core/compare.h b/include/vsg/core/compare.h index 8f2dc74a65..9e748c2e69 100644 --- a/include/vsg/core/compare.h +++ b/include/vsg/core/compare.h @@ -102,6 +102,24 @@ namespace vsg return 0; } + template + int compare_map(const T& lhs, const T& rhs) + { + if (lhs.size() < rhs.size()) return -1; + if (lhs.size() > rhs.size()) return 1; + if (lhs.empty()) return 0; + + auto rhs_itr = rhs.begin(); + for(auto lhs_itr = lhs.begin(); lhs_itr != lhs.end(); ++lhs_itr) + { + if (lhs_itr->first < rhs_itr->first) return -1; + if (rhs_itr->first < lhs_itr->first) return 1; + if (lhs_itr->second < rhs_itr->second) return -1; + if (rhs_itr->second < lhs_itr->second) return 1; + } + return 0; + } + /// less functor for comparing ref_ptr typically used with std::set<> etc. struct DereferenceLess { diff --git a/include/vsg/io/Input.h b/include/vsg/io/Input.h index 427021a6e8..a776557510 100644 --- a/include/vsg/io/Input.h +++ b/include/vsg/io/Input.h @@ -177,6 +177,8 @@ namespace vsg uint32_t numElements = 0; read(1, &numElements); + values.clear(); + for (uint32_t i = 0; i < numElements; ++i) { T v; @@ -185,6 +187,25 @@ namespace vsg } } + template + void readValues(const char* propertyName, std::map& values) + { + if (!matchPropertyName(propertyName)) return; + + uint32_t numElements = 0; + read(1, &numElements); + + values.clear(); + + for (uint32_t i = 0; i < numElements; ++i) + { + K k; + V v; + read("element", k, v); + values[k] = v; + } + } + /// match property name and read value(s) template void read(const char* propertyName, Args&... args) diff --git a/include/vsg/io/Output.h b/include/vsg/io/Output.h index cc547e4d52..48468a7836 100644 --- a/include/vsg/io/Output.h +++ b/include/vsg/io/Output.h @@ -175,6 +175,19 @@ namespace vsg } } + + template + void writeValues(const char* propertyName, const std::map& values) + { + uint32_t numElements = static_cast(values.size()); + write(propertyName, numElements); + + for (const auto& [k, v] : values) + { + write("element", k, v); + } + } + /// match propertyname and write value(s) template void write(const char* propertyName, Args&... args) diff --git a/include/vsg/state/ShaderModule.h b/include/vsg/state/ShaderModule.h index a57ff2e295..ed6f6b8c50 100644 --- a/include/vsg/state/ShaderModule.h +++ b/include/vsg/state/ShaderModule.h @@ -22,6 +22,59 @@ namespace vsg // forward declare class Context; + + /// Inputs to #pragma(tic) shader composition + class DefineMap + { + public: + using Map = std::map; + using iterator = Map::iterator; + using const_iterator = Map::const_iterator; + + DefineMap() {} + DefineMap(const DefineMap& rhs) : values(rhs.values) {} + + Map values; + + iterator begin() { return values.begin(); } + iterator end() { return values.end(); } + + const_iterator begin() const { return values.begin(); } + const_iterator end() const { return values.end(); } + + void insert(const std::string& str) { values[str] = ""; } + void insert(const std::string& str, const std::string& value) { values[str] = value; } + + void clear() { values.clear(); } + bool empty() const { return values.empty(); } + Map::size_type size() const { return values.size(); } + + bool operator < (const DefineMap& rhs) const { return values < rhs.values; } + bool operator == (const DefineMap& rhs) const { return values == rhs.values; } + + Map::size_type count(const std::string& key) const { return values.count(key); } + + DefineMap& operator = (const DefineMap& rhs) + { + values = rhs.values; + return *this; + } + + DefineMap& operator = (const std::set& rhs) + { + values.clear(); + for(auto& value : rhs) insert(value); + return *this; + } + + std::set keys() const + { + std::set key_values; + for(auto itr = values.begin(); itr != values.end(); ++itr) key_values.insert(itr->first); + return key_values; + } + }; + /// Settings passed to glsLang when compiling GLSL/HLSL shader source to SPIR-V /// Provides the values to pass to glsLang::TShader::setEnvInput, setEnvClient and setEnvTarget. class VSG_DECLSPEC ShaderCompileSettings : public Inherit @@ -55,7 +108,7 @@ namespace vsg bool generateDebugInfo = false; // maps to SpvOptions::generateDebugInfo bool optimize = false; - std::set defines; + DefineMap defines; public: ref_ptr clone(const CopyOp& copyop = {}) const override { return ShaderCompileSettings::create(*this, copyop); } diff --git a/include/vsg/utils/ShaderCompiler.h b/include/vsg/utils/ShaderCompiler.h index d418011c8f..dccf5eca64 100644 --- a/include/vsg/utils/ShaderCompiler.h +++ b/include/vsg/utils/ShaderCompiler.h @@ -28,7 +28,7 @@ namespace vsg bool compile(ShaderStages& shaders, const std::vector& defines = {}, ref_ptr options = {}); bool compile(ref_ptr shaderStage, const std::vector& defines = {}, ref_ptr options = {}); - std::string combineSourceAndDefines(const std::string& source, const std::vector& defines); + std::string combineSourceAndDefines(const std::string& source, const std::map& defines); void apply(Node& node) override; void apply(BindGraphicsPipeline& bgp) override; diff --git a/include/vsg/utils/ShaderSet.h b/include/vsg/utils/ShaderSet.h index e3bf15ac4d..60280a9675 100644 --- a/include/vsg/utils/ShaderSet.h +++ b/include/vsg/utils/ShaderSet.h @@ -17,6 +17,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include +#include #include namespace vsg @@ -67,7 +68,7 @@ namespace vsg struct VSG_DECLSPEC DefinesArrayState { - std::set defines; + DefineMap defines; ref_ptr arrayState; int compare(const DefinesArrayState& rhs) const; @@ -124,7 +125,7 @@ namespace vsg std::vector descriptorBindings; std::vector pushConstantRanges; std::vector definesArrayStates; // put more constrained ArrayState matches first so they are matched first. - std::set optionalDefines; + DefineMap optionalDefines; GraphicsPipelineStates defaultGraphicsPipelineStates; std::vector> customDescriptorSetBindings; @@ -163,7 +164,7 @@ namespace vsg [[deprecated("use getDescriptorBinding(..)")]] const DescriptorBinding& getUnifomrBinding(const std::string& name) const { return getDescriptorBinding(name); } /// get the first ArrayState that has matches with defines in the specified list of defines. - ref_ptr getSuitableArrayState(const std::set& defines) const; + ref_ptr getSuitableArrayState(const DefineMap& defines) const; /// get the ShaderStages variant that uses specified ShaderCompileSettings. ShaderStages getShaderStages(ref_ptr scs = {}); @@ -172,23 +173,23 @@ namespace vsg std::pair descriptorSetRange() const; /// return true of specified descriptor set layout is compatible with what is required for this ShaderSet - virtual bool compatibleDescriptorSetLayout(const DescriptorSetLayout& dsl, const std::set& defines, uint32_t set) const; + virtual bool compatibleDescriptorSetLayout(const DescriptorSetLayout& dsl, const DefineMap& defines, uint32_t set) const; /// create the descriptor set layout. - virtual ref_ptr createDescriptorSetLayout(const std::set& defines, uint32_t set) const; + virtual ref_ptr createDescriptorSetLayout(const DefineMap& defines, uint32_t set) const; /// return true of specified pipeline layout is compatible with what is required for this ShaderSet - virtual bool compatiblePipelineLayout(const PipelineLayout& layout, const std::set& defines) const; + virtual bool compatiblePipelineLayout(const PipelineLayout& layout, const DefineMap& defines) const; /// create the pipeline layout for all descriptor sets enabled by specified defines or required by default. - inline ref_ptr createPipelineLayout(const std::set& defines) { return createPipelineLayout(defines, descriptorSetRange()); } + inline ref_ptr createPipelineLayout(const DefineMap& defines) { return createPipelineLayout(defines, descriptorSetRange()); } /// create pipeline layout for specified range of descriptor sets that are enabled by specified defines or required by default. /// /// Note: the underlying Vulkan call vkCreatePipelineLayout assumes that the array of /// descriptor sets starts with set 0. Therefore the minimum_set argument should be 0 unless /// you really know what you're doing. - virtual ref_ptr createPipelineLayout(const std::set& defines, std::pair range) const; + virtual ref_ptr createPipelineLayout(const DefineMap& defines, std::pair range) const; int compare(const Object& rhs) const override; diff --git a/src/vsg/state/ShaderModule.cpp b/src/vsg/state/ShaderModule.cpp index e3f566e0e3..8b5a16fe42 100644 --- a/src/vsg/state/ShaderModule.cpp +++ b/src/vsg/state/ShaderModule.cpp @@ -55,7 +55,7 @@ int ShaderCompileSettings::compare(const Object& rhs_object) const if ((result = compare_value(forwardCompatible, rhs.forwardCompatible))) return result; if ((result = compare_value(generateDebugInfo, rhs.generateDebugInfo))) return result; if ((result = compare_value(optimize, rhs.optimize))) return result; - return compare_container(defines, rhs.defines); + return compare_map(defines.values, rhs.defines.values); } void ShaderCompileSettings::read(Input& input) @@ -77,7 +77,16 @@ void ShaderCompileSettings::read(Input& input) input.read("optimize", optimize); } - input.readValues("defines", defines); + if (input.version_greater_equal(1, 1, 12)) + { + input.readValues("defines", defines.values); + } + else + { + std::set old_defines; + input.readValues("defines", old_defines); + defines = old_defines; + } } void ShaderCompileSettings::write(Output& output) const @@ -99,7 +108,15 @@ void ShaderCompileSettings::write(Output& output) const output.write("optimize", optimize); } - output.writeValues("defines", defines); + if (output.version_greater_equal(1, 1, 12)) + { + output.writeValues("defines", defines.values); + } + else + { + // Older version of VSG don't support map, so convert to std::set just retaining the key + output.writeValues("defines", defines.keys()); + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/vsg/utils/ShaderCompiler.cpp b/src/vsg/utils/ShaderCompiler.cpp index ff1364a1bb..2eb9b30986 100644 --- a/src/vsg/utils/ShaderCompiler.cpp +++ b/src/vsg/utils/ShaderCompiler.cpp @@ -280,8 +280,8 @@ bool ShaderCompiler::compile(ShaderStages& shaders, const std::vectormodule->source, options); - std::vector combinedDefines(defines); - for (auto& define : settings->defines) combinedDefines.push_back(define); + std::map combinedDefines(defines); + for (auto& define : settings->defines) combinedDefines.insert(define); if (!combinedDefines.empty()) finalShaderSource = combineSourceAndDefines(finalShaderSource, combinedDefines); vsg::debug("ShaderCompiler::compile() combinedDefines = ", combinedDefines); diff --git a/src/vsg/utils/ShaderSet.cpp b/src/vsg/utils/ShaderSet.cpp index 1313b6d5b0..b88b787a52 100644 --- a/src/vsg/utils/ShaderSet.cpp +++ b/src/vsg/utils/ShaderSet.cpp @@ -74,7 +74,7 @@ int PushConstantRange::compare(const PushConstantRange& rhs) const int DefinesArrayState::compare(const DefinesArrayState& rhs) const { - int result = compare_container(defines, rhs.defines); + int result = compare_map(defines, rhs.defines); if (result) return result; return compare_pointer(arrayState, rhs.arrayState); @@ -221,19 +221,19 @@ const DescriptorBinding& ShaderSet::getDescriptorBinding(const std::string& name return _nullDescriptorBinding; } -ref_ptr ShaderSet::getSuitableArrayState(const std::set& defines) const +ref_ptr ShaderSet::getSuitableArrayState(const DefineMap& defines) const { // not all defines are relevant to the provided ArrayState // so check each one against the entries in the definesArrayState // relevant to the final matching. - std::set relevant_defines; + DefineMap relevant_defines; for (auto& define : defines) { for (const auto& definesArrayState : definesArrayStates) { - if (definesArrayState.defines.count(define) != 0) + if (definesArrayState.defines.count(define.first) != 0) { - relevant_defines.insert(define); + relevant_defines.insert(define.first); break; } } @@ -350,11 +350,33 @@ void ShaderSet::read(Input& input) definesArrayStates.resize(num_definesArrayStates); for (auto& das : definesArrayStates) { - input.readValues("defines", das.defines); + if (input.version_greater_equal(1, 1, 12)) + { + input.readValues("defines", das.defines.values); + } + else + { + std::set defines_set; + input.readValues("defines", defines_set); + das.defines = defines_set; + } + input.readObject("arrayState", das.arrayState); } - input.readValues("optionalDefines", optionalDefines); + if (input.version_greater_equal(1, 1, 12)) + { + input.readValues("optionalDefines", optionalDefines.values); + } + else + { + std::set defines_set; + input.readValues("optionalDefines", defines_set); + optionalDefines = defines_set; + } + + + input.readObjects("defaultGraphicsPipelineStates", defaultGraphicsPipelineStates); auto num_variants = input.readValue("variants"); @@ -428,11 +450,27 @@ void ShaderSet::write(Output& output) const output.writeValue("definesArrayStates", definesArrayStates.size()); for (const auto& das : definesArrayStates) { - output.writeValues("defines", das.defines); + if (output.version_greater_equal(1, 1, 12)) + { + output.writeValues("defines", das.defines.values); + } + else + { + output.writeValues("defines", das.defines.keys()); + } + output.writeObject("arrayState", das.arrayState); } - output.writeValues("optionalDefines", optionalDefines); + if (output.version_greater_equal(1, 1, 12)) + { + output.writeValues("optionalDefines", optionalDefines.values); + } + else + { + output.writeValues("optionalDefines", optionalDefines.keys()); + } + output.writeObjects("defaultGraphicsPipelineStates", defaultGraphicsPipelineStates); output.writeValue("variants", variants.size()); @@ -500,7 +538,7 @@ std::pair ShaderSet::descriptorSetRange() const return {minimum, maximum + 1}; } -bool ShaderSet::compatibleDescriptorSetLayout(const DescriptorSetLayout& dsl, const std::set& defines, uint32_t set) const +bool ShaderSet::compatibleDescriptorSetLayout(const DescriptorSetLayout& dsl, const DefineMap& defines, uint32_t set) const { for (auto& cdsb : customDescriptorSetBindings) { @@ -522,7 +560,7 @@ bool ShaderSet::compatibleDescriptorSetLayout(const DescriptorSetLayout& dsl, co return compare_value_container(dsl.bindings, bindings) == 0; } -ref_ptr ShaderSet::createDescriptorSetLayout(const std::set& defines, uint32_t set) const +ref_ptr ShaderSet::createDescriptorSetLayout(const DefineMap& defines, uint32_t set) const { for (auto& cdsb : customDescriptorSetBindings) { @@ -544,7 +582,7 @@ ref_ptr ShaderSet::createDescriptorSetLayout(const std::set return DescriptorSetLayout::create(bindings); } -bool ShaderSet::compatiblePipelineLayout(const PipelineLayout& layout, const std::set& defines) const +bool ShaderSet::compatiblePipelineLayout(const PipelineLayout& layout, const DefineMap& defines) const { uint32_t set = 0; for (const auto& descriptorSetLayout : layout.setLayouts) @@ -573,7 +611,7 @@ bool ShaderSet::compatiblePipelineLayout(const PipelineLayout& layout, const std return true; } -ref_ptr ShaderSet::createPipelineLayout(const std::set& defines, std::pair range) const +ref_ptr ShaderSet::createPipelineLayout(const DefineMap& defines, std::pair range) const { DescriptorSetLayouts descriptorSetLayouts;