Shader: Add EliminateUnusedPassVisitor and use it when optimizing

This commit is contained in:
Jérôme Leclercq 2022-02-18 13:06:47 +01:00
parent ebd1318512
commit 3f7815175b
12 changed files with 407 additions and 30 deletions

View File

@ -0,0 +1,50 @@
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Shader module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_SHADER_AST_ELIMINATEUNUSEDPASSVISITOR_HPP
#define NAZARA_SHADER_AST_ELIMINATEUNUSEDPASSVISITOR_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Core/Bitset.hpp>
#include <Nazara/Shader/Config.hpp>
#include <Nazara/Shader/Ast/AstCloner.hpp>
namespace Nz::ShaderAst
{
class NAZARA_SHADER_API EliminateUnusedPassVisitor : AstCloner
{
public:
EliminateUnusedPassVisitor() = default;
EliminateUnusedPassVisitor(const EliminateUnusedPassVisitor&) = delete;
EliminateUnusedPassVisitor(EliminateUnusedPassVisitor&&) = delete;
~EliminateUnusedPassVisitor() = default;
StatementPtr Process(Statement& statement);
EliminateUnusedPassVisitor& operator=(const EliminateUnusedPassVisitor&) = delete;
EliminateUnusedPassVisitor& operator=(EliminateUnusedPassVisitor&&) = delete;
private:
using AstCloner::Clone;
StatementPtr Clone(DeclareExternalStatement& node) override;
StatementPtr Clone(DeclareFunctionStatement& node) override;
StatementPtr Clone(DeclareStructStatement& node) override;
StatementPtr Clone(DeclareVariableStatement& node) override;
bool IsFunctionUsed(std::size_t varIndex) const;
bool IsStructUsed(std::size_t varIndex) const;
bool IsVariableUsed(std::size_t varIndex) const;
struct Context;
Context* m_context;
};
inline StatementPtr EliminateUnusedPass(Statement& ast);
}
#include <Nazara/Shader/Ast/EliminateUnusedPassVisitor.inl>
#endif // NAZARA_SHADER_AST_ELIMINATEUNUSEDPASSVISITOR_HPP

View File

@ -0,0 +1,17 @@
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Shader module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Shader/Ast/EliminateUnusedPassVisitor.hpp>
#include <Nazara/Shader/Debug.hpp>
namespace Nz::ShaderAst
{
inline StatementPtr EliminateUnusedPass(Statement& ast)
{
EliminateUnusedPassVisitor visitor;
return visitor.Process(ast);
}
}
#include <Nazara/Shader/DebugOff.hpp>

View File

@ -265,11 +265,11 @@ namespace Nz::ShaderAst
ExpressionValue<UInt32> bindingIndex; ExpressionValue<UInt32> bindingIndex;
ExpressionValue<UInt32> bindingSet; ExpressionValue<UInt32> bindingSet;
ExpressionValue<ExpressionType> type; ExpressionValue<ExpressionType> type;
std::optional<std::size_t> varIndex;
std::string name; std::string name;
}; };
ExpressionValue<UInt32> bindingSet; ExpressionValue<UInt32> bindingSet;
std::optional<std::size_t> varIndex;
std::vector<ExternalVar> externalVars; std::vector<ExternalVar> externalVars;
}; };

View File

