Add initial support for shader binding sets (WIP)

This commit is contained in:
Jérôme Leclercq
2021-06-14 22:35:05 +02:00
parent 815a7b0c62
commit f22b501e25
53 changed files with 885 additions and 511 deletions

View File

@@ -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);

View File

@@ -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)

View File

@@ -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;

View File

@@ -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)

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -221,6 +221,7 @@ namespace Nz::ShaderAst
Value(extVar.name);
Type(extVar.type);
OptVal(extVar.bindingIndex);
OptVal(extVar.bindingSet);
}
}

View File

@@ -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" };

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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" };

View File

@@ -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);
}
}

View File

@@ -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)

View File

@@ -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()));

View File

@@ -17,7 +17,7 @@ namespace Nz
nullptr,
m_handle,
1U,
& setLayouts
&setLayouts
};
VkDescriptorSet handle = VK_NULL_HANDLE;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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());

View File

@@ -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>

View File

@@ -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();

View File

@@ -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;

View File

@@ -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;

View File

@@ -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>

View File

@@ -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();