Graphics: rework ubershaders to prevent duplicate shaders modules
Also rename all remaining conditions to options
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user