Add initial support for shader binding sets (WIP)
This commit is contained in:
parent
815a7b0c62
commit
f22b501e25
|
|
@ -20,6 +20,7 @@ struct Data
|
|||
viewMatrix: mat4<f32>
|
||||
}
|
||||
|
||||
[set(0)]
|
||||
external
|
||||
{
|
||||
[binding(0)] viewerData: uniform<Data>,
|
||||
|
|
@ -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<Nz::RenderDevice> 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<Nz::RenderPipelineLayout> renderPipelineLayout = device->InstantiateRenderPipelineLayout(std::move(pipelineLayoutInfo));
|
||||
|
||||
Nz::ShaderBindingPtr shaderBinding = renderPipelineLayout->AllocateShaderBinding();
|
||||
Nz::ShaderBindingPtr shaderBinding = renderPipelineLayout->AllocateShaderBinding(0);
|
||||
|
||||
std::shared_ptr<Nz::AbstractBuffer> 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) });
|
||||
|
|
|
|||
|
|
@ -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<Recti> scissorRegion;
|
||||
std::optional<Recti> viewportRegion;
|
||||
std::vector<std::pair<const OpenGLRenderPipelineLayout*, const OpenGLShaderBinding*>> shaderBindings;
|
||||
std::vector<VertexBuffer> vertexBuffers;
|
||||
bool shouldFlipY = false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ namespace Nz
|
|||
std::shared_ptr<RenderPass> InstantiateRenderPass(std::vector<RenderPass::Attachment> attachments, std::vector<RenderPass::SubpassDescription> subpassDescriptions, std::vector<RenderPass::SubpassDependency> subpassDependencies) override;
|
||||
std::shared_ptr<RenderPipeline> InstantiateRenderPipeline(RenderPipelineInfo pipelineInfo) override;
|
||||
std::shared_ptr<RenderPipelineLayout> InstantiateRenderPipelineLayout(RenderPipelineLayoutInfo pipelineLayoutInfo) override;
|
||||
std::shared_ptr<ShaderModule> InstantiateShaderModule(ShaderStageTypeFlags shaderStages, ShaderAst::StatementPtr& shaderAst, const ShaderWriter::States& states) override;
|
||||
std::shared_ptr<ShaderModule> InstantiateShaderModule(ShaderStageTypeFlags shaderStages, ShaderAst::Statement& shaderAst, const ShaderWriter::States& states) override;
|
||||
std::shared_ptr<ShaderModule> InstantiateShaderModule(ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const void* source, std::size_t sourceSize, const ShaderWriter::States& states) override;
|
||||
std::shared_ptr<Texture> InstantiateTexture(const TextureInfo& params) override;
|
||||
std::shared_ptr<TextureSampler> InstantiateTextureSampler(const TextureSamplerInfo& params) override;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <Nazara/OpenGLRenderer/OpenGLShaderBinding.hpp>
|
||||
#include <Nazara/OpenGLRenderer/Wrapper/Context.hpp>
|
||||
#include <Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp>
|
||||
#include <Nazara/Shader/GlslWriter.hpp>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
|
@ -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<typename F> 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<std::monostate, TextureDescriptor, UniformBufferDescriptor>;
|
||||
|
||||
struct DescriptorPool
|
||||
{
|
||||
using BindingStorage = std::aligned_storage_t<sizeof(OpenGLShaderBinding), alignof(OpenGLShaderBinding)>;
|
||||
|
||||
Bitset<UInt64> freeBindings;
|
||||
std::vector<TextureDescriptor> textureDescriptor;
|
||||
std::vector<UniformBufferDescriptor> uniformBufferDescriptor;
|
||||
std::vector<Descriptor> descriptors;
|
||||
std::unique_ptr<BindingStorage[]> storage;
|
||||
};
|
||||
|
||||
std::size_t m_textureDescriptorCount;
|
||||
std::size_t m_uniformBufferDescriptorCount;
|
||||
std::size_t m_maxDescriptorCount;
|
||||
std::vector<DescriptorPool> m_descriptorPools;
|
||||
GlslWriter::BindingMapping m_bindingMapping;
|
||||
RenderPipelineLayoutInfo m_layoutInfo;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<typename F>
|
||||
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::decay_t<decltype(arg)>, std::monostate>)
|
||||
functor(UInt32(descriptorIndex), arg);
|
||||
},
|
||||
pool.descriptors[bindingIndex * m_maxDescriptorCount + descriptorIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
inline void OpenGLRenderPipelineLayout::TryToShrink()
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -11,8 +11,10 @@
|
|||
#include <Nazara/Renderer/Enums.hpp>
|
||||
#include <Nazara/Renderer/ShaderModule.hpp>
|
||||
#include <Nazara/OpenGLRenderer/Wrapper/Context.hpp>
|
||||
#include <Nazara/OpenGLRenderer/Wrapper/Program.hpp>
|
||||
#include <Nazara/OpenGLRenderer/Wrapper/Shader.hpp>
|
||||
#include <Nazara/Shader/ShaderWriter.hpp>
|
||||
#include <Nazara/Shader/GlslWriter.hpp>
|
||||
#include <Nazara/Shader/Ast/Nodes.hpp>
|
||||
#include <vector>
|
||||
|
||||
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<Shader>& 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<ShaderAst::Statement> ast;
|
||||
};
|
||||
|
||||
struct Shader
|
||||
{
|
||||
ShaderStageType stage;
|
||||
std::variant<GlslShader, ShaderStatement> shader;
|
||||
};
|
||||
|
||||
OpenGLDevice& m_device;
|
||||
ShaderWriter::States m_states;
|
||||
std::vector<Shader> m_shaders;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,6 @@
|
|||
|
||||
namespace Nz
|
||||
{
|
||||
inline auto OpenGLShaderModule::GetShaders() const -> const std::vector<Shader>&
|
||||
{
|
||||
return m_shaders;
|
||||
}
|
||||
}
|
||||
|
||||
#include <Nazara/OpenGLRenderer/DebugOff.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);
|
||||
|
|
|
|||
|
|
@ -21,9 +21,10 @@ namespace Nz
|
|||
{
|
||||
struct Binding
|
||||
{
|
||||
UInt32 setIndex;
|
||||
UInt32 bindingIndex;
|
||||
ShaderBindingType type;
|
||||
ShaderStageTypeFlags shaderStageFlags;
|
||||
unsigned int index;
|
||||
};
|
||||
|
||||
std::vector<Binding> bindings;
|
||||
|
|
@ -35,7 +36,7 @@ namespace Nz
|
|||
RenderPipelineLayout() = default;
|
||||
virtual ~RenderPipelineLayout();
|
||||
|
||||
virtual ShaderBindingPtr AllocateShaderBinding() = 0;
|
||||
virtual ShaderBindingPtr AllocateShaderBinding(UInt32 setIndex) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ namespace Nz::ShaderAst
|
|||
struct StructMember
|
||||
{
|
||||
std::optional<BuiltinEntry> builtin;
|
||||
std::optional<unsigned int> locationIndex;
|
||||
std::optional<UInt32> locationIndex;
|
||||
std::string name;
|
||||
ExpressionType type;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -252,7 +252,8 @@ namespace Nz::ShaderAst
|
|||
|
||||
struct ExternalVar
|
||||
{
|
||||
std::optional<unsigned int> bindingIndex;
|
||||
std::optional<UInt32> bindingIndex;
|
||||
std::optional<UInt32> bindingSet;
|
||||
std::string name;
|
||||
ExpressionType type;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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<UInt64 /* set | binding */, unsigned /*glBinding*/>;
|
||||
struct Environment;
|
||||
using ExtSupportCallback = std::function<bool(const std::string_view& name)>;
|
||||
|
||||
|
|
@ -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<ShaderStageType> 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<ShaderStageType> shaderStage, ShaderAst::Statement& shader, const BindingMapping& bindingMapping, const States& states = {});
|
||||
|
||||
void SetEnv(Environment environment);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<typename T> void Append(const T& param);
|
||||
template<typename T1, typename T2, typename... Args> void Append(const T1& firstParam, const T2& secondParam, Args&&... params);
|
||||
template<typename... Args> void AppendAttributes(bool appendLine, Args&&... params);
|
||||
template<typename T> void AppendAttributesInternal(bool& first, const T& param);
|
||||
template<typename T1, typename T2, typename... Rest> 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();
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
#include <Nazara/VulkanRenderer/VulkanCommandBuffer.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanCommandPool.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanDescriptorSetLayoutCache.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanDevice.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanFramebuffer.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanMultipleFramebuffer.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;
|
||||
|
|
|
|||
|
|
@ -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 <Nazara/Prerequisites.hpp>
|
||||
#include <Nazara/Core/Algorithm.hpp>
|
||||
#include <Nazara/VulkanRenderer/Wrapper/DescriptorSetLayout.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
namespace Vk
|
||||
{
|
||||
class Device;
|
||||
}
|
||||
|
||||
struct VulkanDescriptorSetLayoutInfo
|
||||
{
|
||||
VkDescriptorSetLayoutCreateFlags createFlags = 0;
|
||||
std::vector<VkDescriptorSetLayoutBinding> 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<VulkanDescriptorSetLayoutInfo, Vk::DescriptorSetLayout, VulkanDescriptorSetLayoutBindingHasher, VulkanDescriptorSetLayoutBindingEqual>;
|
||||
|
||||
mutable Cache m_cache;
|
||||
Vk::Device& m_device;
|
||||
};
|
||||
}
|
||||
|
||||
#include <Nazara/VulkanRenderer/VulkanDescriptorSetLayoutCache.inl>
|
||||
|
||||
#endif
|
||||
|
|
@ -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 <Nazara/VulkanRenderer/VulkanDescriptorSetLayoutCache.hpp>
|
||||
#include <Nazara/VulkanRenderer/Utils.hpp>
|
||||
#include <stdexcept>
|
||||
#include <Nazara/VulkanRenderer/Debug.hpp>
|
||||
|
||||
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 <Nazara/VulkanRenderer/DebugOff.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<Vk::Device> m_device;
|
||||
std::vector<DescriptorPool> m_descriptorPools;
|
||||
Vk::DescriptorSetLayout m_descriptorSetLayout;
|
||||
std::vector<const Vk::DescriptorSetLayout*> m_descriptorSetLayouts;
|
||||
Vk::PipelineLayout m_pipelineLayout;
|
||||
RenderPipelineLayoutInfo m_layoutInfo;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<QueueFamilyInfo>& 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 <Nazara/VulkanRenderer/Wrapper/DeviceFunctions.hpp>
|
||||
|
||||
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<UInt32>::max();
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -8,92 +8,89 @@
|
|||
#include <Nazara/VulkanRenderer/Wrapper/Instance.hpp>
|
||||
#include <Nazara/VulkanRenderer/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
namespace Nz::Vk
|
||||
{
|
||||
namespace Vk
|
||||
inline UInt32 Device::GetDefaultFamilyIndex(QueueType queueType) const
|
||||
{
|
||||
inline const std::vector<Device::QueueFamilyInfo>& Device::GetEnabledQueues() const
|
||||
return m_defaultQueues[UnderlyingCast(queueType)];
|
||||
}
|
||||
|
||||
inline const std::vector<Device::QueueFamilyInfo>& 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,42 +13,39 @@
|
|||
#include <vulkan/vulkan_core.h>
|
||||
#include <string>
|
||||
|
||||
namespace Nz
|
||||
namespace Nz::Vk
|
||||
{
|
||||
namespace Vk
|
||||
template<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
class DeviceObject
|
||||
{
|
||||
template<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
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<Device> m_device;
|
||||
VkAllocationCallbacks m_allocator;
|
||||
VkType m_handle;
|
||||
mutable VkResult m_lastErrorCode;
|
||||
};
|
||||
}
|
||||
protected:
|
||||
MovablePtr<Device> m_device;
|
||||
VkAllocationCallbacks m_allocator;
|
||||
VkType m_handle;
|
||||
mutable VkResult m_lastErrorCode;
|
||||
};
|
||||
}
|
||||
|
||||
#include <Nazara/VulkanRenderer/Wrapper/DeviceObject.inl>
|
||||
|
|
|
|||
|
|
@ -8,126 +8,123 @@
|
|||
#include <type_traits>
|
||||
#include <Nazara/VulkanRenderer/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
namespace Nz::Vk
|
||||
{
|
||||
namespace Vk
|
||||
template<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
DeviceObject<C, VkType, CreateInfo, ObjectType>::DeviceObject() :
|
||||
m_handle(VK_NULL_HANDLE)
|
||||
{
|
||||
template<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
DeviceObject<C, VkType, CreateInfo, ObjectType>::DeviceObject() :
|
||||
m_handle(VK_NULL_HANDLE)
|
||||
}
|
||||
|
||||
template<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
DeviceObject<C, VkType, CreateInfo, ObjectType>::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<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
DeviceObject<C, VkType, CreateInfo, ObjectType>::~DeviceObject()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
template<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
bool DeviceObject<C, VkType, CreateInfo, ObjectType>::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<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
DeviceObject<C, VkType, CreateInfo, ObjectType>::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<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
void DeviceObject<C, VkType, CreateInfo, ObjectType>::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<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
DeviceObject<C, VkType, CreateInfo, ObjectType>::~DeviceObject()
|
||||
template<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
bool DeviceObject<C, VkType, CreateInfo, ObjectType>::IsValid() const
|
||||
{
|
||||
return m_handle != VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
template<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
Device* DeviceObject<C, VkType, CreateInfo, ObjectType>::GetDevice() const
|
||||
{
|
||||
return m_device;
|
||||
}
|
||||
|
||||
template<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
VkResult DeviceObject<C, VkType, CreateInfo, ObjectType>::GetLastErrorCode() const
|
||||
{
|
||||
return m_lastErrorCode;
|
||||
}
|
||||
|
||||
template<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
void DeviceObject<C, VkType, CreateInfo, ObjectType>::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<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
bool DeviceObject<C, VkType, CreateInfo, ObjectType>::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<VkType>)
|
||||
debugName.objectHandle = static_cast<UInt64>(reinterpret_cast<std::uintptr_t>(m_handle));
|
||||
else
|
||||
m_allocator.pfnAllocation = nullptr;
|
||||
debugName.objectHandle = static_cast<UInt64>(m_handle);
|
||||
|
||||
return true;
|
||||
m_device->vkSetDebugUtilsObjectNameEXT(*m_device, &debugName);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
void DeviceObject<C, VkType, CreateInfo, ObjectType>::Destroy()
|
||||
{
|
||||
if (IsValid())
|
||||
{
|
||||
C::DestroyHelper(*m_device, m_handle, (m_allocator.pfnAllocation) ? &m_allocator : nullptr);
|
||||
m_handle = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
template<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
void DeviceObject<C, VkType, CreateInfo, ObjectType>::SetDebugName(const std::string& name)
|
||||
{
|
||||
return SetDebugName(name.data());
|
||||
}
|
||||
|
||||
template<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
bool DeviceObject<C, VkType, CreateInfo, ObjectType>::IsValid() const
|
||||
{
|
||||
return m_handle != VK_NULL_HANDLE;
|
||||
}
|
||||
template<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
auto DeviceObject<C, VkType, CreateInfo, ObjectType>::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<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
Device* DeviceObject<C, VkType, CreateInfo, ObjectType>::GetDevice() const
|
||||
{
|
||||
return m_device;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
VkResult DeviceObject<C, VkType, CreateInfo, ObjectType>::GetLastErrorCode() const
|
||||
{
|
||||
return m_lastErrorCode;
|
||||
}
|
||||
|
||||
template<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
void DeviceObject<C, VkType, CreateInfo, ObjectType>::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<VkType>)
|
||||
debugName.objectHandle = static_cast<UInt64>(reinterpret_cast<std::uintptr_t>(m_handle));
|
||||
else
|
||||
debugName.objectHandle = static_cast<UInt64>(m_handle);
|
||||
|
||||
m_device->vkSetDebugUtilsObjectNameEXT(*m_device, &debugName);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
void DeviceObject<C, VkType, CreateInfo, ObjectType>::SetDebugName(const std::string& name)
|
||||
{
|
||||
return SetDebugName(name.data());
|
||||
}
|
||||
|
||||
template<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
auto DeviceObject<C, VkType, CreateInfo, ObjectType>::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<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
DeviceObject<C, VkType, CreateInfo, ObjectType>::operator VkType() const
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
template<typename C, typename VkType, typename CreateInfo, VkObjectType ObjectType>
|
||||
DeviceObject<C, VkType, CreateInfo, ObjectType>::operator VkType() const
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <Nazara/Core/StackArray.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLCommandPool.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLRenderPass.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLVaoCache.hpp>
|
||||
#include <Nazara/OpenGLRenderer/Wrapper/Context.hpp>
|
||||
#include <Nazara/OpenGLRenderer/Wrapper/VertexArray.hpp>
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<const OpenGLShaderBinding&>(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<const OpenGLRenderPipelineLayout&>(pipelineLayout);
|
||||
const OpenGLShaderBinding& glBinding = static_cast<const OpenGLShaderBinding&>(binding);
|
||||
|
||||
m_commandBuffer.BindShaderBinding(glPipelineLayout, set, &glBinding);
|
||||
}
|
||||
|
||||
void OpenGLCommandBufferBuilder::BindVertexBuffer(UInt32 binding, Nz::AbstractBuffer* vertexBuffer, UInt64 offset)
|
||||
|
|
|
|||
|
|
@ -19,19 +19,23 @@ namespace Nz
|
|||
m_pipelineInfo(std::move(pipelineInfo)),
|
||||
m_isViewportFlipped(false)
|
||||
{
|
||||
OpenGLRenderPipelineLayout& pipelineLayout = static_cast<OpenGLRenderPipelineLayout&>(*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<OpenGLShaderModule&>(*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;
|
||||
|
|
|
|||
|
|
@ -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<std::size_t>(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<DescriptorPool::BindingStorage[]>(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<TextureDescriptor>();
|
||||
}
|
||||
|
||||
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<UniformBufferDescriptor>();
|
||||
}
|
||||
|
||||
void OpenGLRenderPipelineLayout::Release(ShaderBinding& binding)
|
||||
|
|
|
|||
|
|
@ -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<decltype(descriptor)>;
|
||||
|
||||
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<DescriptorType, OpenGLRenderPipelineLayout::TextureDescriptor>)
|
||||
{
|
||||
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<DescriptorType, OpenGLRenderPipelineLayout::UniformBufferDescriptor>)
|
||||
{
|
||||
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<T>::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<decltype(arg)>;
|
||||
|
||||
switch (bindingDesc.type)
|
||||
{
|
||||
case ShaderBindingType::Texture:
|
||||
if constexpr (std::is_same_v<T, TextureBinding>)
|
||||
{
|
||||
if (!std::holds_alternative<TextureBinding>(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<TextureBinding>(binding.content);
|
||||
|
||||
auto& textureDescriptor = m_owner.GetTextureDescriptor(m_poolIndex, m_bindingIndex, resourceIndex);
|
||||
textureDescriptor.bindingIndex = UInt32(binding.bindingIndex);
|
||||
|
||||
if (OpenGLTexture* glTexture = static_cast<OpenGLTexture*>(texBinding.texture))
|
||||
if (OpenGLTexture* glTexture = static_cast<OpenGLTexture*>(arg.texture))
|
||||
{
|
||||
textureDescriptor.texture = glTexture->GetTexture().GetObjectId();
|
||||
|
||||
if (OpenGLTextureSampler* glSampler = static_cast<OpenGLTextureSampler*>(texBinding.sampler))
|
||||
if (OpenGLTextureSampler* glSampler = static_cast<OpenGLTextureSampler*>(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<T, UniformBufferBinding>)
|
||||
{
|
||||
if (!std::holds_alternative<UniformBufferBinding>(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<UniformBufferBinding>(binding.content);
|
||||
|
||||
auto& uboDescriptor = m_owner.GetUniformBufferDescriptor(m_poolIndex, m_bindingIndex, resourceIndex);
|
||||
uboDescriptor.bindingIndex = static_cast<UInt32>(binding.bindingIndex);
|
||||
uboDescriptor.offset = uboBinding.offset;
|
||||
uboDescriptor.size = uboBinding.range;
|
||||
|
||||
if (OpenGLBuffer* glBuffer = static_cast<OpenGLBuffer*>(uboBinding.buffer))
|
||||
if (OpenGLBuffer* glBuffer = static_cast<OpenGLBuffer*>(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<T>::value, "non-exhaustive visitor");
|
||||
|
||||
}, binding.content);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,10 +5,8 @@
|
|||
#include <Nazara/OpenGLRenderer/OpenGLShaderModule.hpp>
|
||||
#include <Nazara/Core/MemoryView.hpp>
|
||||
#include <Nazara/OpenGLRenderer/Utils.hpp>
|
||||
#include <Nazara/Shader/GlslWriter.hpp>
|
||||
#include <Nazara/Shader/ShaderLangLexer.hpp>
|
||||
#include <Nazara/Shader/ShaderLangParser.hpp>
|
||||
#include <Nazara/Shader/Ast/AstCloner.hpp>
|
||||
#include <Nazara/Shader/Ast/AstSerializer.hpp>
|
||||
#include <stdexcept>
|
||||
#include <Nazara/OpenGLRenderer/Debug.hpp>
|
||||
|
|
@ -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<const char*>(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<const char*>(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<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, GlslShader>)
|
||||
shader.SetSource(arg.sourceCode.data(), GLint(arg.sourceCode.size()));
|
||||
else if constexpr (std::is_same_v<T, ShaderStatement>)
|
||||
{
|
||||
std::string code = writer.Generate(shaderEntry.stage, *arg.ast, bindingMapping, m_states);
|
||||
shader.SetSource(code.data(), GLint(code.size()));
|
||||
}
|
||||
else
|
||||
static_assert(AlwaysFalse<T>::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<ShaderAst::Statement> sanitized = GlslWriter::Sanitize(shaderAst);
|
||||
|
||||
for (std::size_t i = 0; i < ShaderStageTypeCount; ++i)
|
||||
{
|
||||
ShaderStageType shaderStage = static_cast<ShaderStageType>(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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ namespace Nz::ShaderAst
|
|||
Value(extVar.name);
|
||||
Type(extVar.type);
|
||||
OptVal(extVar.bindingIndex);
|
||||
OptVal(extVar.bindingSet);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ namespace Nz::ShaderAst
|
|||
Options options;
|
||||
std::array<DeclareFunctionStatement*, ShaderStageTypeCount> entryFunctions = {};
|
||||
std::unordered_set<std::string> declaredExternalVar;
|
||||
std::unordered_set<unsigned int> usedBindingIndexes;
|
||||
std::unordered_set<UInt64> 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" };
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <Nazara/Shader/Ast/AstUtils.hpp>
|
||||
#include <Nazara/Shader/Ast/SanitizeVisitor.hpp>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <Nazara/Shader/Debug.hpp>
|
||||
|
||||
|
|
@ -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<UInt64 /*set | binding*/> bindings;
|
||||
std::optional<ShaderStageType> selectedStage;
|
||||
std::unordered_map<std::size_t, FunctionData> 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<InOutField> inputFields;
|
||||
std::vector<InOutField> 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<ShaderStageType> shaderStage, ShaderAst::Statement& shader, const States& states)
|
||||
std::string GlslWriter::Generate(std::optional<ShaderStageType> 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<ShaderAst::UniformType>(externalVar.type);
|
||||
assert(std::holds_alternative<ShaderAst::StructType>(uniform.containedType));
|
||||
|
||||
EnterScope();
|
||||
std::size_t structIndex = std::get<ShaderAst::StructType>(uniform.containedType).structIndex;
|
||||
auto& structDesc = Retrieve(m_currentState->structs, structIndex);
|
||||
|
||||
bool first = true;
|
||||
for (const auto& member : structDesc.members)
|
||||
{
|
||||
auto& uniform = std::get<ShaderAst::UniformType>(externalVar.type);
|
||||
assert(std::holds_alternative<ShaderAst::StructType>(uniform.containedType));
|
||||
if (!first)
|
||||
AppendLine();
|
||||
|
||||
std::size_t structIndex = std::get<ShaderAst::StructType>(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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ namespace Nz
|
|||
|
||||
struct LangWriter::BindingAttribute
|
||||
{
|
||||
std::optional<unsigned int> bindingIndex;
|
||||
std::optional<UInt32> bindingIndex;
|
||||
|
||||
inline bool HasValue() const { return bindingIndex.has_value(); }
|
||||
};
|
||||
|
|
@ -71,11 +71,18 @@ namespace Nz
|
|||
|
||||
struct LangWriter::LocationAttribute
|
||||
{
|
||||
std::optional<unsigned int> locationIndex;
|
||||
std::optional<UInt32> locationIndex;
|
||||
|
||||
inline bool HasValue() const { return locationIndex.has_value(); }
|
||||
};
|
||||
|
||||
struct LangWriter::SetAttribute
|
||||
{
|
||||
std::optional<UInt32> 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<Args>(params)...);
|
||||
Append("]");
|
||||
|
||||
if (appendLine)
|
||||
|
|
@ -230,6 +239,27 @@ namespace Nz
|
|||
Append(" ");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void LangWriter::AppendAttributesInternal(bool& first, const T& param)
|
||||
{
|
||||
if (!param.HasValue())
|
||||
return;
|
||||
|
||||
if (!first)
|
||||
Append(", ");
|
||||
|
||||
first = false;
|
||||
|
||||
AppendAttribute(param);
|
||||
}
|
||||
|
||||
template<typename T1, typename T2, typename... Rest>
|
||||
void LangWriter::AppendAttributesInternal(bool& first, const T1& firstParam, const T2& secondParam, Rest&&... params)
|
||||
{
|
||||
AppendAttributesInternal(first, firstParam);
|
||||
AppendAttributesInternal(first, secondParam, std::forward<Rest>(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);
|
||||
|
|
|
|||
|
|
@ -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<std::string, ShaderStageType> s_entryPoints = {
|
||||
|
|
@ -379,8 +380,31 @@ namespace Nz::ShaderLang
|
|||
|
||||
ShaderAst::StatementPtr Parser::ParseExternalBlock(std::vector<ShaderAst::Attribute> attributes)
|
||||
{
|
||||
if (!attributes.empty())
|
||||
throw AttributeError{ "unhandled attribute for external block" };
|
||||
std::optional<UInt32> 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<long long>(arg))
|
||||
throw AttributeError{ "attribute set requires a string parameter" };
|
||||
|
||||
std::optional<UInt32> bindingIndex = BoundCast<UInt32>(std::get<long long>(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<long long>(arg))
|
||||
throw AttributeError{ "attribute binding requires a string parameter" };
|
||||
|
||||
std::optional<unsigned int> bindingIndex = BoundCast<unsigned int>(std::get<long long>(arg));
|
||||
std::optional<UInt32> bindingIndex = BoundCast<UInt32>(std::get<long long>(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<long long>(arg))
|
||||
throw AttributeError{ "attribute set requires a string parameter" };
|
||||
|
||||
std::optional<UInt32> bindingIndex = BoundCast<UInt32>(std::get<long long>(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<unsigned int>(std::get<long long>(attributeParam));
|
||||
structField.locationIndex = BoundCast<UInt32>(std::get<long long>(attributeParam));
|
||||
if (!structField.locationIndex)
|
||||
throw AttributeError{ "invalid location index" };
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ namespace Nz
|
|||
struct UniformVar
|
||||
{
|
||||
std::optional<UInt32> bindingIndex;
|
||||
std::optional<UInt32> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<const VulkanShaderBinding&>(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<const VulkanRenderPipelineLayout&>(pipelineLayout);
|
||||
const VulkanShaderBinding& vkBinding = static_cast<const VulkanShaderBinding&>(binding);
|
||||
|
||||
m_commandBuffer.BindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, vkPipelineLayout.GetPipelineLayout(), set, vkBinding.GetDescriptorSet());
|
||||
}
|
||||
|
||||
void VulkanCommandBufferBuilder::BindVertexBuffer(UInt32 binding, Nz::AbstractBuffer* vertexBuffer, UInt64 offset)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@
|
|||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/VulkanRenderer/VulkanRenderPipelineLayout.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanDescriptorSetLayoutCache.hpp>
|
||||
#include <Nazara/Core/ErrorFlags.hpp>
|
||||
#include <Nazara/Core/MemoryHelper.hpp>
|
||||
#include <Nazara/Core/StackArray.hpp>
|
||||
#include <Nazara/Core/StackVector.hpp>
|
||||
#include <Nazara/VulkanRenderer/Utils.hpp>
|
||||
#include <cassert>
|
||||
|
|
@ -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<VkDescriptorSetLayoutBinding> 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<VulkanDescriptorSetLayoutInfo> setLayoutInfo = NazaraStackArray(VulkanDescriptorSetLayoutInfo, setCount);
|
||||
StackArray<VkDescriptorSetLayout> 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()));
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ namespace Nz
|
|||
nullptr,
|
||||
m_handle,
|
||||
1U,
|
||||
& setLayouts
|
||||
&setLayouts
|
||||
};
|
||||
|
||||
VkDescriptorSet handle = VK_NULL_HANDLE;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <Nazara/Core/CallOnExit.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/ErrorFlags.hpp>
|
||||
#include <Nazara/VulkanRenderer/VulkanDescriptorSetLayoutCache.hpp>
|
||||
#include <Nazara/VulkanRenderer/Wrapper/CommandBuffer.hpp>
|
||||
#include <Nazara/VulkanRenderer/Wrapper/CommandPool.hpp>
|
||||
#include <Nazara/VulkanRenderer/Wrapper/QueueHandle.hpp>
|
||||
|
|
@ -23,7 +24,13 @@ namespace Nz
|
|||
{
|
||||
struct Device::InternalData
|
||||
{
|
||||
InternalData(Device& device) :
|
||||
setLayoutCache(device)
|
||||
{
|
||||
}
|
||||
|
||||
std::array<Vk::CommandPool, QueueCount> 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<InternalData>();
|
||||
m_internalData = std::make_unique<InternalData>(*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);
|
||||
|
|
|
|||
|
|
@ -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<StructMemberEnt
|
|||
return index;
|
||||
}
|
||||
|
||||
std::size_t ShaderGraph::AddTexture(std::string name, TextureType type, std::size_t bindingIndex)
|
||||
std::size_t ShaderGraph::AddTexture(std::string name, TextureType type, std::size_t setIndex, std::size_t bindingIndex)
|
||||
{
|
||||
std::size_t index = m_textures.size();
|
||||
auto& textureEntry = m_textures.emplace_back();
|
||||
textureEntry.bindingIndex = bindingIndex;
|
||||
textureEntry.setIndex = setIndex;
|
||||
textureEntry.name = std::move(name);
|
||||
textureEntry.type = type;
|
||||
|
||||
|
|
@ -229,6 +231,7 @@ void ShaderGraph::Load(const QJsonObject& data)
|
|||
BufferEntry& buffer = m_buffers.emplace_back();
|
||||
buffer.bindingIndex = static_cast<std::size_t>(bufferDoc["bindingIndex"].toInt(0));
|
||||
buffer.name = bufferDoc["name"].toString().toStdString();
|
||||
buffer.setIndex = static_cast<std::size_t>(bufferDoc["setIndex"].toInt(0));
|
||||
buffer.structIndex = bufferDoc["structIndex"].toInt();
|
||||
buffer.type = DecodeEnum<BufferType>(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<std::size_t>(textureDoc["bindingIndex"].toInt(0));
|
||||
texture.name = textureDoc["name"].toString().toStdString();
|
||||
texture.setIndex = static_cast<std::size_t>(textureDoc["setIndex"].toInt(0));
|
||||
texture.type = DecodeEnum<TextureType>(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);
|
||||
|
|
|
|||
|
|
@ -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<StructMemberEntry> 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<PrimitiveType, std::size_t>& 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<StructMemberEntry> 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;
|
||||
|
|
|
|||
|
|
@ -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<std::size_t>(m_bindingIndex->value());
|
||||
bufferInfo.name = m_outputName->text().toStdString();
|
||||
bufferInfo.setIndex = static_cast<std::size_t>(m_setIndex->value());
|
||||
bufferInfo.structIndex = m_structList->currentIndex();
|
||||
bufferInfo.type = static_cast<BufferType>(m_typeList->currentIndex());
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <ShaderNode/Widgets/BufferEditDialog.inl>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -20,10 +20,12 @@ QDialog(parent)
|
|||
m_typeList->addItem(EnumToString(static_cast<TextureType>(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<std::size_t>(m_bindingIndex->value());
|
||||
inputInfo.name = m_textureName->text().toStdString();
|
||||
inputInfo.setIndex = static_cast<std::size_t>(m_setIndex->value());
|
||||
inputInfo.type = static_cast<TextureType>(m_typeList->currentIndex());
|
||||
|
||||
return inputInfo;
|
||||
|
|
|
|||
|
|
@ -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 <ShaderNode/Widgets/TextureEditDialog.inl>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in New Issue