@ -91,8 +91,6 @@ namespace Nz::ShaderAst
StatementPtr AstCloner::Clone(DeclareExternalStatement& node) StatementPtr AstCloner::Clone(DeclareExternalStatement& node)
{ {
auto clone = std::make_unique<DeclareExternalStatement>(); auto clone = std::make_unique<DeclareExternalStatement>();
clone->varIndex = node.varIndex;
clone->bindingSet = Clone(node.bindingSet); clone->bindingSet = Clone(node.bindingSet);
clone->externalVars.reserve(node.externalVars.size()); clone->externalVars.reserve(node.externalVars.size());
@ -100,6 +98,7 @@ namespace Nz::ShaderAst
{ {
auto& cloneVar = clone->externalVars.emplace_back(); auto& cloneVar = clone->externalVars.emplace_back();
cloneVar.name = var.name; cloneVar.name = var.name;
cloneVar.varIndex = var.varIndex;
cloneVar.type = Clone(var.type); cloneVar.type = Clone(var.type);
cloneVar.bindingIndex = Clone(var.bindingIndex); cloneVar.bindingIndex = Clone(var.bindingIndex);
cloneVar.bindingSet = Clone(var.bindingSet); cloneVar.bindingSet = Clone(var.bindingSet);

View File

@ -193,14 +193,13 @@ namespace Nz::ShaderAst
void AstSerializerBase::Serialize(DeclareExternalStatement& node) void AstSerializerBase::Serialize(DeclareExternalStatement& node)
{ {
OptVal(node.varIndex);
ExprValue(node.bindingSet); ExprValue(node.bindingSet);
Container(node.externalVars); Container(node.externalVars);
for (auto& extVar : node.externalVars) for (auto& extVar : node.externalVars)
{ {
Value(extVar.name); Value(extVar.name);
OptVal(extVar.varIndex);
ExprValue(extVar.type); ExprValue(extVar.type);
ExprValue(extVar.bindingIndex); ExprValue(extVar.bindingIndex);
ExprValue(extVar.bindingSet); ExprValue(extVar.bindingSet);

View File

@ -0,0 +1,308 @@
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Shader module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Shader/Ast/EliminateUnusedPassVisitor.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <Nazara/Shader/Ast/AstRecursiveVisitor.hpp>
#include <unordered_map>
#include <Nazara/Shader/Debug.hpp>
namespace Nz::ShaderAst
{
namespace
{
template<typename T> T& Retrieve(std::unordered_map<std::size_t, T>& map, std::size_t id)
{
auto it = map.find(id);
assert(it != map.end());
return it->second;
}
template<typename T> const T& Retrieve(const std::unordered_map<std::size_t, T>& map, std::size_t id)
{
auto it = map.find(id);
assert(it != map.end());
return it->second;
}
struct UsageChecker : AstRecursiveVisitor
{
struct UsageSet;
void Resolve()
{
Resolve(globalUsage);
}
void Resolve(const UsageSet& usageSet)
{
resolvedUsage.usedFunctions |= usageSet.usedFunctions;
resolvedUsage.usedStructs |= usageSet.usedStructs;
resolvedUsage.usedVariables |= usageSet.usedVariables;
for (std::size_t funcIndex = usageSet.usedFunctions.FindFirst(); funcIndex != usageSet.usedFunctions.npos; funcIndex = usageSet.usedFunctions.FindNext(funcIndex))
Resolve(Retrieve(functionUsages, funcIndex));
for (std::size_t structIndex = usageSet.usedStructs.FindFirst(); structIndex != usageSet.usedStructs.npos; structIndex = usageSet.usedStructs.FindNext(structIndex))
Resolve(Retrieve(structUsages, structIndex));
for (std::size_t varIndex = usageSet.usedVariables.FindFirst(); varIndex != usageSet.usedVariables.npos; varIndex = usageSet.usedVariables.FindNext(varIndex))
Resolve(Retrieve(variableUsages, varIndex));
}
using AstRecursiveVisitor::Visit;
void Visit(CallFunctionExpression& node) override
{
const auto& targetFuncType = GetExpressionType(node);
assert(std::holds_alternative<FunctionType>(targetFuncType));
const auto& funcType = std::get<FunctionType>(targetFuncType);
assert(currentFunctionIndex);
UsageSet& usageSet = Retrieve(functionUsages, *currentFunctionIndex);
usageSet.usedFunctions.UnboundedSet(funcType.funcIndex);
}
void Visit(DeclareExternalStatement& node) override
{
for (const auto& externalVar : node.externalVars)
{
assert(externalVar.varIndex);
std::size_t varIndex = *externalVar.varIndex;
assert(variableUsages.find(varIndex) == variableUsages.end());
UsageSet& usageSet = variableUsages[varIndex];
const auto& exprType = externalVar.type.GetResultingValue();
if (IsUniformType(exprType))
{
const UniformType& uniformType = std::get<UniformType>(exprType);
usageSet.usedStructs.UnboundedSet(uniformType.containedType.structIndex);
}
++varIndex;
}
AstRecursiveVisitor::Visit(node);
}
void Visit(DeclareFunctionStatement& node) override
{
assert(node.funcIndex);
assert(functionUsages.find(*node.funcIndex) == functionUsages.end());
UsageSet& usageSet = functionUsages[*node.funcIndex];
// Register struct used in parameters or return type
if (!node.parameters.empty())
{
assert(node.varIndex);
std::size_t parameterVarIndex = *node.varIndex;
for (auto& parameter : node.parameters)
{
// Since parameters must always be defined, their type isn't a dependency of parameter variables
assert(variableUsages.find(parameterVarIndex) == variableUsages.end());
variableUsages.emplace(parameterVarIndex, UsageSet{});
const auto& exprType = parameter.type.GetResultingValue();
if (IsStructType(exprType))
{
std::size_t structIndex = std::get<ShaderAst::StructType>(exprType).structIndex;
usageSet.usedStructs.UnboundedSet(structIndex);
}
++parameterVarIndex;
}
}
if (node.returnType.HasValue())
{
const auto& returnExprType = node.returnType.GetResultingValue();
if (IsStructType(returnExprType))
{
std::size_t structIndex = std::get<ShaderAst::StructType>(returnExprType).structIndex;
usageSet.usedStructs.UnboundedSet(structIndex);
}
}
if (node.entryStage.HasValue())
globalUsage.usedFunctions.UnboundedSet(*node.funcIndex);
currentFunctionIndex = node.funcIndex;
AstRecursiveVisitor::Visit(node);
currentFunctionIndex = {};
}
void Visit(DeclareStructStatement& node) override
{
assert(node.structIndex);
assert(structUsages.find(*node.structIndex) == structUsages.end());
UsageSet& usageSet = structUsages[*node.structIndex];
for (const auto& structMember : node.description.members)
{
const auto& memberExprType = structMember.type.GetResultingValue();
if (IsStructType(memberExprType))
{
std::size_t structIndex = std::get<ShaderAst::StructType>(memberExprType).structIndex;
usageSet.usedStructs.UnboundedSet(structIndex);
}
}
AstRecursiveVisitor::Visit(node);
}
void Visit(DeclareVariableStatement& node) override
{
assert(node.varIndex);
assert(variableUsages.find(*node.varIndex) == variableUsages.end());
UsageSet& usageSet = variableUsages[*node.varIndex];
const auto& varType = node.varType.GetResultingValue();
if (IsStructType(varType))
{
const auto& structType = std::get<StructType>(varType);
usageSet.usedStructs.UnboundedSet(structType.structIndex);
}
currentVariableDeclIndex = node.varIndex;
AstRecursiveVisitor::Visit(node);
currentVariableDeclIndex = {};
}
void Visit(VariableExpression& node) override
{
assert(currentFunctionIndex);
if (currentVariableDeclIndex)
{
UsageSet& usageSet = Retrieve(variableUsages, *currentVariableDeclIndex);
usageSet.usedVariables.UnboundedSet(node.variableId);
}
else
{
UsageSet& usageSet = Retrieve(functionUsages, *currentFunctionIndex);
usageSet.usedVariables.UnboundedSet(node.variableId);
}
}
struct UsageSet
{
Bitset<> usedFunctions;
Bitset<> usedStructs;
Bitset<> usedVariables;
};
std::optional<std::size_t> currentFunctionIndex;
std::optional<std::size_t> currentVariableDeclIndex;
std::unordered_map<std::size_t, UsageSet> functionUsages;
std::unordered_map<std::size_t, UsageSet> structUsages;
std::unordered_map<std::size_t, UsageSet> variableUsages;
UsageSet globalUsage;
UsageSet resolvedUsage;
};
}
struct EliminateUnusedPassVisitor::Context
{
UsageChecker usageChecker;
};
StatementPtr EliminateUnusedPassVisitor::Process(Statement& statement)
{
Context context;
statement.Visit(context.usageChecker);
context.usageChecker.Resolve();
m_context = &context;
CallOnExit onExit([this]()
{
m_context = nullptr;
});
return Clone(statement);
}
StatementPtr EliminateUnusedPassVisitor::Clone(DeclareExternalStatement& node)
{
bool isUsed = false;
for (const auto& externalVar : node.externalVars)
{
assert(externalVar.varIndex);
std::size_t varIndex = *externalVar.varIndex;
if (IsVariableUsed(varIndex))
{
isUsed = true;
break;
}
}
if (!isUsed)
return ShaderBuilder::NoOp();
auto clonedNode = AstCloner::Clone(node);
auto& externalStatement = static_cast<DeclareExternalStatement&>(*clonedNode);
for (auto it = externalStatement.externalVars.begin(); it != externalStatement.externalVars.end(); )
{
const auto& externalVar = *it;
assert(externalVar.varIndex);
std::size_t varIndex = *externalVar.varIndex;
if (!IsVariableUsed(varIndex))
it = externalStatement.externalVars.erase(it);
else
++it;
}
return clonedNode;
}
StatementPtr EliminateUnusedPassVisitor::Clone(DeclareFunctionStatement& node)
{
assert(node.funcIndex);
if (!IsFunctionUsed(*node.funcIndex))
return ShaderBuilder::NoOp();
return AstCloner::Clone(node);
}
StatementPtr EliminateUnusedPassVisitor::Clone(DeclareStructStatement& node)
{
assert(node.structIndex);
if (!IsStructUsed(*node.structIndex))
return ShaderBuilder::NoOp();
return AstCloner::Clone(node);
}
StatementPtr EliminateUnusedPassVisitor::Clone(DeclareVariableStatement& node)
{
assert(node.varIndex);
if (!IsVariableUsed(*node.varIndex))
return ShaderBuilder::NoOp();
return AstCloner::Clone(node);
}
bool EliminateUnusedPassVisitor::IsFunctionUsed(std::size_t varIndex) const
{
assert(m_context);
return m_context->usageChecker.resolvedUsage.usedFunctions.UnboundedTest(varIndex);
}
bool EliminateUnusedPassVisitor::IsStructUsed(std::size_t varIndex) const
{
assert(m_context);
return m_context->usageChecker.resolvedUsage.usedStructs.UnboundedTest(varIndex);
}
bool EliminateUnusedPassVisitor::IsVariableUsed(std::size_t varIndex) const
{
assert(m_context);
return m_context->usageChecker.resolvedUsage.usedVariables.UnboundedTest(varIndex);
}
}

View File

@ -786,10 +786,7 @@ namespace Nz::ShaderAst
throw AstError{ "external variable " + extVar.name + " is of wrong type: only uniform and sampler are allowed in external blocks" }; throw AstError{ "external variable " + extVar.name + " is of wrong type: only uniform and sampler are allowed in external blocks" };
extVar.type = std::move(resolvedType); extVar.type = std::move(resolvedType);
extVar.varIndex = RegisterVariable(extVar.name, std::move(varType));
std::size_t varIndex = RegisterVariable(extVar.name, std::move(varType));
if (!clone->varIndex)
clone->varIndex = varIndex; //< First external variable index is node variable index
SanitizeIdentifier(extVar.name); SanitizeIdentifier(extVar.name);
} }

View File

@ -12,6 +12,7 @@
#include <Nazara/Shader/Ast/AstOptimizer.hpp> #include <Nazara/Shader/Ast/AstOptimizer.hpp>
#include <Nazara/Shader/Ast/AstRecursiveVisitor.hpp> #include <Nazara/Shader/Ast/AstRecursiveVisitor.hpp>
#include <Nazara/Shader/Ast/AstUtils.hpp> #include <Nazara/Shader/Ast/AstUtils.hpp>
#include <Nazara/Shader/Ast/EliminateUnusedPassVisitor.hpp>
#include <Nazara/Shader/Ast/SanitizeVisitor.hpp> #include <Nazara/Shader/Ast/SanitizeVisitor.hpp>
#include <optional> #include <optional>
#include <set> #include <set>
@ -177,7 +178,11 @@ namespace Nz
ShaderAst::StatementPtr optimizedAst; ShaderAst::StatementPtr optimizedAst;
if (states.optimize) if (states.optimize)
{ {
optimizedAst = ShaderAst::Optimize(*targetAst); ShaderAst::StatementPtr tempAst;
tempAst = ShaderAst::Optimize(*targetAst);
optimizedAst = ShaderAst::EliminateUnusedPass(*tempAst);
targetAst = optimizedAst.get(); targetAst = optimizedAst.get();
} }
@ -992,9 +997,6 @@ namespace Nz
void GlslWriter::Visit(ShaderAst::DeclareExternalStatement& node) void GlslWriter::Visit(ShaderAst::DeclareExternalStatement& node)
{ {
assert(node.varIndex);
std::size_t varIndex = *node.varIndex;
for (const auto& externalVar : node.externalVars) for (const auto& externalVar : node.externalVars)
{ {
bool isStd140 = false; bool isStd140 = false;
@ -1075,7 +1077,8 @@ namespace Nz
if (IsUniformType(externalVar.type.GetResultingValue())) if (IsUniformType(externalVar.type.GetResultingValue()))
AppendLine(); AppendLine();
RegisterVariable(varIndex++, externalVar.name); assert(externalVar.varIndex);
RegisterVariable(*externalVar.varIndex, externalVar.name);
} }
} }

View File

@ -787,9 +787,6 @@ namespace Nz
void LangWriter::Visit(ShaderAst::DeclareExternalStatement& node) void LangWriter::Visit(ShaderAst::DeclareExternalStatement& node)
{ {
assert(node.varIndex);
std::size_t varIndex = *node.varIndex;
AppendLine("external"); AppendLine("external");
EnterScope(); EnterScope();
@ -804,7 +801,8 @@ namespace Nz
AppendAttributes(false, SetAttribute{ externalVar.bindingSet }, BindingAttribute{ externalVar.bindingIndex }); AppendAttributes(false, SetAttribute{ externalVar.bindingSet }, BindingAttribute{ externalVar.bindingIndex });
Append(externalVar.name, ": ", externalVar.type); Append(externalVar.name, ": ", externalVar.type);
RegisterVariable(varIndex++, externalVar.name); assert(externalVar.varIndex);
RegisterVariable(*externalVar.varIndex, externalVar.name);
} }
LeaveScope(); LeaveScope();

View File

@ -579,11 +579,11 @@ namespace Nz
void SpirvAstVisitor::Visit(ShaderAst::DeclareExternalStatement& node) void SpirvAstVisitor::Visit(ShaderAst::DeclareExternalStatement& node)
{ {
assert(node.varIndex);
std::size_t varIndex = *node.varIndex;
for (auto&& extVar : node.externalVars) for (auto&& extVar : node.externalVars)
RegisterExternalVariable(varIndex++, extVar.type.GetResultingValue()); {
assert(extVar.varIndex);
RegisterExternalVariable(*extVar.varIndex, extVar.type.GetResultingValue());
}
} }
void SpirvAstVisitor::Visit(ShaderAst::DeclareFunctionStatement& node) void SpirvAstVisitor::Visit(ShaderAst::DeclareFunctionStatement& node)

View File

@ -13,6 +13,7 @@
#include <Nazara/Shader/Ast/AstCloner.hpp> #include <Nazara/Shader/Ast/AstCloner.hpp>
#include <Nazara/Shader/Ast/AstOptimizer.hpp> #include <Nazara/Shader/Ast/AstOptimizer.hpp>
#include <Nazara/Shader/Ast/AstRecursiveVisitor.hpp> #include <Nazara/Shader/Ast/AstRecursiveVisitor.hpp>
#include <Nazara/Shader/Ast/EliminateUnusedPassVisitor.hpp>
#include <Nazara/Shader/Ast/SanitizeVisitor.hpp> #include <Nazara/Shader/Ast/SanitizeVisitor.hpp>
#include <SpirV/GLSL.std.450.h> #include <SpirV/GLSL.std.450.h>
#include <tsl/ordered_map.h> #include <tsl/ordered_map.h>
@ -137,8 +138,6 @@ namespace Nz
void Visit(ShaderAst::DeclareExternalStatement& node) override void Visit(ShaderAst::DeclareExternalStatement& node) override
{ {
assert(node.varIndex);
std::size_t varIndex = *node.varIndex;
for (auto& extVar : node.externalVars) for (auto& extVar : node.externalVars)
{ {
SpirvConstantCache::Variable variable; SpirvConstantCache::Variable variable;
@ -165,7 +164,8 @@ namespace Nz
assert(extVar.bindingIndex.IsResultingValue()); assert(extVar.bindingIndex.IsResultingValue());
UniformVar& uniformVar = extVars[varIndex++]; assert(extVar.varIndex);
UniformVar& uniformVar = extVars[*extVar.varIndex];
uniformVar.pointerId = m_constantCache.Register(variable); uniformVar.pointerId = m_constantCache.Register(variable);
uniformVar.bindingIndex = extVar.bindingIndex.GetResultingValue(); uniformVar.bindingIndex = extVar.bindingIndex.GetResultingValue();
uniformVar.descriptorSet = (extVar.bindingSet.HasValue()) ? extVar.bindingSet.GetResultingValue() : 0; uniformVar.descriptorSet = (extVar.bindingSet.HasValue()) ? extVar.bindingSet.GetResultingValue() : 0;
@ -519,7 +519,11 @@ namespace Nz
ShaderAst::StatementPtr optimizedAst; ShaderAst::StatementPtr optimizedAst;
if (states.optimize) if (states.optimize)
{ {
optimizedAst = ShaderAst::Optimize(*targetAst); ShaderAst::StatementPtr tempAst;
tempAst = ShaderAst::Optimize(*targetAst);
optimizedAst = ShaderAst::EliminateUnusedPass(*tempAst);
targetAst = optimizedAst.get(); targetAst = optimizedAst.get();
} }

View File

@ -2,6 +2,7 @@
#include <Nazara/Shader/GlslWriter.hpp> #include <Nazara/Shader/GlslWriter.hpp>
#include <Nazara/Shader/LangWriter.hpp> #include <Nazara/Shader/LangWriter.hpp>
#include <Nazara/Shader/Ast/AstOptimizer.hpp> #include <Nazara/Shader/Ast/AstOptimizer.hpp>
#include <Nazara/Shader/Ast/EliminateUnusedPassVisitor.hpp>
#include <Nazara/Shader/Ast/SanitizeVisitor.hpp> #include <Nazara/Shader/Ast/SanitizeVisitor.hpp>
#include <Nazara/Shader/SpirvPrinter.hpp> #include <Nazara/Shader/SpirvPrinter.hpp>
#include <Nazara/Shader/SpirvWriter.hpp> #include <Nazara/Shader/SpirvWriter.hpp>
@ -16,7 +17,7 @@
enum class OutputLanguage enum class OutputLanguage
{ {
GLSL, GLSL,
Nazalang, NZSL,
SpirV SpirV
}; };
@ -27,7 +28,7 @@ m_shaderGraph(shaderGraph)
m_outputLang = new QComboBox; m_outputLang = new QComboBox;
m_outputLang->addItem("GLSL", int(OutputLanguage::GLSL)); m_outputLang->addItem("GLSL", int(OutputLanguage::GLSL));
m_outputLang->addItem("Nazalang", int(OutputLanguage::Nazalang)); m_outputLang->addItem("NZSL", int(OutputLanguage::NZSL));
m_outputLang->addItem("SPIR-V", int(OutputLanguage::SpirV)); m_outputLang->addItem("SPIR-V", int(OutputLanguage::SpirV));
connect(m_outputLang, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) connect(m_outputLang, qOverload<int>(&QComboBox::currentIndexChanged), [this](int)
{ {
@ -70,7 +71,8 @@ void CodeOutputWidget::Refresh()
shaderAst = Nz::ShaderAst::Sanitize(*shaderAst, sanitizeOptions); shaderAst = Nz::ShaderAst::Sanitize(*shaderAst, sanitizeOptions);
Nz::ShaderAst::AstOptimizer optimiser; Nz::ShaderAst::AstOptimizer optimiser;
shaderAst = optimiser.Optimise(*shaderAst); shaderAst = Nz::ShaderAst::Optimize(*shaderAst);
shaderAst = Nz::ShaderAst::EliminateUnusedPass(*shaderAst);
} }
std::string output; std::string output;
@ -91,7 +93,7 @@ void CodeOutputWidget::Refresh()
break; break;
} }
case OutputLanguage::Nazalang: case OutputLanguage::NZSL:
{ {
Nz::LangWriter writer; Nz::LangWriter writer;
output = writer.Generate(*shaderAst, states); output = writer.Generate(*shaderAst, states);