Handle shader options of any type

This commit is contained in:
Jérôme Leclercq
2021-09-03 19:33:41 +02:00
parent 2f9e495739
commit 02a12d9328
38 changed files with 236 additions and 1118 deletions

View File

@@ -26,7 +26,7 @@ namespace Nz
inline void BasicMaterial::EnableAlphaTest(bool alphaTest)
{
NazaraAssert(HasAlphaTest(), "Material has no alpha test option");
m_material.EnableOption(m_optionIndexes.alphaTest, alphaTest);
m_material.SetOptionValue(m_optionIndexes.alphaTest, alphaTest);
}
inline const std::shared_ptr<Texture>& BasicMaterial::GetAlphaMap() const
@@ -56,7 +56,11 @@ namespace Nz
inline bool BasicMaterial::IsAlphaTestEnabled() const
{
NazaraAssert(HasAlphaTest(), "Material has no alpha test option");
return m_material.IsOptionEnabled(m_optionIndexes.alphaTest);
const auto& optionOpt = m_material.GetOptionValue(m_optionIndexes.alphaTest);
if (std::holds_alternative<ShaderAst::NoValue>(optionOpt))
return false;
return std::get<bool>(optionOpt);
}
inline bool BasicMaterial::HasAlphaMap() const
@@ -91,7 +95,7 @@ namespace Nz
m_material.SetTexture(m_textureIndexes.alpha, std::move(alphaMap));
if (m_optionIndexes.hasDiffuseMap != MaterialSettings::InvalidIndex)
m_material.EnableOption(m_optionIndexes.hasAlphaMap, hasAlphaMap);
m_material.SetOptionValue(m_optionIndexes.hasAlphaMap, hasAlphaMap);
}
inline void BasicMaterial::SetAlphaSampler(TextureSamplerInfo alphaSampler)
@@ -107,7 +111,7 @@ namespace Nz
m_material.SetTexture(m_textureIndexes.diffuse, std::move(diffuseMap));
if (m_optionIndexes.hasDiffuseMap != MaterialSettings::InvalidIndex)
m_material.EnableOption(m_optionIndexes.hasDiffuseMap, hasDiffuseMap);
m_material.SetOptionValue(m_optionIndexes.hasDiffuseMap, hasDiffuseMap);
}
inline void BasicMaterial::SetDiffuseSampler(TextureSamplerInfo diffuseSampler)

View File

@@ -20,6 +20,7 @@
#include <Nazara/Graphics/MaterialPipeline.hpp>
#include <Nazara/Renderer/Texture.hpp>
#include <Nazara/Renderer/TextureSampler.hpp>
#include <Nazara/Shader/Ast/ConstantValue.hpp>
#include <Nazara/Utility/UniformBuffer.hpp>
#include <array>
#include <string>
@@ -45,7 +46,6 @@ namespace Nz
inline void EnableDepthClamp(bool depthClamp);
inline void EnableDepthWrite(bool depthWrite);
inline void EnableFaceCulling(bool faceCulling);
inline void EnableOption(std::size_t optionIndex, bool enable);
inline void EnableScissorTest(bool scissorTest);
inline void EnableStencilTest(bool stencilTest);
@@ -61,6 +61,7 @@ namespace Nz
inline FaceSide GetFaceCulling() const;
inline FaceFilling GetFaceFilling() const;
inline float GetLineWidth() const;
inline const ShaderAst::ConstantValue& GetOptionValue(std::size_t optionIndex) const;
inline const std::shared_ptr<MaterialPipeline>& GetPipeline() const;
inline const MaterialPipelineInfo& GetPipelineInfo() const;
inline float GetPointSize() const;
@@ -82,7 +83,6 @@ namespace Nz
inline bool IsDepthClampEnabled() const;
inline bool IsDepthWriteEnabled() const;
inline bool IsFaceCullingEnabled() const;
inline bool IsOptionEnabled(std::size_t optionIndex) const;
inline bool IsScissorTestEnabled() const;
inline bool IsStencilTestEnabled() const;
@@ -92,6 +92,7 @@ namespace Nz
inline void SetFaceCulling(FaceSide faceSide);
inline void SetFaceFilling(FaceFilling filling);
inline void SetLineWidth(float lineWidth);
inline void SetOptionValue(std::size_t optionIndex, ShaderAst::ConstantValue value);
inline void SetPointSize(float pointSize);
inline void SetPrimitiveMode(PrimitiveMode mode);
inline void SetTexture(std::size_t textureIndex, std::shared_ptr<Texture> texture);
@@ -126,11 +127,11 @@ namespace Nz
bool dataInvalidated = true;
};
std::array<ShaderAst::ConstantValue, 64> m_optionValues;
std::shared_ptr<const MaterialSettings> m_settings;
std::vector<MaterialTexture> m_textures;
std::vector<UniformBuffer> m_uniformBuffers;
mutable std::shared_ptr<MaterialPipeline> m_pipeline;
UInt64 m_enabledOptions;
mutable MaterialPipelineInfo m_pipelineInfo;
ShaderBindingPtr m_shaderBinding;
bool m_forceCommandBufferRegeneration;

