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

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