diff --git a/include/Nazara/Renderer/GlslWriter.hpp b/include/Nazara/Renderer/GlslWriter.hpp index e0f9bf11e..999a93c48 100644 --- a/include/Nazara/Renderer/GlslWriter.hpp +++ b/include/Nazara/Renderer/GlslWriter.hpp @@ -44,15 +44,17 @@ namespace Nz }; private: + void Append(ShaderAst::Type type); void Append(ShaderNodes::BuiltinEntry builtin); void Append(ShaderNodes::ExpressionType type); + void Append(ShaderNodes::MemoryLayout layout); template void Append(const T& param); void AppendCommentSection(const std::string& section); void AppendFunction(const ShaderAst::Function& func); void AppendFunctionPrototype(const ShaderAst::Function& func); void AppendLine(const std::string& txt = {}); - template void DeclareVariables(const std::vector& variables, const std::string& keyword = {}, const std::string& section = {}); + template void DeclareVariables(const ShaderAst& shader, const std::vector& variables, const std::string& keyword = {}, const std::string& section = {}); void EnterScope(); void LeaveScope(); diff --git a/include/Nazara/Renderer/GlslWriter.inl b/include/Nazara/Renderer/GlslWriter.inl index fcaca4b98..9b67eaa89 100644 --- a/include/Nazara/Renderer/GlslWriter.inl +++ b/include/Nazara/Renderer/GlslWriter.inl @@ -17,7 +17,7 @@ namespace Nz } template - void GlslWriter::DeclareVariables(const std::vector& variables, const std::string& keyword, const std::string& section) + void GlslWriter::DeclareVariables(const ShaderAst& shader, const std::vector& variables, const std::string& keyword, const std::string& section) { if (!variables.empty()) { @@ -34,27 +34,94 @@ namespace Nz Append(*var.locationIndex); Append(") "); } + + if (!keyword.empty()) + { + Append(keyword); + Append(" "); + } + + Append(var.type); + Append(" "); + Append(var.name); + AppendLine(";"); } else if constexpr (std::is_same_v) { - if (var.bindingIndex) + if (var.bindingIndex || var.memoryLayout) { - Append("layout(binding = "); - Append(*var.bindingIndex); + Append("layout("); + + bool first = true; + if (var.bindingIndex) + { + if (!first) + Append(", "); + + Append("binding = "); + Append(*var.bindingIndex); + + first = false; + } + + if (var.memoryLayout) + { + if (!first) + Append(", "); + + Append(*var.memoryLayout); + + first = false; + } + Append(") "); } - } - if (!keyword.empty()) - { - Append(keyword); - Append(" "); - } + if (!keyword.empty()) + { + Append(keyword); + Append(" "); + } - Append(var.type); - Append(" "); - Append(var.name); - AppendLine(";"); + std::visit([&](auto&& arg) + { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + Append(arg); + Append(" "); + Append(var.name); + } + else if constexpr (std::is_same_v) + { + const auto& structs = shader.GetStructs(); + auto it = std::find_if(structs.begin(), structs.end(), [&](const auto& s) { return s.name == arg; }); + if (it == structs.end()) + throw std::runtime_error("struct " + arg + " has not been defined"); + + const auto& s = *it; + + AppendLine(var.name + "_interface"); + AppendLine("{"); + for (const auto& m : s.members) + { + Append("\t"); + Append(m.type); + Append(" "); + Append(m.name); + AppendLine(";"); + } + Append("} "); + Append(var.name); + } + else + static_assert(AlwaysFalse::value, "non-exhaustive visitor"); + + }, var.type); + + AppendLine(";"); + AppendLine(); + } } AppendLine(); diff --git a/include/Nazara/Renderer/ShaderAst.hpp b/include/Nazara/Renderer/ShaderAst.hpp index 0aa094a10..4b57bbced 100644 --- a/include/Nazara/Renderer/ShaderAst.hpp +++ b/include/Nazara/Renderer/ShaderAst.hpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include namespace Nz @@ -21,16 +23,21 @@ namespace Nz struct Function; struct FunctionParameter; struct InputOutput; - struct VariableBase; + struct Struct; + struct StructMember; struct Uniform; + struct VariableBase; + + using Type = std::variant; ShaderAst() = default; ~ShaderAst() = default; void AddFunction(std::string name, ShaderNodes::StatementPtr statement, std::vector parameters = {}, ShaderNodes::ExpressionType returnType = ShaderNodes::ExpressionType::Void); - void AddInput(std::string name, ShaderNodes::ExpressionType type, std::optional locationIndex); - void AddOutput(std::string name, ShaderNodes::ExpressionType type, std::optional locationIndex); - void AddUniform(std::string name, ShaderNodes::ExpressionType type, std::optional bindingIndex); + void AddInput(std::string name, Type type, std::optional locationIndex); + void AddOutput(std::string name, Type type, std::optional locationIndex); + void AddStruct(std::string name, std::vector members); + void AddUniform(std::string name, Type type, std::optional bindingIndex, std::optional memoryLayout); inline const Function& GetFunction(std::size_t i) const; inline std::size_t GetFunctionCount() const; @@ -41,6 +48,9 @@ namespace Nz inline const InputOutput& GetOutput(std::size_t i) const; inline std::size_t GetOutputCount() const; inline const std::vector& GetOutputs() const; + inline const Struct& GetStruct(std::size_t i) const; + inline std::size_t GetStructCount() const; + inline const std::vector& GetStructs() const; inline const Uniform& GetUniform(std::size_t i) const; inline std::size_t GetUniformCount() const; inline const std::vector& GetUniforms() const; @@ -48,7 +58,7 @@ namespace Nz struct VariableBase { std::string name; - ShaderNodes::ExpressionType type; + Type type; }; struct FunctionParameter : VariableBase @@ -71,12 +81,26 @@ namespace Nz struct Uniform : VariableBase { std::optional bindingIndex; + std::optional memoryLayout; + }; + + struct Struct + { + std::string name; + std::vector members; + }; + + struct StructMember + { + std::string name; + Type type; }; private: std::vector m_functions; std::vector m_inputs; std::vector m_outputs; + std::vector m_structs; std::vector m_uniforms; }; } diff --git a/include/Nazara/Renderer/ShaderAst.inl b/include/Nazara/Renderer/ShaderAst.inl index d9f8f88aa..c42c1e9c1 100644 --- a/include/Nazara/Renderer/ShaderAst.inl +++ b/include/Nazara/Renderer/ShaderAst.inl @@ -55,6 +55,22 @@ namespace Nz return m_outputs; } + inline auto ShaderAst::GetStruct(std::size_t i) const -> const Struct& + { + assert(i < m_structs.size()); + return m_structs[i]; + } + + inline std::size_t ShaderAst::GetStructCount() const + { + return m_structs.size(); + } + + inline auto ShaderAst::GetStructs() const -> const std::vector& + { + return m_structs; + } + inline auto ShaderAst::GetUniform(std::size_t i) const -> const Uniform& { assert(i < m_uniforms.size()); diff --git a/include/Nazara/Renderer/ShaderEnums.hpp b/include/Nazara/Renderer/ShaderEnums.hpp index f400c7516..7a93cea7b 100644 --- a/include/Nazara/Renderer/ShaderEnums.hpp +++ b/include/Nazara/Renderer/ShaderEnums.hpp @@ -56,6 +56,11 @@ namespace Nz::ShaderNodes DotProduct }; + enum class MemoryLayout + { + Std140 + }; + enum class NodeType { None = -1, diff --git a/include/Nazara/Renderer/ShaderSerializer.hpp b/include/Nazara/Renderer/ShaderSerializer.hpp index eac0c2bf8..f79040b2c 100644 --- a/include/Nazara/Renderer/ShaderSerializer.hpp +++ b/include/Nazara/Renderer/ShaderSerializer.hpp @@ -11,13 +11,12 @@ #include #include #include +#include #include #include namespace Nz { - class ShaderAst; - class NAZARA_RENDERER_API ShaderSerializerBase { public: @@ -44,6 +43,7 @@ namespace Nz protected: template void Container(T& container); template void Enum(T& enumVal); + template void OptEnum(std::optional& optVal); template void OptVal(std::optional& optVal); virtual bool IsWriting() const = 0; @@ -57,6 +57,8 @@ namespace Nz virtual void Value(Vector2f& val) = 0; virtual void Value(Vector3f& val) = 0; virtual void Value(Vector4f& val) = 0; + virtual void Value(UInt8& val) = 0; + virtual void Value(UInt16& val) = 0; virtual void Value(UInt32& val) = 0; inline void Value(std::size_t& val); @@ -82,6 +84,8 @@ namespace Nz void Value(Vector2f& val) override; void Value(Vector3f& val) override; void Value(Vector4f& val) override; + void Value(UInt8& val) override; + void Value(UInt16& val) override; void Value(UInt32& val) override; void Variable(ShaderNodes::VariablePtr& var) override; @@ -99,12 +103,15 @@ namespace Nz private: bool IsWriting() const override; void Node(ShaderNodes::NodePtr& node) override; + void Type(ShaderAst::Type& type); void Value(bool& val) override; void Value(float& val) override; void Value(std::string& val) override; void Value(Vector2f& val) override; void Value(Vector3f& val) override; void Value(Vector4f& val) override; + void Value(UInt8& val) override; + void Value(UInt16& val) override; void Value(UInt32& val) override; void Variable(ShaderNodes::VariablePtr& var) override; diff --git a/include/Nazara/Renderer/ShaderSerializer.inl b/include/Nazara/Renderer/ShaderSerializer.inl index 0daeb6653..51187d520 100644 --- a/include/Nazara/Renderer/ShaderSerializer.inl +++ b/include/Nazara/Renderer/ShaderSerializer.inl @@ -36,6 +36,24 @@ namespace Nz enumVal = static_cast(value); } + template + void ShaderSerializerBase::OptEnum(std::optional& optVal) + { + bool isWriting = IsWriting(); + + bool hasValue; + if (isWriting) + hasValue = optVal.has_value(); + + Value(hasValue); + + if (!isWriting && hasValue) + optVal.emplace(); + + if (optVal.has_value()) + Enum(optVal.value()); + } + template void ShaderSerializerBase::OptVal(std::optional& optVal) { diff --git a/include/Nazara/Renderer/ShaderValidator.hpp b/include/Nazara/Renderer/ShaderValidator.hpp index bfdab9844..3df9f4ae5 100644 --- a/include/Nazara/Renderer/ShaderValidator.hpp +++ b/include/Nazara/Renderer/ShaderValidator.hpp @@ -11,12 +11,11 @@ #include #include #include +#include #include namespace Nz { - class ShaderAst; - class NAZARA_RENDERER_API ShaderValidator : public ShaderVisitor { public: @@ -31,7 +30,7 @@ namespace Nz const ShaderNodes::ExpressionPtr& MandatoryExpr(const ShaderNodes::ExpressionPtr& node); const ShaderNodes::NodePtr& MandatoryNode(const ShaderNodes::NodePtr& node); void TypeMustMatch(const ShaderNodes::ExpressionPtr& left, const ShaderNodes::ExpressionPtr& right); - void TypeMustMatch(ShaderNodes::ExpressionType left, ShaderNodes::ExpressionType right); + void TypeMustMatch(const ShaderAst::Type& left, const ShaderAst::Type& right); using ShaderVisitor::Visit; void Visit(const ShaderNodes::AssignOp& node) override; diff --git a/src/Nazara/Renderer/GlslWriter.cpp b/src/Nazara/Renderer/GlslWriter.cpp index 61dc030da..accb493b7 100644 --- a/src/Nazara/Renderer/GlslWriter.cpp +++ b/src/Nazara/Renderer/GlslWriter.cpp @@ -108,13 +108,35 @@ namespace Nz AppendLine(); } + // Structures + /*if (shader.GetStructCount() > 0) + { + AppendCommentSection("Structures"); + for (const auto& s : shader.GetStructs()) + { + Append("struct "); + AppendLine(s.name); + AppendLine("{"); + for (const auto& m : s.members) + { + Append("\t"); + Append(m.type); + Append(" "); + Append(m.name); + AppendLine(";"); + } + AppendLine("};"); + AppendLine(); + } + }*/ + // Global variables (uniforms, input and outputs) const char* inKeyword = (glslVersion >= 130) ? "in" : "varying"; const char* outKeyword = (glslVersion >= 130) ? "out" : "varying"; - DeclareVariables(shader.GetUniforms(), "uniform", "Uniforms"); - DeclareVariables(shader.GetInputs(), inKeyword, "Inputs"); - DeclareVariables(shader.GetOutputs(), outKeyword, "Outputs"); + DeclareVariables(shader, shader.GetUniforms(), "uniform", "Uniforms"); + DeclareVariables(shader, shader.GetInputs(), inKeyword, "Inputs"); + DeclareVariables(shader, shader.GetOutputs(), outKeyword, "Outputs"); std::size_t functionCount = shader.GetFunctionCount(); if (functionCount > 1) @@ -141,6 +163,14 @@ namespace Nz m_environment = std::move(environment); } + void GlslWriter::Append(ShaderAst::Type type) + { + std::visit([&](auto&& arg) + { + Append(arg); + }, type); + } + void GlslWriter::Append(ShaderNodes::BuiltinEntry builtin) { switch (builtin) @@ -182,6 +212,16 @@ namespace Nz } } + void GlslWriter::Append(ShaderNodes::MemoryLayout layout) + { + switch (layout) + { + case ShaderNodes::MemoryLayout::Std140: + Append("std140"); + break; + } + } + void GlslWriter::AppendCommentSection(const std::string& section) { NazaraAssert(m_currentState, "This function should only be called while processing an AST"); diff --git a/src/Nazara/Renderer/ShaderAst.cpp b/src/Nazara/Renderer/ShaderAst.cpp index a52cca657..05d3eb94d 100644 --- a/src/Nazara/Renderer/ShaderAst.cpp +++ b/src/Nazara/Renderer/ShaderAst.cpp @@ -16,27 +16,35 @@ namespace Nz functionEntry.statement = std::move(statement); } - void ShaderAst::AddInput(std::string name, ShaderNodes::ExpressionType type, std::optional locationIndex) + void ShaderAst::AddInput(std::string name, Type type, std::optional locationIndex) { auto& inputEntry = m_inputs.emplace_back(); inputEntry.name = std::move(name); inputEntry.locationIndex = std::move(locationIndex); - inputEntry.type = type; + inputEntry.type = std::move(type); } - void ShaderAst::AddOutput(std::string name, ShaderNodes::ExpressionType type, std::optional locationIndex) + void ShaderAst::AddOutput(std::string name, Type type, std::optional locationIndex) { auto& outputEntry = m_outputs.emplace_back(); outputEntry.name = std::move(name); outputEntry.locationIndex = std::move(locationIndex); - outputEntry.type = type; + outputEntry.type = std::move(type); } - void ShaderAst::AddUniform(std::string name, ShaderNodes::ExpressionType type, std::optional bindingIndex) + void ShaderAst::AddStruct(std::string name, std::vector members) + { + auto& structEntry = m_structs.emplace_back(); + structEntry.name = std::move(name); + structEntry.members = std::move(members); + } + + void ShaderAst::AddUniform(std::string name, Type type, std::optional bindingIndex, std::optional memoryLayout) { auto& uniformEntry = m_uniforms.emplace_back(); uniformEntry.bindingIndex = std::move(bindingIndex); + uniformEntry.memoryLayout = std::move(memoryLayout); uniformEntry.name = std::move(name); - uniformEntry.type = type; + uniformEntry.type = std::move(type); } } diff --git a/src/Nazara/Renderer/ShaderSerializer.cpp b/src/Nazara/Renderer/ShaderSerializer.cpp index f97186acf..9cf7c47ff 100644 --- a/src/Nazara/Renderer/ShaderSerializer.cpp +++ b/src/Nazara/Renderer/ShaderSerializer.cpp @@ -3,7 +3,6 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include -#include #include #include #include @@ -250,12 +249,33 @@ namespace Nz { m_stream << s_magicNumber << s_currentVersion; + auto SerializeType = [&](const ShaderAst::Type& type) + { + std::visit([&](auto&& arg) + { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + m_stream << UInt8(0); + m_stream << UInt32(arg); + } + else if constexpr (std::is_same_v) + { + m_stream << UInt8(1); + m_stream << arg; + } + else + static_assert(AlwaysFalse::value, "non-exhaustive visitor"); + }, type); + }; + auto SerializeInputOutput = [&](auto& inout) { m_stream << UInt32(inout.size()); for (const auto& data : inout) { - m_stream << data.name << UInt32(data.type); + m_stream << data.name; + SerializeType(data.type); m_stream << data.locationIndex.has_value(); if (data.locationIndex) @@ -263,17 +283,34 @@ namespace Nz } }; + m_stream << UInt32(shader.GetStructCount()); + for (const auto& s : shader.GetStructs()) + { + m_stream << s.name; + m_stream << UInt32(s.members.size()); + for (const auto& member : s.members) + { + m_stream << member.name; + SerializeType(member.type); + } + } + SerializeInputOutput(shader.GetInputs()); SerializeInputOutput(shader.GetOutputs()); m_stream << UInt32(shader.GetUniformCount()); for (const auto& uniform : shader.GetUniforms()) { - m_stream << uniform.name << UInt32(uniform.type); + m_stream << uniform.name; + SerializeType(uniform.type); m_stream << uniform.bindingIndex.has_value(); if (uniform.bindingIndex) m_stream << UInt32(uniform.bindingIndex.value()); + + m_stream << uniform.memoryLayout.has_value(); + if (uniform.memoryLayout) + m_stream << UInt32(uniform.memoryLayout.value()); } m_stream << UInt32(shader.GetFunctionCount()); @@ -283,7 +320,10 @@ namespace Nz m_stream << UInt32(func.parameters.size()); for (const auto& param : func.parameters) - m_stream << param.name << UInt32(param.type); + { + m_stream << param.name; + SerializeType(param.type); + } Node(func.statement); } @@ -343,6 +383,16 @@ namespace Nz m_stream << val; } + void ShaderSerializer::Value(UInt8& val) + { + m_stream << val; + } + + void ShaderSerializer::Value(UInt16& val) + { + m_stream << val; + } + void ShaderSerializer::Value(UInt32& val) { m_stream << val; @@ -374,19 +424,38 @@ namespace Nz ShaderAst shader; + UInt32 structCount; + m_stream >> structCount; + for (UInt32 i = 0; i < structCount; ++i) + { + std::string structName; + std::vector members; + + Value(structName); + Container(members); + + for (auto& member : members) + { + Value(member.name); + Type(member.type); + } + + shader.AddStruct(std::move(structName), std::move(members)); + } + UInt32 inputCount; m_stream >> inputCount; for (UInt32 i = 0; i < inputCount; ++i) { std::string inputName; - ShaderNodes::ExpressionType inputType; + ShaderAst::Type inputType; std::optional location; Value(inputName); - Enum(inputType); + Type(inputType); OptVal(location); - shader.AddInput(std::move(inputName), inputType, location); + shader.AddInput(std::move(inputName), std::move(inputType), location); } UInt32 outputCount; @@ -394,14 +463,14 @@ namespace Nz for (UInt32 i = 0; i < outputCount; ++i) { std::string outputName; - ShaderNodes::ExpressionType outputType; + ShaderAst::Type outputType; std::optional location; Value(outputName); - Enum(outputType); + Type(outputType); OptVal(location); - shader.AddOutput(std::move(outputName), outputType, location); + shader.AddOutput(std::move(outputName), std::move(outputType), location); } UInt32 uniformCount; @@ -409,14 +478,16 @@ namespace Nz for (UInt32 i = 0; i < uniformCount; ++i) { std::string name; - ShaderNodes::ExpressionType type; + ShaderAst::Type type; std::optional binding; + std::optional memLayout; Value(name); - Enum(type); + Type(type); OptVal(binding); + OptEnum(memLayout); - shader.AddUniform(std::move(name), type, binding); + shader.AddUniform(std::move(name), std::move(type), std::move(binding), std::move(memLayout)); } UInt32 funcCount; @@ -433,7 +504,7 @@ namespace Nz for (auto& param : parameters) { Value(param.name); - Enum(param.type); + Type(param.type); } ShaderNodes::NodePtr node; @@ -489,6 +560,36 @@ namespace Nz } } + void ShaderUnserializer::Type(ShaderAst::Type& type) + { + UInt8 typeIndex; + Value(typeIndex); + + switch (typeIndex) + { + case 0: //< Primitive + { + ShaderNodes::ExpressionType exprType; + Enum(exprType); + + type = exprType; + break; + } + + case 1: //< Struct (name) + { + std::string structName; + Value(structName); + + type = std::move(structName); + break; + } + + default: + break; + } + } + void ShaderUnserializer::Value(bool& val) { m_stream >> val; @@ -519,6 +620,16 @@ namespace Nz m_stream >> val; } + void ShaderUnserializer::Value(UInt8& val) + { + m_stream >> val; + } + + void ShaderUnserializer::Value(UInt16& val) + { + m_stream >> val; + } + void ShaderUnserializer::Value(UInt32& val) { m_stream >> val; diff --git a/src/Nazara/Renderer/ShaderValidator.cpp b/src/Nazara/Renderer/ShaderValidator.cpp index aab559a61..37eecb767 100644 --- a/src/Nazara/Renderer/ShaderValidator.cpp +++ b/src/Nazara/Renderer/ShaderValidator.cpp @@ -77,7 +77,7 @@ namespace Nz return TypeMustMatch(left->GetExpressionType(), right->GetExpressionType()); } - void ShaderValidator::TypeMustMatch(ShaderNodes::ExpressionType left, ShaderNodes::ExpressionType right) + void ShaderValidator::TypeMustMatch(const ShaderAst::Type& left, const ShaderAst::Type& right) { if (left != right) throw AstError{ "Left expression type must match right expression type" };