Graphics: rework ubershaders to prevent duplicate shaders modules

Also rename all remaining conditions to options
This commit is contained in:
Jérôme Leclercq 2021-07-08 14:52:39 +02:00
parent 9ab47edd11
commit a895e553d4
38 changed files with 524 additions and 485 deletions

View File

@ -56,7 +56,7 @@ namespace Nz
};
private:
struct ConditionIndexes
struct OptionIndexes
{
std::size_t alphaTest;
std::size_t hasAlphaMap;
@ -74,13 +74,13 @@ namespace Nz
Material& m_material;
std::size_t m_uniformBlockIndex;
ConditionIndexes m_conditionIndexes;
OptionIndexes m_optionIndexes;
TextureIndexes m_textureIndexes;
UniformOffsets m_uniformOffsets;
static std::shared_ptr<MaterialSettings> s_materialSettings;
static std::size_t s_uniformBlockIndex;
static ConditionIndexes s_conditionIndexes;
static OptionIndexes s_optionIndexes;
static TextureIndexes s_textureIndexes;
static UniformOffsets s_uniformOffsets;
};

View File

@ -25,8 +25,8 @@ namespace Nz
*/
inline void BasicMaterial::EnableAlphaTest(bool alphaTest)
{
NazaraAssert(HasAlphaTest(), "Material has no alpha test condition");
m_material.EnableCondition(m_conditionIndexes.alphaTest, alphaTest);
NazaraAssert(HasAlphaTest(), "Material has no alpha test option");
m_material.EnableOption(m_optionIndexes.alphaTest, alphaTest);
}
inline const std::shared_ptr<Texture>& BasicMaterial::GetAlphaMap() const
@ -55,8 +55,8 @@ namespace Nz
inline bool BasicMaterial::IsAlphaTestEnabled() const
{
NazaraAssert(HasAlphaTest(), "Material has no alpha test condition");
return m_material.IsConditionEnabled(m_conditionIndexes.alphaTest);
NazaraAssert(HasAlphaTest(), "Material has no alpha test option");
return m_material.IsOptionEnabled(m_optionIndexes.alphaTest);
}
inline bool BasicMaterial::HasAlphaMap() const
@ -66,7 +66,7 @@ namespace Nz
inline bool BasicMaterial::HasAlphaTest() const
{
return m_conditionIndexes.alphaTest != MaterialSettings::InvalidIndex;
return m_optionIndexes.alphaTest != MaterialSettings::InvalidIndex;
}
inline bool BasicMaterial::HasAlphaTestThreshold() const
@ -90,8 +90,8 @@ namespace Nz
bool hasAlphaMap = (alphaMap != nullptr);
m_material.SetTexture(m_textureIndexes.alpha, std::move(alphaMap));
if (m_conditionIndexes.hasDiffuseMap != MaterialSettings::InvalidIndex)
m_material.EnableCondition(m_conditionIndexes.hasAlphaMap, hasAlphaMap);
if (m_optionIndexes.hasDiffuseMap != MaterialSettings::InvalidIndex)
m_material.EnableOption(m_optionIndexes.hasAlphaMap, hasAlphaMap);
}
inline void BasicMaterial::SetAlphaSampler(TextureSamplerInfo alphaSampler)
@ -106,8 +106,8 @@ namespace Nz
bool hasDiffuseMap = (diffuseMap != nullptr);
m_material.SetTexture(m_textureIndexes.diffuse, std::move(diffuseMap));
if (m_conditionIndexes.hasDiffuseMap != MaterialSettings::InvalidIndex)
m_material.EnableCondition(m_conditionIndexes.hasDiffuseMap, hasDiffuseMap);
if (m_optionIndexes.hasDiffuseMap != MaterialSettings::InvalidIndex)
m_material.EnableOption(m_optionIndexes.hasDiffuseMap, hasDiffuseMap);
}
inline void BasicMaterial::SetDiffuseSampler(TextureSamplerInfo diffuseSampler)

View File

@ -41,11 +41,11 @@ namespace Nz
inline void EnableBlending(bool blending);
inline void EnableColorWrite(bool colorWrite);
inline void EnableCondition(std::size_t conditionIndex, bool enable);
inline void EnableDepthBuffer(bool depthBuffer);
inline void EnableDepthSorting(bool depthSorting);
inline void EnableDepthWrite(bool depthWrite);
inline void EnableFaceCulling(bool faceCulling);
inline void EnableOption(std::size_t optionIndex, bool enable);
inline void EnableReflectionMapping(bool reflection);
inline void EnableScissorTest(bool scissorTest);
inline void EnableShadowCasting(bool castShadows);
@ -83,11 +83,11 @@ namespace Nz
inline bool IsBlendingEnabled() const;
inline bool IsColorWriteEnabled() const;
inline bool IsConditionEnabled(std::size_t conditionIndex) const;
inline bool IsDepthBufferEnabled() const;
inline bool IsDepthSortingEnabled() const;
inline bool IsDepthWriteEnabled() const;
inline bool IsFaceCullingEnabled() const;
inline bool IsOptionEnabled(std::size_t optionIndex) const;
inline bool IsReflectionMappingEnabled() const;
inline bool IsScissorTestEnabled() const;
inline bool IsStencilTestEnabled() const;
@ -117,7 +117,7 @@ namespace Nz
inline void InvalidateShaderBinding();
inline void InvalidateTextureSampler(std::size_t textureIndex);
inline void InvalidateUniformData(std::size_t uniformBufferIndex);
inline void UpdatePipeline() const;
void UpdatePipeline() const;
void UpdateShaderBinding();
struct MaterialTexture
@ -138,7 +138,7 @@ namespace Nz
std::vector<MaterialTexture> m_textures;
std::vector<UniformBuffer> m_uniformBuffers;
mutable std::shared_ptr<MaterialPipeline> m_pipeline;
UInt64 m_enabledConditions;
UInt64 m_enabledOptions;
mutable MaterialPipelineInfo m_pipelineInfo;
ShaderBindingPtr m_shaderBinding;
mutable bool m_pipelineUpdated;

View File

@ -90,15 +90,6 @@ namespace Nz
InvalidatePipeline();
}
inline void Material::EnableCondition(std::size_t conditionIndex, bool enable)
{
if (TestBit<UInt64>(m_enabledConditions, conditionIndex) != enable)
{
m_enabledConditions = ToggleBit<UInt64>(m_enabledConditions, conditionIndex);
InvalidatePipeline();
}
}
/*!
* \brief Enable/Disable depth buffer for this material
*
@ -189,6 +180,15 @@ namespace Nz
InvalidatePipeline();
}
inline void Material::EnableOption(std::size_t optionIndex, bool enable)
{
if (TestBit<UInt64>(m_enabledOptions, optionIndex) != enable)
{
m_enabledOptions = ToggleBit<UInt64>(m_enabledOptions, optionIndex);
InvalidatePipeline();
}
}
/*!
* \brief Enable/Disable reflection mapping for this material
*
@ -515,11 +515,6 @@ namespace Nz
return m_pipelineInfo.colorWrite;
}
inline bool Material::IsConditionEnabled(std::size_t conditionIndex) const
{
return TestBit<UInt64>(m_enabledConditions, conditionIndex);
}
/*!
* \brief Checks whether this material has depth buffer enabled
* \return true If it is the case
@ -556,6 +551,11 @@ namespace Nz
return m_pipelineInfo.faceCulling;
}
inline bool Material::IsOptionEnabled(std::size_t optionIndex) const
{
return TestBit<UInt64>(m_enabledOptions, optionIndex);
}
/*!
* \brief Checks whether this material has reflection mapping enabled
* \return true If it is the case
@ -766,25 +766,6 @@ namespace Nz
OnMaterialInvalidated(this);
}
inline void Material::UpdatePipeline() const
{
for (auto& shader : m_pipelineInfo.shaders)
shader.enabledConditions = 0;
const auto& conditions = m_settings->GetConditions();
for (std::size_t conditionIndex = 0; conditionIndex < conditions.size(); ++conditionIndex)
{
if (TestBit<UInt64>(m_enabledConditions, conditionIndex))
{
for (std::size_t shaderStage = 0; shaderStage < ShaderStageTypeCount; ++shaderStage)
m_pipelineInfo.shaders[shaderStage].enabledConditions |= conditions[conditionIndex].enabledConditions[shaderStage];
}
}
m_pipeline = MaterialPipeline::Get(m_pipelineInfo);
m_pipelineUpdated = true;
}
}
#include <Nazara/Graphics/DebugOff.hpp>

View File

@ -21,10 +21,10 @@ namespace Nz
struct MaterialPipelineInfo : RenderStates
{
struct ShaderStage
struct Shader
{
std::shared_ptr<UberShader> uberShader;
Nz::UInt64 enabledConditions = 0;
Nz::UInt64 enabledOptions = 0;
};
bool depthSorting = false;
@ -32,7 +32,7 @@ namespace Nz
bool reflectionMapping = false;
bool shadowReceive = true;
std::array<ShaderStage, ShaderStageTypeCount> shaders;
std::vector<Shader> shaders;
std::shared_ptr<const MaterialSettings> settings;
};

View File

@ -40,7 +40,7 @@ namespace Nz
for (std::size_t i = 0; i < lhs.shaders.size(); ++i)
{
if (lhs.shaders[i].enabledConditions != rhs.shaders[i].enabledConditions)
if (lhs.shaders[i].enabledOptions != rhs.shaders[i].enabledOptions)
return false;
if (lhs.shaders[i].uberShader != rhs.shaders[i].uberShader)
@ -85,7 +85,7 @@ namespace std
for (const auto& shader : pipelineInfo.shaders)
{
Nz::HashCombine(seed, shader.enabledConditions);
Nz::HashCombine(seed, shader.enabledOptions);
Nz::HashCombine(seed, shader.uberShader.get());
}

View File

@ -24,10 +24,8 @@ namespace Nz
class MaterialSettings
{
public:
using Shaders = std::array<std::shared_ptr<UberShader>, ShaderStageTypeCount>;
struct Builder;
struct Condition;
struct Option;
struct SharedUniformBlock;
struct Texture;
struct UniformBlock;
@ -39,11 +37,11 @@ namespace Nz
~MaterialSettings() = default;
inline const Builder& GetBuilderData() const;
inline const std::vector<Condition>& GetConditions() const;
inline std::size_t GetConditionIndex(const std::string_view& name) const;
inline const std::vector<Option>& GetOptions() const;
inline std::size_t GetOptionIndex(const std::string_view& name) const;
inline const std::shared_ptr<RenderPipelineLayout>& GetRenderPipelineLayout() const;
inline const std::shared_ptr<UberShader>& GetShader(ShaderStageType stage) const;
inline const Shaders& GetShaders() const;
inline const std::vector<std::shared_ptr<UberShader>>& GetShaders() const;
inline const std::vector<SharedUniformBlock>& GetSharedUniformBlocks() const;
inline std::size_t GetSharedUniformBlockIndex(const std::string_view& name) const;
inline std::size_t GetSharedUniformBlockVariableOffset(std::size_t uniformBlockIndex, const std::string_view& name) const;
@ -60,17 +58,17 @@ namespace Nz
struct Builder
{
Shaders shaders;
std::vector<Condition> conditions;
std::vector<std::shared_ptr<UberShader>> shaders;
std::vector<Option> options;
std::vector<Texture> textures;
std::vector<UniformBlock> uniformBlocks;
std::vector<SharedUniformBlock> sharedUniformBlocks;
};
struct Condition
struct Option
{
std::string name;
std::array<UInt64, ShaderStageTypeCount> enabledConditions;
std::array<UInt64, ShaderStageTypeCount> enabledOptions;
};
struct UniformVariable

View File

@ -59,16 +59,16 @@ namespace Nz
return m_data;
}
inline auto MaterialSettings::GetConditions() const -> const std::vector<Condition>&
inline auto MaterialSettings::GetOptions() const -> const std::vector<Option>&
{
return m_data.conditions;
return m_data.options;
}
inline std::size_t MaterialSettings::GetConditionIndex(const std::string_view& name) const
inline std::size_t MaterialSettings::GetOptionIndex(const std::string_view& name) const
{
for (std::size_t i = 0; i < m_data.conditions.size(); ++i)
for (std::size_t i = 0; i < m_data.options.size(); ++i)
{
if (m_data.conditions[i].name == name)
if (m_data.options[i].name == name)
return i;
}
@ -85,7 +85,7 @@ namespace Nz
return m_data.shaders[UnderlyingCast(stage)];
}
inline auto MaterialSettings::GetShaders() const -> const Shaders&
inline const std::vector<std::shared_ptr<UberShader>>& MaterialSettings::GetShaders() const
{
return m_data.shaders;
}

View File

@ -20,18 +20,20 @@ namespace Nz
class NAZARA_GRAPHICS_API UberShader
{
public:
UberShader(ShaderStageType shaderStage, const ShaderAst::StatementPtr& shaderAst);
UberShader(ShaderStageTypeFlags shaderStages, const ShaderAst::StatementPtr& shaderAst);
~UberShader() = default;
UInt64 GetOptionFlagByName(const std::string& optionName) const;
inline ShaderStageTypeFlags GetSupportedStages() const;
const std::shared_ptr<ShaderModule>& Get(UInt64 combination);
private:
std::unordered_map<UInt64 /*combination*/, std::shared_ptr<ShaderModule>> m_combinations;
std::unordered_map<std::string, std::size_t> m_optionIndexByName;
ShaderAst::StatementPtr m_shaderAst;
ShaderStageType m_shaderStage;
ShaderStageTypeFlags m_shaderStages;
UInt64 m_combinationMask;
};
}

View File

@ -7,6 +7,10 @@
namespace Nz
{
inline ShaderStageTypeFlags UberShader::GetSupportedStages() const
{
return m_shaderStages;
}
}
#include <Nazara/Graphics/DebugOff.hpp>

View File

@ -31,10 +31,12 @@ namespace Nz::ShaderAst
struct Callbacks
{
std::function<void(ShaderStageType stageType, const std::string& functionName)> onEntryPointDeclaration;
std::function<void(const std::string& optionName, const ExpressionType& optionType)> onOptionDeclaration;
};
private:
void Visit(DeclareFunctionStatement& node) override;
void Visit(DeclareOptionStatement& node) override;
const Callbacks* m_callbacks;

View File

@ -46,7 +46,7 @@ namespace Nz
};
static const char* GetFlipYUniformName();
static ShaderAst::StatementPtr Sanitize(ShaderAst::Statement& ast, UInt64 enabledConditions, std::string* error = nullptr);
static ShaderAst::StatementPtr Sanitize(ShaderAst::Statement& ast, UInt64 enabledOptions, std::string* error = nullptr);
private:
void Append(const ShaderAst::ExpressionType& type);

View File

@ -33,16 +33,16 @@ namespace Nz
const std::shared_ptr<const MaterialSettings>& materialSettings = material.GetSettings();
if (materialSettings == s_materialSettings)
{
m_conditionIndexes = s_conditionIndexes;
m_optionIndexes = s_optionIndexes;
m_textureIndexes = s_textureIndexes;
m_uniformBlockIndex = s_uniformBlockIndex;
m_uniformOffsets = s_uniformOffsets;
}
else
{
m_conditionIndexes.alphaTest = materialSettings->GetConditionIndex("AlphaTest");
m_conditionIndexes.hasAlphaMap = materialSettings->GetConditionIndex("HasAlphaMap");
m_conditionIndexes.hasDiffuseMap = materialSettings->GetConditionIndex("HasDiffuseMap");
m_optionIndexes.alphaTest = materialSettings->GetOptionIndex("AlphaTest");
m_optionIndexes.hasAlphaMap = materialSettings->GetOptionIndex("HasAlphaMap");
m_optionIndexes.hasDiffuseMap = materialSettings->GetOptionIndex("HasDiffuseMap");
m_textureIndexes.alpha = materialSettings->GetTextureIndex("Alpha");
m_textureIndexes.diffuse = materialSettings->GetTextureIndex("Diffuse");
@ -146,54 +146,51 @@ namespace Nz
});
// Shaders
auto& fragmentShader = settings.shaders[UnderlyingCast(ShaderStageType::Fragment)];
auto& vertexShader = settings.shaders[UnderlyingCast(ShaderStageType::Vertex)];
ShaderAst::StatementPtr shaderAst = ShaderLang::Parse(std::string_view(reinterpret_cast<const char*>(r_shader), sizeof(r_shader)));
auto uberShader = std::make_shared<UberShader>(ShaderStageType::Fragment | ShaderStageType::Vertex, shaderAst);
fragmentShader = std::make_shared<UberShader>(ShaderStageType::Fragment, shaderAst);
vertexShader = std::make_shared<UberShader>(ShaderStageType::Vertex, shaderAst);
settings.shaders.emplace_back(uberShader);
// Conditions
// Options
// HasDiffuseMap
{
std::array<UInt64, ShaderStageTypeCount> shaderConditions;
shaderConditions.fill(0);
shaderConditions[UnderlyingCast(ShaderStageType::Fragment)] = fragmentShader->GetOptionFlagByName("HAS_DIFFUSE_TEXTURE");
shaderConditions[UnderlyingCast(ShaderStageType::Vertex)] = vertexShader->GetOptionFlagByName("HAS_DIFFUSE_TEXTURE");
std::array<UInt64, ShaderStageTypeCount> shaderOptions;
shaderOptions.fill(0);
shaderOptions[UnderlyingCast(ShaderStageType::Fragment)] = uberShader->GetOptionFlagByName("HAS_DIFFUSE_TEXTURE");
shaderOptions[UnderlyingCast(ShaderStageType::Vertex)] = uberShader->GetOptionFlagByName("HAS_DIFFUSE_TEXTURE");
s_conditionIndexes.hasDiffuseMap = settings.conditions.size();
settings.conditions.push_back({
s_optionIndexes.hasDiffuseMap = settings.options.size();
settings.options.push_back({
"HasDiffuseMap",
shaderConditions
shaderOptions
});
}
// HasAlphaMap
{
std::array<UInt64, ShaderStageTypeCount> shaderConditions;
shaderConditions.fill(0);
shaderConditions[UnderlyingCast(ShaderStageType::Fragment)] = fragmentShader->GetOptionFlagByName("HAS_ALPHA_TEXTURE");
shaderConditions[UnderlyingCast(ShaderStageType::Vertex)] = vertexShader->GetOptionFlagByName("HAS_ALPHA_TEXTURE");
std::array<UInt64, ShaderStageTypeCount> shaderOptions;
shaderOptions.fill(0);
shaderOptions[UnderlyingCast(ShaderStageType::Fragment)] = uberShader->GetOptionFlagByName("HAS_ALPHA_TEXTURE");
shaderOptions[UnderlyingCast(ShaderStageType::Vertex)] = uberShader->GetOptionFlagByName("HAS_ALPHA_TEXTURE");
s_conditionIndexes.hasAlphaMap = settings.conditions.size();
settings.conditions.push_back({
s_optionIndexes.hasAlphaMap = settings.options.size();
settings.options.push_back({
"HasAlphaMap",
shaderConditions
shaderOptions
});
}
// AlphaTest
{
std::array<UInt64, ShaderStageTypeCount> shaderConditions;
shaderConditions.fill(0);
shaderConditions[UnderlyingCast(ShaderStageType::Fragment)] = fragmentShader->GetOptionFlagByName("ALPHA_TEST");
std::array<UInt64, ShaderStageTypeCount> shaderOptions;
shaderOptions.fill(0);
shaderOptions[UnderlyingCast(ShaderStageType::Fragment)] = uberShader->GetOptionFlagByName("ALPHA_TEST");
s_conditionIndexes.alphaTest = settings.conditions.size();
settings.conditions.push_back({
s_optionIndexes.alphaTest = settings.options.size();
settings.options.push_back({
"AlphaTest",
shaderConditions
shaderOptions
});
}
@ -209,7 +206,7 @@ namespace Nz
std::shared_ptr<MaterialSettings> BasicMaterial::s_materialSettings;
std::size_t BasicMaterial::s_uniformBlockIndex;
BasicMaterial::ConditionIndexes BasicMaterial::s_conditionIndexes;
BasicMaterial::OptionIndexes BasicMaterial::s_optionIndexes;
BasicMaterial::TextureIndexes BasicMaterial::s_textureIndexes;
BasicMaterial::UniformOffsets BasicMaterial::s_uniformOffsets;
}

View File

@ -5,6 +5,7 @@
#include <Nazara/Graphics/Material.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Graphics/BasicMaterial.hpp>
#include <Nazara/Graphics/UberShader.hpp>
#include <Nazara/Renderer/CommandBufferBuilder.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/RenderFrame.hpp>
@ -27,7 +28,7 @@ namespace Nz
*/
Material::Material(std::shared_ptr<const MaterialSettings> settings) :
m_settings(std::move(settings)),
m_enabledConditions(0),
m_enabledOptions(0),
m_pipelineUpdated(false),
m_shaderBindingUpdated(false),
m_shadowCastingEnabled(true)
@ -35,8 +36,11 @@ namespace Nz
m_pipelineInfo.settings = m_settings;
const auto& shaders = m_settings->GetShaders();
for (std::size_t i = 0; i < ShaderStageTypeCount; ++i)
m_pipelineInfo.shaders[i].uberShader = shaders[i];
for (const auto& shader : shaders)
{
auto& shaderData = m_pipelineInfo.shaders.emplace_back();
shaderData.uberShader = shader;
}
const auto& textureSettings = m_settings->GetTextures();
const auto& uboSettings = m_settings->GetUniformBlocks();
@ -92,6 +96,33 @@ namespace Nz
return shouldRegenerateCommandBuffer;
}
void Material::UpdatePipeline() const
{
for (auto& shader : m_pipelineInfo.shaders)
shader.enabledOptions = 0;
const auto& options = m_settings->GetOptions();
for (std::size_t optionIndex = 0; optionIndex < options.size(); ++optionIndex)
{
if (TestBit<UInt64>(m_enabledOptions, optionIndex))
{
for (auto& shader : m_pipelineInfo.shaders)
{
ShaderStageTypeFlags supportedStages = shader.uberShader->GetSupportedStages();
for (std::size_t i = 0; i < ShaderStageTypeCount; ++i)
{
ShaderStageType shaderStage = static_cast<ShaderStageType>(i);
if (supportedStages & shaderStage)
shader.enabledOptions |= options[optionIndex].enabledOptions[i];
}
}
}
}
m_pipeline = MaterialPipeline::Get(m_pipelineInfo);
m_pipelineUpdated = true;
}
void Material::UpdateShaderBinding()
{
assert(!m_shaderBinding);

View File

@ -48,10 +48,10 @@ namespace Nz
renderPipelineInfo.pipelineLayout = m_pipelineInfo.settings->GetRenderPipelineLayout();
for (const auto& shaderEntry : m_pipelineInfo.shaders)
for (const auto& shader : m_pipelineInfo.shaders)
{
if (shaderEntry.uberShader)
renderPipelineInfo.shaderModules.push_back(shaderEntry.uberShader->Get(shaderEntry.enabledConditions));
if (shader.uberShader)
renderPipelineInfo.shaderModules.push_back(shader.uberShader->Get(shader.enabledOptions));
}
renderPipelineInfo.vertexBuffers = vertexBuffers;

View File

@ -13,15 +13,24 @@
namespace Nz
{
UberShader::UberShader(ShaderStageType shaderStage, const ShaderAst::StatementPtr& shaderAst) :
m_shaderStage(shaderStage)
UberShader::UberShader(ShaderStageTypeFlags shaderStages, const ShaderAst::StatementPtr& shaderAst) :
m_shaderStages(shaderStages)
{
NazaraAssert(m_shaderStages != 0, "there must be at least one shader stage");
//TODO: Try to partially sanitize shader?
m_shaderAst = ShaderAst::Clone(*shaderAst);
std::size_t optionCount = 0;
ShaderStageTypeFlags supportedStageType;
ShaderAst::AstReflect::Callbacks callbacks;
callbacks.onEntryPointDeclaration = [&](ShaderStageType stageType, const std::string& /*name*/)
{
supportedStageType |= stageType;
};
callbacks.onOptionDeclaration = [&](const std::string& optionName, const ShaderAst::ExpressionType& optionType)
{
m_optionIndexByName[optionName] = optionCount;
@ -31,6 +40,9 @@ namespace Nz
ShaderAst::AstReflect reflect;
reflect.Reflect(*m_shaderAst, callbacks);
if (m_shaderStages & shaderStages != m_shaderStages)
throw std::runtime_error("shader doesn't support all required shader stages");
if (optionCount >= 64)
throw std::runtime_error("Too many conditions");
@ -58,7 +70,7 @@ namespace Nz
ShaderWriter::States states;
states.enabledOptions = combination;
std::shared_ptr<ShaderModule> stage = Graphics::Instance()->GetRenderDevice()->InstantiateShaderModule(m_shaderStage, *m_shaderAst, std::move(states));
std::shared_ptr<ShaderModule> stage = Graphics::Instance()->GetRenderDevice()->InstantiateShaderModule(m_shaderStages, *m_shaderAst, std::move(states));
it = m_combinations.emplace(combination, std::move(stage)).first;
}

View File

@ -14,6 +14,18 @@ namespace Nz::ShaderAst
statement.Visit(*this);
}
void AstReflect::Visit(DeclareFunctionStatement& node)
{
assert(m_callbacks);
if (m_callbacks->onEntryPointDeclaration)
{
if (!node.entryStage.HasValue())
return;
m_callbacks->onEntryPointDeclaration(node.entryStage.GetResultingValue(), node.name);
}
}
void AstReflect::Visit(DeclareOptionStatement& node)
{
assert(m_callbacks);

View File

@ -203,11 +203,11 @@ namespace Nz
return s_flipYUniformName;
}
ShaderAst::StatementPtr GlslWriter::Sanitize(ShaderAst::Statement& ast, UInt64 enabledConditions, std::string* error)
ShaderAst::StatementPtr GlslWriter::Sanitize(ShaderAst::Statement& ast, UInt64 enabledOptions, std::string* error)
{
// Always sanitize for reserved identifiers
ShaderAst::SanitizeVisitor::Options options;
options.enabledOptions = enabledConditions;
options.enabledOptions = enabledOptions;
options.makeVariableNameUnique = true;
options.reservedIdentifiers = {
// All reserved GLSL keywords as of GLSL ES 3.2

View File

@ -14,20 +14,20 @@
ConditionalExpression::ConditionalExpression(ShaderGraph& graph) :
ShaderNode(graph)
{
m_onConditionListUpdateSlot.Connect(GetGraph().OnConditionListUpdate, [&](ShaderGraph*) { OnConditionListUpdate(); });
m_onConditionUpdateSlot.Connect(GetGraph().OnConditionUpdate, [&](ShaderGraph*, std::size_t conditionIndex)
m_onOptionListUpdateSlot.Connect(GetGraph().OnOptionListUpdate, [&](ShaderGraph*) { OnOptionListUpdate(); });
m_onOptionUpdateSlot.Connect(GetGraph().OnOptionUpdate, [&](ShaderGraph*, std::size_t optionIndex)
{
if (m_currentConditionIndex == conditionIndex)
if (m_currentOptionIndex == optionIndex)
{
UpdatePreview();
Q_EMIT dataUpdated(0);
}
});
if (graph.GetConditionCount() > 0)
if (graph.GetOptionCount() > 0)
{
m_currentConditionIndex = 0;
UpdateConditionText();
m_currentOptionIndex = 0;
UpdateOptionText();
}
EnablePreview();
@ -40,18 +40,18 @@ Nz::ShaderAst::NodePtr ConditionalExpression::BuildNode(Nz::ShaderAst::Expressio
assert(count == 2);
assert(outputIndex == 0);
if (!m_currentConditionIndex)
throw std::runtime_error("no condition");
if (!m_currentOptionIndex)
throw std::runtime_error("no option");
const ShaderGraph& graph = GetGraph();
const auto& conditionEntry = graph.GetCondition(*m_currentConditionIndex);
return Nz::ShaderBuilder::ConditionalExpression(Nz::ShaderBuilder::Identifier(conditionEntry.name), std::move(expressions[0]), std::move(expressions[1]));
const auto& optionEntry = graph.GetOption(*m_currentOptionIndex);
return Nz::ShaderBuilder::ConditionalExpression(Nz::ShaderBuilder::Identifier(optionEntry.name), std::move(expressions[0]), std::move(expressions[1]));
}
QString ConditionalExpression::caption() const
{
return "ConditionalExpression (" + QString::fromStdString(m_currentConditionText) + ")";
return "ConditionalExpression (" + QString::fromStdString(m_currentOptionText) + ")";
}
QString ConditionalExpression::name() const
@ -74,29 +74,29 @@ void ConditionalExpression::BuildNodeEdition(QFormLayout* layout)
{
ShaderNode::BuildNodeEdition(layout);
QComboBox* conditionSelection = new QComboBox;
for (const auto& conditionEntry : GetGraph().GetConditions())
conditionSelection->addItem(QString::fromStdString(conditionEntry.name));
QComboBox* optionSelection = new QComboBox;
for (const auto& optionEntry : GetGraph().GetOptions())
optionSelection->addItem(QString::fromStdString(optionEntry.name));
if (m_currentConditionIndex)
conditionSelection->setCurrentIndex(int(*m_currentConditionIndex));
if (m_currentOptionIndex)
optionSelection->setCurrentIndex(int(*m_currentOptionIndex));
else
conditionSelection->setCurrentIndex(-1);
optionSelection->setCurrentIndex(-1);
connect(conditionSelection, qOverload<int>(&QComboBox::currentIndexChanged), [&](int index)
connect(optionSelection, qOverload<int>(&QComboBox::currentIndexChanged), [&](int index)
{
if (index >= 0)
m_currentConditionIndex = static_cast<std::size_t>(index);
m_currentOptionIndex = static_cast<std::size_t>(index);
else
m_currentConditionIndex.reset();
m_currentOptionIndex.reset();
UpdateConditionText();
UpdateOptionText();
UpdatePreview();
Q_EMIT dataUpdated(0);
});
layout->addRow(tr("Condition"), conditionSelection);
layout->addRow(tr("Option"), optionSelection);
}
auto ConditionalExpression::dataType(QtNodes::PortType portType, QtNodes::PortIndex portIndex) const -> QtNodes::NodeDataType
@ -178,17 +178,17 @@ bool ConditionalExpression::portCaptionVisible(QtNodes::PortType portType, QtNod
std::shared_ptr<QtNodes::NodeData> ConditionalExpression::outData(QtNodes::PortIndex port)
{
if (!m_currentConditionIndex)
if (!m_currentOptionIndex)
return nullptr;
assert(port == 0);
return (GetGraph().IsConditionEnabled(*m_currentConditionIndex)) ? m_truePath : m_falsePath;
return (GetGraph().IsOptionEnabled(*m_currentOptionIndex)) ? m_truePath : m_falsePath;
}
void ConditionalExpression::restore(const QJsonObject& data)
{
m_currentConditionText = data["condition_name"].toString().toStdString();
OnConditionListUpdate();
m_currentOptionText = data["option_name"].toString().toStdString();
OnOptionListUpdate();
ShaderNode::restore(data);
}
@ -196,7 +196,7 @@ void ConditionalExpression::restore(const QJsonObject& data)
QJsonObject ConditionalExpression::save() const
{
QJsonObject data = ShaderNode::save();
data["condition_name"] = QString::fromStdString(m_currentConditionText);
data["option_name"] = QString::fromStdString(m_currentOptionText);
return data;
}
@ -223,8 +223,8 @@ QtNodes::NodeValidationState ConditionalExpression::validationState() const
QString ConditionalExpression::validationMessage() const
{
if (!m_currentConditionIndex)
return "Invalid condition";
if (!m_currentOptionIndex)
return "Invalid option";
if (!m_truePath || !m_falsePath)
return "Missing input";
@ -234,7 +234,7 @@ QString ConditionalExpression::validationMessage() const
bool ConditionalExpression::ComputePreview(QPixmap& pixmap)
{
if (!m_currentConditionIndex)
if (!m_currentOptionIndex)
return false;
auto input = outData(0);
@ -248,30 +248,30 @@ bool ConditionalExpression::ComputePreview(QPixmap& pixmap)
return true;
}
void ConditionalExpression::OnConditionListUpdate()
void ConditionalExpression::OnOptionListUpdate()
{
m_currentConditionIndex.reset();
m_currentOptionIndex.reset();
std::size_t conditionIndex = 0;
for (const auto& conditionEntry : GetGraph().GetConditions())
std::size_t optionIndex = 0;
for (const auto& optionEntry : GetGraph().GetOptions())
{
if (conditionEntry.name == m_currentConditionText)
if (optionEntry.name == m_currentOptionText)
{
m_currentConditionIndex = conditionIndex;
m_currentOptionIndex = optionIndex;
break;
}
conditionIndex++;
optionIndex++;
}
}
void ConditionalExpression::UpdateConditionText()
void ConditionalExpression::UpdateOptionText()
{
if (m_currentConditionIndex)
if (m_currentOptionIndex)
{
auto& condition = GetGraph().GetCondition(*m_currentConditionIndex);
m_currentConditionText = condition.name;
auto& option = GetGraph().GetOption(*m_currentOptionIndex);
m_currentOptionText = option.name;
}
else
m_currentConditionText.clear();
m_currentOptionText.clear();
}

View File

@ -40,16 +40,16 @@ class ConditionalExpression : public ShaderNode
private:
bool ComputePreview(QPixmap& pixmap) override;
void OnConditionListUpdate();
void UpdateConditionText();
void OnOptionListUpdate();
void UpdateOptionText();
NazaraSlot(ShaderGraph, OnConditionListUpdate, m_onConditionListUpdateSlot);
NazaraSlot(ShaderGraph, OnConditionUpdate, m_onConditionUpdateSlot);
NazaraSlot(ShaderGraph, OnOptionListUpdate, m_onOptionListUpdateSlot);
NazaraSlot(ShaderGraph, OnOptionUpdate, m_onOptionUpdateSlot);
std::optional<std::size_t> m_currentConditionIndex;
std::optional<std::size_t> m_currentOptionIndex;
std::shared_ptr<QtNodes::NodeData> m_falsePath;
std::shared_ptr<QtNodes::NodeData> m_truePath;
std::string m_currentConditionText;
std::string m_currentOptionText;
};
#include <ShaderNode/DataModels/BufferField.inl>

View File

@ -121,13 +121,13 @@ std::size_t ShaderGraph::AddBuffer(std::string name, BufferType bufferType, std:
return index;
}
std::size_t ShaderGraph::AddCondition(std::string name)
std::size_t ShaderGraph::AddOption(std::string name)
{
std::size_t index = m_conditions.size();
auto& conditionEntry = m_conditions.emplace_back();
conditionEntry.name = std::move(name);
std::size_t index = m_options.size();
auto& optionEntry = m_options.emplace_back();
optionEntry.name = std::move(name);
OnConditionListUpdate(this);
OnOptionListUpdate(this);
return index;
}
@ -194,7 +194,7 @@ void ShaderGraph::Clear()
m_flowScene->clear();
m_buffers.clear();
m_conditions.clear();
m_options.clear();
m_inputs.clear();
m_structs.clear();
m_outputs.clear();
@ -207,13 +207,13 @@ void ShaderGraph::Clear()
OnTextureListUpdate(this);
}
void ShaderGraph::EnableCondition(std::size_t conditionIndex, bool enable)
void ShaderGraph::EnableOption(std::size_t optionIndex, bool enable)
{
assert(conditionIndex < m_conditions.size());
auto& conditionEntry = m_conditions[conditionIndex];
conditionEntry.enabled = enable;
assert(optionIndex < m_options.size());
auto& optionEntry = m_options[optionIndex];
optionEntry.enabled = enable;
OnConditionUpdate(this, conditionIndex);
OnOptionUpdate(this, optionIndex);
}
void ShaderGraph::Load(const QJsonObject& data)
@ -238,16 +238,16 @@ void ShaderGraph::Load(const QJsonObject& data)
OnBufferListUpdate(this);
QJsonArray conditionArray = data["conditions"].toArray();
for (const auto& conditionDocRef : conditionArray)
QJsonArray optionArray = data["options"].toArray();
for (const auto& optionDocRef : optionArray)
{
QJsonObject conditionDoc = conditionDocRef.toObject();
QJsonObject optionDoc = optionDocRef.toObject();
ConditionEntry& condition = m_conditions.emplace_back();
condition.name = conditionDoc["name"].toString().toStdString();
OptionEntry& option = m_options.emplace_back();
option.name = optionDoc["name"].toString().toStdString();
}
OnConditionListUpdate(this);
OnOptionListUpdate(this);
QJsonArray inputArray = data["inputs"].toArray();
for (const auto& inputDocRef : inputArray)
@ -345,17 +345,17 @@ QJsonObject ShaderGraph::Save()
}
sceneJson["buffers"] = bufferArray;
QJsonArray conditionArray;
QJsonArray optionArray;
{
for (const auto& condition : m_conditions)
for (const auto& option : m_options)
{
QJsonObject inputDoc;
inputDoc["name"] = QString::fromStdString(condition.name);
inputDoc["name"] = QString::fromStdString(option.name);
conditionArray.append(inputDoc);
optionArray.append(inputDoc);
}
}
sceneJson["conditions"] = conditionArray;
sceneJson["options"] = optionArray;
QJsonArray inputArray;
{
@ -462,8 +462,8 @@ Nz::ShaderAst::StatementPtr ShaderGraph::ToAst() const
std::vector<Nz::ShaderAst::StatementPtr> statements;
// Declare all options
for (const auto& condition : m_conditions)
statements.push_back(Nz::ShaderBuilder::DeclareOption(condition.name, Nz::ShaderAst::PrimitiveType::Boolean));
for (const auto& option : m_options)
statements.push_back(Nz::ShaderBuilder::DeclareOption(option.name, Nz::ShaderAst::PrimitiveType::Boolean));
// Declare all structures
for (const auto& structInfo : m_structs)
@ -590,13 +590,13 @@ void ShaderGraph::UpdateBuffer(std::size_t bufferIndex, std::string name, Buffer
OnBufferUpdate(this, bufferIndex);
}
void ShaderGraph::UpdateCondition(std::size_t conditionIndex, std::string condition)
void ShaderGraph::UpdateOption(std::size_t optionIndex, std::string option)
{
assert(conditionIndex < m_conditions.size());
auto& conditionEntry = m_conditions[conditionIndex];
conditionEntry.name = std::move(condition);
assert(optionIndex < m_options.size());
auto& optionEntry = m_options[optionIndex];
optionEntry.name = std::move(option);
OnConditionUpdate(this, conditionIndex);
OnOptionUpdate(this, optionIndex);
}
void ShaderGraph::UpdateInput(std::size_t inputIndex, std::string name, PrimitiveType type, InputRole role, std::size_t roleIndex, std::size_t locationIndex)

View File

@ -19,7 +19,7 @@ class ShaderGraph
{
public:
struct BufferEntry;
struct ConditionEntry;
struct OptionEntry;
struct InputEntry;
struct OutputEntry;
struct StructEntry;
@ -30,7 +30,7 @@ class ShaderGraph
~ShaderGraph();
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 AddOption(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);
@ -38,17 +38,17 @@ class ShaderGraph
void Clear();
void EnableCondition(std::size_t conditionIndex, bool enable);
void EnableOption(std::size_t optionIndex, bool enable);
inline const BufferEntry& GetBuffer(std::size_t bufferIndex) const;
inline std::size_t GetBufferCount() const;
inline const std::vector<BufferEntry>& GetBuffers() const;
inline const ConditionEntry& GetCondition(std::size_t conditionIndex) const;
inline std::size_t GetConditionCount() const;
inline const std::vector<ConditionEntry>& GetConditions() const;
inline const InputEntry& GetInput(std::size_t bufferIndex) const;
inline std::size_t GetInputCount() const;
inline const std::vector<InputEntry>& GetInputs() const;
inline const OptionEntry& GetOption(std::size_t optionIndex) const;
inline std::size_t GetOptionCount() const;
inline const std::vector<OptionEntry>& GetOptions() const;
inline const OutputEntry& GetOutput(std::size_t outputIndex) const;
inline std::size_t GetOutputCount() const;
inline const std::vector<OutputEntry>& GetOutputs() const;
@ -62,7 +62,7 @@ class ShaderGraph
inline const std::vector<TextureEntry>& GetTextures() const;
inline ShaderType GetType() const;
inline bool IsConditionEnabled(std::size_t conditionIndex) const;
inline bool IsOptionEnabled(std::size_t optionIndex) const;
void Load(const QJsonObject& data);
QJsonObject Save();
@ -71,7 +71,7 @@ class ShaderGraph
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 setIndex, std::size_t bindingIndex);
void UpdateCondition(std::size_t conditionIndex, std::string condition);
void UpdateOption(std::size_t optionIndex, std::string option);
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);
@ -88,7 +88,7 @@ class ShaderGraph
BufferType type;
};
struct ConditionEntry
struct OptionEntry
{
std::string name;
bool enabled = false;
@ -133,8 +133,8 @@ class ShaderGraph
NazaraSignal(OnBufferListUpdate, ShaderGraph*);
NazaraSignal(OnBufferUpdate, ShaderGraph*, std::size_t /*bufferIndex*/);
NazaraSignal(OnConditionListUpdate, ShaderGraph*);
NazaraSignal(OnConditionUpdate, ShaderGraph*, std::size_t /*conditionIndex*/);
NazaraSignal(OnOptionListUpdate, ShaderGraph*);
NazaraSignal(OnOptionUpdate, ShaderGraph*, std::size_t /*optionIndex*/);
NazaraSignal(OnInputListUpdate, ShaderGraph*);
NazaraSignal(OnInputUpdate, ShaderGraph*, std::size_t /*inputIndex*/);
NazaraSignal(OnOutputListUpdate, ShaderGraph*);
@ -158,7 +158,7 @@ class ShaderGraph
mutable std::optional<QtNodes::FlowScene> m_flowScene;
std::vector<BufferEntry> m_buffers;
std::vector<ConditionEntry> m_conditions;
std::vector<OptionEntry> m_options;
std::vector<InputEntry> m_inputs;
std::vector<OutputEntry> m_outputs;
std::vector<StructEntry> m_structs;

View File

@ -16,22 +16,6 @@ inline auto ShaderGraph::GetBuffers() const -> const std::vector<BufferEntry>&
return m_buffers;
}
inline auto ShaderGraph::GetCondition(std::size_t conditionIndex) const -> const ConditionEntry&
{
assert(conditionIndex < m_conditions.size());
return m_conditions[conditionIndex];
}
inline std::size_t ShaderGraph::GetConditionCount() const
{
return m_conditions.size();
}
inline auto ShaderGraph::GetConditions() const -> const std::vector<ConditionEntry>&
{
return m_conditions;
}
inline auto ShaderGraph::GetInput(std::size_t inputIndex) const -> const InputEntry&
{
assert(inputIndex < m_inputs.size());
@ -48,6 +32,22 @@ inline auto ShaderGraph::GetInputs() const -> const std::vector<InputEntry>&
return m_inputs;
}
inline auto ShaderGraph::GetOption(std::size_t optionIndex) const -> const OptionEntry&
{
assert(optionIndex < m_options.size());
return m_options[optionIndex];
}
inline std::size_t ShaderGraph::GetOptionCount() const
{
return m_options.size();
}
inline auto ShaderGraph::GetOptions() const -> const std::vector<OptionEntry>&
{
return m_options;
}
inline auto ShaderGraph::GetOutput(std::size_t outputIndex) const -> const OutputEntry&
{
assert(outputIndex < m_outputs.size());
@ -111,9 +111,9 @@ inline ShaderType ShaderGraph::GetType() const
return m_type;
}
inline bool ShaderGraph::IsConditionEnabled(std::size_t conditionIndex) const
inline bool ShaderGraph::IsOptionEnabled(std::size_t optionIndex) const
{
assert(conditionIndex < m_conditions.size());
return m_conditions[conditionIndex].enabled;
assert(optionIndex < m_options.size());
return m_options[optionIndex].enabled;
}

View File

@ -55,11 +55,11 @@ void CodeOutputWidget::Refresh()
{
try
{
Nz::UInt64 enabledConditions = 0;
for (std::size_t i = 0; i < m_shaderGraph.GetConditionCount(); ++i)
Nz::UInt64 enabledOptions = 0;
for (std::size_t i = 0; i < m_shaderGraph.GetOptionCount(); ++i)
{
if (m_shaderGraph.IsConditionEnabled(i))
enabledConditions = Nz::SetBit<Nz::UInt64>(enabledConditions, i);
if (m_shaderGraph.IsOptionEnabled(i))
enabledOptions = Nz::SetBit<Nz::UInt64>(enabledOptions, i);
}
Nz::ShaderAst::StatementPtr shaderAst = m_shaderGraph.ToAst();
@ -69,14 +69,14 @@ void CodeOutputWidget::Refresh()
shaderAst = Nz::ShaderAst::Sanitize(*shaderAst);
Nz::ShaderAst::AstOptimizer::Options optimOptions;
optimOptions.enabledOptions = enabledConditions;
optimOptions.enabledOptions = enabledOptions;
Nz::ShaderAst::AstOptimizer optimiser;
shaderAst = optimiser.Optimise(*shaderAst, optimOptions);
}
Nz::ShaderWriter::States states;
states.enabledOptions = enabledConditions;
states.enabledOptions = enabledOptions;
std::string output;
OutputLanguage outputLang = static_cast<OutputLanguage>(m_outputLang->currentIndex());

View File

@ -1,55 +0,0 @@
#include <ShaderNode/Widgets/ConditionEditDialog.hpp>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QDialogButtonBox>
#include <QtWidgets/QFormLayout>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QSpinBox>
#include <QtWidgets/QVBoxLayout>
ConditionEditDialog::ConditionEditDialog(QWidget* parent) :
QDialog(parent)
{
setWindowTitle(tr("Condition edit dialog"));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
m_conditionName = new QLineEdit;
QFormLayout* formLayout = new QFormLayout;
formLayout->addRow(tr("Name"), m_conditionName);
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttonBox, &QDialogButtonBox::accepted, this, &ConditionEditDialog::OnAccept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
QVBoxLayout* verticalLayout = new QVBoxLayout;
verticalLayout->addLayout(formLayout);
verticalLayout->addWidget(buttonBox);
setLayout(verticalLayout);
}
ConditionEditDialog::ConditionEditDialog(const ConditionInfo& condition, QWidget* parent) :
ConditionEditDialog(parent)
{
m_conditionName->setText(QString::fromStdString(condition.name));
}
ConditionInfo ConditionEditDialog::GetConditionInfo() const
{
ConditionInfo inputInfo;
inputInfo.name = m_conditionName->text().toStdString();
return inputInfo;
}
void ConditionEditDialog::OnAccept()
{
if (m_conditionName->text().isEmpty())
{
QMessageBox::critical(this, tr("Empty name"), tr("Condition name must be set"), QMessageBox::Ok);
return;
}
accept();
}

View File

@ -1,35 +0,0 @@
#pragma once
#ifndef NAZARA_SHADERNODES_CONDITIONEDITDIALOG_HPP
#define NAZARA_SHADERNODES_CONDITIONEDITDIALOG_HPP
#include <ShaderNode/Enums.hpp>
#include <QtWidgets/QDialog>
class QComboBox;
class QLineEdit;
class QSpinBox;
struct ConditionInfo
{
std::string name;
};
class ConditionEditDialog : public QDialog
{
public:
ConditionEditDialog(QWidget* parent = nullptr);
ConditionEditDialog(const ConditionInfo& input, QWidget* parent = nullptr);
~ConditionEditDialog() = default;
ConditionInfo GetConditionInfo() const;
private:
void OnAccept();
QLineEdit* m_conditionName;
};
#include <ShaderNode/Widgets/ConditionEditDialog.inl>
#endif

View File

@ -1 +0,0 @@
#include <ShaderNode/Widgets/ConditionEditDialog.hpp>

View File

@ -1,119 +0,0 @@
#include <ShaderNode/Widgets/ConditionEditor.hpp>
#include <ShaderNode/Widgets/ConditionEditDialog.hpp>
#include <ShaderNode/ShaderGraph.hpp>
#include <QtGui/QStandardItemModel>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QListWidget>
#include <QtWidgets/QTableView>
#include <QtWidgets/QVBoxLayout>
ConditionEditor::ConditionEditor(ShaderGraph& graph) :
m_shaderGraph(graph)
{
QTableView* tableView = new QTableView;
m_model = new QStandardItemModel(0, 2, tableView);
tableView->setModel(m_model);
m_model->setHorizontalHeaderLabels({ tr("Condition"), tr("Enabled") });
connect(tableView, &QTableView::doubleClicked, [this](const QModelIndex& index)
{
if (index.column() == 0)
OnEditCondition(index.row());
});
connect(m_model, &QStandardItemModel::itemChanged, [this](QStandardItem* item)
{
if (item->column() == 1)
{
std::size_t conditionIndex = static_cast<std::size_t>(item->row());
bool value = item->checkState() == Qt::Checked;
m_shaderGraph.EnableCondition(conditionIndex, value);
}
});
QPushButton* addStructButton = new QPushButton(tr("Add condition..."));
connect(addStructButton, &QPushButton::released, this, &ConditionEditor::OnAddCondition);
m_layout = new QVBoxLayout;
m_layout->addWidget(tableView);
m_layout->addWidget(addStructButton);
setLayout(m_layout);
m_onConditionListUpdateSlot.Connect(m_shaderGraph.OnConditionListUpdate, this, &ConditionEditor::OnConditionListUpdate);
m_onConditionUpdateSlot.Connect(m_shaderGraph.OnConditionUpdate, this, &ConditionEditor::OnConditionUpdate);
RefreshConditions();
}
void ConditionEditor::OnAddCondition()
{
ConditionEditDialog* dialog = new ConditionEditDialog(this);
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
connect(dialog, &QDialog::accepted, [this, dialog]
{
ConditionInfo conditionInfo = dialog->GetConditionInfo();
m_shaderGraph.AddCondition(std::move(conditionInfo.name));
});
dialog->open();
}
void ConditionEditor::OnEditCondition(int conditionIndex)
{
const auto& conditionInfo = m_shaderGraph.GetCondition(conditionIndex);
ConditionInfo info;
info.name = conditionInfo.name;
ConditionEditDialog* dialog = new ConditionEditDialog(info, this);
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
connect(dialog, &QDialog::accepted, [this, dialog, conditionIndex]
{
ConditionInfo conditionInfo = dialog->GetConditionInfo();
m_shaderGraph.UpdateCondition(conditionIndex, std::move(conditionInfo.name));
});
dialog->open();
}
void ConditionEditor::OnConditionListUpdate(ShaderGraph* /*graph*/)
{
RefreshConditions();
}
void ConditionEditor::OnConditionUpdate(ShaderGraph* /*graph*/, std::size_t conditionIndex)
{
const auto& conditionEntry = m_shaderGraph.GetCondition(conditionIndex);
int row = int(conditionIndex);
m_model->item(row, 0)->setText(QString::fromStdString(conditionEntry.name));
m_model->item(row, 1)->setCheckState((conditionEntry.enabled) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
}
void ConditionEditor::RefreshConditions()
{
m_model->setRowCount(int(m_shaderGraph.GetConditionCount()));
int rowIndex = 0;
for (const auto& conditionEntry : m_shaderGraph.GetConditions())
{
QStandardItem* label = new QStandardItem(1);
label->setEditable(false);
label->setText(QString::fromStdString(conditionEntry.name));
m_model->setItem(rowIndex, 0, label);
QStandardItem* checkbox = new QStandardItem(1);
checkbox->setCheckable(true);
checkbox->setCheckState((conditionEntry.enabled) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
m_model->setItem(rowIndex, 1, checkbox);
rowIndex++;
}
}

View File

@ -1,36 +0,0 @@
#pragma once
#ifndef NAZARA_SHADERNODES_CONDITIONEDITOR_HPP
#define NAZARA_SHADERNODES_CONDITIONEDITOR_HPP
#include <ShaderNode/ShaderGraph.hpp>
#include <QtWidgets/QWidget>
#include <optional>
class QStandardItemModel;
class QVBoxLayout;
class ConditionEditor : public QWidget
{
public:
ConditionEditor(ShaderGraph& graph);
~ConditionEditor() = default;
private:
void OnAddCondition();
void OnConditionListUpdate(ShaderGraph* graph);
void OnConditionUpdate(ShaderGraph* graph, std::size_t conditionIndex);
void OnEditCondition(int inputIndex);
void RefreshConditions();
NazaraSlot(ShaderGraph, OnStructListUpdate, m_onConditionListUpdateSlot);
NazaraSlot(ShaderGraph, OnStructUpdate, m_onConditionUpdateSlot);
ShaderGraph& m_shaderGraph;
QStandardItemModel* m_model;
QVBoxLayout* m_layout;
};
#include <ShaderNode/Widgets/ConditionEditor.inl>
#endif

View File

@ -1 +0,0 @@
#include <ShaderNode/Widgets/ConditionEditor.hpp>

View File

@ -5,7 +5,7 @@
#include <ShaderNode/ShaderGraph.hpp>
#include <ShaderNode/Widgets/BufferEditor.hpp>
#include <ShaderNode/Widgets/CodeOutputWidget.hpp>
#include <ShaderNode/Widgets/ConditionEditor.hpp>
#include <ShaderNode/Widgets/OptionEditor.hpp>
#include <ShaderNode/Widgets/InputEditor.hpp>
#include <ShaderNode/Widgets/OutputEditor.hpp>
#include <ShaderNode/Widgets/NodeEditor.hpp>
@ -81,13 +81,13 @@ m_shaderGraph(graph)
addDockWidget(Qt::RightDockWidgetArea, structDock);
// Condition editor
ConditionEditor* conditionEditor = new ConditionEditor(m_shaderGraph);
// Option editor
OptionEditor* optionEditor = new OptionEditor(m_shaderGraph);
QDockWidget* conditionDock = new QDockWidget(tr("Conditions"));
conditionDock->setWidget(conditionEditor);
QDockWidget* optionDock = new QDockWidget(tr("Options"));
optionDock->setWidget(optionEditor);
addDockWidget(Qt::RightDockWidgetArea, conditionDock);
addDockWidget(Qt::RightDockWidgetArea, optionDock);
// Code output
CodeOutputWidget* codeOutput = new CodeOutputWidget(m_shaderGraph);
@ -120,7 +120,7 @@ m_shaderGraph(graph)
view->addAction(nodeEditorDock->toggleViewAction());
view->addAction(bufferDock->toggleViewAction());
view->addAction(structDock->toggleViewAction());
view->addAction(conditionDock->toggleViewAction());
view->addAction(optionDock->toggleViewAction());
view->addAction(codeOutputDock->toggleViewAction());
}
@ -142,7 +142,7 @@ m_shaderGraph(graph)
});
});
m_onConditionUpdate.Connect(m_shaderGraph.OnConditionUpdate, [=](ShaderGraph*, std::size_t /*conditionIndex*/)
m_onOptionUpdate.Connect(m_shaderGraph.OnOptionUpdate, [=](ShaderGraph*, std::size_t /*optionIndex*/)
{
if (codeOutput->isVisible())
codeOutput->Refresh();

View File

@ -22,7 +22,7 @@ class MainWindow : public QMainWindow
void OnSave();
void OnUpdateInfo();
NazaraSlot(ShaderGraph, OnConditionUpdate, m_onConditionUpdate);
NazaraSlot(ShaderGraph, OnOptionUpdate, m_onOptionUpdate);
NazaraSlot(ShaderGraph, OnSelectedNodeUpdate, m_onSelectedNodeUpdate);
NodeEditor* m_nodeEditor;

View File

@ -0,0 +1,55 @@
#include <ShaderNode/Widgets/OptionEditDialog.hpp>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QDialogButtonBox>
#include <QtWidgets/QFormLayout>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QSpinBox>
#include <QtWidgets/QVBoxLayout>
OptionEditDialog::OptionEditDialog(QWidget* parent) :
QDialog(parent)
{
setWindowTitle(tr("Option edit dialog"));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
m_optionName = new QLineEdit;
QFormLayout* formLayout = new QFormLayout;
formLayout->addRow(tr("Name"), m_optionName);
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttonBox, &QDialogButtonBox::accepted, this, &OptionEditDialog::OnAccept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
QVBoxLayout* verticalLayout = new QVBoxLayout;
verticalLayout->addLayout(formLayout);
verticalLayout->addWidget(buttonBox);
setLayout(verticalLayout);
}
OptionEditDialog::OptionEditDialog(const OptionInfo& option, QWidget* parent) :
OptionEditDialog(parent)
{
m_optionName->setText(QString::fromStdString(option.name));
}
OptionInfo OptionEditDialog::GetOptionInfo() const
{
OptionInfo inputInfo;
inputInfo.name = m_optionName->text().toStdString();
return inputInfo;
}
void OptionEditDialog::OnAccept()
{
if (m_optionName->text().isEmpty())
{
QMessageBox::critical(this, tr("Empty name"), tr("Option name must be set"), QMessageBox::Ok);
return;
}
accept();
}

View File

@ -0,0 +1,35 @@
#pragma once
#ifndef NAZARA_SHADERNODES_OPTIONEDITDIALOG_HPP
#define NAZARA_SHADERNODES_OPTIONEDITDIALOG_HPP
#include <ShaderNode/Enums.hpp>
#include <QtWidgets/QDialog>
class QComboBox;
class QLineEdit;
class QSpinBox;
struct OptionInfo
{
std::string name;
};
class OptionEditDialog : public QDialog
{
public:
OptionEditDialog(QWidget* parent = nullptr);
OptionEditDialog(const OptionInfo& input, QWidget* parent = nullptr);
~OptionEditDialog() = default;
OptionInfo GetOptionInfo() const;
private:
void OnAccept();
QLineEdit* m_optionName;
};
#include <ShaderNode/Widgets/OptionEditDialog.inl>
#endif

View File

@ -0,0 +1 @@
#include <ShaderNode/Widgets/OptionEditDialog.hpp>

View File

@ -0,0 +1,119 @@
#include <ShaderNode/Widgets/OptionEditor.hpp>
#include <ShaderNode/Widgets/OptionEditDialog.hpp>
#include <ShaderNode/ShaderGraph.hpp>
#include <QtGui/QStandardItemModel>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QListWidget>
#include <QtWidgets/QTableView>
#include <QtWidgets/QVBoxLayout>
OptionEditor::OptionEditor(ShaderGraph& graph) :
m_shaderGraph(graph)
{
QTableView* tableView = new QTableView;
m_model = new QStandardItemModel(0, 2, tableView);
tableView->setModel(m_model);
m_model->setHorizontalHeaderLabels({ tr("Option"), tr("Enabled") });
connect(tableView, &QTableView::doubleClicked, [this](const QModelIndex& index)
{
if (index.column() == 0)
OnEditOption(index.row());
});
connect(m_model, &QStandardItemModel::itemChanged, [this](QStandardItem* item)
{
if (item->column() == 1)
{
std::size_t optionIndex = static_cast<std::size_t>(item->row());
bool value = item->checkState() == Qt::Checked;
m_shaderGraph.EnableOption(optionIndex, value);
}
});
QPushButton* addStructButton = new QPushButton(tr("Add option..."));
connect(addStructButton, &QPushButton::released, this, &OptionEditor::OnAddOption);
m_layout = new QVBoxLayout;
m_layout->addWidget(tableView);
m_layout->addWidget(addStructButton);
setLayout(m_layout);
m_onOptionListUpdateSlot.Connect(m_shaderGraph.OnOptionListUpdate, this, &OptionEditor::OnOptionListUpdate);
m_onOptionUpdateSlot.Connect(m_shaderGraph.OnOptionUpdate, this, &OptionEditor::OnOptionUpdate);
RefreshOptions();
}
void OptionEditor::OnAddOption()
{
OptionEditDialog* dialog = new OptionEditDialog(this);
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
connect(dialog, &QDialog::accepted, [this, dialog]
{
OptionInfo optionInfo = dialog->GetOptionInfo();
m_shaderGraph.AddOption(std::move(optionInfo.name));
});
dialog->open();
}
void OptionEditor::OnEditOption(int optionIndex)
{
const auto& optionInfo = m_shaderGraph.GetOption(optionIndex);
OptionInfo info;
info.name = optionInfo.name;
OptionEditDialog* dialog = new OptionEditDialog(info, this);
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
connect(dialog, &QDialog::accepted, [this, dialog, optionIndex]
{
OptionInfo optionInfo = dialog->GetOptionInfo();
m_shaderGraph.UpdateOption(optionIndex, std::move(optionInfo.name));
});
dialog->open();
}
void OptionEditor::OnOptionListUpdate(ShaderGraph* /*graph*/)
{
RefreshOptions();
}
void OptionEditor::OnOptionUpdate(ShaderGraph* /*graph*/, std::size_t optionIndex)
{
const auto& optionEntry = m_shaderGraph.GetOption(optionIndex);
int row = int(optionIndex);
m_model->item(row, 0)->setText(QString::fromStdString(optionEntry.name));
m_model->item(row, 1)->setCheckState((optionEntry.enabled) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
}
void OptionEditor::RefreshOptions()
{
m_model->setRowCount(int(m_shaderGraph.GetOptionCount()));
int rowIndex = 0;
for (const auto& optionEntry : m_shaderGraph.GetOptions())
{
QStandardItem* label = new QStandardItem(1);
label->setEditable(false);
label->setText(QString::fromStdString(optionEntry.name));
m_model->setItem(rowIndex, 0, label);
QStandardItem* checkbox = new QStandardItem(1);
checkbox->setCheckable(true);
checkbox->setCheckState((optionEntry.enabled) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
m_model->setItem(rowIndex, 1, checkbox);
rowIndex++;
}
}

View File

@ -0,0 +1,36 @@
#pragma once
#ifndef NAZARA_SHADERNODES_OPTIONEDITOR_HPP
#define NAZARA_SHADERNODES_OPTIONEDITOR_HPP
#include <ShaderNode/ShaderGraph.hpp>
#include <QtWidgets/QWidget>
#include <optional>
class QStandardItemModel;
class QVBoxLayout;
class OptionEditor : public QWidget
{
public:
OptionEditor(ShaderGraph& graph);
~OptionEditor() = default;
private:
void OnAddOption();
void OnOptionListUpdate(ShaderGraph* graph);
void OnOptionUpdate(ShaderGraph* graph, std::size_t optionIndex);
void OnEditOption(int optionIndex);
void RefreshOptions();
NazaraSlot(ShaderGraph, OnStructListUpdate, m_onOptionListUpdateSlot);
NazaraSlot(ShaderGraph, OnStructUpdate, m_onOptionUpdateSlot);
ShaderGraph& m_shaderGraph;
QStandardItemModel* m_model;
QVBoxLayout* m_layout;
};
#include <ShaderNode/Widgets/OptionEditor.inl>
#endif

View File

@ -0,0 +1 @@
#include <ShaderNode/Widgets/OptionEditor.hpp>