View File

@@ -182,15 +182,6 @@ namespace Nz
InvalidatePipeline();
}
inline void MaterialPass::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 scissor test for this material
*
@@ -315,6 +306,12 @@ namespace Nz
return m_pipelineInfo.lineWidth;
}
inline const ShaderAst::ConstantValue& MaterialPass::GetOptionValue(std::size_t optionIndex) const
{
assert(optionIndex < m_optionValues.size());
return m_optionValues[optionIndex];
}
/*!
* \brief Gets the render states
* \return Constant reference to the render states
@@ -461,11 +458,6 @@ namespace Nz
return m_pipelineInfo.faceCulling;
}
inline bool MaterialPass::IsOptionEnabled(std::size_t optionIndex) const
{
return TestBit<UInt64>(m_enabledOptions, optionIndex);
}
/*!
* \brief Checks whether this material has scissor test enabled
* \return true If it is the case
@@ -562,6 +554,16 @@ namespace Nz
InvalidatePipeline();
}
inline void MaterialPass::SetOptionValue(std::size_t optionIndex, ShaderAst::ConstantValue value)
{
assert(optionIndex < m_optionValues.size());
if (m_optionValues[optionIndex] != value)
{
m_optionValues[optionIndex] = std::move(value);
InvalidatePipeline();
}
}
/*!
* \brief Sets the point size for this material
*
@@ -654,3 +656,4 @@ namespace Nz
}
#include <Nazara/Graphics/DebugOff.hpp>
#include "MaterialPass.hpp"

View File

@@ -12,6 +12,7 @@
#include <Nazara/Graphics/Enums.hpp>
#include <Nazara/Graphics/MaterialSettings.hpp>
#include <Nazara/Renderer/RenderPipeline.hpp>
#include <Nazara/Shader/Ast/ConstantValue.hpp>
#include <array>
#include <memory>
@@ -23,8 +24,8 @@ namespace Nz
{
struct Shader
{
std::array<ShaderAst::ConstantValue, 32> optionValues;
std::shared_ptr<UberShader> uberShader;
Nz::UInt64 enabledOptions = 0;
};
std::vector<Shader> shaders;

View File

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

View File

@@ -55,7 +55,7 @@ namespace Nz
static constexpr std::size_t InvalidIndex = std::numeric_limits<std::size_t>::max();
static void BuildOption(std::vector<Option>& options, const std::vector<std::shared_ptr<UberShader>>& uberShaders, std::string optionName, const std::string& shaderOptionName);
static inline void BuildOption(std::vector<Option>& options, const std::vector<std::shared_ptr<UberShader>>& uberShaders, std::string optionName, const std::string& shaderOptionName);
struct Builder
{
@@ -69,7 +69,7 @@ namespace Nz
struct Option
{
std::string name;
std::array<UInt64, ShaderStageTypeCount> enabledOptions;
std::vector<std::optional<std::size_t>> optionIndexByShader;
};
struct UniformVariable

View File

@@ -168,22 +168,23 @@ namespace Nz
inline void MaterialSettings::BuildOption(std::vector<Option>& options, const std::vector<std::shared_ptr<UberShader>>& uberShaders, std::string optionName, const std::string& shaderOptionName)
{
std::array<UInt64, ShaderStageTypeCount> shaderOptions;
shaderOptions.fill(0);
std::vector<std::optional<std::size_t>> shaderOptions;
for (std::size_t i = 0; i < ShaderStageTypeCount; ++i)
for (std::size_t shaderIndex = 0; shaderIndex < uberShaders.size(); ++shaderIndex)
{
for (const auto& uberShader : uberShaders)
const auto& uberShader = uberShaders[shaderIndex];
const UberShader::Option* optionData;
if (uberShader->HasOption(shaderOptionName, &optionData))
{
if (uberShader->GetSupportedStages() & static_cast<ShaderStageType>(i))
{
assert(shaderOptions[i] == 0);
shaderOptions[i] |= uberShader->GetOptionFlagByName(shaderOptionName);
}
if (shaderIndex >= shaderOptions.size())
shaderOptions.resize(shaderIndex + 1);
shaderOptions[shaderIndex] = optionData->index;
}
}
if (std::any_of(shaderOptions.begin(), shaderOptions.end(), [&](UInt64 flags) { return flags != 0; }))
if (std::any_of(shaderOptions.begin(), shaderOptions.end(), [&](std::optional<std::size_t> optionIndex) { return optionIndex.has_value(); }))
{
options.push_back({
std::move(optionName),

View File

@@ -8,8 +8,10 @@
#define NAZARA_UBER_SHADER_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Core/Algorithm.hpp>
#include <Nazara/Core/Bitset.hpp>
#include <Nazara/Graphics/Config.hpp>
#include <Nazara/Renderer/RenderPipeline.hpp>
#include <Nazara/Shader/Ast/Nodes.hpp>
#include <unordered_map>
@@ -20,21 +22,45 @@ namespace Nz
class NAZARA_GRAPHICS_API UberShader
{
public:
struct Config;
struct Option;
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);
const std::shared_ptr<ShaderModule>& Get(const Config& config);
inline bool HasOption(const std::string& optionName, Pointer<const Option>* option = nullptr) const;
static constexpr std::size_t MaximumOptionValue = 32;
struct Config
{
std::array<ShaderAst::ConstantValue, MaximumOptionValue> optionValues;
};
struct ConfigEqual
{
inline bool operator()(const Config& lhs, const Config& rhs) const;
};
struct ConfigHasher
{
inline std::size_t operator()(const Config& config) const;
};
struct Option
{
std::size_t index;
ShaderAst::ExpressionType type;
};
private:
std::unordered_map<UInt64 /*combination*/, std::shared_ptr<ShaderModule>> m_combinations;
std::unordered_map<std::string, std::size_t> m_optionIndexByName;
std::unordered_map<Config, std::shared_ptr<ShaderModule>, ConfigHasher, ConfigEqual> m_combinations;
std::unordered_map<std::string, Option> m_optionIndexByName;
ShaderAst::StatementPtr m_shaderAst;
ShaderStageTypeFlags m_shaderStages;
UInt64 m_combinationMask;
};
}

View File

@@ -11,6 +11,38 @@ namespace Nz
{
return m_shaderStages;
}
inline bool UberShader::HasOption(const std::string& optionName, Pointer<const Option>* option) const
{
auto it = m_optionIndexByName.find(optionName);
if (it == m_optionIndexByName.end())
return false;
if (option)
*option = &it->second;
return true;
}
inline bool UberShader::ConfigEqual::operator()(const Config& lhs, const Config& rhs) const
{
for (std::size_t i = 0; i < lhs.optionValues.size(); ++i)
{
if (lhs.optionValues[i] != rhs.optionValues[i])
return false;
}
return true;
}
inline std::size_t UberShader::ConfigHasher::operator()(const Config& config) const
{
std::size_t hash = 0;
for (std::size_t i = 0; i < config.optionValues.size(); ++i)
HashCombine(hash, config.optionValues[i]);
return hash;
}
}
#include <Nazara/Graphics/DebugOff.hpp>