diff --git a/examples/RenderTest/main.cpp b/examples/RenderTest/main.cpp index c31f02352..e096c5daa 100644 --- a/examples/RenderTest/main.cpp +++ b/examples/RenderTest/main.cpp @@ -20,6 +20,7 @@ struct Data viewMatrix: mat4 } +[set(0)] external { [binding(0)] viewerData: uniform, @@ -87,7 +88,8 @@ int main() Nz::RenderWindow window; Nz::MeshParams meshParams; - meshParams.matrix = Nz::Matrix4f::Rotate(Nz::EulerAnglesf(0.f, 90.f, 0.f)) * Nz::Matrix4f::Scale(Nz::Vector3f(0.002f)); + meshParams.center = true; + meshParams.matrix = Nz::Matrix4f::Rotate(Nz::EulerAnglesf(0.f, -90.f, 0.f)) * Nz::Matrix4f::Scale(Nz::Vector3f(0.002f)); meshParams.vertexDeclaration = Nz::VertexDeclaration::Get(Nz::VertexLayout::XYZ_Normal_UV); std::shared_ptr device = Nz::Renderer::Instance()->InstanciateRenderDevice(0); @@ -147,24 +149,27 @@ int main() Nz::Vector2ui windowSize = window.GetSize(); ubo.projectionMatrix = Nz::Matrix4f::Perspective(Nz::DegreeAnglef(70.f), float(windowSize.x) / windowSize.y, 0.1f, 1000.f); ubo.viewMatrix = Nz::Matrix4f::Translate(Nz::Vector3f::Backward() * 1); - ubo.modelMatrix = Nz::Matrix4f::Translate(Nz::Vector3f::Forward() * 2 + Nz::Vector3f::Right()); + ubo.modelMatrix = Nz::Matrix4f::Translate(Nz::Vector3f::Forward() * 2); Nz::UInt32 uniformSize = sizeof(ubo); Nz::RenderPipelineLayoutInfo pipelineLayoutInfo; + auto& uboBinding = pipelineLayoutInfo.bindings.emplace_back(); - uboBinding.index = 0; + uboBinding.setIndex = 0; + uboBinding.bindingIndex = 0; uboBinding.shaderStageFlags = Nz::ShaderStageType::Vertex; uboBinding.type = Nz::ShaderBindingType::UniformBuffer; auto& textureBinding = pipelineLayoutInfo.bindings.emplace_back(); - textureBinding.index = 1; + textureBinding.setIndex = 0; + textureBinding.bindingIndex = 1; textureBinding.shaderStageFlags = Nz::ShaderStageType::Fragment; textureBinding.type = Nz::ShaderBindingType::Texture; std::shared_ptr renderPipelineLayout = device->InstantiateRenderPipelineLayout(std::move(pipelineLayoutInfo)); - Nz::ShaderBindingPtr shaderBinding = renderPipelineLayout->AllocateShaderBinding(); + Nz::ShaderBindingPtr shaderBinding = renderPipelineLayout->AllocateShaderBinding(0); std::shared_ptr uniformBuffer = device->InstantiateBuffer(Nz::BufferType::Uniform); if (!uniformBuffer->Initialize(uniformSize, Nz::BufferUsage::DeviceLocal | Nz::BufferUsage::Dynamic)) @@ -245,7 +250,7 @@ int main() builder.BindIndexBuffer(indexBufferImpl); builder.BindPipeline(*pipeline); builder.BindVertexBuffer(0, vertexBufferImpl); - builder.BindShaderBinding(*shaderBinding); + builder.BindShaderBinding(0, *shaderBinding); builder.SetScissor(Nz::Recti{ 0, 0, int(windowSize.x), int(windowSize.y) }); builder.SetViewport(Nz::Recti{ 0, 0, int(windowSize.x), int(windowSize.y) }); diff --git a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp index 6e8e8fad1..e5354471c 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp @@ -39,7 +39,7 @@ namespace Nz inline void BindIndexBuffer(GLuint indexBuffer, UInt64 offset = 0); inline void BindPipeline(const OpenGLRenderPipeline* pipeline); - inline void BindShaderBinding(const OpenGLShaderBinding* binding); + inline void BindShaderBinding(const OpenGLRenderPipelineLayout& pipelineLayout, UInt32 set, const OpenGLShaderBinding* binding); inline void BindVertexBuffer(UInt32 binding, GLuint vertexBuffer, UInt64 offset = 0); inline void CopyBuffer(GLuint source, GLuint target, UInt64 size, UInt64 sourceOffset = 0, UInt64 targetOffset = 0); @@ -102,10 +102,10 @@ namespace Nz GLuint indexBuffer = 0; const OpenGLRenderPipeline* pipeline = nullptr; - const OpenGLShaderBinding* shaderBindings = nullptr; UInt64 indexBufferOffset; std::optional scissorRegion; std::optional viewportRegion; + std::vector> shaderBindings; std::vector vertexBuffers; bool shouldFlipY = false; }; diff --git a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl index 89135f196..83613e960 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl +++ b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl @@ -44,9 +44,12 @@ namespace Nz m_currentStates.pipeline = pipeline; } - inline void OpenGLCommandBuffer::BindShaderBinding(const OpenGLShaderBinding* binding) + inline void OpenGLCommandBuffer::BindShaderBinding(const OpenGLRenderPipelineLayout& pipelineLayout, UInt32 set, const OpenGLShaderBinding* binding) { - m_currentStates.shaderBindings = binding; + if (set >= m_currentStates.shaderBindings.size()) + m_currentStates.shaderBindings.resize(set + 1); + + m_currentStates.shaderBindings[set] = std::make_pair(&pipelineLayout, binding); } inline void OpenGLCommandBuffer::BindVertexBuffer(UInt32 binding, GLuint vertexBuffer, UInt64 offset) diff --git a/include/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.hpp b/include/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.hpp index fc590989b..697ee0397 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.hpp @@ -28,7 +28,8 @@ namespace Nz void BindIndexBuffer(AbstractBuffer* indexBuffer, UInt64 offset = 0) override; void BindPipeline(const RenderPipeline& pipeline) override; - void BindShaderBinding(const ShaderBinding& binding) override; + void BindShaderBinding(UInt32 set, const ShaderBinding& binding) override; + void BindShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding) override; void BindVertexBuffer(UInt32 binding, Nz::AbstractBuffer* vertexBuffer, UInt64 offset = 0) override; void CopyBuffer(const RenderBufferView& source, const RenderBufferView& target, UInt64 size, UInt64 sourceOffset = 0, UInt64 targetOffset = 0) override; diff --git a/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp b/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp index 9950dbe64..01b6c578a 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp @@ -40,7 +40,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::StatementPtr& shaderAst, const ShaderWriter::States& states) override; + std::shared_ptr InstantiateShaderModule(ShaderStageTypeFlags shaderStages, ShaderAst::Statement& shaderAst, 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/OpenGLRenderPipelineLayout.hpp b/include/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp index 0340f43ae..86b2f6b80 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -30,13 +31,11 @@ namespace Nz OpenGLRenderPipelineLayout(OpenGLRenderPipelineLayout&&) = delete; ~OpenGLRenderPipelineLayout(); - ShaderBindingPtr AllocateShaderBinding() override; + ShaderBindingPtr AllocateShaderBinding(UInt32 setIndex) override; + inline const GlslWriter::BindingMapping& GetBindingMapping() const; inline const RenderPipelineLayoutInfo& GetLayoutInfo() const; - inline std::size_t GetTextureDescriptorCount() const; - inline std::size_t GetUniformBufferDescriptorCount() const; - OpenGLRenderPipelineLayout& operator=(const OpenGLRenderPipelineLayout&) = delete; OpenGLRenderPipelineLayout& operator=(OpenGLRenderPipelineLayout&&) = delete; @@ -46,17 +45,15 @@ namespace Nz struct UniformBufferDescriptor; DescriptorPool& AllocatePool(); - ShaderBindingPtr AllocateFromPool(std::size_t poolIndex); - TextureDescriptor& GetTextureDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t textureIndex); - UniformBufferDescriptor& GetUniformBufferDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t uniformBufferIndex); + ShaderBindingPtr AllocateFromPool(std::size_t poolIndex, UInt32 setIndex); + template void ForEachDescriptor(std::size_t poolIndex, std::size_t bindingIndex, F&& functor); + TextureDescriptor& GetTextureDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex); + UniformBufferDescriptor& GetUniformBufferDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex); void Release(ShaderBinding& binding); inline void TryToShrink(); - static constexpr UInt32 InvalidIndex = 0xFFFFFFFF; - struct TextureDescriptor { - UInt32 bindingIndex = InvalidIndex; GLuint texture; GLuint sampler; GL::TextureTarget textureTarget; @@ -64,25 +61,25 @@ namespace Nz struct UniformBufferDescriptor { - UInt32 bindingIndex = InvalidIndex; GLuint buffer; GLintptr offset; GLsizeiptr size; }; + using Descriptor = std::variant; + struct DescriptorPool { using BindingStorage = std::aligned_storage_t; Bitset freeBindings; - std::vector textureDescriptor; - std::vector uniformBufferDescriptor; + std::vector descriptors; std::unique_ptr storage; }; - std::size_t m_textureDescriptorCount; - std::size_t m_uniformBufferDescriptorCount; + std::size_t m_maxDescriptorCount; std::vector m_descriptorPools; + GlslWriter::BindingMapping m_bindingMapping; RenderPipelineLayoutInfo m_layoutInfo; }; } diff --git a/include/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.inl b/include/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.inl index bde85f51c..201f6e9fe 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.inl +++ b/include/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.inl @@ -7,19 +7,32 @@ namespace Nz { + inline const GlslWriter::BindingMapping& OpenGLRenderPipelineLayout::GetBindingMapping() const + { + return m_bindingMapping; + } + inline const RenderPipelineLayoutInfo& OpenGLRenderPipelineLayout::GetLayoutInfo() const { return m_layoutInfo; } - inline std::size_t OpenGLRenderPipelineLayout::GetTextureDescriptorCount() const + template + void OpenGLRenderPipelineLayout::ForEachDescriptor(std::size_t poolIndex, std::size_t bindingIndex, F&& functor) { - return m_textureDescriptorCount; - } + assert(poolIndex < m_descriptorPools.size()); + auto& pool = m_descriptorPools[poolIndex]; + assert(!pool.freeBindings.Test(bindingIndex)); - inline std::size_t OpenGLRenderPipelineLayout::GetUniformBufferDescriptorCount() const - { - return m_uniformBufferDescriptorCount; + for (std::size_t descriptorIndex = 0; descriptorIndex < m_maxDescriptorCount; ++descriptorIndex) + { + std::visit([&](auto&& arg) + { + if constexpr (!std::is_same_v, std::monostate>) + functor(UInt32(descriptorIndex), arg); + }, + pool.descriptors[bindingIndex * m_maxDescriptorCount + descriptorIndex]); + } } inline void OpenGLRenderPipelineLayout::TryToShrink() diff --git a/include/Nazara/OpenGLRenderer/OpenGLShaderBinding.hpp b/include/Nazara/OpenGLRenderer/OpenGLShaderBinding.hpp index dc7d144ef..dff543d52 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLShaderBinding.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLShaderBinding.hpp @@ -24,7 +24,7 @@ namespace Nz OpenGLShaderBinding(OpenGLShaderBinding&&) = delete; ~OpenGLShaderBinding() = default; - void Apply(const GL::Context& context) const; + void Apply(const OpenGLRenderPipelineLayout& pipelineLayout, UInt32 setIndex, const GL::Context& context) const; inline std::size_t GetBindingIndex() const; inline std::size_t GetPoolIndex() const; diff --git a/include/Nazara/OpenGLRenderer/OpenGLShaderModule.hpp b/include/Nazara/OpenGLRenderer/OpenGLShaderModule.hpp index e39746eb0..964341bec 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLShaderModule.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLShaderModule.hpp @@ -11,8 +11,10 @@ #include #include #include +#include #include -#include +#include +#include #include namespace Nz @@ -20,30 +22,40 @@ namespace Nz class NAZARA_OPENGLRENDERER_API OpenGLShaderModule : public ShaderModule { public: - struct Shader; - - OpenGLShaderModule(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderAst::StatementPtr& shaderAst, const ShaderWriter::States& states = {}); + OpenGLShaderModule(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderAst::Statement& shaderAst, 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; ~OpenGLShaderModule() = default; - inline const std::vector& GetShaders() const; + ShaderStageTypeFlags Attach(GL::Program& program, const GlslWriter::BindingMapping& bindingMapping) const; OpenGLShaderModule& operator=(const OpenGLShaderModule&) = delete; OpenGLShaderModule& operator=(OpenGLShaderModule&&) noexcept = default; - struct Shader - { - ShaderStageType stage; - GL::Shader shader; - }; - private: - void Create(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderAst::StatementPtr& shaderAst, const ShaderWriter::States& states); + void Create(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderAst::Statement& shaderAst, const ShaderWriter::States& states); static void CheckCompilationStatus(GL::Shader& shader); + struct GlslShader + { + std::string sourceCode; + }; + + struct ShaderStatement + { + std::shared_ptr ast; + }; + + struct Shader + { + ShaderStageType stage; + std::variant shader; + }; + + OpenGLDevice& m_device; + ShaderWriter::States m_states; std::vector m_shaders; }; } diff --git a/include/Nazara/OpenGLRenderer/OpenGLShaderModule.inl b/include/Nazara/OpenGLRenderer/OpenGLShaderModule.inl index afc50af68..1078237ac 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLShaderModule.inl +++ b/include/Nazara/OpenGLRenderer/OpenGLShaderModule.inl @@ -7,10 +7,6 @@ namespace Nz { - inline auto OpenGLShaderModule::GetShaders() const -> const std::vector& - { - return m_shaders; - } } #include diff --git a/include/Nazara/Renderer/CommandBufferBuilder.hpp b/include/Nazara/Renderer/CommandBufferBuilder.hpp index 0363703a4..0dc7615da 100644 --- a/include/Nazara/Renderer/CommandBufferBuilder.hpp +++ b/include/Nazara/Renderer/CommandBufferBuilder.hpp @@ -21,6 +21,7 @@ namespace Nz class Framebuffer; class RenderPass; class RenderPipeline; + class RenderPipelineLayout; class ShaderBinding; class Texture; @@ -41,7 +42,8 @@ namespace Nz virtual void BindIndexBuffer(Nz::AbstractBuffer* indexBuffer, UInt64 offset = 0) = 0; virtual void BindPipeline(const RenderPipeline& pipeline) = 0; - virtual void BindShaderBinding(const ShaderBinding& binding) = 0; + virtual void BindShaderBinding(UInt32 set, const ShaderBinding& binding) = 0; + virtual void BindShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding) = 0; virtual void BindVertexBuffer(UInt32 binding, Nz::AbstractBuffer* vertexBuffer, UInt64 offset = 0) = 0; inline void CopyBuffer(const RenderBufferView& source, const RenderBufferView& target); diff --git a/include/Nazara/Renderer/RenderPipelineLayout.hpp b/include/Nazara/Renderer/RenderPipelineLayout.hpp index c0d64b26a..d7e9e3f45 100644 --- a/include/Nazara/Renderer/RenderPipelineLayout.hpp +++ b/include/Nazara/Renderer/RenderPipelineLayout.hpp @@ -21,9 +21,10 @@ namespace Nz { struct Binding { + UInt32 setIndex; + UInt32 bindingIndex; ShaderBindingType type; ShaderStageTypeFlags shaderStageFlags; - unsigned int index; }; std::vector bindings; @@ -35,7 +36,7 @@ namespace Nz RenderPipelineLayout() = default; virtual ~RenderPipelineLayout(); - virtual ShaderBindingPtr AllocateShaderBinding() = 0; + virtual ShaderBindingPtr AllocateShaderBinding(UInt32 setIndex) = 0; }; } diff --git a/include/Nazara/Shader/Ast/Enums.hpp b/include/Nazara/Shader/Ast/Enums.hpp index 830353ae0..a567aaa4a 100644 --- a/include/Nazara/Shader/Ast/Enums.hpp +++ b/include/Nazara/Shader/Ast/Enums.hpp @@ -29,6 +29,7 @@ namespace Nz Layout, //< Struct layout (struct only) - has argument style Location, //< Location (struct member only) - has argument index Option, //< Conditional compilation option - has argument expr + Set, //< Binding set (external var only) - has argument index }; enum class BinaryType diff --git a/include/Nazara/Shader/Ast/ExpressionType.hpp b/include/Nazara/Shader/Ast/ExpressionType.hpp index e7a949e5b..185dc12ab 100644 --- a/include/Nazara/Shader/Ast/ExpressionType.hpp +++ b/include/Nazara/Shader/Ast/ExpressionType.hpp @@ -83,7 +83,7 @@ namespace Nz::ShaderAst struct StructMember { std::optional builtin; - std::optional locationIndex; + std::optional locationIndex; std::string name; ExpressionType type; }; diff --git a/include/Nazara/Shader/Ast/Nodes.hpp b/include/Nazara/Shader/Ast/Nodes.hpp index 9a9974a0b..1863518c5 100644 --- a/include/Nazara/Shader/Ast/Nodes.hpp +++ b/include/Nazara/Shader/Ast/Nodes.hpp @@ -252,7 +252,8 @@ namespace Nz::ShaderAst struct ExternalVar { - std::optional bindingIndex; + std::optional bindingIndex; + std::optional bindingSet; std::string name; ExpressionType type; }; diff --git a/include/Nazara/Shader/GlslWriter.hpp b/include/Nazara/Shader/GlslWriter.hpp index 579e31fcc..c5480be34 100644 --- a/include/Nazara/Shader/GlslWriter.hpp +++ b/include/Nazara/Shader/GlslWriter.hpp @@ -22,6 +22,7 @@ namespace Nz class NAZARA_SHADER_API GlslWriter : public ShaderWriter, public ShaderAst::ExpressionVisitorExcept, public ShaderAst::StatementVisitorExcept { public: + using BindingMapping = std::unordered_map; struct Environment; using ExtSupportCallback = std::function; @@ -30,8 +31,8 @@ namespace Nz GlslWriter(GlslWriter&&) = delete; ~GlslWriter() = default; - inline std::string Generate(ShaderAst::Statement& shader, const States& states = {}); - std::string Generate(std::optional shaderStage, ShaderAst::Statement& shader, const States& states = {}); + 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 = {}); void SetEnv(Environment environment); diff --git a/include/Nazara/Shader/GlslWriter.inl b/include/Nazara/Shader/GlslWriter.inl index 93104ce07..c4196383c 100644 --- a/include/Nazara/Shader/GlslWriter.inl +++ b/include/Nazara/Shader/GlslWriter.inl @@ -12,9 +12,9 @@ namespace Nz { } - inline std::string GlslWriter::Generate(ShaderAst::Statement& shader, const States& states) + inline std::string GlslWriter::Generate(ShaderAst::Statement& shader, const BindingMapping& bindingMapping, const States& states) { - return Generate(std::nullopt, shader, states); + return Generate(std::nullopt, shader, bindingMapping, states); } } diff --git a/include/Nazara/Shader/LangWriter.hpp b/include/Nazara/Shader/LangWriter.hpp index 86b7320a6..5982d3627 100644 --- a/include/Nazara/Shader/LangWriter.hpp +++ b/include/Nazara/Shader/LangWriter.hpp @@ -44,6 +44,7 @@ namespace Nz struct EntryAttribute; struct LayoutAttribute; struct LocationAttribute; + struct SetAttribute; void Append(const ShaderAst::ExpressionType& type); void Append(const ShaderAst::IdentifierType& identifierType); @@ -57,6 +58,8 @@ namespace Nz template void Append(const T& param); template void Append(const T1& firstParam, const T2& secondParam, Args&&... params); template void AppendAttributes(bool appendLine, Args&&... params); + template void AppendAttributesInternal(bool& first, const T& param); + template void AppendAttributesInternal(bool& first, const T1& firstParam, const T2& secondParam, Rest&&... params); void AppendAttribute(BindingAttribute binding); void AppendAttribute(BuiltinAttribute builtin); void AppendAttribute(DepthWriteAttribute depthWrite); @@ -64,6 +67,7 @@ namespace Nz void AppendAttribute(EntryAttribute entry); void AppendAttribute(LayoutAttribute layout); void AppendAttribute(LocationAttribute location); + void AppendAttribute(SetAttribute location); void AppendCommentSection(const std::string& section); void AppendField(std::size_t structIndex, const ShaderAst::ExpressionPtr* memberIndices, std::size_t remainingMembers); void AppendHeader(); diff --git a/include/Nazara/VulkanRenderer.hpp b/include/Nazara/VulkanRenderer.hpp index 633e24ee4..f5ce90f75 100644 --- a/include/Nazara/VulkanRenderer.hpp +++ b/include/Nazara/VulkanRenderer.hpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include diff --git a/include/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp b/include/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp index a3c8173a2..f395faf2c 100644 --- a/include/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp +++ b/include/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp @@ -29,7 +29,8 @@ namespace Nz void BindIndexBuffer(AbstractBuffer* indexBuffer, UInt64 offset = 0) override; void BindPipeline(const RenderPipeline& pipeline) override; - void BindShaderBinding(const ShaderBinding& binding) override; + void BindShaderBinding(UInt32 set, const ShaderBinding& binding) override; + void BindShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding) override; void BindVertexBuffer(UInt32 binding, Nz::AbstractBuffer* vertexBuffer, UInt64 offset = 0) override; void CopyBuffer(const RenderBufferView& source, const RenderBufferView& target, UInt64 size, UInt64 sourceOffset = 0, UInt64 targetOffset = 0) override; diff --git a/include/Nazara/VulkanRenderer/VulkanDescriptorSetLayoutCache.hpp b/include/Nazara/VulkanRenderer/VulkanDescriptorSetLayoutCache.hpp new file mode 100644 index 000000000..7430db976 --- /dev/null +++ b/include/Nazara/VulkanRenderer/VulkanDescriptorSetLayoutCache.hpp @@ -0,0 +1,58 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_VULKANRENDERER_VULKANDESCRIPTORSETLAYOUTCACHE_HPP +#define NAZARA_VULKANRENDERER_VULKANDESCRIPTORSETLAYOUTCACHE_HPP + +#include +#include +#include +#include + +namespace Nz +{ + namespace Vk + { + class Device; + } + + struct VulkanDescriptorSetLayoutInfo + { + VkDescriptorSetLayoutCreateFlags createFlags = 0; + std::vector bindings; + }; + + struct VulkanDescriptorSetLayoutBindingHasher + { + inline std::size_t operator()(const VulkanDescriptorSetLayoutInfo& layoutInfo) const; + }; + + struct VulkanDescriptorSetLayoutBindingEqual + { + inline bool operator()(const VulkanDescriptorSetLayoutInfo& lhs, const VulkanDescriptorSetLayoutInfo& rhs) const; + }; + + class VulkanDescriptorSetLayoutCache + { + public: + inline VulkanDescriptorSetLayoutCache(Vk::Device& device); + ~VulkanDescriptorSetLayoutCache() = default; + + inline void Clear(); + + inline const Vk::DescriptorSetLayout& Get(const VulkanDescriptorSetLayoutInfo& layoutInfo) const; + + private: + using Cache = std::unordered_map; + + mutable Cache m_cache; + Vk::Device& m_device; + }; +} + +#include + +#endif diff --git a/include/Nazara/VulkanRenderer/VulkanDescriptorSetLayoutCache.inl b/include/Nazara/VulkanRenderer/VulkanDescriptorSetLayoutCache.inl new file mode 100644 index 000000000..4b9f228ca --- /dev/null +++ b/include/Nazara/VulkanRenderer/VulkanDescriptorSetLayoutCache.inl @@ -0,0 +1,84 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan Renderer" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include + +namespace Nz +{ + inline std::size_t VulkanDescriptorSetLayoutBindingHasher::operator()(const VulkanDescriptorSetLayoutInfo& layoutInfo) const + { + std::size_t hash = 0; + HashCombine(hash, layoutInfo.createFlags); + for (const auto& binding : layoutInfo.bindings) + { + HashCombine(hash, binding.binding); + HashCombine(hash, binding.descriptorCount); + HashCombine(hash, binding.descriptorType); + HashCombine(hash, binding.pImmutableSamplers); + HashCombine(hash, binding.stageFlags); + } + + return hash; + } + + inline bool VulkanDescriptorSetLayoutBindingEqual::operator()(const VulkanDescriptorSetLayoutInfo& lhs, const VulkanDescriptorSetLayoutInfo& rhs) const + { + if (lhs.createFlags != rhs.createFlags) + return false; + + if (lhs.bindings.size() != rhs.bindings.size()) + return false; + + for (std::size_t i = 0; i < lhs.bindings.size(); ++i) + { + const auto& lhsBinding = lhs.bindings[i]; + const auto& bindingRhs = rhs.bindings[i]; + + if (lhsBinding.binding != bindingRhs.binding) + return false; + + if (lhsBinding.descriptorCount != bindingRhs.descriptorCount) + return false; + + if (lhsBinding.descriptorType != bindingRhs.descriptorType) + return false; + + if (lhsBinding.pImmutableSamplers != bindingRhs.pImmutableSamplers) + return false; + + if (lhsBinding.stageFlags != bindingRhs.stageFlags) + return false; + } + + return true; + } + + inline VulkanDescriptorSetLayoutCache::VulkanDescriptorSetLayoutCache(Vk::Device& device) : + m_device(device) + { + } + + inline void VulkanDescriptorSetLayoutCache::Clear() + { + m_cache.clear(); + } + + inline const Vk::DescriptorSetLayout& VulkanDescriptorSetLayoutCache::Get(const VulkanDescriptorSetLayoutInfo& layoutInfo) const + { + auto it = m_cache.find(layoutInfo); + if (it != m_cache.end()) + return it->second; + + Vk::DescriptorSetLayout setLayout; + if (!setLayout.Create(m_device, UInt32(layoutInfo.bindings.size()), layoutInfo.bindings.data(), layoutInfo.createFlags)) + throw std::runtime_error("failed to create descriptor set layout: " + TranslateVulkanError(setLayout.GetLastErrorCode())); + + return m_cache.emplace(layoutInfo, std::move(setLayout)).first->second; + } +} + +#include diff --git a/include/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.hpp b/include/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.hpp index a62c3f681..e8a0c2a3f 100644 --- a/include/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.hpp +++ b/include/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.hpp @@ -31,20 +31,19 @@ namespace Nz VulkanRenderPipelineLayout() = default; ~VulkanRenderPipelineLayout(); - ShaderBindingPtr AllocateShaderBinding() override; + ShaderBindingPtr AllocateShaderBinding(UInt32 setIndex) override; bool Create(Vk::Device& device, RenderPipelineLayoutInfo layoutInfo); inline Vk::Device* GetDevice() const; - inline const Vk::DescriptorSetLayout& GetDescriptorSetLayout() const; inline const Vk::PipelineLayout& GetPipelineLayout() const; private: struct DescriptorPool; DescriptorPool& AllocatePool(); - ShaderBindingPtr AllocateFromPool(std::size_t poolIndex); + ShaderBindingPtr AllocateFromPool(std::size_t poolIndex, UInt32 setIndex); void Release(ShaderBinding& binding); inline void TryToShrink(); @@ -59,7 +58,7 @@ namespace Nz MovablePtr m_device; std::vector m_descriptorPools; - Vk::DescriptorSetLayout m_descriptorSetLayout; + std::vector m_descriptorSetLayouts; Vk::PipelineLayout m_pipelineLayout; RenderPipelineLayoutInfo m_layoutInfo; }; diff --git a/include/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.inl b/include/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.inl index 5b341a558..a77927a90 100644 --- a/include/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.inl +++ b/include/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.inl @@ -12,11 +12,6 @@ namespace Nz return m_device.Get(); } - inline const Vk::DescriptorSetLayout& VulkanRenderPipelineLayout::GetDescriptorSetLayout() const - { - return m_descriptorSetLayout; - } - inline const Vk::PipelineLayout& VulkanRenderPipelineLayout::GetPipelineLayout() const { return m_pipelineLayout; diff --git a/include/Nazara/VulkanRenderer/Wrapper/Device.hpp b/include/Nazara/VulkanRenderer/Wrapper/Device.hpp index a6fe18e51..2408d3239 100644 --- a/include/Nazara/VulkanRenderer/Wrapper/Device.hpp +++ b/include/Nazara/VulkanRenderer/Wrapper/Device.hpp @@ -23,6 +23,8 @@ VK_DEFINE_HANDLE(VmaAllocation) namespace Nz { + class VulkanDescriptorSetLayoutCache; + namespace Vk { class AutoCommandBuffer; @@ -46,18 +48,17 @@ namespace Nz bool Create(const Vk::PhysicalDevice& deviceInfo, const VkDeviceCreateInfo& createInfo, const VkAllocationCallbacks* allocator = nullptr); inline void Destroy(); + inline UInt32 GetDefaultFamilyIndex(QueueType queueType) const; + const VulkanDescriptorSetLayoutCache& GetDescriptorSetLayoutCache() const; inline const std::vector& GetEnabledQueues() const; inline const QueueList& GetEnabledQueues(UInt32 familyQueue) const; - - QueueHandle GetQueue(UInt32 queueFamilyIndex, UInt32 queueIndex); inline Instance& GetInstance(); inline const Instance& GetInstance() const; inline VkResult GetLastErrorCode() const; inline VmaAllocator GetMemoryAllocator() const; inline VkPhysicalDevice GetPhysicalDevice() const; inline const Vk::PhysicalDevice& GetPhysicalDeviceInfo() const; - - inline UInt32 GetDefaultFamilyIndex(QueueType queueType) const; + QueueHandle GetQueue(UInt32 queueFamilyIndex, UInt32 queueIndex); inline bool IsExtensionLoaded(const std::string& extensionName); inline bool IsLayerLoaded(const std::string& layerName); @@ -75,13 +76,6 @@ namespace Nz #include - struct QueueInfo - { - QueueFamilyInfo* familyInfo; - VkQueue queue; - float priority; - }; - struct QueueFamilyInfo { QueueList queues; @@ -91,6 +85,13 @@ namespace Nz UInt32 timestampValidBits; }; + struct QueueInfo + { + QueueFamilyInfo* familyInfo; + VkQueue queue; + float priority; + }; + static constexpr UInt32 InvalidQueue = std::numeric_limits::max(); private: diff --git a/include/Nazara/VulkanRenderer/Wrapper/Device.inl b/include/Nazara/VulkanRenderer/Wrapper/Device.inl index 308bf442d..0174fe7fb 100644 --- a/include/Nazara/VulkanRenderer/Wrapper/Device.inl +++ b/include/Nazara/VulkanRenderer/Wrapper/Device.inl @@ -8,92 +8,89 @@ #include #include -namespace Nz +namespace Nz::Vk { - namespace Vk + inline UInt32 Device::GetDefaultFamilyIndex(QueueType queueType) const { - inline const std::vector& Device::GetEnabledQueues() const + return m_defaultQueues[UnderlyingCast(queueType)]; + } + + inline const std::vector& Device::GetEnabledQueues() const + { + return m_enabledQueuesInfos; + } + + inline const Device::QueueList& Device::GetEnabledQueues(UInt32 familyQueue) const + { + NazaraAssert(familyQueue < m_enabledQueuesInfos.size(), "Invalid family queue"); + + return *m_queuesByFamily[familyQueue]; + } + + inline Instance& Device::GetInstance() + { + return m_instance; + } + + inline const Instance& Device::GetInstance() const + { + return m_instance; + } + + inline VkResult Device::GetLastErrorCode() const + { + return m_lastErrorCode; + } + + inline VmaAllocator Device::GetMemoryAllocator() const + { + return m_memAllocator; + } + + inline VkPhysicalDevice Device::GetPhysicalDevice() const + { + return m_physicalDevice->physDevice; + } + + inline const Vk::PhysicalDevice& Device::GetPhysicalDeviceInfo() const + { + return *m_physicalDevice; + } + + inline bool Device::IsExtensionLoaded(const std::string& extensionName) + { + return m_loadedExtensions.count(extensionName) > 0; + } + + inline bool Device::IsLayerLoaded(const std::string& layerName) + { + return m_loadedLayers.count(layerName) > 0; + } + + inline bool Device::WaitForIdle() + { + m_lastErrorCode = vkDeviceWaitIdle(m_device); + if (m_lastErrorCode != VkResult::VK_SUCCESS) { - return m_enabledQueuesInfos; + NazaraError("Failed to wait for device idle: " + TranslateVulkanError(m_lastErrorCode)); + return false; } - inline const Device::QueueList& Device::GetEnabledQueues(UInt32 familyQueue) const - { - NazaraAssert(familyQueue < m_enabledQueuesInfos.size(), "Invalid family queue"); + return true; + } - return *m_queuesByFamily[familyQueue]; - } + inline Device::operator VkDevice() + { + return m_device; + } - inline Instance& Device::GetInstance() - { - return m_instance; - } - - inline const Instance& Device::GetInstance() const - { - return m_instance; - } - - inline VkResult Device::GetLastErrorCode() const - { - return m_lastErrorCode; - } - - inline VmaAllocator Device::GetMemoryAllocator() const - { - return m_memAllocator; - } - - inline VkPhysicalDevice Device::GetPhysicalDevice() const - { - return m_physicalDevice->physDevice; - } - - inline const Vk::PhysicalDevice& Device::GetPhysicalDeviceInfo() const - { - return *m_physicalDevice; - } - - inline UInt32 Device::GetDefaultFamilyIndex(QueueType queueType) const - { - return m_defaultQueues[UnderlyingCast(queueType)]; - } - - inline bool Device::IsExtensionLoaded(const std::string& extensionName) - { - return m_loadedExtensions.count(extensionName) > 0; - } - - inline bool Device::IsLayerLoaded(const std::string& layerName) - { - return m_loadedLayers.count(layerName) > 0; - } - - inline bool Device::WaitForIdle() - { - m_lastErrorCode = vkDeviceWaitIdle(m_device); - if (m_lastErrorCode != VkResult::VK_SUCCESS) - { - NazaraError("Failed to wait for device idle: " + TranslateVulkanError(m_lastErrorCode)); - return false; - } - - return true; - } - - inline Device::operator VkDevice() - { - return m_device; - } - - inline PFN_vkVoidFunction Device::GetProcAddr(const char* name) - { - PFN_vkVoidFunction func = m_instance.GetDeviceProcAddr(m_device, name); - if (!func) - NazaraError("Failed to get " + std::string(name) + " address"); + inline PFN_vkVoidFunction Device::GetProcAddr(const char* name) + { + PFN_vkVoidFunction func = m_instance.GetDeviceProcAddr(m_device, name); + if (!func) + NazaraError("Failed to get " + std::string(name) + " address"); - return func; - } + return func; } } diff --git a/include/Nazara/VulkanRenderer/Wrapper/DeviceObject.hpp b/include/Nazara/VulkanRenderer/Wrapper/DeviceObject.hpp index e2b9d30c6..288580a25 100644 --- a/include/Nazara/VulkanRenderer/Wrapper/DeviceObject.hpp +++ b/include/Nazara/VulkanRenderer/Wrapper/DeviceObject.hpp @@ -13,42 +13,39 @@ #include #include -namespace Nz +namespace Nz::Vk { - namespace Vk + template + class DeviceObject { - template - class DeviceObject - { - public: - DeviceObject(); - DeviceObject(const DeviceObject&) = delete; - DeviceObject(DeviceObject&& object) noexcept; - ~DeviceObject(); + public: + DeviceObject(); + DeviceObject(const DeviceObject&) = delete; + DeviceObject(DeviceObject&& object) noexcept; + ~DeviceObject(); - bool Create(Device& device, const CreateInfo& createInfo, const VkAllocationCallbacks* allocator = nullptr); - void Destroy(); + bool Create(Device& device, const CreateInfo& createInfo, const VkAllocationCallbacks* allocator = nullptr); + void Destroy(); - bool IsValid() const; + bool IsValid() const; - Device* GetDevice() const; - VkResult GetLastErrorCode() const; + Device* GetDevice() const; + VkResult GetLastErrorCode() const; - void SetDebugName(const char* name); - void SetDebugName(const std::string& name); + void SetDebugName(const char* name); + void SetDebugName(const std::string& name); - DeviceObject& operator=(const DeviceObject&) = delete; - DeviceObject& operator=(DeviceObject&& object) noexcept; + DeviceObject& operator=(const DeviceObject&) = delete; + DeviceObject& operator=(DeviceObject&& object) noexcept; - operator VkType() const; + operator VkType() const; - protected: - MovablePtr m_device; - VkAllocationCallbacks m_allocator; - VkType m_handle; - mutable VkResult m_lastErrorCode; - }; - } + protected: + MovablePtr m_device; + VkAllocationCallbacks m_allocator; + VkType m_handle; + mutable VkResult m_lastErrorCode; + }; } #include diff --git a/include/Nazara/VulkanRenderer/Wrapper/DeviceObject.inl b/include/Nazara/VulkanRenderer/Wrapper/DeviceObject.inl index 64fe9a353..a42020d31 100644 --- a/include/Nazara/VulkanRenderer/Wrapper/DeviceObject.inl +++ b/include/Nazara/VulkanRenderer/Wrapper/DeviceObject.inl @@ -8,126 +8,123 @@ #include #include -namespace Nz +namespace Nz::Vk { - namespace Vk + template + DeviceObject::DeviceObject() : + m_handle(VK_NULL_HANDLE) { - template - DeviceObject::DeviceObject() : - m_handle(VK_NULL_HANDLE) + } + + template + DeviceObject::DeviceObject(DeviceObject&& object) noexcept : + m_device(std::move(object.m_device)), + m_allocator(object.m_allocator), + m_handle(object.m_handle), + m_lastErrorCode(object.m_lastErrorCode) + { + object.m_handle = VK_NULL_HANDLE; + } + + template + DeviceObject::~DeviceObject() + { + Destroy(); + } + + template + bool DeviceObject::Create(Device& device, const CreateInfo& createInfo, const VkAllocationCallbacks* allocator) + { + Destroy(); + + m_device = &device; + m_lastErrorCode = C::CreateHelper(*m_device, &createInfo, allocator, &m_handle); + if (m_lastErrorCode != VkResult::VK_SUCCESS) { + NazaraError("Failed to create Vulkan object: " + TranslateVulkanError(m_lastErrorCode)); + return false; } - template - DeviceObject::DeviceObject(DeviceObject&& object) noexcept : - m_device(std::move(object.m_device)), - m_allocator(object.m_allocator), - m_handle(object.m_handle), - m_lastErrorCode(object.m_lastErrorCode) + // Store the allocator to access them when needed + if (allocator) + m_allocator = *allocator; + else + m_allocator.pfnAllocation = nullptr; + + return true; + } + + template + void DeviceObject::Destroy() + { + if (IsValid()) { - object.m_handle = VK_NULL_HANDLE; + C::DestroyHelper(*m_device, m_handle, (m_allocator.pfnAllocation) ? &m_allocator : nullptr); + m_handle = VK_NULL_HANDLE; } + } - template - DeviceObject::~DeviceObject() + template + bool DeviceObject::IsValid() const + { + return m_handle != VK_NULL_HANDLE; + } + + template + Device* DeviceObject::GetDevice() const + { + return m_device; + } + + template + VkResult DeviceObject::GetLastErrorCode() const + { + return m_lastErrorCode; + } + + template + void DeviceObject::SetDebugName(const char* name) + { + if (m_device->vkSetDebugUtilsObjectNameEXT) { - Destroy(); - } + VkDebugUtilsObjectNameInfoEXT debugName = { + VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, + nullptr, + ObjectType, + 0, + name + }; - template - bool DeviceObject::Create(Device& device, const CreateInfo& createInfo, const VkAllocationCallbacks* allocator) - { - Destroy(); - - m_device = &device; - m_lastErrorCode = C::CreateHelper(*m_device, &createInfo, allocator, &m_handle); - if (m_lastErrorCode != VkResult::VK_SUCCESS) - { - NazaraError("Failed to create Vulkan object: " + TranslateVulkanError(m_lastErrorCode)); - return false; - } - - // Store the allocator to access them when needed - if (allocator) - m_allocator = *allocator; + if constexpr (std::is_pointer_v) + debugName.objectHandle = static_cast(reinterpret_cast(m_handle)); else - m_allocator.pfnAllocation = nullptr; + debugName.objectHandle = static_cast(m_handle); - return true; + m_device->vkSetDebugUtilsObjectNameEXT(*m_device, &debugName); } + } - template - void DeviceObject::Destroy() - { - if (IsValid()) - { - C::DestroyHelper(*m_device, m_handle, (m_allocator.pfnAllocation) ? &m_allocator : nullptr); - m_handle = VK_NULL_HANDLE; - } - } + template + void DeviceObject::SetDebugName(const std::string& name) + { + return SetDebugName(name.data()); + } - template - bool DeviceObject::IsValid() const - { - return m_handle != VK_NULL_HANDLE; - } + template + auto DeviceObject::operator=(DeviceObject&& object) noexcept -> DeviceObject& + { + std::swap(m_allocator, object.m_allocator); + std::swap(m_device, object.m_device); + std::swap(m_handle, object.m_handle); + std::swap(m_lastErrorCode, object.m_lastErrorCode); - template - Device* DeviceObject::GetDevice() const - { - return m_device; - } + return *this; + } - template - VkResult DeviceObject::GetLastErrorCode() const - { - return m_lastErrorCode; - } - - template - void DeviceObject::SetDebugName(const char* name) - { - if (m_device->vkSetDebugUtilsObjectNameEXT) - { - VkDebugUtilsObjectNameInfoEXT debugName = { - VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, - nullptr, - ObjectType, - 0, - name - }; - - if constexpr (std::is_pointer_v) - debugName.objectHandle = static_cast(reinterpret_cast(m_handle)); - else - debugName.objectHandle = static_cast(m_handle); - - m_device->vkSetDebugUtilsObjectNameEXT(*m_device, &debugName); - } - } - - template - void DeviceObject::SetDebugName(const std::string& name) - { - return SetDebugName(name.data()); - } - - template - auto DeviceObject::operator=(DeviceObject&& object) noexcept -> DeviceObject& - { - std::swap(m_allocator, object.m_allocator); - std::swap(m_device, object.m_device); - std::swap(m_handle, object.m_handle); - std::swap(m_lastErrorCode, object.m_lastErrorCode); - - return *this; - } - - template - DeviceObject::operator VkType() const - { - return m_handle; - } + template + DeviceObject::operator VkType() const + { + return m_handle; } } diff --git a/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp b/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp index 7066d8384..dcb319871 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -242,7 +243,10 @@ namespace Nz void OpenGLCommandBuffer::ApplyStates(const GL::Context& context, const DrawStates& states) { states.pipeline->Apply(context, states.shouldFlipY); - states.shaderBindings->Apply(context); + + unsigned int setIndex = 0; + for (const auto& [pipelineLayout, shaderBinding] : states.shaderBindings) + shaderBinding->Apply(*pipelineLayout, setIndex++, context); if (states.scissorRegion) context.SetScissorBox(states.scissorRegion->x, states.scissorRegion->y, states.scissorRegion->width, states.scissorRegion->height); diff --git a/src/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.cpp b/src/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.cpp index 435e2c1fe..50efedf60 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.cpp @@ -38,11 +38,19 @@ namespace Nz m_commandBuffer.BindPipeline(&glPipeline); } - void OpenGLCommandBufferBuilder::BindShaderBinding(const ShaderBinding& binding) + void OpenGLCommandBufferBuilder::BindShaderBinding(UInt32 set, const ShaderBinding& binding) { const OpenGLShaderBinding& glBinding = static_cast(binding); - m_commandBuffer.BindShaderBinding(&glBinding); + m_commandBuffer.BindShaderBinding(glBinding.GetOwner(), set, &glBinding); + } + + void OpenGLCommandBufferBuilder::BindShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding) + { + const OpenGLRenderPipelineLayout& glPipelineLayout = static_cast(pipelineLayout); + const OpenGLShaderBinding& glBinding = static_cast(binding); + + m_commandBuffer.BindShaderBinding(glPipelineLayout, set, &glBinding); } void OpenGLCommandBufferBuilder::BindVertexBuffer(UInt32 binding, Nz::AbstractBuffer* vertexBuffer, UInt64 offset) diff --git a/src/Nazara/OpenGLRenderer/OpenGLRenderPipeline.cpp b/src/Nazara/OpenGLRenderer/OpenGLRenderPipeline.cpp index 639104c0d..89cb79c8b 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLRenderPipeline.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLRenderPipeline.cpp @@ -19,19 +19,23 @@ namespace Nz m_pipelineInfo(std::move(pipelineInfo)), m_isViewportFlipped(false) { + OpenGLRenderPipelineLayout& pipelineLayout = static_cast(*m_pipelineInfo.pipelineLayout); + if (!m_program.Create(device)) throw std::runtime_error("failed to create program"); + const GL::Context* activeContext = GL::Context::GetCurrentContext(); + assert(activeContext); + + // Enable pipeline states before compiling and linking the program, for drivers which embed some pipeline states into the shader binary (to avoid recompilation later) + activeContext->UpdateStates(m_pipelineInfo, false); + ShaderStageTypeFlags stageFlags; for (const auto& shaderModulePtr : m_pipelineInfo.shaderModules) { OpenGLShaderModule& shaderModule = static_cast(*shaderModulePtr); - for (const auto& shaderEntry : shaderModule.GetShaders()) - { - m_program.AttachShader(shaderEntry.shader.GetObjectId()); - stageFlags |= shaderEntry.stage; - } + stageFlags |= shaderModule.Attach(m_program, pipelineLayout.GetBindingMapping()); } // OpenGL ES programs must have both vertex and fragment shaders or a compute shader or a mesh and fragment shader. @@ -42,12 +46,8 @@ namespace Nz if (!stageFlags.Test(stage)) { ShaderAst::StatementPtr dummyAst = ShaderBuilder::DeclareFunction(stage, "main", {}, {}); - OpenGLShaderModule shaderModule(device, stage, dummyAst); - for (const auto& shaderEntry : shaderModule.GetShaders()) - { - m_program.AttachShader(shaderEntry.shader.GetObjectId()); - stageFlags |= shaderEntry.stage; - } + OpenGLShaderModule shaderModule(device, stage, *dummyAst); + stageFlags |= shaderModule.Attach(m_program, pipelineLayout.GetBindingMapping()); } }; @@ -70,7 +70,7 @@ namespace Nz { context.UpdateStates(m_pipelineInfo, flipViewport); context.BindProgram(m_program.GetObjectId()); //< Bind program after states (for shader caching) - if (m_isViewportFlipped != flipViewport) + if (m_flipYUniformLocation != -1 && m_isViewportFlipped != flipViewport) { m_program.Uniform(m_flipYUniformLocation, (flipViewport) ? -1.f : 1.f); m_isViewportFlipped = flipViewport; diff --git a/src/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.cpp b/src/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.cpp index f5c69190a..934e2ed50 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.cpp @@ -14,25 +14,17 @@ namespace Nz { OpenGLRenderPipelineLayout::OpenGLRenderPipelineLayout(RenderPipelineLayoutInfo layoutInfo) : - m_textureDescriptorCount(0), - m_uniformBufferDescriptorCount(0), + m_maxDescriptorCount(0), m_layoutInfo(std::move(layoutInfo)) { - for (const auto& bindingInfo : m_layoutInfo.bindings) + // Build binding mapping (vulkan-like set | binding => GL binding) and register max descriptor count + unsigned int bindingIndex = 0; + for (const auto& binding : m_layoutInfo.bindings) { - switch (bindingInfo.type) - { - case ShaderBindingType::Texture: - m_textureDescriptorCount++; - break; + UInt64 bindingKey = UInt64(binding.setIndex) << 32 | UInt64(binding.bindingIndex); - case ShaderBindingType::UniformBuffer: - m_uniformBufferDescriptorCount++; - break; - - default: - throw std::runtime_error("unknown binding type 0x" + NumberToString(UnderlyingCast(bindingInfo.type), 16)); - } + m_bindingMapping[bindingKey] = bindingIndex++; + m_maxDescriptorCount = std::max(m_maxDescriptorCount, binding.bindingIndex + 1); } } @@ -45,11 +37,11 @@ namespace Nz } } - ShaderBindingPtr OpenGLRenderPipelineLayout::AllocateShaderBinding() + ShaderBindingPtr OpenGLRenderPipelineLayout::AllocateShaderBinding(UInt32 setIndex) { for (std::size_t i = 0; i < m_descriptorPools.size(); ++i) { - ShaderBindingPtr bindingPtr = AllocateFromPool(i); + ShaderBindingPtr bindingPtr = AllocateFromPool(i, setIndex); if (!bindingPtr) continue; @@ -60,7 +52,7 @@ namespace Nz std::size_t newPoolIndex = m_descriptorPools.size(); AllocatePool(); - ShaderBindingPtr bindingPtr = AllocateFromPool(newPoolIndex); + ShaderBindingPtr bindingPtr = AllocateFromPool(newPoolIndex, setIndex); if (!bindingPtr) throw std::runtime_error("Failed to allocate shader binding"); @@ -74,14 +66,15 @@ namespace Nz DescriptorPool pool; pool.freeBindings.Resize(MaxSet, true); pool.storage = std::make_unique(MaxSet); - pool.textureDescriptor.resize(m_textureDescriptorCount * MaxSet); - pool.uniformBufferDescriptor.resize(m_uniformBufferDescriptorCount * MaxSet); + pool.descriptors.resize(m_maxDescriptorCount * MaxSet); return m_descriptorPools.emplace_back(std::move(pool)); } - ShaderBindingPtr OpenGLRenderPipelineLayout::AllocateFromPool(std::size_t poolIndex) + ShaderBindingPtr OpenGLRenderPipelineLayout::AllocateFromPool(std::size_t poolIndex, UInt32 /*setIndex*/) { + //FIXME: Make use of set index to use less memory + auto& pool = m_descriptorPools[poolIndex]; std::size_t freeBindingId = pool.freeBindings.FindFirst(); @@ -94,24 +87,24 @@ namespace Nz return ShaderBindingPtr(PlacementNew(freeBindingMemory, *this, poolIndex, freeBindingId)); } - auto OpenGLRenderPipelineLayout::GetTextureDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t textureIndex) -> TextureDescriptor& + auto OpenGLRenderPipelineLayout::GetTextureDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex) -> TextureDescriptor& { assert(poolIndex < m_descriptorPools.size()); auto& pool = m_descriptorPools[poolIndex]; assert(!pool.freeBindings.Test(bindingIndex)); - assert(textureIndex < m_textureDescriptorCount); + assert(descriptorIndex < m_maxDescriptorCount); - return pool.textureDescriptor[bindingIndex * m_textureDescriptorCount + textureIndex]; + return pool.descriptors[bindingIndex * m_maxDescriptorCount + descriptorIndex].emplace(); } - auto OpenGLRenderPipelineLayout::GetUniformBufferDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t uniformBufferIndex) -> UniformBufferDescriptor& + auto OpenGLRenderPipelineLayout::GetUniformBufferDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex) -> UniformBufferDescriptor& { assert(poolIndex < m_descriptorPools.size()); auto& pool = m_descriptorPools[poolIndex]; assert(!pool.freeBindings.Test(bindingIndex)); - assert(uniformBufferIndex < m_uniformBufferDescriptorCount); + assert(descriptorIndex < m_maxDescriptorCount); - return pool.uniformBufferDescriptor[bindingIndex * m_uniformBufferDescriptorCount + uniformBufferIndex]; + return pool.descriptors[bindingIndex * m_maxDescriptorCount + descriptorIndex].emplace(); } void OpenGLRenderPipelineLayout::Release(ShaderBinding& binding) diff --git a/src/Nazara/OpenGLRenderer/OpenGLShaderBinding.cpp b/src/Nazara/OpenGLRenderer/OpenGLShaderBinding.cpp index cf0c031d1..0255d5a28 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLShaderBinding.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLShaderBinding.cpp @@ -13,68 +13,66 @@ namespace Nz { - void OpenGLShaderBinding::Apply(const GL::Context& context) const + void OpenGLShaderBinding::Apply(const OpenGLRenderPipelineLayout& pipelineLayout, UInt32 setIndex, const GL::Context& context) const { - for (std::size_t i = 0; i < m_owner.GetTextureDescriptorCount(); ++i) + //TODO: Check layout compaitiblity + const auto& bindingMapping = pipelineLayout.GetBindingMapping(); + const auto& layoutInfo = pipelineLayout.GetLayoutInfo(); + + m_owner.ForEachDescriptor(m_poolIndex, m_bindingIndex, [&](UInt32 bindingIndex, auto&& descriptor) { - const auto& textureDescriptor = m_owner.GetTextureDescriptor(m_poolIndex, m_bindingIndex, i); + using DescriptorType = std::decay_t; - UInt32 textureIndex = textureDescriptor.bindingIndex; - if (textureIndex == OpenGLRenderPipelineLayout::InvalidIndex) - continue; + auto bindingIt = std::find_if(layoutInfo.bindings.begin(), layoutInfo.bindings.end(), [&](const auto& binding) { return binding.setIndex == setIndex && binding.bindingIndex == bindingIndex; }); + if (bindingIt == layoutInfo.bindings.end()) + throw std::runtime_error("invalid binding index"); - context.BindSampler(textureIndex, textureDescriptor.sampler); - context.BindTexture(textureIndex, textureDescriptor.textureTarget, textureDescriptor.texture); - } + const auto& bindingInfo = *bindingIt; - for (std::size_t i = 0; i < m_owner.GetUniformBufferDescriptorCount(); ++i) - { - const auto& uboDescriptor = m_owner.GetUniformBufferDescriptor(m_poolIndex, m_bindingIndex, i); + auto bindingMappingIt = bindingMapping.find(UInt64(setIndex) << 32 | bindingIndex); + assert(bindingMappingIt != bindingMapping.end()); - UInt32 uboIndex = uboDescriptor.bindingIndex; - if (uboIndex == OpenGLRenderPipelineLayout::InvalidIndex) - continue; + UInt32 bindingPoint = bindingMappingIt->second; - context.BindUniformBuffer(uboDescriptor.bindingIndex, uboDescriptor.buffer, uboDescriptor.offset, uboDescriptor.size); - } + if constexpr (std::is_same_v) + { + if (bindingInfo.type != ShaderBindingType::Texture) + throw std::runtime_error("descriptor (set=" + std::to_string(setIndex) + ", binding=" + std::to_string(bindingIndex) + ") is not a texture"); + + context.BindSampler(bindingPoint, descriptor.sampler); + context.BindTexture(bindingPoint, descriptor.textureTarget, descriptor.texture); + } + else if constexpr (std::is_same_v) + { + if (bindingInfo.type != ShaderBindingType::UniformBuffer) + throw std::runtime_error("descriptor (set=" + std::to_string(setIndex) + ", binding=" + std::to_string(bindingIndex) + ") is not an uniform buffer"); + + context.BindUniformBuffer(bindingPoint, descriptor.buffer, descriptor.offset, descriptor.size); + } + else + static_assert(AlwaysFalse::value, "non-exhaustive visitor"); + }); } void OpenGLShaderBinding::Update(const Binding* bindings, std::size_t bindingCount) { - const auto& layoutInfo = m_owner.GetLayoutInfo(); - for (std::size_t i = 0; i < bindingCount; ++i) { const Binding& binding = bindings[i]; - - assert(binding.bindingIndex < layoutInfo.bindings.size()); - const auto& bindingDesc = layoutInfo.bindings[binding.bindingIndex]; - - std::size_t resourceIndex = 0; - for (std::size_t j = binding.bindingIndex; j > 0; --j) + + std::visit([&](auto&& arg) { - // Use j-1 to prevent underflow in for loop - if (layoutInfo.bindings[j - 1].type == bindingDesc.type) - resourceIndex++; - } + using T = std::decay_t; - switch (bindingDesc.type) - { - case ShaderBindingType::Texture: + if constexpr (std::is_same_v) { - if (!std::holds_alternative(binding.content)) - throw std::runtime_error("binding #" + std::to_string(binding.bindingIndex) + " is a texture but another binding type has been supplied"); + auto& textureDescriptor = m_owner.GetTextureDescriptor(m_poolIndex, m_bindingIndex, binding.bindingIndex); - const TextureBinding& texBinding = std::get(binding.content); - - auto& textureDescriptor = m_owner.GetTextureDescriptor(m_poolIndex, m_bindingIndex, resourceIndex); - textureDescriptor.bindingIndex = UInt32(binding.bindingIndex); - - if (OpenGLTexture* glTexture = static_cast(texBinding.texture)) + if (OpenGLTexture* glTexture = static_cast(arg.texture)) { textureDescriptor.texture = glTexture->GetTexture().GetObjectId(); - if (OpenGLTextureSampler* glSampler = static_cast(texBinding.sampler)) + if (OpenGLTextureSampler* glSampler = static_cast(arg.sampler)) textureDescriptor.sampler = glSampler->GetSampler(glTexture->GetLevelCount() > 1).GetObjectId(); else textureDescriptor.sampler = 0; @@ -87,23 +85,14 @@ namespace Nz textureDescriptor.texture = 0; textureDescriptor.textureTarget = GL::TextureTarget::Target2D; } - - break; } - - case ShaderBindingType::UniformBuffer: + else if constexpr (std::is_same_v) { - if (!std::holds_alternative(binding.content)) - throw std::runtime_error("binding #" + std::to_string(binding.bindingIndex) + " is an uniform buffer but another binding type has been supplied"); + auto& uboDescriptor = m_owner.GetUniformBufferDescriptor(m_poolIndex, m_bindingIndex, binding.bindingIndex); + uboDescriptor.offset = arg.offset; + uboDescriptor.size = arg.range; - const UniformBufferBinding& uboBinding = std::get(binding.content); - - auto& uboDescriptor = m_owner.GetUniformBufferDescriptor(m_poolIndex, m_bindingIndex, resourceIndex); - uboDescriptor.bindingIndex = static_cast(binding.bindingIndex); - uboDescriptor.offset = uboBinding.offset; - uboDescriptor.size = uboBinding.range; - - if (OpenGLBuffer* glBuffer = static_cast(uboBinding.buffer)) + if (OpenGLBuffer* glBuffer = static_cast(arg.buffer)) { if (glBuffer->GetType() != BufferType::Uniform) throw std::runtime_error("expected uniform buffer"); @@ -112,10 +101,11 @@ namespace Nz } else uboDescriptor.buffer = 0; - - break; } - } + else + static_assert(AlwaysFalse::value, "non-exhaustive visitor"); + + }, binding.content); } } diff --git a/src/Nazara/OpenGLRenderer/OpenGLShaderModule.cpp b/src/Nazara/OpenGLRenderer/OpenGLShaderModule.cpp index a1abde93f..4d70be832 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLShaderModule.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLShaderModule.cpp @@ -5,10 +5,8 @@ #include #include #include -#include #include #include -#include #include #include #include @@ -16,12 +14,14 @@ namespace Nz { OpenGLShaderModule::OpenGLShaderModule(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderAst::Statement& shaderAst, const ShaderWriter::States& states) : + m_device(device) { NazaraAssert(shaderStages != 0, "at least one shader stage must be specified"); Create(device, shaderStages, shaderAst, states); } - OpenGLShaderModule::OpenGLShaderModule(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const void* source, std::size_t sourceSize, const ShaderWriter::States& states) + OpenGLShaderModule::OpenGLShaderModule(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const void* source, std::size_t sourceSize, const ShaderWriter::States& states) : + m_device(device) { NazaraAssert(shaderStages != 0, "at least one shader stage must be specified"); @@ -36,16 +36,8 @@ namespace Nz { NazaraAssert(shaderStages == shaderStage, "when supplying GLSL, only one shader stage type can be specified"); - GL::Shader shader; - if (!shader.Create(device, ToOpenGL(shaderStage))) - throw std::runtime_error("failed to create shader"); //< TODO: Handle error message - - shader.SetSource(reinterpret_cast(source), GLint(sourceSize)); - shader.Compile(); - CheckCompilationStatus(shader); - auto& entry = m_shaders.emplace_back(); - entry.shader = std::move(shader); + entry.shader = GlslShader{ std::string(reinterpret_cast(source), std::size_t(sourceSize)) }; entry.stage = shaderStage; break; } @@ -90,16 +82,9 @@ namespace Nz } } - void OpenGLShaderModule::CheckCompilationStatus(GL::Shader& shader) + ShaderStageTypeFlags OpenGLShaderModule::Attach(GL::Program& program, const GlslWriter::BindingMapping& bindingMapping) const { - std::string errorLog; - if (!shader.GetCompilationStatus(&errorLog)) - throw std::runtime_error("Failed to compile shader: " + errorLog); - } - - void OpenGLShaderModule::Create(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderAst::StatementPtr& shaderAst, const ShaderWriter::States& states) - { - const auto& context = device.GetReferenceContext(); + const auto& context = m_device.GetReferenceContext(); const auto& contextParams = context.GetParams(); GlslWriter::Environment env; @@ -115,27 +100,64 @@ namespace Nz GlslWriter writer; writer.SetEnv(env); + ShaderStageTypeFlags stageFlags; + for (const auto& shaderEntry : m_shaders) + { + GL::Shader shader; + + if (!shader.Create(m_device, ToOpenGL(shaderEntry.stage))) + throw std::runtime_error("failed to create shader"); //< TODO: Handle error message + + std::visit([&](auto&& arg) + { + using T = std::decay_t; + if constexpr (std::is_same_v) + shader.SetSource(arg.sourceCode.data(), GLint(arg.sourceCode.size())); + else if constexpr (std::is_same_v) + { + std::string code = writer.Generate(shaderEntry.stage, *arg.ast, bindingMapping, m_states); + shader.SetSource(code.data(), GLint(code.size())); + } + else + static_assert(AlwaysFalse::value, "non-exhaustive visitor"); + + }, shaderEntry.shader); + + shader.Compile(); + + CheckCompilationStatus(shader); + + program.AttachShader(shader.GetObjectId()); + // Shader can be deleted now (it won't be deleted by the driver until program gets deleted) + + stageFlags |= shaderEntry.stage; + } + + return stageFlags; + } + + void OpenGLShaderModule::Create(OpenGLDevice& /*device*/, ShaderStageTypeFlags shaderStages, ShaderAst::Statement& shaderAst, 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); + for (std::size_t i = 0; i < ShaderStageTypeCount; ++i) { ShaderStageType shaderStage = static_cast(i); if (shaderStages.Test(shaderStage)) { - GL::Shader shader; - - if (!shader.Create(device, ToOpenGL(shaderStage))) - throw std::runtime_error("failed to create shader"); //< TODO: Handle error message - - std::string code = writer.Generate(shaderStage, shaderAst, states); - - shader.SetSource(code.data(), GLint(code.size())); - shader.Compile(); - - CheckCompilationStatus(shader); - auto& entry = m_shaders.emplace_back(); - entry.shader = std::move(shader); + entry.shader = ShaderStatement{ sanitized }; entry.stage = shaderStage; } } } + + void OpenGLShaderModule::CheckCompilationStatus(GL::Shader& shader) + { + std::string errorLog; + if (!shader.GetCompilationStatus(&errorLog)) + throw std::runtime_error("Failed to compile shader: " + errorLog); + } } diff --git a/src/Nazara/Shader/Ast/AstSerializer.cpp b/src/Nazara/Shader/Ast/AstSerializer.cpp index 6c489bbce..05578f94c 100644 --- a/src/Nazara/Shader/Ast/AstSerializer.cpp +++ b/src/Nazara/Shader/Ast/AstSerializer.cpp @@ -221,6 +221,7 @@ namespace Nz::ShaderAst Value(extVar.name); Type(extVar.type); OptVal(extVar.bindingIndex); + OptVal(extVar.bindingSet); } } diff --git a/src/Nazara/Shader/Ast/SanitizeVisitor.cpp b/src/Nazara/Shader/Ast/SanitizeVisitor.cpp index 74286c964..c52117a7c 100644 --- a/src/Nazara/Shader/Ast/SanitizeVisitor.cpp +++ b/src/Nazara/Shader/Ast/SanitizeVisitor.cpp @@ -41,7 +41,7 @@ namespace Nz::ShaderAst Options options; std::array entryFunctions = {}; std::unordered_set declaredExternalVar; - std::unordered_set usedBindingIndexes; + std::unordered_set usedBindingIndexes; FunctionData* currentFunction = nullptr; }; @@ -704,14 +704,19 @@ namespace Nz::ShaderAst for (const auto& extVar : node.externalVars) { - if (extVar.bindingIndex) - { - unsigned int bindingIndex = extVar.bindingIndex.value(); - if (m_context->usedBindingIndexes.find(bindingIndex) != m_context->usedBindingIndexes.end()) - throw AstError{ "Binding #" + std::to_string(bindingIndex) + " is already in use" }; + if (!extVar.bindingIndex) + throw AstError{ "external variable " + extVar.name + " requires a binding index" }; - m_context->usedBindingIndexes.insert(bindingIndex); - } + if (!extVar.bindingSet) + throw AstError{ "external variable " + extVar.name + " requires a binding set" }; + + UInt64 bindingIndex = *extVar.bindingIndex; + UInt64 bindingSet = *extVar.bindingSet; + UInt64 bindingKey = bindingSet << 32 | bindingIndex; + if (m_context->usedBindingIndexes.find(bindingKey) != m_context->usedBindingIndexes.end()) + throw AstError{ "Binding (set=" + std::to_string(bindingSet) + ", binding=" + std::to_string(bindingIndex) + ") is already in use" }; + + m_context->usedBindingIndexes.insert(bindingKey); if (m_context->declaredExternalVar.find(extVar.name) != m_context->declaredExternalVar.end()) throw AstError{ "External variable " + extVar.name + " is already declared" }; diff --git a/src/Nazara/Shader/GlslWriter.cpp b/src/Nazara/Shader/GlslWriter.cpp index e07ad3e3a..668963604 100644 --- a/src/Nazara/Shader/GlslWriter.cpp +++ b/src/Nazara/Shader/GlslWriter.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -51,6 +52,22 @@ namespace Nz node.statement->Visit(*this); } + void Visit(ShaderAst::DeclareExternalStatement& node) override + { + AstRecursiveVisitor::Visit(node); + + for (auto& extVar : node.externalVars) + { + assert(extVar.bindingIndex); + assert(extVar.bindingSet); + + UInt64 set = *extVar.bindingSet; + UInt64 binding = *extVar.bindingIndex; + + bindings.insert(set << 32 | binding); + } + } + void Visit(ShaderAst::DeclareFunctionStatement& node) override { // Dismiss function if it's an entry point of another type than the one selected @@ -96,6 +113,7 @@ namespace Nz FunctionData* currentFunction = nullptr; + std::set bindings; std::optional selectedStage; std::unordered_map functions; ShaderAst::DeclareFunctionStatement* entryPoint = nullptr; @@ -118,6 +136,11 @@ namespace Nz struct GlslWriter::State { + State(const GlslWriter::BindingMapping& bindings) : + bindingMapping(bindings) + { + } + struct InOutField { std::string memberName; @@ -131,6 +154,7 @@ namespace Nz std::vector inputFields; std::vector outputFields; Bitset<> declaredFunctions; + const GlslWriter::BindingMapping& bindingMapping; PreVisitor previsitor; const States* states = nullptr; UInt64 enabledOptions = 0; @@ -138,9 +162,9 @@ namespace Nz unsigned int indentLevel = 0; }; - std::string GlslWriter::Generate(std::optional shaderStage, ShaderAst::Statement& shader, const States& states) + std::string GlslWriter::Generate(std::optional shaderStage, ShaderAst::Statement& shader, const BindingMapping& bindingMapping, const States& states) { - State state; + State state(bindingMapping); state.enabledOptions = states.enabledOptions; state.stage = shaderStage; @@ -254,7 +278,7 @@ namespace Nz { case ShaderAst::PrimitiveType::Boolean: return Append("bool"); case ShaderAst::PrimitiveType::Float32: return Append("float"); - case ShaderAst::PrimitiveType::Int32: return Append("ivec2"); + case ShaderAst::PrimitiveType::Int32: return Append("int"); case ShaderAst::PrimitiveType::UInt32: return Append("uint"); } } @@ -521,7 +545,7 @@ namespace Nz { if (node.entryStage == ShaderStageType::Fragment && node.earlyFragmentTests && *node.earlyFragmentTests) { - if ((m_environment.glES && m_environment.glMajorVersion >= 3 && m_environment.glMinorVersion >= 1) || (!m_environment.glES && m_environment.glMajorVersion >= 4 && m_environment.glMinorVersion >= 2) || m_environment.extCallback("GL_ARB_shader_image_load_store")) + if ((m_environment.glES && m_environment.glMajorVersion >= 3 && m_environment.glMinorVersion >= 1) || (!m_environment.glES && m_environment.glMajorVersion >= 4 && m_environment.glMinorVersion >= 2) || (m_environment.extCallback && m_environment.extCallback("GL_ARB_shader_image_load_store"))) { AppendLine("layout(early_fragment_tests) in;"); AppendLine(); @@ -843,54 +867,60 @@ namespace Nz isStd140 = structInfo.layout == StructLayout::Std140; } - if (externalVar.bindingIndex) + assert(externalVar.bindingIndex); + assert(externalVar.bindingSet); + + UInt64 bindingIndex = *externalVar.bindingIndex; + UInt64 bindingSet = *externalVar.bindingSet; + + auto bindingIt = m_currentState->bindingMapping.find(bindingSet << 32 | bindingIndex); + if (bindingIt == m_currentState->bindingMapping.end()) + throw std::runtime_error("no binding found for (set=" + std::to_string(bindingSet) + ", binding=" + std::to_string(bindingIndex) + ")"); + + Append("layout(binding = ", bindingIt->second); + if (isStd140) + Append(", std140"); + + Append(") uniform "); + + if (IsUniformType(externalVar.type)) { - Append("layout(binding = "); - Append(*externalVar.bindingIndex); - if (isStd140) - Append(", std140"); + Append("_NzBinding_"); + AppendLine(externalVar.name); - Append(") uniform "); - - if (IsUniformType(externalVar.type)) + EnterScope(); { - Append("_NzBinding_"); - AppendLine(externalVar.name); + auto& uniform = std::get(externalVar.type); + assert(std::holds_alternative(uniform.containedType)); - EnterScope(); + std::size_t structIndex = std::get(uniform.containedType).structIndex; + auto& structDesc = Retrieve(m_currentState->structs, structIndex); + + bool first = true; + for (const auto& member : structDesc.members) { - auto& uniform = std::get(externalVar.type); - assert(std::holds_alternative(uniform.containedType)); + if (!first) + AppendLine(); - std::size_t structIndex = std::get(uniform.containedType).structIndex; - auto& structDesc = Retrieve(m_currentState->structs, structIndex); + first = false; - bool first = true; - for (const auto& member : structDesc.members) - { - if (!first) - AppendLine(); - - first = false; - - Append(member.type); - Append(" "); - Append(member.name); - Append(";"); - } + Append(member.type); + Append(" "); + Append(member.name); + Append(";"); } - LeaveScope(false); } - else - Append(externalVar.type); - - Append(" "); - Append(externalVar.name); - AppendLine(";"); - - if (IsUniformType(externalVar.type)) - AppendLine(); + LeaveScope(false); } + else + Append(externalVar.type); + + Append(" "); + Append(externalVar.name); + AppendLine(";"); + + if (IsUniformType(externalVar.type)) + AppendLine(); RegisterVariable(varIndex++, externalVar.name); } diff --git a/src/Nazara/Shader/LangWriter.cpp b/src/Nazara/Shader/LangWriter.cpp index cfa1e7a50..6ed7d73ca 100644 --- a/src/Nazara/Shader/LangWriter.cpp +++ b/src/Nazara/Shader/LangWriter.cpp @@ -29,7 +29,7 @@ namespace Nz struct LangWriter::BindingAttribute { - std::optional bindingIndex; + std::optional bindingIndex; inline bool HasValue() const { return bindingIndex.has_value(); } }; @@ -71,11 +71,18 @@ namespace Nz struct LangWriter::LocationAttribute { - std::optional locationIndex; + std::optional locationIndex; inline bool HasValue() const { return locationIndex.has_value(); } }; + struct LangWriter::SetAttribute + { + std::optional setIndex; + + inline bool HasValue() const { return setIndex.has_value(); } + }; + struct LangWriter::State { const States* states = nullptr; @@ -87,7 +94,7 @@ namespace Nz unsigned int indentLevel = 0; }; - std::string LangWriter::Generate(ShaderAst::StatementPtr& shader, const States& states) + std::string LangWriter::Generate(ShaderAst::Statement& shader, const States& states) { State state; m_currentState = &state; @@ -220,8 +227,10 @@ namespace Nz if (!hasAnyAttribute) return; + bool first = true; + Append("["); - (AppendAttribute(params), ...); + AppendAttributesInternal(first, std::forward(params)...); Append("]"); if (appendLine) @@ -230,6 +239,27 @@ namespace Nz Append(" "); } + template + void LangWriter::AppendAttributesInternal(bool& first, const T& param) + { + if (!param.HasValue()) + return; + + if (!first) + Append(", "); + + first = false; + + AppendAttribute(param); + } + + template + void LangWriter::AppendAttributesInternal(bool& first, const T1& firstParam, const T2& secondParam, Rest&&... params) + { + AppendAttributesInternal(first, firstParam); + AppendAttributesInternal(first, secondParam, std::forward(params)...); + } + void LangWriter::AppendAttribute(BindingAttribute binding) { if (!binding.HasValue()) @@ -333,6 +363,14 @@ namespace Nz Append("location(", *location.locationIndex, ")"); } + void LangWriter::AppendAttribute(SetAttribute set) + { + if (!set.HasValue()) + return; + + Append("set(", *set.setIndex, ")"); + } + void LangWriter::AppendCommentSection(const std::string& section) { NazaraAssert(m_currentState, "This function should only be called while processing an AST"); @@ -607,7 +645,7 @@ namespace Nz first = false; - AppendAttributes(false, BindingAttribute{ externalVar.bindingIndex }); + AppendAttributes(false, SetAttribute{ externalVar.bindingSet }, BindingAttribute{ externalVar.bindingIndex }); Append(externalVar.name, ": ", externalVar.type); RegisterVariable(varIndex++, externalVar.name); diff --git a/src/Nazara/Shader/ShaderLangParser.cpp b/src/Nazara/Shader/ShaderLangParser.cpp index 9fbd9e8f1..4af04eef1 100644 --- a/src/Nazara/Shader/ShaderLangParser.cpp +++ b/src/Nazara/Shader/ShaderLangParser.cpp @@ -35,6 +35,7 @@ namespace Nz::ShaderLang { "layout", ShaderAst::AttributeType::Layout }, { "location", ShaderAst::AttributeType::Location }, { "opt", ShaderAst::AttributeType::Option }, + { "set", ShaderAst::AttributeType::Set }, }; std::unordered_map s_entryPoints = { @@ -379,8 +380,31 @@ namespace Nz::ShaderLang ShaderAst::StatementPtr Parser::ParseExternalBlock(std::vector attributes) { - if (!attributes.empty()) - throw AttributeError{ "unhandled attribute for external block" }; + std::optional blockSetIndex; + for (const auto& [attributeType, arg] : attributes) + { + switch (attributeType) + { + case ShaderAst::AttributeType::Set: + { + if (blockSetIndex) + throw AttributeError{ "attribute set must be present once" }; + + if (!std::holds_alternative(arg)) + throw AttributeError{ "attribute set requires a string parameter" }; + + std::optional bindingIndex = BoundCast(std::get(arg)); + if (!bindingIndex) + throw AttributeError{ "invalid set index" }; + + blockSetIndex = bindingIndex.value(); + break; + } + + default: + throw AttributeError{ "unhandled attribute for external block" }; + } + } Expect(Advance(), TokenType::External); Expect(Advance(), TokenType::OpenCurlyBracket); @@ -424,7 +448,7 @@ namespace Nz::ShaderLang if (!std::holds_alternative(arg)) throw AttributeError{ "attribute binding requires a string parameter" }; - std::optional bindingIndex = BoundCast(std::get(arg)); + std::optional bindingIndex = BoundCast(std::get(arg)); if (!bindingIndex) throw AttributeError{ "invalid binding index" }; @@ -432,6 +456,22 @@ namespace Nz::ShaderLang break; } + case ShaderAst::AttributeType::Set: + { + if (extVar.bindingSet) + throw AttributeError{ "attribute set must be present once" }; + + if (!std::holds_alternative(arg)) + throw AttributeError{ "attribute set requires a string parameter" }; + + std::optional bindingIndex = BoundCast(std::get(arg)); + if (!bindingIndex) + throw AttributeError{ "invalid set index" }; + + extVar.bindingSet = bindingIndex.value(); + break; + } + default: throw AttributeError{ "unhandled attribute for external variable" }; } @@ -442,6 +482,9 @@ namespace Nz::ShaderLang Expect(Advance(), TokenType::Colon); extVar.type = ParseType(); + if (!extVar.bindingSet) + extVar.bindingSet = blockSetIndex.value_or(0); + RegisterVariable(extVar.name); } @@ -704,7 +747,7 @@ namespace Nz::ShaderLang if (structField.locationIndex) throw AttributeError{ "attribute location must be present once" }; - structField.locationIndex = BoundCast(std::get(attributeParam)); + structField.locationIndex = BoundCast(std::get(attributeParam)); if (!structField.locationIndex) throw AttributeError{ "invalid location index" }; diff --git a/src/Nazara/Shader/SpirvWriter.cpp b/src/Nazara/Shader/SpirvWriter.cpp index 8df68f1de..f4532e05e 100644 --- a/src/Nazara/Shader/SpirvWriter.cpp +++ b/src/Nazara/Shader/SpirvWriter.cpp @@ -47,6 +47,7 @@ namespace Nz struct UniformVar { std::optional bindingIndex; + std::optional descriptorSet; UInt32 pointerId; }; @@ -125,6 +126,7 @@ namespace Nz UniformVar& uniformVar = extVars[varIndex++]; uniformVar.pointerId = m_constantCache.Register(variable); uniformVar.bindingIndex = extVar.bindingIndex; + uniformVar.descriptorSet = extVar.bindingSet; } } @@ -492,7 +494,7 @@ namespace Nz if (extVar.bindingIndex) { state.annotations.Append(SpirvOp::OpDecorate, extVar.pointerId, SpirvDecoration::Binding, *extVar.bindingIndex); - state.annotations.Append(SpirvOp::OpDecorate, extVar.pointerId, SpirvDecoration::DescriptorSet, 0); + state.annotations.Append(SpirvOp::OpDecorate, extVar.pointerId, SpirvDecoration::DescriptorSet, *extVar.descriptorSet); } } diff --git a/src/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.cpp b/src/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.cpp index 96e1ca51a..b08019573 100644 --- a/src/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.cpp +++ b/src/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.cpp @@ -105,13 +105,20 @@ namespace Nz m_commandBuffer.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vkBinding.Get(*m_currentRenderPass, m_currentSubpassIndex)); } - void VulkanCommandBufferBuilder::BindShaderBinding(const ShaderBinding& binding) + void VulkanCommandBufferBuilder::BindShaderBinding(UInt32 set, const ShaderBinding& binding) { const VulkanShaderBinding& vkBinding = static_cast(binding); - const VulkanRenderPipelineLayout& pipelineLayout = vkBinding.GetOwner(); - m_commandBuffer.BindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout.GetPipelineLayout(), 0U, vkBinding.GetDescriptorSet()); + m_commandBuffer.BindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout.GetPipelineLayout(), set, vkBinding.GetDescriptorSet()); + } + + void VulkanCommandBufferBuilder::BindShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding) + { + const VulkanRenderPipelineLayout& vkPipelineLayout = static_cast(pipelineLayout); + const VulkanShaderBinding& vkBinding = static_cast(binding); + + m_commandBuffer.BindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, vkPipelineLayout.GetPipelineLayout(), set, vkBinding.GetDescriptorSet()); } void VulkanCommandBufferBuilder::BindVertexBuffer(UInt32 binding, Nz::AbstractBuffer* vertexBuffer, UInt64 offset) diff --git a/src/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.cpp b/src/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.cpp index 7d7a9c333..6c06de87d 100644 --- a/src/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.cpp +++ b/src/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.cpp @@ -3,8 +3,10 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include +#include #include #include #include @@ -22,11 +24,13 @@ namespace Nz } } - ShaderBindingPtr VulkanRenderPipelineLayout::AllocateShaderBinding() + ShaderBindingPtr VulkanRenderPipelineLayout::AllocateShaderBinding(UInt32 setIndex) { + NazaraAssert(setIndex < m_descriptorSetLayouts.size(), "invalid set index"); + for (std::size_t i = 0; i < m_descriptorPools.size(); ++i) { - ShaderBindingPtr bindingPtr = AllocateFromPool(i); + ShaderBindingPtr bindingPtr = AllocateFromPool(i, setIndex); if (!bindingPtr) continue; @@ -37,7 +41,7 @@ namespace Nz std::size_t newPoolIndex = m_descriptorPools.size(); AllocatePool(); - ShaderBindingPtr bindingPtr = AllocateFromPool(newPoolIndex); + ShaderBindingPtr bindingPtr = AllocateFromPool(newPoolIndex, setIndex); if (!bindingPtr) throw std::runtime_error("Failed to allocate shader binding"); @@ -49,21 +53,35 @@ namespace Nz m_device = &device; m_layoutInfo = std::move(layoutInfo); - StackVector layoutBindings = NazaraStackVector(VkDescriptorSetLayoutBinding, m_layoutInfo.bindings.size()); + UInt32 setCount = 0; + for (const auto& bindingInfo : m_layoutInfo.bindings) + setCount = std::max(setCount, bindingInfo.setIndex + 1); + //TODO: Assert set count before stack allocation + + StackArray setLayoutInfo = NazaraStackArray(VulkanDescriptorSetLayoutInfo, setCount); + StackArray setLayouts = NazaraStackArrayNoInit(VkDescriptorSetLayout, setCount); + + m_descriptorSetLayouts.resize(setCount); for (const auto& bindingInfo : m_layoutInfo.bindings) { - VkDescriptorSetLayoutBinding& layoutBinding = layoutBindings.emplace_back(); - layoutBinding.binding = bindingInfo.index; + VulkanDescriptorSetLayoutInfo& descriptorSetLayoutInfo = setLayoutInfo[bindingInfo.setIndex]; + + VkDescriptorSetLayoutBinding& layoutBinding = descriptorSetLayoutInfo.bindings.emplace_back(); + layoutBinding.binding = bindingInfo.bindingIndex; layoutBinding.descriptorCount = 1U; layoutBinding.descriptorType = ToVulkan(bindingInfo.type); + layoutBinding.pImmutableSamplers = nullptr; layoutBinding.stageFlags = ToVulkan(bindingInfo.shaderStageFlags); } - if (!m_descriptorSetLayout.Create(*m_device, UInt32(layoutBindings.size()), layoutBindings.data())) - return false; + for (UInt32 i = 0; i < setCount; ++i) + { + m_descriptorSetLayouts[i] = &device.GetDescriptorSetLayoutCache().Get(setLayoutInfo[i]); + setLayouts[i] = *m_descriptorSetLayouts[i]; + } - if (!m_pipelineLayout.Create(*m_device, m_descriptorSetLayout)) + if (!m_pipelineLayout.Create(*m_device, UInt32(setLayouts.size()), setLayouts.data())) return false; return true; @@ -94,7 +112,7 @@ namespace Nz return m_descriptorPools.emplace_back(std::move(pool)); } - ShaderBindingPtr VulkanRenderPipelineLayout::AllocateFromPool(std::size_t poolIndex) + ShaderBindingPtr VulkanRenderPipelineLayout::AllocateFromPool(std::size_t poolIndex, UInt32 setIndex) { auto& pool = m_descriptorPools[poolIndex]; @@ -102,7 +120,7 @@ namespace Nz if (freeBindingId == pool.freeBindings.npos) return {}; //< No free binding in this pool - Vk::DescriptorSet descriptorSet = pool.descriptorPool->AllocateDescriptorSet(m_descriptorSetLayout); + Vk::DescriptorSet descriptorSet = pool.descriptorPool->AllocateDescriptorSet(*m_descriptorSetLayouts[setIndex]); if (!descriptorSet) { NazaraWarning("Failed to allocate descriptor set: " + TranslateVulkanError(pool.descriptorPool->GetLastErrorCode())); diff --git a/src/Nazara/VulkanRenderer/Wrapper/DescriptorPool.cpp b/src/Nazara/VulkanRenderer/Wrapper/DescriptorPool.cpp index ddcc30002..9ff6b3b71 100644 --- a/src/Nazara/VulkanRenderer/Wrapper/DescriptorPool.cpp +++ b/src/Nazara/VulkanRenderer/Wrapper/DescriptorPool.cpp @@ -17,7 +17,7 @@ namespace Nz nullptr, m_handle, 1U, - & setLayouts + &setLayouts }; VkDescriptorSet handle = VK_NULL_HANDLE; diff --git a/src/Nazara/VulkanRenderer/Wrapper/Device.cpp b/src/Nazara/VulkanRenderer/Wrapper/Device.cpp index bdc9c7e20..f423acc7f 100644 --- a/src/Nazara/VulkanRenderer/Wrapper/Device.cpp +++ b/src/Nazara/VulkanRenderer/Wrapper/Device.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -23,7 +24,13 @@ namespace Nz { struct Device::InternalData { + InternalData(Device& device) : + setLayoutCache(device) + { + } + std::array commandPools; + VulkanDescriptorSetLayoutCache setLayoutCache; }; Device::Device(Instance& instance) : @@ -131,7 +138,7 @@ namespace Nz for (const QueueFamilyInfo& familyInfo : m_enabledQueuesInfos) m_queuesByFamily[familyInfo.familyIndex] = &familyInfo.queues; - m_internalData = std::make_unique(); + m_internalData = std::make_unique(*this); m_defaultQueues.fill(InvalidQueue); for (QueueType queueType : { QueueType::Graphics, QueueType::Compute, QueueType::Transfer }) @@ -243,6 +250,12 @@ namespace Nz } } + const VulkanDescriptorSetLayoutCache& Device::GetDescriptorSetLayoutCache() const + { + assert(m_internalData); + return m_internalData->setLayoutCache; + } + QueueHandle Device::GetQueue(UInt32 queueFamilyIndex, UInt32 queueIndex) { const auto& queues = GetEnabledQueues(queueFamilyIndex); diff --git a/src/ShaderNode/ShaderGraph.cpp b/src/ShaderNode/ShaderGraph.cpp index 2dd799ad6..2b6de7f31 100644 --- a/src/ShaderNode/ShaderGraph.cpp +++ b/src/ShaderNode/ShaderGraph.cpp @@ -75,7 +75,7 @@ m_type(ShaderType::NotSet) m_type = ShaderType::Fragment; AddInput("UV", PrimitiveType::Float2, InputRole::TexCoord, 0, 0); AddOutput("RenderTarget0", PrimitiveType::Float4, 0); - AddTexture("Potato", TextureType::Sampler2D, 1); + AddTexture("Potato", TextureType::Sampler2D, 0, 1); UpdateTexturePreview(0, QImage(R"(C:\Users\Lynix\Pictures\potatavril.png)")); @@ -106,12 +106,13 @@ ShaderGraph::~ShaderGraph() m_flowScene.reset(); } -std::size_t ShaderGraph::AddBuffer(std::string name, BufferType bufferType, std::size_t structIndex, std::size_t bindingIndex) +std::size_t ShaderGraph::AddBuffer(std::string name, BufferType bufferType, std::size_t structIndex, std::size_t setIndex, std::size_t bindingIndex) { std::size_t index = m_buffers.size(); auto& bufferEntry = m_buffers.emplace_back(); bufferEntry.bindingIndex = bindingIndex; bufferEntry.name = std::move(name); + bufferEntry.setIndex = setIndex; bufferEntry.structIndex = structIndex; bufferEntry.type = bufferType; @@ -171,11 +172,12 @@ std::size_t ShaderGraph::AddStruct(std::string name, std::vector(bufferDoc["bindingIndex"].toInt(0)); buffer.name = bufferDoc["name"].toString().toStdString(); + buffer.setIndex = static_cast(bufferDoc["setIndex"].toInt(0)); buffer.structIndex = bufferDoc["structIndex"].toInt(); buffer.type = DecodeEnum(bufferDoc["type"].toString().toStdString()).value(); } @@ -308,6 +311,7 @@ void ShaderGraph::Load(const QJsonObject& data) TextureEntry& texture = m_textures.emplace_back(); texture.bindingIndex = static_cast(textureDoc["bindingIndex"].toInt(0)); texture.name = textureDoc["name"].toString().toStdString(); + texture.setIndex = static_cast(textureDoc["setIndex"].toInt(0)); texture.type = DecodeEnum(textureDoc["type"].toString().toStdString()).value(); } @@ -332,6 +336,7 @@ QJsonObject ShaderGraph::Save() QJsonObject bufferDoc; bufferDoc["bindingIndex"] = int(buffer.bindingIndex); bufferDoc["name"] = QString::fromStdString(buffer.name); + bufferDoc["setIndex"] = int(buffer.setIndex); bufferDoc["structIndex"] = int(buffer.structIndex); bufferDoc["type"] = QString(EnumToString(buffer.type)); @@ -422,6 +427,7 @@ QJsonObject ShaderGraph::Save() QJsonObject textureDoc; textureDoc["bindingIndex"] = int(texture.bindingIndex); textureDoc["name"] = QString::fromStdString(texture.name); + textureDoc["setIndex"] = int(texture.setIndex); textureDoc["type"] = QString(EnumToString(texture.type)); textureArray.append(textureDoc); @@ -488,6 +494,7 @@ Nz::ShaderAst::StatementPtr ShaderGraph::ToAst() const auto& extVar = external->externalVars.emplace_back(); extVar.bindingIndex = buffer.bindingIndex; + extVar.bindingSet = buffer.setIndex; extVar.name = buffer.name; extVar.type = Nz::ShaderAst::UniformType{ Nz::ShaderAst::IdentifierType{ structInfo.name } }; } @@ -496,6 +503,7 @@ Nz::ShaderAst::StatementPtr ShaderGraph::ToAst() const { auto& extVar = external->externalVars.emplace_back(); extVar.bindingIndex = texture.bindingIndex; + extVar.bindingSet = texture.setIndex; extVar.name = texture.name; extVar.type = ToShaderExpressionType(texture.type); } @@ -569,12 +577,13 @@ Nz::ShaderAst::ExpressionType ShaderGraph::ToShaderExpressionType(const std::var }, type); }; -void ShaderGraph::UpdateBuffer(std::size_t bufferIndex, std::string name, BufferType bufferType, std::size_t structIndex, std::size_t bindingIndex) +void ShaderGraph::UpdateBuffer(std::size_t bufferIndex, std::string name, BufferType bufferType, std::size_t structIndex, std::size_t setIndex, std::size_t bindingIndex) { assert(bufferIndex < m_buffers.size()); auto& bufferEntry = m_buffers[bufferIndex]; bufferEntry.bindingIndex = bindingIndex; bufferEntry.name = std::move(name); + bufferEntry.setIndex = setIndex; bufferEntry.structIndex = structIndex; bufferEntry.type = bufferType; @@ -624,12 +633,13 @@ void ShaderGraph::UpdateStruct(std::size_t structIndex, std::string name, std::v OnStructUpdate(this, structIndex); } -void ShaderGraph::UpdateTexture(std::size_t textureIndex, std::string name, TextureType type, std::size_t bindingIndex) +void ShaderGraph::UpdateTexture(std::size_t textureIndex, std::string name, TextureType type, std::size_t setIndex, std::size_t bindingIndex) { assert(textureIndex < m_textures.size()); auto& textureEntry = m_textures[textureIndex]; textureEntry.bindingIndex = bindingIndex; textureEntry.name = std::move(name); + textureEntry.setIndex = setIndex; textureEntry.type = type; OnTextureUpdate(this, textureIndex); diff --git a/src/ShaderNode/ShaderGraph.hpp b/src/ShaderNode/ShaderGraph.hpp index 2e278a8ac..45844d72c 100644 --- a/src/ShaderNode/ShaderGraph.hpp +++ b/src/ShaderNode/ShaderGraph.hpp @@ -29,12 +29,12 @@ class ShaderGraph ShaderGraph(); ~ShaderGraph(); - std::size_t AddBuffer(std::string name, BufferType bufferType, std::size_t structIndex, std::size_t bindingIndex); + std::size_t AddBuffer(std::string name, BufferType bufferType, std::size_t structIndex, std::size_t setIndex, std::size_t bindingIndex); std::size_t AddCondition(std::string name); std::size_t AddInput(std::string name, PrimitiveType type, InputRole role, std::size_t roleIndex, std::size_t locationIndex); std::size_t AddOutput(std::string name, PrimitiveType type, std::size_t locationIndex); std::size_t AddStruct(std::string name, std::vector members); - std::size_t AddTexture(std::string name, TextureType type, std::size_t bindingIndex); + std::size_t AddTexture(std::string name, TextureType type, std::size_t setIndex, std::size_t bindingIndex); void Clear(); @@ -70,18 +70,19 @@ class ShaderGraph Nz::ShaderAst::StatementPtr ToAst() const; Nz::ShaderAst::ExpressionType ToShaderExpressionType(const std::variant& type) const; - void UpdateBuffer(std::size_t bufferIndex, std::string name, BufferType bufferType, std::size_t structIndex, std::size_t bindingIndex); + void UpdateBuffer(std::size_t bufferIndex, std::string name, BufferType bufferType, std::size_t structIndex, std::size_t setIndex, std::size_t bindingIndex); void UpdateCondition(std::size_t conditionIndex, std::string condition); void UpdateInput(std::size_t inputIndex, std::string name, PrimitiveType type, InputRole role, std::size_t roleIndex, std::size_t locationIndex); void UpdateOutput(std::size_t outputIndex, std::string name, PrimitiveType type, std::size_t locationIndex); void UpdateStruct(std::size_t structIndex, std::string name, std::vector members); - void UpdateTexture(std::size_t textureIndex, std::string name, TextureType type, std::size_t bindingIndex); + void UpdateTexture(std::size_t textureIndex, std::string name, TextureType type, std::size_t setIndex, std::size_t bindingIndex); void UpdateTexturePreview(std::size_t texture, QImage preview); void UpdateType(ShaderType type); struct BufferEntry { std::size_t bindingIndex; + std::size_t setIndex; std::size_t structIndex; std::string name; BufferType type; @@ -124,6 +125,7 @@ class ShaderGraph struct TextureEntry { std::size_t bindingIndex; + std::size_t setIndex; std::string name; TextureType type; QImage preview; diff --git a/src/ShaderNode/Widgets/BufferEditDialog.cpp b/src/ShaderNode/Widgets/BufferEditDialog.cpp index 3f08ca2fa..83215fb04 100644 --- a/src/ShaderNode/Widgets/BufferEditDialog.cpp +++ b/src/ShaderNode/Widgets/BufferEditDialog.cpp @@ -26,11 +26,13 @@ m_shaderGraph(shaderGraph) m_structList->addItem(QString::fromStdString(structEntry.name)); m_bindingIndex = new QSpinBox; + m_setIndex = new QSpinBox; QFormLayout* formLayout = new QFormLayout; formLayout->addRow(tr("Name"), m_outputName); formLayout->addRow(tr("Type"), m_typeList); formLayout->addRow(tr("Struct"), m_structList); + formLayout->addRow(tr("Set index"), m_setIndex); formLayout->addRow(tr("Binding index"), m_bindingIndex); QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); @@ -48,6 +50,7 @@ BufferEditDialog::BufferEditDialog(const ShaderGraph& shaderGraph, const BufferI BufferEditDialog(shaderGraph, parent) { m_bindingIndex->setValue(int(buffer.bindingIndex)); + m_setIndex->setValue(int(buffer.setIndex)); m_outputName->setText(QString::fromStdString(buffer.name)); m_structList->setCurrentIndex(buffer.structIndex); m_typeList->setCurrentIndex(int(buffer.type)); @@ -58,6 +61,7 @@ BufferInfo BufferEditDialog::GetBufferInfo() const BufferInfo bufferInfo; bufferInfo.bindingIndex = static_cast(m_bindingIndex->value()); bufferInfo.name = m_outputName->text().toStdString(); + bufferInfo.setIndex = static_cast(m_setIndex->value()); bufferInfo.structIndex = m_structList->currentIndex(); bufferInfo.type = static_cast(m_typeList->currentIndex()); diff --git a/src/ShaderNode/Widgets/BufferEditDialog.hpp b/src/ShaderNode/Widgets/BufferEditDialog.hpp index 3e5c12e51..c98afd19e 100644 --- a/src/ShaderNode/Widgets/BufferEditDialog.hpp +++ b/src/ShaderNode/Widgets/BufferEditDialog.hpp @@ -14,6 +14,7 @@ class ShaderGraph; struct BufferInfo { std::size_t bindingIndex; + std::size_t setIndex; std::size_t structIndex; std::string name; BufferType type; @@ -36,6 +37,7 @@ class BufferEditDialog : public QDialog QComboBox* m_structList; QLineEdit* m_outputName; QSpinBox* m_bindingIndex; + QSpinBox* m_setIndex; }; #include diff --git a/src/ShaderNode/Widgets/BufferEditor.cpp b/src/ShaderNode/Widgets/BufferEditor.cpp index cd795ee09..6ec612825 100644 --- a/src/ShaderNode/Widgets/BufferEditor.cpp +++ b/src/ShaderNode/Widgets/BufferEditor.cpp @@ -38,7 +38,7 @@ void BufferEditor::OnAddBuffer() connect(dialog, &QDialog::accepted, [this, dialog] { BufferInfo bufferInfo = dialog->GetBufferInfo(); - m_shaderGraph.AddBuffer(std::move(bufferInfo.name), bufferInfo.type, bufferInfo.structIndex, bufferInfo.bindingIndex); + m_shaderGraph.AddBuffer(std::move(bufferInfo.name), bufferInfo.type, bufferInfo.structIndex, bufferInfo.setIndex, bufferInfo.bindingIndex); }); dialog->open(); @@ -49,7 +49,9 @@ void BufferEditor::OnEditBuffer(int inputIndex) const auto& buffer = m_shaderGraph.GetBuffer(inputIndex); BufferInfo info; + info.bindingIndex = buffer.bindingIndex; info.name = buffer.name; + info.setIndex = buffer.setIndex; info.structIndex = buffer.structIndex; info.type = buffer.type; @@ -58,7 +60,7 @@ void BufferEditor::OnEditBuffer(int inputIndex) connect(dialog, &QDialog::accepted, [this, dialog, inputIndex] { BufferInfo bufferInfo = dialog->GetBufferInfo(); - m_shaderGraph.UpdateBuffer(inputIndex, std::move(bufferInfo.name), bufferInfo.type, bufferInfo.structIndex, bufferInfo.bindingIndex); + m_shaderGraph.UpdateBuffer(inputIndex, std::move(bufferInfo.name), bufferInfo.type, bufferInfo.structIndex, bufferInfo.setIndex, bufferInfo.bindingIndex); }); dialog->open(); diff --git a/src/ShaderNode/Widgets/CodeOutputWidget.cpp b/src/ShaderNode/Widgets/CodeOutputWidget.cpp index 3db1efe44..cf33d1945 100644 --- a/src/ShaderNode/Widgets/CodeOutputWidget.cpp +++ b/src/ShaderNode/Widgets/CodeOutputWidget.cpp @@ -81,6 +81,13 @@ void CodeOutputWidget::Refresh() { case OutputLanguage::GLSL: { + Nz::GlslWriter::BindingMapping bindingMapping; + for (const auto& buffer : m_shaderGraph.GetBuffers()) + bindingMapping.emplace(Nz::UInt64(buffer.setIndex) << 32 | Nz::UInt64(buffer.bindingIndex), bindingMapping.size()); + + for (const auto& texture : m_shaderGraph.GetTextures()) + bindingMapping.emplace(Nz::UInt64(texture.setIndex) << 32 | Nz::UInt64(texture.bindingIndex), bindingMapping.size()); + Nz::GlslWriter writer; output = writer.Generate(ShaderGraph::ToShaderStageType(m_shaderGraph.GetType()), *shaderAst, bindingMapping, states); break; diff --git a/src/ShaderNode/Widgets/TextureEditDialog.cpp b/src/ShaderNode/Widgets/TextureEditDialog.cpp index f16530e1f..7d03054ef 100644 --- a/src/ShaderNode/Widgets/TextureEditDialog.cpp +++ b/src/ShaderNode/Widgets/TextureEditDialog.cpp @@ -20,10 +20,12 @@ QDialog(parent) m_typeList->addItem(EnumToString(static_cast(i))); m_bindingIndex = new QSpinBox; + m_setIndex = new QSpinBox; QFormLayout* formLayout = new QFormLayout; formLayout->addRow(tr("Name"), m_textureName); formLayout->addRow(tr("Type"), m_typeList); + formLayout->addRow(tr("Set index"), m_setIndex); formLayout->addRow(tr("Binding index"), m_bindingIndex); QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); @@ -41,6 +43,7 @@ TextureEditDialog::TextureEditDialog(const TextureInfo& texture, QWidget* parent TextureEditDialog(parent) { m_bindingIndex->setValue(int(texture.bindingIndex)); + m_setIndex->setValue(int(texture.setIndex)); m_textureName->setText(QString::fromStdString(texture.name)); m_typeList->setCurrentText(EnumToString(texture.type)); } @@ -50,6 +53,7 @@ TextureInfo TextureEditDialog::GetTextureInfo() const TextureInfo inputInfo; inputInfo.bindingIndex = static_cast(m_bindingIndex->value()); inputInfo.name = m_textureName->text().toStdString(); + inputInfo.setIndex = static_cast(m_setIndex->value()); inputInfo.type = static_cast(m_typeList->currentIndex()); return inputInfo; diff --git a/src/ShaderNode/Widgets/TextureEditDialog.hpp b/src/ShaderNode/Widgets/TextureEditDialog.hpp index ea402f31d..22b84f789 100644 --- a/src/ShaderNode/Widgets/TextureEditDialog.hpp +++ b/src/ShaderNode/Widgets/TextureEditDialog.hpp @@ -13,6 +13,7 @@ class QSpinBox; struct TextureInfo { std::size_t bindingIndex; + std::size_t setIndex; std::string name; TextureType type; }; @@ -32,6 +33,7 @@ class TextureEditDialog : public QDialog QComboBox* m_typeList; QLineEdit* m_textureName; QSpinBox* m_bindingIndex; + QSpinBox* m_setIndex; }; #include diff --git a/src/ShaderNode/Widgets/TextureEditor.cpp b/src/ShaderNode/Widgets/TextureEditor.cpp index c9bcf7d1c..e8c21347f 100644 --- a/src/ShaderNode/Widgets/TextureEditor.cpp +++ b/src/ShaderNode/Widgets/TextureEditor.cpp @@ -47,7 +47,7 @@ void TextureEditor::OnAddTexture() connect(dialog, &QDialog::accepted, [this, dialog] { TextureInfo outputInfo = dialog->GetTextureInfo(); - m_shaderGraph.AddTexture(std::move(outputInfo.name), outputInfo.type, outputInfo.bindingIndex); + m_shaderGraph.AddTexture(std::move(outputInfo.name), outputInfo.type, outputInfo.setIndex, outputInfo.bindingIndex); }); dialog->open(); @@ -67,7 +67,7 @@ void TextureEditor::OnEditTexture(int inputIndex) connect(dialog, &QDialog::accepted, [this, dialog, inputIndex] { TextureInfo textureInfo = dialog->GetTextureInfo(); - m_shaderGraph.UpdateTexture(inputIndex, std::move(textureInfo.name), textureInfo.type, textureInfo.bindingIndex); + m_shaderGraph.UpdateTexture(inputIndex, std::move(textureInfo.name), textureInfo.type, textureInfo.setIndex, textureInfo.bindingIndex); }); dialog->open();