From 99e07e6e1e1ecd09c6f83521e8217f9f161c9778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Tue, 1 Mar 2022 19:36:18 +0100 Subject: [PATCH] Shader: Add module statement --- bin/resources/bloom_bright.nzsl | 3 + bin/resources/bloom_final.nzsl | 3 + bin/resources/deferred_frag.nzsl | 3 + bin/resources/deferred_vert.nzsl | 3 + bin/resources/gamma.nzsl | 3 + bin/resources/gaussian_blur.nzsl | 3 + bin/resources/god_rays.nzsl | 3 + bin/resources/lighting.nzsl | 3 + bin/resources/skybox.nzsl | 3 + bin/resources/tone_mapping.nzsl | 3 + examples/RenderTest/main.cpp | 2 + include/Nazara/Graphics/UberShader.hpp | 6 +- .../Nazara/OpenGLRenderer/OpenGLDevice.hpp | 2 +- .../OpenGLRenderer/OpenGLShaderModule.hpp | 8 +- include/Nazara/Renderer/RenderDevice.hpp | 4 +- include/Nazara/Shader/Ast/AstSerializer.hpp | 16 ++- include/Nazara/Shader/Ast/AstSerializer.inl | 2 +- include/Nazara/Shader/Ast/ConstantValue.hpp | 3 +- include/Nazara/Shader/Ast/Enums.hpp | 2 + include/Nazara/Shader/Ast/Module.hpp | 28 ++++ include/Nazara/Shader/Ast/SanitizeVisitor.hpp | 9 +- include/Nazara/Shader/Ast/SanitizeVisitor.inl | 12 +- include/Nazara/Shader/GlslWriter.hpp | 7 +- include/Nazara/Shader/GlslWriter.inl | 2 +- include/Nazara/Shader/LangWriter.hpp | 5 +- include/Nazara/Shader/ShaderLangLexer.hpp | 10 ++ include/Nazara/Shader/ShaderLangParser.hpp | 20 ++- include/Nazara/Shader/ShaderLangParser.inl | 4 +- include/Nazara/Shader/ShaderLangTokenList.hpp | 2 + include/Nazara/Shader/SpirvWriter.hpp | 4 +- .../Nazara/VulkanRenderer/VulkanDevice.hpp | 2 +- .../VulkanRenderer/VulkanShaderModule.hpp | 4 +- src/Nazara/Graphics/BasicMaterial.cpp | 4 +- src/Nazara/Graphics/DepthMaterial.cpp | 4 +- src/Nazara/Graphics/FrameGraph.cpp | 9 +- src/Nazara/Graphics/PhongLightingMaterial.cpp | 10 +- .../Resources/Shaders/basic_material.nzsl | 3 + .../Graphics/Resources/Shaders/blit.nzsl | 3 + .../Resources/Shaders/depth_material.nzsl | 3 + .../Resources/Shaders/phong_material.nzsl | 3 + src/Nazara/Graphics/UberShader.cpp | 11 +- src/Nazara/OpenGLRenderer/OpenGLDevice.cpp | 4 +- .../OpenGLRenderer/OpenGLRenderPipeline.cpp | 8 +- .../OpenGLRenderer/OpenGLShaderModule.cpp | 10 +- .../Ast/AstConstantPropagationVisitor.cpp | 3 +- src/Nazara/Shader/Ast/AstSerializer.cpp | 29 ++-- src/Nazara/Shader/Ast/ConstantValue.cpp | 2 + src/Nazara/Shader/Ast/SanitizeVisitor.cpp | 7 +- src/Nazara/Shader/GlslWriter.cpp | 16 ++- src/Nazara/Shader/LangWriter.cpp | 19 ++- src/Nazara/Shader/ShaderLangLexer.cpp | 54 +++++++- src/Nazara/Shader/ShaderLangParser.cpp | 128 ++++++++++++++++-- src/Nazara/Shader/SpirvConstantCache.cpp | 2 + src/Nazara/Shader/SpirvWriter.cpp | 13 +- src/Nazara/VulkanRenderer/VulkanDevice.cpp | 4 +- .../VulkanRenderer/VulkanShaderModule.cpp | 8 +- 56 files changed, 418 insertions(+), 123 deletions(-) create mode 100644 include/Nazara/Shader/Ast/Module.hpp diff --git a/bin/resources/bloom_bright.nzsl b/bin/resources/bloom_bright.nzsl index 63aaea8d8..9ce33a720 100644 --- a/bin/resources/bloom_bright.nzsl +++ b/bin/resources/bloom_bright.nzsl @@ -1,3 +1,6 @@ +[nzsl_version("1.0")] +module; + [layout(std140)] struct ViewerData { diff --git a/bin/resources/bloom_final.nzsl b/bin/resources/bloom_final.nzsl index 3b8da880c..5abd7b6f0 100644 --- a/bin/resources/bloom_final.nzsl +++ b/bin/resources/bloom_final.nzsl @@ -1,3 +1,6 @@ +[nzsl_version("1.0")] +module; + [layout(std140)] struct ViewerData { diff --git a/bin/resources/deferred_frag.nzsl b/bin/resources/deferred_frag.nzsl index 7a4e6b554..d69915ce5 100644 --- a/bin/resources/deferred_frag.nzsl +++ b/bin/resources/deferred_frag.nzsl @@ -1,3 +1,6 @@ +[nzsl_version("1.0")] +module; + option HasDiffuseTexture: bool = false; option HasAlphaTexture: bool = false; option AlphaTest: bool = false; diff --git a/bin/resources/deferred_vert.nzsl b/bin/resources/deferred_vert.nzsl index 5d61da5fb..04543cdbf 100644 --- a/bin/resources/deferred_vert.nzsl +++ b/bin/resources/deferred_vert.nzsl @@ -1,3 +1,6 @@ +[nzsl_version("1.0")] +module; + [layout(std140)] struct BasicSettings { diff --git a/bin/resources/gamma.nzsl b/bin/resources/gamma.nzsl index ee34a66af..24c991ce0 100644 --- a/bin/resources/gamma.nzsl +++ b/bin/resources/gamma.nzsl @@ -1,3 +1,6 @@ +[nzsl_version("1.0")] +module; + external { [binding(0)] colorTexture: sampler2D[f32] diff --git a/bin/resources/gaussian_blur.nzsl b/bin/resources/gaussian_blur.nzsl index 4f8323cb5..aebfb5ce0 100644 --- a/bin/resources/gaussian_blur.nzsl +++ b/bin/resources/gaussian_blur.nzsl @@ -1,3 +1,6 @@ +[nzsl_version("1.0")] +module; + [layout(std140)] struct ViewerData { diff --git a/bin/resources/god_rays.nzsl b/bin/resources/god_rays.nzsl index 8a88f4fcd..4f3aef10b 100644 --- a/bin/resources/god_rays.nzsl +++ b/bin/resources/god_rays.nzsl @@ -1,3 +1,6 @@ +[nzsl_version("1.0")] +module; + [layout(std140)] struct ViewerData { diff --git a/bin/resources/lighting.nzsl b/bin/resources/lighting.nzsl index 71c1c819d..fe65b51c0 100644 --- a/bin/resources/lighting.nzsl +++ b/bin/resources/lighting.nzsl @@ -1,3 +1,6 @@ +[nzsl_version("1.0")] +module; + [layout(std140)] struct PointLight { diff --git a/bin/resources/skybox.nzsl b/bin/resources/skybox.nzsl index 72acb51d0..138921a9e 100644 --- a/bin/resources/skybox.nzsl +++ b/bin/resources/skybox.nzsl @@ -1,3 +1,6 @@ +[nzsl_version("1.0")] +module; + [layout(std140)] struct ViewerData { diff --git a/bin/resources/tone_mapping.nzsl b/bin/resources/tone_mapping.nzsl index 6f9aac1d1..b10123e4c 100644 --- a/bin/resources/tone_mapping.nzsl +++ b/bin/resources/tone_mapping.nzsl @@ -1,3 +1,6 @@ +[nzsl_version("1.0")] +module; + [layout(std140)] struct ViewerData { diff --git a/examples/RenderTest/main.cpp b/examples/RenderTest/main.cpp index 594455d36..6634f5ff1 100644 --- a/examples/RenderTest/main.cpp +++ b/examples/RenderTest/main.cpp @@ -11,6 +11,8 @@ NAZARA_REQUEST_DEDICATED_GPU() const char shaderSource[] = R"( +[nzsl_version("1.0")] +module; option red: bool = false; diff --git a/include/Nazara/Graphics/UberShader.hpp b/include/Nazara/Graphics/UberShader.hpp index 77d7219ea..744ecbb44 100644 --- a/include/Nazara/Graphics/UberShader.hpp +++ b/include/Nazara/Graphics/UberShader.hpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include namespace Nz @@ -26,7 +26,7 @@ namespace Nz struct Option; using ConfigCallback = std::function& vertexBuffers)>; - UberShader(ShaderStageTypeFlags shaderStages, const ShaderAst::StatementPtr& shaderAst); + UberShader(ShaderStageTypeFlags shaderStages, ShaderAst::ModulePtr shaderModule); ~UberShader() = default; inline ShaderStageTypeFlags GetSupportedStages() const; @@ -63,7 +63,7 @@ namespace Nz private: std::unordered_map, ConfigHasher, ConfigEqual> m_combinations; std::unordered_map m_optionIndexByName; - ShaderAst::StatementPtr m_shaderAst; + ShaderAst::ModulePtr m_shaderModule; ConfigCallback m_configCallback; ShaderStageTypeFlags m_shaderStages; }; diff --git a/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp b/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp index f57bfba9b..473064893 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp @@ -41,7 +41,7 @@ namespace Nz std::shared_ptr InstantiateRenderPass(std::vector attachments, std::vector subpassDescriptions, std::vector subpassDependencies) override; std::shared_ptr InstantiateRenderPipeline(RenderPipelineInfo pipelineInfo) override; std::shared_ptr InstantiateRenderPipelineLayout(RenderPipelineLayoutInfo pipelineLayoutInfo) override; - std::shared_ptr InstantiateShaderModule(ShaderStageTypeFlags shaderStages, ShaderAst::Statement& shaderAst, const ShaderWriter::States& states) override; + std::shared_ptr InstantiateShaderModule(ShaderStageTypeFlags shaderStages, ShaderAst::Module& shaderModule, const ShaderWriter::States& states) override; std::shared_ptr InstantiateShaderModule(ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const void* source, std::size_t sourceSize, const ShaderWriter::States& states) override; std::shared_ptr InstantiateTexture(const TextureInfo& params) override; std::shared_ptr InstantiateTextureSampler(const TextureSamplerInfo& params) override; diff --git a/include/Nazara/OpenGLRenderer/OpenGLShaderModule.hpp b/include/Nazara/OpenGLRenderer/OpenGLShaderModule.hpp index 37ff03c67..4b9581ef2 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLShaderModule.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLShaderModule.hpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include namespace Nz @@ -22,7 +22,7 @@ namespace Nz class NAZARA_OPENGLRENDERER_API OpenGLShaderModule : public ShaderModule { public: - OpenGLShaderModule(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderAst::Statement& shaderAst, const ShaderWriter::States& states = {}); + OpenGLShaderModule(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderAst::Module& shaderModule, const ShaderWriter::States& states = {}); OpenGLShaderModule(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const void* source, std::size_t sourceSize, const ShaderWriter::States& states = {}); OpenGLShaderModule(const OpenGLShaderModule&) = delete; OpenGLShaderModule(OpenGLShaderModule&&) noexcept = default; @@ -34,7 +34,7 @@ namespace Nz OpenGLShaderModule& operator=(OpenGLShaderModule&&) noexcept = default; private: - void Create(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderAst::Statement& shaderAst, const ShaderWriter::States& states); + void Create(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderAst::Module& shaderModule, const ShaderWriter::States& states); static void CheckCompilationStatus(GL::Shader& shader); @@ -45,7 +45,7 @@ namespace Nz struct ShaderStatement { - std::shared_ptr ast; + ShaderAst::ModulePtr ast; }; struct Shader diff --git a/include/Nazara/Renderer/RenderDevice.hpp b/include/Nazara/Renderer/RenderDevice.hpp index 946389da2..83461beb5 100644 --- a/include/Nazara/Renderer/RenderDevice.hpp +++ b/include/Nazara/Renderer/RenderDevice.hpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include #include @@ -44,7 +44,7 @@ namespace Nz virtual std::shared_ptr InstantiateRenderPass(std::vector attachments, std::vector subpassDescriptions, std::vector subpassDependencies) = 0; virtual std::shared_ptr InstantiateRenderPipeline(RenderPipelineInfo pipelineInfo) = 0; virtual std::shared_ptr InstantiateRenderPipelineLayout(RenderPipelineLayoutInfo pipelineLayoutInfo) = 0; - virtual std::shared_ptr InstantiateShaderModule(ShaderStageTypeFlags shaderStages, ShaderAst::Statement& shaderAst, const ShaderWriter::States& states) = 0; + virtual std::shared_ptr InstantiateShaderModule(ShaderStageTypeFlags shaderStages, ShaderAst::Module& shaderModule, const ShaderWriter::States& states) = 0; virtual std::shared_ptr InstantiateShaderModule(ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const void* source, std::size_t sourceSize, const ShaderWriter::States& states) = 0; std::shared_ptr InstantiateShaderModule(ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const std::filesystem::path& sourcePath, const ShaderWriter::States& states); virtual std::shared_ptr InstantiateTexture(const TextureInfo& params) = 0; diff --git a/include/Nazara/Shader/Ast/AstSerializer.hpp b/include/Nazara/Shader/Ast/AstSerializer.hpp index 4fda20db6..117f6aa55 100644 --- a/include/Nazara/Shader/Ast/AstSerializer.hpp +++ b/include/Nazara/Shader/Ast/AstSerializer.hpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include namespace Nz::ShaderAst { @@ -94,9 +94,11 @@ namespace Nz::ShaderAst inline ShaderAstSerializer(ByteStream& stream); ~ShaderAstSerializer() = default; - void Serialize(StatementPtr& shader); + void Serialize(Module& shader); private: + using AstSerializerBase::Serialize; + bool IsWriting() const override; void Node(ExpressionPtr& node) override; void Node(StatementPtr& node) override; @@ -125,9 +127,11 @@ namespace Nz::ShaderAst ShaderAstUnserializer(ByteStream& stream); ~ShaderAstUnserializer() = default; - StatementPtr Unserialize(); + ModulePtr Unserialize(); private: + using AstSerializerBase::Serialize; + bool IsWriting() const override; void Node(ExpressionPtr& node) override; void Node(StatementPtr& node) override; @@ -150,9 +154,9 @@ namespace Nz::ShaderAst ByteStream& m_stream; }; - NAZARA_SHADER_API ByteArray SerializeShader(StatementPtr& shader); - inline StatementPtr UnserializeShader(const void* data, std::size_t size); - NAZARA_SHADER_API StatementPtr UnserializeShader(ByteStream& stream); + NAZARA_SHADER_API ByteArray SerializeShader(Module& shader); + inline ModulePtr UnserializeShader(const void* data, std::size_t size); + NAZARA_SHADER_API ModulePtr UnserializeShader(ByteStream& stream); } #include diff --git a/include/Nazara/Shader/Ast/AstSerializer.inl b/include/Nazara/Shader/Ast/AstSerializer.inl index 1b6889878..9922530ff 100644 --- a/include/Nazara/Shader/Ast/AstSerializer.inl +++ b/include/Nazara/Shader/Ast/AstSerializer.inl @@ -173,7 +173,7 @@ namespace Nz::ShaderAst { } - inline StatementPtr UnserializeShader(const void* data, std::size_t size) + inline ModulePtr UnserializeShader(const void* data, std::size_t size) { ByteStream byteStream(data, size); return UnserializeShader(byteStream); diff --git a/include/Nazara/Shader/Ast/ConstantValue.hpp b/include/Nazara/Shader/Ast/ConstantValue.hpp index 5628dc4c1..07a1e5e19 100644 --- a/include/Nazara/Shader/Ast/ConstantValue.hpp +++ b/include/Nazara/Shader/Ast/ConstantValue.hpp @@ -31,7 +31,8 @@ namespace Nz::ShaderAst Vector4f, Vector2i32, Vector3i32, - Vector4i32 + Vector4i32, + std::string >; using ConstantValue = TypeListInstantiate; diff --git a/include/Nazara/Shader/Ast/Enums.hpp b/include/Nazara/Shader/Ast/Enums.hpp index 4c0d3cb85..cdd34b9f1 100644 --- a/include/Nazara/Shader/Ast/Enums.hpp +++ b/include/Nazara/Shader/Ast/Enums.hpp @@ -35,6 +35,7 @@ namespace Nz Entry, //< Entry point (function only) - has argument type Layout, //< Struct layout (struct only) - has argument style Location, //< Location (struct member only) - has argument index + LangVersion, //< NZSL version - has argument version string Set, //< Binding set (external var only) - has argument index Unroll, //< Unroll (for/for each only) - has argument mode }; @@ -136,6 +137,7 @@ namespace Nz Float32, //< f32 Int32, //< i32 UInt32, //< ui32 + String //< str }; enum class UnaryType diff --git a/include/Nazara/Shader/Ast/Module.hpp b/include/Nazara/Shader/Ast/Module.hpp new file mode 100644 index 000000000..a2860bca5 --- /dev/null +++ b/include/Nazara/Shader/Ast/Module.hpp @@ -0,0 +1,28 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Shader module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_SHADER_AST_MODULE_HPP +#define NAZARA_SHADER_AST_MODULE_HPP + +#include +#include +#include +#include + +namespace Nz::ShaderAst +{ + struct Module; + + using ModulePtr = std::shared_ptr; + + struct Module + { + MultiStatementPtr rootNode; + UInt32 shaderLangVersion; + }; +} + +#endif // NAZARA_SHADER_AST_MODULE_HPP diff --git a/include/Nazara/Shader/Ast/SanitizeVisitor.hpp b/include/Nazara/Shader/Ast/SanitizeVisitor.hpp index 0e443ef42..c9a6ff9e9 100644 --- a/include/Nazara/Shader/Ast/SanitizeVisitor.hpp +++ b/include/Nazara/Shader/Ast/SanitizeVisitor.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -30,8 +31,8 @@ namespace Nz::ShaderAst SanitizeVisitor(SanitizeVisitor&&) = delete; ~SanitizeVisitor() = default; - inline StatementPtr Sanitize(Statement& statement, std::string* error = nullptr); - StatementPtr Sanitize(Statement& statement, const Options& options, std::string* error = nullptr); + inline ModulePtr Sanitize(Module& module, std::string* error = nullptr); + ModulePtr Sanitize(Module& module, const Options& options, std::string* error = nullptr); SanitizeVisitor& operator=(const SanitizeVisitor&) = delete; SanitizeVisitor& operator=(SanitizeVisitor&&) = delete; @@ -177,8 +178,8 @@ namespace Nz::ShaderAst Context* m_context; }; - inline StatementPtr Sanitize(Statement& ast, std::string* error = nullptr); - inline StatementPtr Sanitize(Statement& ast, const SanitizeVisitor::Options& options, std::string* error = nullptr); + inline ModulePtr Sanitize(Module& module, std::string* error = nullptr); + inline ModulePtr Sanitize(Module& module, const SanitizeVisitor::Options& options, std::string* error = nullptr); } #include diff --git a/include/Nazara/Shader/Ast/SanitizeVisitor.inl b/include/Nazara/Shader/Ast/SanitizeVisitor.inl index f2c06ad00..3bb54f683 100644 --- a/include/Nazara/Shader/Ast/SanitizeVisitor.inl +++ b/include/Nazara/Shader/Ast/SanitizeVisitor.inl @@ -7,21 +7,21 @@ namespace Nz::ShaderAst { - inline StatementPtr SanitizeVisitor::Sanitize(Statement& statement, std::string* error) + inline ModulePtr SanitizeVisitor::Sanitize(Module& module, std::string* error) { - return Sanitize(statement, {}, error); + return Sanitize(module, {}, error); } - inline StatementPtr Sanitize(Statement& ast, std::string* error) + inline ModulePtr Sanitize(Module& module, std::string* error) { SanitizeVisitor sanitizer; - return sanitizer.Sanitize(ast, error); + return sanitizer.Sanitize(module, error); } - inline StatementPtr Sanitize(Statement& ast, const SanitizeVisitor::Options& options, std::string* error) + inline ModulePtr Sanitize(Module& module, const SanitizeVisitor::Options& options, std::string* error) { SanitizeVisitor sanitizer; - return sanitizer.Sanitize(ast, options, error); + return sanitizer.Sanitize(module, options, error); } } diff --git a/include/Nazara/Shader/GlslWriter.hpp b/include/Nazara/Shader/GlslWriter.hpp index 744f9fd8c..3beebf2f8 100644 --- a/include/Nazara/Shader/GlslWriter.hpp +++ b/include/Nazara/Shader/GlslWriter.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -31,8 +32,8 @@ namespace Nz GlslWriter(GlslWriter&&) = delete; ~GlslWriter() = default; - inline std::string Generate(ShaderAst::Statement& shader, const BindingMapping& bindingMapping = {}, const States& states = {}); - std::string Generate(std::optional shaderStage, ShaderAst::Statement& shader, const BindingMapping& bindingMapping = {}, const States& states = {}); + inline std::string Generate(ShaderAst::Module& module, const BindingMapping& bindingMapping = {}, const States& states = {}); + std::string Generate(std::optional shaderStage, ShaderAst::Module& module, const BindingMapping& bindingMapping = {}, const States& states = {}); void SetEnv(Environment environment); @@ -47,7 +48,7 @@ namespace Nz }; static const char* GetFlipYUniformName(); - static ShaderAst::StatementPtr Sanitize(ShaderAst::Statement& ast, std::unordered_map optionValues, std::string* error = nullptr); + static ShaderAst::ModulePtr Sanitize(ShaderAst::Module& module, std::unordered_map optionValues, std::string* error = nullptr); private: void Append(const ShaderAst::ArrayType& type); diff --git a/include/Nazara/Shader/GlslWriter.inl b/include/Nazara/Shader/GlslWriter.inl index 34f0354ea..c607fce79 100644 --- a/include/Nazara/Shader/GlslWriter.inl +++ b/include/Nazara/Shader/GlslWriter.inl @@ -12,7 +12,7 @@ namespace Nz { } - inline std::string GlslWriter::Generate(ShaderAst::Statement& shader, const BindingMapping& bindingMapping, const States& states) + inline std::string GlslWriter::Generate(ShaderAst::Module& shader, const BindingMapping& bindingMapping, const States& states) { return Generate(std::nullopt, shader, bindingMapping, states); } diff --git a/include/Nazara/Shader/LangWriter.hpp b/include/Nazara/Shader/LangWriter.hpp index 914ece90d..0bcccfab9 100644 --- a/include/Nazara/Shader/LangWriter.hpp +++ b/include/Nazara/Shader/LangWriter.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -28,7 +29,7 @@ namespace Nz LangWriter(LangWriter&&) = delete; ~LangWriter() = default; - std::string Generate(ShaderAst::Statement& shader, const States& conditions = {}); + std::string Generate(ShaderAst::Module& module, const States& conditions = {}); void SetEnv(Environment environment); @@ -44,6 +45,7 @@ namespace Nz struct EntryAttribute; struct LayoutAttribute; struct LocationAttribute; + struct NzslAttribute; struct SetAttribute; struct UnrollAttribute; @@ -74,6 +76,7 @@ namespace Nz void AppendAttribute(EntryAttribute entry); void AppendAttribute(LayoutAttribute layout); void AppendAttribute(LocationAttribute location); + void AppendAttribute(NzslAttribute nzslVersion); void AppendAttribute(SetAttribute set); void AppendAttribute(UnrollAttribute unroll); void AppendCommentSection(const std::string& section); diff --git a/include/Nazara/Shader/ShaderLangLexer.hpp b/include/Nazara/Shader/ShaderLangLexer.hpp index e39276b3e..39aa363e0 100644 --- a/include/Nazara/Shader/ShaderLangLexer.hpp +++ b/include/Nazara/Shader/ShaderLangLexer.hpp @@ -41,6 +41,16 @@ namespace Nz::ShaderLang using exception::exception; }; + class UnfinishedString : public std::exception + { + using exception::exception; + }; + + class UnrecognizedChar : public std::exception + { + using exception::exception; + }; + class UnrecognizedToken : public std::exception { using exception::exception; diff --git a/include/Nazara/Shader/ShaderLangParser.hpp b/include/Nazara/Shader/ShaderLangParser.hpp index 7e4176cc4..34b6bb15d 100644 --- a/include/Nazara/Shader/ShaderLangParser.hpp +++ b/include/Nazara/Shader/ShaderLangParser.hpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include @@ -34,6 +34,12 @@ namespace Nz::ShaderLang using runtime_error::runtime_error; }; + class DuplicateModule : public std::runtime_error + { + public: + using runtime_error::runtime_error; + }; + class ReservedKeyword : public std::exception { public: @@ -64,7 +70,7 @@ namespace Nz::ShaderLang inline Parser(); ~Parser() = default; - ShaderAst::StatementPtr Parse(const std::vector& tokens); + ShaderAst::ModulePtr Parse(const std::vector& tokens); private: // Flow control @@ -87,6 +93,7 @@ namespace Nz::ShaderLang std::vector ParseFunctionBody(); ShaderAst::StatementPtr ParseFunctionDeclaration(std::vector attributes = {}); ShaderAst::DeclareFunctionStatement::Parameter ParseFunctionParameter(); + void ParseModuleStatement(std::vector attributes); ShaderAst::StatementPtr ParseOptionDeclaration(); ShaderAst::StatementPtr ParseReturnStatement(); ShaderAst::StatementPtr ParseSingleStatement(); @@ -106,6 +113,7 @@ namespace Nz::ShaderLang std::vector ParseParameters(); ShaderAst::ExpressionPtr ParseParenthesisExpression(); ShaderAst::ExpressionPtr ParsePrimaryExpression(); + ShaderAst::ExpressionPtr ParseStringExpression(); ShaderAst::ExpressionPtr ParseVariableAssignation(); ShaderAst::AttributeType ParseIdentifierAsAttributeType(); @@ -118,16 +126,16 @@ namespace Nz::ShaderLang { std::size_t tokenCount; std::size_t tokenIndex = 0; - std::unique_ptr root; + ShaderAst::ModulePtr module; const Token* tokens; }; Context* m_context; }; - inline ShaderAst::StatementPtr Parse(const std::string_view& source); - inline ShaderAst::StatementPtr Parse(const std::vector& tokens); - NAZARA_SHADER_API ShaderAst::StatementPtr ParseFromFile(const std::filesystem::path& sourcePath); + inline ShaderAst::ModulePtr Parse(const std::string_view& source); + inline ShaderAst::ModulePtr Parse(const std::vector& tokens); + NAZARA_SHADER_API ShaderAst::ModulePtr ParseFromFile(const std::filesystem::path& sourcePath); } #include diff --git a/include/Nazara/Shader/ShaderLangParser.inl b/include/Nazara/Shader/ShaderLangParser.inl index 6e6eb19ec..8c2d3d9ed 100644 --- a/include/Nazara/Shader/ShaderLangParser.inl +++ b/include/Nazara/Shader/ShaderLangParser.inl @@ -12,12 +12,12 @@ namespace Nz::ShaderLang { } - inline ShaderAst::StatementPtr Parse(const std::string_view& source) + inline ShaderAst::ModulePtr Parse(const std::string_view& source) { return Parse(Tokenize(source)); } - inline ShaderAst::StatementPtr Parse(const std::vector& tokens) + inline ShaderAst::ModulePtr Parse(const std::vector& tokens) { Parser parser; return parser.Parse(tokens); diff --git a/include/Nazara/Shader/ShaderLangTokenList.hpp b/include/Nazara/Shader/ShaderLangTokenList.hpp index 22a68ded3..beb4cf17c 100644 --- a/include/Nazara/Shader/ShaderLangTokenList.hpp +++ b/include/Nazara/Shader/ShaderLangTokenList.hpp @@ -51,6 +51,7 @@ NAZARA_SHADERLANG_TOKEN(Multiply) NAZARA_SHADERLANG_TOKEN(MultiplyAssign) NAZARA_SHADERLANG_TOKEN(Minus) NAZARA_SHADERLANG_TOKEN(MinusAssign) +NAZARA_SHADERLANG_TOKEN(Module) NAZARA_SHADERLANG_TOKEN(Not) NAZARA_SHADERLANG_TOKEN(NotEqual) NAZARA_SHADERLANG_TOKEN(Plus) @@ -61,6 +62,7 @@ NAZARA_SHADERLANG_TOKEN(OpenParenthesis) NAZARA_SHADERLANG_TOKEN(Option) NAZARA_SHADERLANG_TOKEN(Return) NAZARA_SHADERLANG_TOKEN(Semicolon) +NAZARA_SHADERLANG_TOKEN(StringValue) NAZARA_SHADERLANG_TOKEN(Struct) NAZARA_SHADERLANG_TOKEN(While) diff --git a/include/Nazara/Shader/SpirvWriter.hpp b/include/Nazara/Shader/SpirvWriter.hpp index 1838383a3..95d3bf4df 100644 --- a/include/Nazara/Shader/SpirvWriter.hpp +++ b/include/Nazara/Shader/SpirvWriter.hpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -36,7 +36,7 @@ namespace Nz SpirvWriter(SpirvWriter&&) = delete; ~SpirvWriter() = default; - std::vector Generate(ShaderAst::Statement& shader, const States& states = {}); + std::vector Generate(ShaderAst::Module& module, const States& states = {}); void SetEnv(Environment environment); diff --git a/include/Nazara/VulkanRenderer/VulkanDevice.hpp b/include/Nazara/VulkanRenderer/VulkanDevice.hpp index a6f0d5230..7af627f0b 100644 --- a/include/Nazara/VulkanRenderer/VulkanDevice.hpp +++ b/include/Nazara/VulkanRenderer/VulkanDevice.hpp @@ -32,7 +32,7 @@ namespace Nz std::shared_ptr InstantiateRenderPass(std::vector attachments, std::vector subpassDescriptions, std::vector subpassDependencies) override; std::shared_ptr InstantiateRenderPipeline(RenderPipelineInfo pipelineInfo) override; std::shared_ptr InstantiateRenderPipelineLayout(RenderPipelineLayoutInfo pipelineLayoutInfo) override; - std::shared_ptr InstantiateShaderModule(ShaderStageTypeFlags stages, ShaderAst::Statement& shaderAst, const ShaderWriter::States& states) override; + std::shared_ptr InstantiateShaderModule(ShaderStageTypeFlags stages, ShaderAst::Module& shaderModule, const ShaderWriter::States& states) override; std::shared_ptr InstantiateShaderModule(ShaderStageTypeFlags stages, ShaderLanguage lang, const void* source, std::size_t sourceSize, const ShaderWriter::States& states) override; std::shared_ptr InstantiateTexture(const TextureInfo& params) override; std::shared_ptr InstantiateTextureSampler(const TextureSamplerInfo& params) override; diff --git a/include/Nazara/VulkanRenderer/VulkanShaderModule.hpp b/include/Nazara/VulkanRenderer/VulkanShaderModule.hpp index 7a06cc956..118434b18 100644 --- a/include/Nazara/VulkanRenderer/VulkanShaderModule.hpp +++ b/include/Nazara/VulkanRenderer/VulkanShaderModule.hpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include @@ -27,7 +27,7 @@ namespace Nz VulkanShaderModule(VulkanShaderModule&&) = delete; ~VulkanShaderModule() = default; - bool Create(Vk::Device& device, ShaderStageTypeFlags shaderStages, ShaderAst::Statement& shaderAst, const ShaderWriter::States& states); + bool Create(Vk::Device& device, ShaderStageTypeFlags shaderStages, ShaderAst::Module& shaderModule, const ShaderWriter::States& states); bool Create(Vk::Device& device, ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const void* source, std::size_t sourceSize, const ShaderWriter::States& states); inline const Vk::ShaderModule& GetHandle() const; diff --git a/src/Nazara/Graphics/BasicMaterial.cpp b/src/Nazara/Graphics/BasicMaterial.cpp index b02e922ff..ccb832b8a 100644 --- a/src/Nazara/Graphics/BasicMaterial.cpp +++ b/src/Nazara/Graphics/BasicMaterial.cpp @@ -260,8 +260,8 @@ namespace Nz std::vector> BasicMaterial::BuildShaders() { - ShaderAst::StatementPtr shaderAst = ShaderLang::Parse(std::string_view(reinterpret_cast(r_shader), sizeof(r_shader))); - auto shader = std::make_shared(ShaderStageType::Fragment | ShaderStageType::Vertex, shaderAst); + ShaderAst::ModulePtr shaderModule = ShaderLang::Parse(std::string_view(reinterpret_cast(r_shader), sizeof(r_shader))); + auto shader = std::make_shared(ShaderStageType::Fragment | ShaderStageType::Vertex, std::move(shaderModule)); return { std::move(shader) }; } diff --git a/src/Nazara/Graphics/DepthMaterial.cpp b/src/Nazara/Graphics/DepthMaterial.cpp index 146739129..e8fbf6fa1 100644 --- a/src/Nazara/Graphics/DepthMaterial.cpp +++ b/src/Nazara/Graphics/DepthMaterial.cpp @@ -18,8 +18,8 @@ namespace Nz std::vector> DepthMaterial::BuildShaders() { - ShaderAst::StatementPtr shaderAst = ShaderLang::Parse(std::string_view(reinterpret_cast(r_shader), sizeof(r_shader))); - auto shader = std::make_shared(ShaderStageType::Fragment | ShaderStageType::Vertex, shaderAst); + ShaderAst::ModulePtr shaderModule = ShaderLang::Parse(std::string_view(reinterpret_cast(r_shader), sizeof(r_shader))); + auto shader = std::make_shared(ShaderStageType::Fragment | ShaderStageType::Vertex, std::move(shaderModule)); return { std::move(shader) }; } diff --git a/src/Nazara/Graphics/FrameGraph.cpp b/src/Nazara/Graphics/FrameGraph.cpp index 979145004..4bc9dcfad 100644 --- a/src/Nazara/Graphics/FrameGraph.cpp +++ b/src/Nazara/Graphics/FrameGraph.cpp @@ -668,7 +668,7 @@ namespace Nz void FrameGraph::BuildPhysicalPasses() { - const std::shared_ptr& renderDevice = Graphics::Instance()->GetRenderDevice(); + const RenderPassCache& renderPassCache = Graphics::Instance()->GetRenderPassCache(); std::vector textureLayouts(m_pending.textures.size(), TextureLayout::Undefined); @@ -683,7 +683,7 @@ namespace Nz std::vector subpassesDesc; std::vector subpassesDeps; - auto RegisterColorInputRead = [&](const FramePass::Input& input, PhysicalPassData::Subpass& subpass) + auto RegisterColorInputRead = [&](const FramePass::Input& input) { std::size_t textureId = Retrieve(m_pending.attachmentToTextures, input.attachmentId); @@ -766,7 +766,7 @@ namespace Nz for (const auto& input : subpassInputs) { if (input.doesRead) - RegisterColorInputRead(input, subpass); + RegisterColorInputRead(input); } for (const auto& output : subpassOutputs) @@ -887,8 +887,7 @@ namespace Nz BuildPhysicalPassDependencies(colorAttachmentCount, depthStencilAttachmentIndex.has_value(), renderPassAttachments, subpassesDesc, subpassesDeps); - m_pending.renderPasses.push_back(Graphics::Instance()->GetRenderPassCache().Get(renderPassAttachments, subpassesDesc, subpassesDeps)); - //m_pending.renderPasses.push_back(renderDevice->InstantiateRenderPass(std::move(renderPassAttachments), std::move(subpassesDesc), std::move(subpassesDeps))); + m_pending.renderPasses.push_back(renderPassCache.Get(renderPassAttachments, subpassesDesc, subpassesDeps)); physicalPassIndex++; } diff --git a/src/Nazara/Graphics/PhongLightingMaterial.cpp b/src/Nazara/Graphics/PhongLightingMaterial.cpp index 4568d6fc3..7a87abb4b 100644 --- a/src/Nazara/Graphics/PhongLightingMaterial.cpp +++ b/src/Nazara/Graphics/PhongLightingMaterial.cpp @@ -347,7 +347,7 @@ namespace Nz std::vector> PhongLightingMaterial::BuildShaders() { - ShaderAst::StatementPtr shaderAst; + ShaderAst::ModulePtr shaderModule; #ifdef NAZARA_DEBUG std::filesystem::path shaderPath = "../../src/Nazara/Graphics/Resources/Shaders/phong_material.nzsl"; @@ -355,7 +355,7 @@ namespace Nz { try { - shaderAst = ShaderLang::ParseFromFile(shaderPath); + shaderModule = ShaderLang::ParseFromFile(shaderPath); } catch (const std::exception& e) { @@ -364,10 +364,10 @@ namespace Nz } #endif - if (!shaderAst) - shaderAst = ShaderLang::Parse(std::string_view(reinterpret_cast(r_shader), sizeof(r_shader))); + if (!shaderModule) + shaderModule = ShaderLang::Parse(std::string_view(reinterpret_cast(r_shader), sizeof(r_shader))); - auto shader = std::make_shared(ShaderStageType::Fragment | ShaderStageType::Vertex, shaderAst); + auto shader = std::make_shared(ShaderStageType::Fragment | ShaderStageType::Vertex, std::move(shaderModule)); return { std::move(shader) }; } diff --git a/src/Nazara/Graphics/Resources/Shaders/basic_material.nzsl b/src/Nazara/Graphics/Resources/Shaders/basic_material.nzsl index 5277c74ff..d8b11faf0 100644 --- a/src/Nazara/Graphics/Resources/Shaders/basic_material.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/basic_material.nzsl @@ -1,3 +1,6 @@ +[nzsl_version("1.0")] +module; + option HasDiffuseTexture: bool = false; option HasAlphaTexture: bool = false; option AlphaTest: bool = false; diff --git a/src/Nazara/Graphics/Resources/Shaders/blit.nzsl b/src/Nazara/Graphics/Resources/Shaders/blit.nzsl index c934a5728..1be6c2d4b 100644 --- a/src/Nazara/Graphics/Resources/Shaders/blit.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/blit.nzsl @@ -1,3 +1,6 @@ +[nzsl_version("1.0")] +module; + external { [binding(0)] texture: sampler2D[f32] diff --git a/src/Nazara/Graphics/Resources/Shaders/depth_material.nzsl b/src/Nazara/Graphics/Resources/Shaders/depth_material.nzsl index 8a14d22b2..b7c64d788 100644 --- a/src/Nazara/Graphics/Resources/Shaders/depth_material.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/depth_material.nzsl @@ -1,3 +1,6 @@ +[nzsl_version("1.0")] +module; + option HasDiffuseTexture: bool = false; option HasAlphaTexture: bool = false; option AlphaTest: bool = false; diff --git a/src/Nazara/Graphics/Resources/Shaders/phong_material.nzsl b/src/Nazara/Graphics/Resources/Shaders/phong_material.nzsl index 446ce857d..f2748e10c 100644 --- a/src/Nazara/Graphics/Resources/Shaders/phong_material.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/phong_material.nzsl @@ -1,3 +1,6 @@ +[nzsl_version("1.0")] +module; + // Basic material options option HasDiffuseTexture: bool = false; option HasAlphaTexture: bool = false; diff --git a/src/Nazara/Graphics/UberShader.cpp b/src/Nazara/Graphics/UberShader.cpp index 84a33e426..8182bcf35 100644 --- a/src/Nazara/Graphics/UberShader.cpp +++ b/src/Nazara/Graphics/UberShader.cpp @@ -13,13 +13,14 @@ namespace Nz { - UberShader::UberShader(ShaderStageTypeFlags shaderStages, const ShaderAst::StatementPtr& shaderAst) : + UberShader::UberShader(ShaderStageTypeFlags shaderStages, ShaderAst::ModulePtr shaderModule) : + m_shaderModule(std::move(shaderModule)), m_shaderStages(shaderStages) { NazaraAssert(m_shaderStages != 0, "there must be at least one shader stage"); + NazaraAssert(m_shaderModule, "invalid shader module"); //TODO: Try to partially sanitize shader? - m_shaderAst = ShaderAst::Clone(*shaderAst); std::size_t optionCount = 0; @@ -33,6 +34,8 @@ namespace Nz callbacks.onOptionDeclaration = [&](const std::string& optionName, const ShaderAst::ExpressionValue& optionType) { + //TODO: Check optionType + m_optionIndexByName[optionName] = Option{ optionCount }; @@ -41,7 +44,7 @@ namespace Nz }; ShaderAst::AstReflect reflect; - reflect.Reflect(*m_shaderAst, callbacks); + reflect.Reflect(*m_shaderModule->rootNode, callbacks); if ((m_shaderStages & supportedStageType) != m_shaderStages) throw std::runtime_error("shader doesn't support all required shader stages"); @@ -63,7 +66,7 @@ namespace Nz states.optionValues[i] = config.optionValues[i]; } - std::shared_ptr stage = Graphics::Instance()->GetRenderDevice()->InstantiateShaderModule(m_shaderStages, *m_shaderAst, std::move(states)); + std::shared_ptr stage = Graphics::Instance()->GetRenderDevice()->InstantiateShaderModule(m_shaderStages, *m_shaderModule, std::move(states)); it = m_combinations.emplace(config, std::move(stage)).first; } diff --git a/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp b/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp index 2d8e94653..1696336f1 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp @@ -144,9 +144,9 @@ namespace Nz return std::make_shared(std::move(pipelineLayoutInfo)); } - std::shared_ptr OpenGLDevice::InstantiateShaderModule(ShaderStageTypeFlags shaderStages, ShaderAst::Statement& shaderAst, const ShaderWriter::States& states) + std::shared_ptr OpenGLDevice::InstantiateShaderModule(ShaderStageTypeFlags shaderStages, ShaderAst::Module& shaderModule, const ShaderWriter::States& states) { - return std::make_shared(*this, shaderStages, shaderAst, states); + return std::make_shared(*this, shaderStages, shaderModule, states); } std::shared_ptr OpenGLDevice::InstantiateShaderModule(ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const void* source, std::size_t sourceSize, const ShaderWriter::States& states) diff --git a/src/Nazara/OpenGLRenderer/OpenGLRenderPipeline.cpp b/src/Nazara/OpenGLRenderer/OpenGLRenderPipeline.cpp index 055342ef9..e60b644be 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLRenderPipeline.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLRenderPipeline.cpp @@ -47,8 +47,12 @@ namespace Nz { if (!stageFlags.Test(stage)) { - ShaderAst::StatementPtr dummyAst = ShaderBuilder::DeclareFunction(stage, "main", {}, {}); - OpenGLShaderModule shaderModule(device, stage, *dummyAst); + ShaderAst::Module dummyModule; + dummyModule.rootNode = ShaderBuilder::MultiStatement(); + dummyModule.rootNode->statements.push_back(ShaderBuilder::DeclareFunction(stage, "main", {}, {})); + dummyModule.shaderLangVersion = 100; + + OpenGLShaderModule shaderModule(device, stage, dummyModule); stageFlags |= shaderModule.Attach(m_program, pipelineLayout.GetBindingMapping()); } }; diff --git a/src/Nazara/OpenGLRenderer/OpenGLShaderModule.cpp b/src/Nazara/OpenGLRenderer/OpenGLShaderModule.cpp index 9fc12d6da..5e964ef52 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLShaderModule.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLShaderModule.cpp @@ -13,11 +13,11 @@ namespace Nz { - OpenGLShaderModule::OpenGLShaderModule(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderAst::Statement& shaderAst, const ShaderWriter::States& states) : + OpenGLShaderModule::OpenGLShaderModule(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderAst::Module& shaderModule, const ShaderWriter::States& states) : m_device(device) { NazaraAssert(shaderStages != 0, "at least one shader stage must be specified"); - Create(device, shaderStages, shaderAst, states); + Create(device, shaderStages, shaderModule, states); } OpenGLShaderModule::OpenGLShaderModule(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const void* source, std::size_t sourceSize, const ShaderWriter::States& states) : @@ -58,7 +58,7 @@ namespace Nz std::vector tokens = Nz::ShaderLang::Tokenize(std::string_view(static_cast(source), sourceSize)); Nz::ShaderLang::Parser parser; - Nz::ShaderAst::StatementPtr shaderAst = parser.Parse(tokens); + Nz::ShaderAst::ModulePtr shaderAst = parser.Parse(tokens); Create(device, shaderStages, *shaderAst, states); break; } @@ -137,11 +137,11 @@ namespace Nz return stageFlags; } - void OpenGLShaderModule::Create(OpenGLDevice& /*device*/, ShaderStageTypeFlags shaderStages, ShaderAst::Statement& shaderAst, const ShaderWriter::States& states) + void OpenGLShaderModule::Create(OpenGLDevice& /*device*/, ShaderStageTypeFlags shaderStages, ShaderAst::Module& shaderModule, const ShaderWriter::States& states) { m_states = states; m_states.sanitized = true; //< Shader is always sanitized (because of keywords) - std::shared_ptr sanitized = GlslWriter::Sanitize(shaderAst, states.optionValues); + ShaderAst::ModulePtr sanitized = GlslWriter::Sanitize(shaderModule, states.optionValues); for (std::size_t i = 0; i < ShaderStageTypeCount; ++i) { diff --git a/src/Nazara/Shader/Ast/AstConstantPropagationVisitor.cpp b/src/Nazara/Shader/Ast/AstConstantPropagationVisitor.cpp index d648c7596..44a670b41 100644 --- a/src/Nazara/Shader/Ast/AstConstantPropagationVisitor.cpp +++ b/src/Nazara/Shader/Ast/AstConstantPropagationVisitor.cpp @@ -834,6 +834,7 @@ namespace Nz::ShaderAst case PrimitiveType::Float32: optimized = PropagateSingleValueCast(constantExpr); break; case PrimitiveType::Int32: optimized = PropagateSingleValueCast(constantExpr); break; case PrimitiveType::UInt32: optimized = PropagateSingleValueCast(constantExpr); break; + case PrimitiveType::String: break; } } } @@ -866,7 +867,7 @@ namespace Nz::ShaderAst if constexpr (std::is_same_v) throw std::runtime_error("invalid type (value expected)"); - else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v) + else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v) constantValues.push_back(arg); else if constexpr (std::is_same_v || std::is_same_v) { diff --git a/src/Nazara/Shader/Ast/AstSerializer.cpp b/src/Nazara/Shader/Ast/AstSerializer.cpp index 5d5a2a70d..9092f4e11 100644 --- a/src/Nazara/Shader/Ast/AstSerializer.cpp +++ b/src/Nazara/Shader/Ast/AstSerializer.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include namespace Nz::ShaderAst @@ -120,7 +121,7 @@ namespace Nz::ShaderAst Value(value); }; - static_assert(std::variant_size_v == 11); + static_assert(std::variant_size_v == 12); switch (typeIndex) { case 0: break; @@ -134,6 +135,7 @@ namespace Nz::ShaderAst case 8: SerializeValue(Vector2i32()); break; case 9: SerializeValue(Vector3i32()); break; case 10: SerializeValue(Vector4i32()); break; + case 11: SerializeValue(std::string()); break; default: throw std::runtime_error("unexpected data type"); } } @@ -327,11 +329,12 @@ namespace Nz::ShaderAst Node(node.body); } - void ShaderAstSerializer::Serialize(StatementPtr& shader) + void ShaderAstSerializer::Serialize(Module& module) { m_stream << s_magicNumber << s_currentVersion; - Node(shader); + m_stream << module.shaderLangVersion; + Serialize(*module.rootNode); m_stream.FlushBits(); } @@ -514,7 +517,7 @@ namespace Nz::ShaderAst m_stream << val; } - StatementPtr ShaderAstUnserializer::Unserialize() + ModulePtr ShaderAstUnserializer::Unserialize() { UInt32 magicNumber; UInt32 version; @@ -526,13 +529,15 @@ namespace Nz::ShaderAst if (version > s_currentVersion) throw std::runtime_error("unsupported version"); - StatementPtr node; + ModulePtr module = std::make_shared(); - Node(node); - if (!node) - throw std::runtime_error("functions can only have statements"); + m_stream >> module->shaderLangVersion; - return node; + module->rootNode = ShaderBuilder::MultiStatement(); + ShaderSerializerVisitor visitor(*this); + module->rootNode->Visit(visitor); + + return module; } bool ShaderAstUnserializer::IsWriting() const @@ -827,18 +832,18 @@ namespace Nz::ShaderAst } - ByteArray SerializeShader(StatementPtr& shader) + ByteArray SerializeShader(Module& module) { ByteArray byteArray; ByteStream stream(&byteArray, OpenModeFlags(OpenMode::WriteOnly)); ShaderAstSerializer serializer(stream); - serializer.Serialize(shader); + serializer.Serialize(module); return byteArray; } - StatementPtr UnserializeShader(ByteStream& stream) + ModulePtr UnserializeShader(ByteStream& stream) { ShaderAstUnserializer unserializer(stream); return unserializer.Unserialize(); diff --git a/src/Nazara/Shader/Ast/ConstantValue.cpp b/src/Nazara/Shader/Ast/ConstantValue.cpp index 2de3b2e83..959b40700 100644 --- a/src/Nazara/Shader/Ast/ConstantValue.cpp +++ b/src/Nazara/Shader/Ast/ConstantValue.cpp @@ -24,6 +24,8 @@ namespace Nz::ShaderAst return PrimitiveType::Int32; else if constexpr (std::is_same_v) return PrimitiveType::UInt32; + else if constexpr (std::is_same_v) + return PrimitiveType::String; else if constexpr (std::is_same_v) return VectorType{ 2, PrimitiveType::Float32 }; else if constexpr (std::is_same_v) diff --git a/src/Nazara/Shader/Ast/SanitizeVisitor.cpp b/src/Nazara/Shader/Ast/SanitizeVisitor.cpp index 5a81b883e..550f45b48 100644 --- a/src/Nazara/Shader/Ast/SanitizeVisitor.cpp +++ b/src/Nazara/Shader/Ast/SanitizeVisitor.cpp @@ -112,9 +112,10 @@ namespace Nz::ShaderAst std::vector* currentStatementList = nullptr; }; - StatementPtr SanitizeVisitor::Sanitize(Statement& statement, const Options& options, std::string* error) + ModulePtr SanitizeVisitor::Sanitize(Module& module, const Options& options, std::string* error) { - StatementPtr clone; + ModulePtr clone = std::make_shared(); + clone->shaderLangVersion = module.shaderLangVersion; Context currentContext; currentContext.options = options; @@ -129,7 +130,7 @@ namespace Nz::ShaderAst // First pass, evaluate everything except function code try { - clone = AstCloner::Clone(statement); + clone->rootNode = static_unique_pointer_cast(AstCloner::Clone(*module.rootNode)); } catch (const AstError& err) { diff --git a/src/Nazara/Shader/GlslWriter.cpp b/src/Nazara/Shader/GlslWriter.cpp index a00c2056c..bab6822da 100644 --- a/src/Nazara/Shader/GlslWriter.cpp +++ b/src/Nazara/Shader/GlslWriter.cpp @@ -152,7 +152,7 @@ namespace Nz unsigned int indentLevel = 0; }; - std::string GlslWriter::Generate(std::optional shaderStage, ShaderAst::Statement& shader, const BindingMapping& bindingMapping, const States& states) + std::string GlslWriter::Generate(std::optional shaderStage, ShaderAst::Module& module, const BindingMapping& bindingMapping, const States& states) { State state(bindingMapping); state.optionValues = states.optionValues; @@ -164,15 +164,15 @@ namespace Nz m_currentState = nullptr; }); - ShaderAst::StatementPtr sanitizedAst; + ShaderAst::ModulePtr sanitizedModule; ShaderAst::Statement* targetAst; if (!states.sanitized) { - sanitizedAst = Sanitize(shader, states.optionValues); - targetAst = sanitizedAst.get(); + sanitizedModule = Sanitize(module, states.optionValues); + targetAst = sanitizedModule->rootNode.get(); } else - targetAst = &shader; + targetAst = module.rootNode.get(); ShaderAst::StatementPtr optimizedAst; @@ -210,7 +210,7 @@ namespace Nz return s_flipYUniformName; } - ShaderAst::StatementPtr GlslWriter::Sanitize(ShaderAst::Statement& ast, std::unordered_map optionValues, std::string* error) + ShaderAst::ModulePtr GlslWriter::Sanitize(ShaderAst::Module& module, std::unordered_map optionValues, std::string* error) { // Always sanitize for reserved identifiers ShaderAst::SanitizeVisitor::Options options; @@ -228,7 +228,7 @@ namespace Nz "cross", "dot", "exp", "length", "max", "min", "pow", "texture" }; - return ShaderAst::Sanitize(ast, options, error); + return ShaderAst::Sanitize(module, options, error); } void GlslWriter::Append(const ShaderAst::ArrayType& /*type*/) @@ -856,6 +856,8 @@ namespace Nz if constexpr (std::is_same_v) throw std::runtime_error("invalid type (value expected)"); + else if constexpr (std::is_same_v) + throw std::runtime_error("unexpected string litteral"); else if constexpr (std::is_same_v) Append((arg) ? "true" : "false"); else if constexpr (std::is_same_v || std::is_same_v) diff --git a/src/Nazara/Shader/LangWriter.cpp b/src/Nazara/Shader/LangWriter.cpp index e32dbf8a6..10ee07690 100644 --- a/src/Nazara/Shader/LangWriter.cpp +++ b/src/Nazara/Shader/LangWriter.cpp @@ -76,6 +76,13 @@ namespace Nz inline bool HasValue() const { return locationIndex.HasValue(); } }; + struct LangWriter::NzslAttribute + { + const ShaderAst::ExpressionValue& version; + + inline bool HasValue() const { return version.HasValue(); } + }; + struct LangWriter::SetAttribute { const ShaderAst::ExpressionValue& setIndex; @@ -101,7 +108,7 @@ namespace Nz unsigned int indentLevel = 0; }; - std::string LangWriter::Generate(ShaderAst::Statement& shader, const States& /*states*/) + std::string LangWriter::Generate(ShaderAst::Module& module, const States& /*states*/) { State state; m_currentState = &state; @@ -110,11 +117,11 @@ namespace Nz m_currentState = nullptr; }); - ShaderAst::StatementPtr sanitizedAst = ShaderAst::Sanitize(shader); + ShaderAst::ModulePtr sanitizedModule = ShaderAst::Sanitize(module); AppendHeader(); - sanitizedAst->Visit(*this); + sanitizedModule->rootNode->Visit(*this); return state.stream.str(); } @@ -453,6 +460,10 @@ namespace Nz Append(")"); } + void LangWriter::AppendAttribute(NzslAttribute nzslVersion) + { + } + void LangWriter::AppendAttribute(SetAttribute set) { if (!set.HasValue()) @@ -766,6 +777,8 @@ namespace Nz Append((arg) ? "true" : "false"); else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) Append(std::to_string(arg)); + else if constexpr (std::is_same_v) + Append('"', arg, '"'); //< TODO: Escape string else if constexpr (std::is_same_v) Append("vec2[f32](" + std::to_string(arg.x) + ", " + std::to_string(arg.y) + ")"); else if constexpr (std::is_same_v) diff --git a/src/Nazara/Shader/ShaderLangLexer.cpp b/src/Nazara/Shader/ShaderLangLexer.cpp index 70955d6e4..798a92f3c 100644 --- a/src/Nazara/Shader/ShaderLangLexer.cpp +++ b/src/Nazara/Shader/ShaderLangLexer.cpp @@ -51,6 +51,7 @@ namespace Nz::ShaderLang { "if", TokenType::If }, { "in", TokenType::In }, { "let", TokenType::Let }, + { "module", TokenType::Module }, { "option", TokenType::Option }, { "return", TokenType::Return }, { "struct", TokenType::Struct }, @@ -65,7 +66,7 @@ namespace Nz::ShaderLang if (currentPos + advance < str.size() && str[currentPos + advance] != '\0') return str[currentPos + advance]; else - return char(-1); + return '\0'; }; auto IsAlphaNum = [&](const char c) @@ -85,7 +86,7 @@ namespace Nz::ShaderLang token.column = static_cast(currentPos - lastLineFeed); token.line = lineNumber; - if (c == -1) + if (c == '\0') { token.type = TokenType::EndOfStream; tokens.push_back(std::move(token)); @@ -385,6 +386,55 @@ namespace Nz::ShaderLang case '[': tokenType = TokenType::OpenSquareBracket; break; case ']': tokenType = TokenType::ClosingSquareBracket; break; + case '"': + { + // string litteral + currentPos++; + + std::string litteral; + + char current; + while ((current = Peek(0)) != '"') + { + char character; + switch (current) + { + case '\0': + case '\n': + case '\r': + throw UnfinishedString{}; + + case '\\': + { + currentPos++; + char next = Peek(); + switch (next) + { + case 'n': character = '\n'; break; + case 'r': character = '\r'; break; + case 't': character = '\t'; break; + case '"': character = '"'; break; + case '\\': character = '\\'; break; + default: + throw UnrecognizedChar{}; + } + break; + } + + default: + character = current; + break; + } + + litteral.push_back(character); + currentPos++; + } + + tokenType = TokenType::StringValue; + token.data = std::move(litteral); + break; + } + default: { if (IsAlphaNum(c)) diff --git a/src/Nazara/Shader/ShaderLangParser.cpp b/src/Nazara/Shader/ShaderLangParser.cpp index 232943410..e89ff77f7 100644 --- a/src/Nazara/Shader/ShaderLangParser.cpp +++ b/src/Nazara/Shader/ShaderLangParser.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include namespace Nz::ShaderLang @@ -29,6 +30,7 @@ namespace Nz::ShaderLang { "entry", ShaderAst::AttributeType::Entry }, { "layout", ShaderAst::AttributeType::Layout }, { "location", ShaderAst::AttributeType::Location }, + { "nzsl_version", ShaderAst::AttributeType::LangVersion }, { "set", ShaderAst::AttributeType::Set }, { "unroll", ShaderAst::AttributeType::Unroll }, }; @@ -106,18 +108,24 @@ namespace Nz::ShaderLang } } - ShaderAst::StatementPtr Parser::Parse(const std::vector& tokens) + ShaderAst::ModulePtr Parser::Parse(const std::vector& tokens) { Context context; context.tokenCount = tokens.size(); context.tokens = tokens.data(); - context.root = std::make_unique(); - m_context = &context; std::vector attributes; + auto EnsureModule = [this]() -> ShaderAst::Module& + { + if (!m_context->module) + throw UnexpectedToken{ "unexpected token before module declaration" }; + + return *m_context->module; + }; + bool reachedEndOfStream = false; while (!reachedEndOfStream) { @@ -125,11 +133,14 @@ namespace Nz::ShaderLang switch (nextToken.type) { case TokenType::Const: + { if (!attributes.empty()) throw UnexpectedToken{}; - context.root->statements.push_back(ParseConstStatement()); + const auto& module = EnsureModule(); + module.rootNode->statements.push_back(ParseConstStatement()); break; + } case TokenType::EndOfStream: if (!attributes.empty()) @@ -139,38 +150,58 @@ namespace Nz::ShaderLang break; case TokenType::External: - context.root->statements.push_back(ParseExternalBlock(std::move(attributes))); + { + const auto& module = EnsureModule(); + module.rootNode->statements.push_back(ParseExternalBlock(std::move(attributes))); attributes.clear(); break; + } case TokenType::OpenSquareBracket: assert(attributes.empty()); attributes = ParseAttributes(); break; + case TokenType::Module: + if (attributes.empty()) + throw UnexpectedToken{}; + + ParseModuleStatement(std::move(attributes)); + attributes.clear(); + break; + case TokenType::Option: + { if (!attributes.empty()) throw UnexpectedToken{}; - context.root->statements.push_back(ParseOptionDeclaration()); + const auto& module = EnsureModule(); + module.rootNode->statements.push_back(ParseOptionDeclaration()); break; + } case TokenType::FunctionDeclaration: - context.root->statements.push_back(ParseFunctionDeclaration(std::move(attributes))); + { + const auto& module = EnsureModule(); + module.rootNode->statements.push_back(ParseFunctionDeclaration(std::move(attributes))); attributes.clear(); break; + } case TokenType::Struct: - context.root->statements.push_back(ParseStructDeclaration(std::move(attributes))); + { + const auto& module = EnsureModule(); + module.rootNode->statements.push_back(ParseStructDeclaration(std::move(attributes))); attributes.clear(); break; + } default: throw UnexpectedToken{}; } } - return std::move(context.root); + return std::move(context.module); } const Token& Parser::Advance() @@ -604,6 +635,74 @@ namespace Nz::ShaderLang return { parameterName, std::move(parameterType) }; } + void Parser::ParseModuleStatement(std::vector attributes) + { + Expect(Advance(), TokenType::Module); + + if (m_context->module) + throw DuplicateModule{ "you must set one module statement per file" }; + + std::optional moduleVersion; + + for (auto&& [attributeType, arg] : attributes) + { + switch (attributeType) + { + case ShaderAst::AttributeType::LangVersion: + { + // Version parsing + if (moduleVersion.has_value()) + throw AttributeError{ "attribute " + std::string("nzsl_version") + " must be present once" }; + + if (!arg) + throw AttributeError{ "attribute " + std::string("nzsl_version") + " requires a parameter"}; + + const ShaderAst::ExpressionPtr& expr = *arg; + if (expr->GetType() != ShaderAst::NodeType::ConstantValueExpression) + throw AttributeError{ "attribute " + std::string("nzsl_version") + " expect a single string parameter" }; + + auto& constantValue = SafeCast(*expr); + if (ShaderAst::GetExpressionType(constantValue.value) != ShaderAst::ExpressionType{ ShaderAst::PrimitiveType::String }) + throw AttributeError{ "attribute " + std::string("nzsl_version") + " expect a single string parameter" }; + + const std::string& versionStr = std::get(constantValue.value); + + std::regex versionRegex(R"(^(\d+)(\.(\d+)(\.(\d+))?)?$)", std::regex::ECMAScript); + + std::smatch versionMatch; + if (!std::regex_match(versionStr, versionMatch, versionRegex)) + throw AttributeError("invalid version for attribute nzsl"); + + assert(versionMatch.size() == 6); + + std::uint32_t version = 0; + version += std::stoi(versionMatch[1]) * 100; + + if (versionMatch.length(3) > 0) + version += std::stoi(versionMatch[3]) * 10; + + if (versionMatch.length(5) > 0) + version += std::stoi(versionMatch[5]) * 1; + + moduleVersion = version; + break; + } + + default: + throw AttributeError{ "unhandled attribute for module" }; + } + } + + if (!moduleVersion.has_value()) + throw AttributeError{ "missing module version" }; + + m_context->module = std::make_shared(); + m_context->module->rootNode = ShaderBuilder::MultiStatement(); + m_context->module->shaderLangVersion = *moduleVersion; + + Expect(Advance(), TokenType::Semicolon); + } + ShaderAst::StatementPtr Parser::ParseOptionDeclaration() { Expect(Advance(), TokenType::Option); @@ -1132,11 +1231,20 @@ namespace Nz::ShaderLang case TokenType::OpenParenthesis: return ParseParenthesisExpression(); + case TokenType::StringValue: + return ParseStringExpression(); + default: throw UnexpectedToken{}; } } + ShaderAst::ExpressionPtr Parser::ParseStringExpression() + { + const Token& litteralToken = Expect(Advance(), TokenType::StringValue); + return ShaderBuilder::Constant(std::get(litteralToken.data)); + } + ShaderAst::AttributeType Parser::ParseIdentifierAsAttributeType() { const Token& identifierToken = Expect(Advance(), TokenType::Identifier); @@ -1192,7 +1300,7 @@ namespace Nz::ShaderLang } } - ShaderAst::StatementPtr ParseFromFile(const std::filesystem::path& sourcePath) + ShaderAst::ModulePtr ParseFromFile(const std::filesystem::path& sourcePath) { File file(sourcePath); if (!file.Open(OpenMode::ReadOnly | OpenMode::Text)) diff --git a/src/Nazara/Shader/SpirvConstantCache.cpp b/src/Nazara/Shader/SpirvConstantCache.cpp index d7e94a5c1..289632a6f 100644 --- a/src/Nazara/Shader/SpirvConstantCache.cpp +++ b/src/Nazara/Shader/SpirvConstantCache.cpp @@ -465,6 +465,8 @@ namespace Nz if constexpr (std::is_same_v) throw std::runtime_error("invalid type (value expected)"); + else if constexpr (std::is_same_v) + throw std::runtime_error("unexpected string litteral"); else if constexpr (std::is_same_v) return ConstantBool{ arg }; else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) diff --git a/src/Nazara/Shader/SpirvWriter.cpp b/src/Nazara/Shader/SpirvWriter.cpp index 5416240e4..079e2e9f4 100644 --- a/src/Nazara/Shader/SpirvWriter.cpp +++ b/src/Nazara/Shader/SpirvWriter.cpp @@ -496,11 +496,10 @@ namespace Nz { } - std::vector SpirvWriter::Generate(ShaderAst::Statement& shader, const States& states) + std::vector SpirvWriter::Generate(ShaderAst::Module& module, const States& states) { - ShaderAst::Statement* targetAst = &shader; - - ShaderAst::StatementPtr sanitizedAst; + ShaderAst::ModulePtr sanitizedModule; + ShaderAst::Statement* targetAst; if (!states.sanitized) { ShaderAst::SanitizeVisitor::Options options; @@ -512,9 +511,11 @@ namespace Nz options.splitMultipleBranches = true; options.useIdentifierAccessesForStructs = false; - sanitizedAst = ShaderAst::Sanitize(shader, options); - targetAst = sanitizedAst.get(); + sanitizedModule = ShaderAst::Sanitize(module, options); + targetAst = sanitizedModule->rootNode.get(); } + else + targetAst = module.rootNode.get(); ShaderAst::StatementPtr optimizedAst; if (states.optimize) diff --git a/src/Nazara/VulkanRenderer/VulkanDevice.cpp b/src/Nazara/VulkanRenderer/VulkanDevice.cpp index 0f03c63b6..4b6a69ced 100644 --- a/src/Nazara/VulkanRenderer/VulkanDevice.cpp +++ b/src/Nazara/VulkanRenderer/VulkanDevice.cpp @@ -61,10 +61,10 @@ namespace Nz return pipelineLayout; } - std::shared_ptr VulkanDevice::InstantiateShaderModule(ShaderStageTypeFlags stages, ShaderAst::Statement& shaderAst, const ShaderWriter::States& states) + std::shared_ptr VulkanDevice::InstantiateShaderModule(ShaderStageTypeFlags stages, ShaderAst::Module& shaderModule, const ShaderWriter::States& states) { auto stage = std::make_shared(); - if (!stage->Create(*this, stages, shaderAst, states)) + if (!stage->Create(*this, stages, shaderModule, states)) throw std::runtime_error("failed to instantiate vulkan shader module"); return stage; diff --git a/src/Nazara/VulkanRenderer/VulkanShaderModule.cpp b/src/Nazara/VulkanRenderer/VulkanShaderModule.cpp index 22cd120fc..e83335317 100644 --- a/src/Nazara/VulkanRenderer/VulkanShaderModule.cpp +++ b/src/Nazara/VulkanRenderer/VulkanShaderModule.cpp @@ -57,14 +57,14 @@ namespace Nz }; } - bool VulkanShaderModule::Create(Vk::Device& device, ShaderStageTypeFlags shaderStages, ShaderAst::Statement& shaderAst, const ShaderWriter::States& states) + bool VulkanShaderModule::Create(Vk::Device& device, ShaderStageTypeFlags shaderStages, ShaderAst::Module& shaderModule, const ShaderWriter::States& states) { SpirvWriter::Environment env; SpirvWriter writer; writer.SetEnv(env); - std::vector code = writer.Generate(shaderAst, states); + std::vector code = writer.Generate(shaderModule, states); return Create(device, shaderStages, ShaderLanguage::SpirV, code.data(), code.size() * sizeof(UInt32), {}); } @@ -88,8 +88,8 @@ namespace Nz std::vector tokens = ShaderLang::Tokenize(std::string_view(static_cast(source), sourceSize)); ShaderLang::Parser parser; - ShaderAst::StatementPtr shaderAst = parser.Parse(tokens); - return Create(device, shaderStages, *shaderAst, states); + ShaderAst::ModulePtr shaderModule = parser.Parse(tokens); + return Create(device, shaderStages, *shaderModule, states); } case ShaderLanguage::SpirV: