Split shader generation to a new module
This commit is contained in:
@@ -1,631 +0,0 @@
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Renderer module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Renderer/GlslWriter.hpp>
|
||||
#include <Nazara/Core/Algorithm.hpp>
|
||||
#include <Nazara/Core/CallOnExit.hpp>
|
||||
#include <Nazara/Renderer/ShaderBuilder.hpp>
|
||||
#include <Nazara/Renderer/ShaderAstCloner.hpp>
|
||||
#include <Nazara/Renderer/ShaderAstValidator.hpp>
|
||||
#include <stdexcept>
|
||||
#include <Nazara/Renderer/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct AstAdapter : ShaderAstCloner
|
||||
{
|
||||
void Visit(ShaderNodes::AssignOp& node) override
|
||||
{
|
||||
if (!flipYPosition)
|
||||
return ShaderAstCloner::Visit(node);
|
||||
|
||||
if (node.left->GetType() != ShaderNodes::NodeType::Identifier)
|
||||
return ShaderAstCloner::Visit(node);
|
||||
|
||||
const auto& identifier = static_cast<const ShaderNodes::Identifier&>(*node.left);
|
||||
if (identifier.var->GetType() != ShaderNodes::VariableType::BuiltinVariable)
|
||||
return ShaderAstCloner::Visit(node);
|
||||
|
||||
const auto& builtinVar = static_cast<const ShaderNodes::BuiltinVariable&>(*identifier.var);
|
||||
if (builtinVar.entry != ShaderNodes::BuiltinEntry::VertexPosition)
|
||||
return ShaderAstCloner::Visit(node);
|
||||
|
||||
auto fixYConstant = ShaderBuilder::Constant(Nz::Vector4f(1.f, -1.f, 1.f, 1.f));
|
||||
auto mulFix = ShaderBuilder::Multiply(CloneExpression(node.right), fixYConstant);
|
||||
|
||||
PushExpression(ShaderNodes::AssignOp::Build(node.op, CloneExpression(node.left), mulFix));
|
||||
}
|
||||
|
||||
bool flipYPosition = false;
|
||||
};
|
||||
}
|
||||
|
||||
GlslWriter::GlslWriter() :
|
||||
m_currentState(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
std::string GlslWriter::Generate(const ShaderAst& shader)
|
||||
{
|
||||
std::string error;
|
||||
if (!ValidateShader(shader, &error))
|
||||
throw std::runtime_error("Invalid shader AST: " + error);
|
||||
|
||||
m_context.shader = &shader;
|
||||
|
||||
State state;
|
||||
m_currentState = &state;
|
||||
CallOnExit onExit([this]()
|
||||
{
|
||||
m_currentState = nullptr;
|
||||
});
|
||||
|
||||
unsigned int glslVersion;
|
||||
if (m_environment.glES)
|
||||
{
|
||||
if (m_environment.glMajorVersion >= 3 && m_environment.glMinorVersion >= 2)
|
||||
glslVersion = 320;
|
||||
else if (m_environment.glMajorVersion >= 3 && m_environment.glMinorVersion >= 1)
|
||||
glslVersion = 310;
|
||||
else if (m_environment.glMajorVersion >= 3 && m_environment.glMinorVersion >= 0)
|
||||
glslVersion = 300;
|
||||
else if (m_environment.glMajorVersion >= 2 && m_environment.glMinorVersion >= 0)
|
||||
glslVersion = 100;
|
||||
else
|
||||
throw std::runtime_error("This version of OpenGL ES does not support shaders");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_environment.glMajorVersion >= 3 && m_environment.glMinorVersion >= 3)
|
||||
glslVersion = m_environment.glMajorVersion * 100 + m_environment.glMinorVersion * 10;
|
||||
else if (m_environment.glMajorVersion >= 3 && m_environment.glMinorVersion >= 2)
|
||||
glslVersion = 150;
|
||||
else if (m_environment.glMajorVersion >= 3 && m_environment.glMinorVersion >= 1)
|
||||
glslVersion = 140;
|
||||
else if (m_environment.glMajorVersion >= 3 && m_environment.glMinorVersion >= 0)
|
||||
glslVersion = 130;
|
||||
else if (m_environment.glMajorVersion >= 2 && m_environment.glMinorVersion >= 1)
|
||||
glslVersion = 120;
|
||||
else if (m_environment.glMajorVersion >= 2 && m_environment.glMinorVersion >= 0)
|
||||
glslVersion = 110;
|
||||
else
|
||||
throw std::runtime_error("This version of OpenGL does not support shaders");
|
||||
}
|
||||
|
||||
// Header
|
||||
Append("#version ");
|
||||
Append(glslVersion);
|
||||
if (m_environment.glES)
|
||||
Append(" es");
|
||||
|
||||
AppendLine();
|
||||
AppendLine();
|
||||
|
||||
// Extensions
|
||||
|
||||
std::vector<std::string> requiredExtensions;
|
||||
|
||||
if (!m_environment.glES && m_environment.extCallback)
|
||||
{
|
||||
// GL_ARB_shading_language_420pack (required for layout(binding = X))
|
||||
if (glslVersion < 420 && HasExplicitBinding(shader))
|
||||
{
|
||||
if (m_environment.extCallback("GL_ARB_shading_language_420pack"))
|
||||
requiredExtensions.emplace_back("GL_ARB_shading_language_420pack");
|
||||
}
|
||||
|
||||
// GL_ARB_separate_shader_objects (required for layout(location = X))
|
||||
if (glslVersion < 410 && HasExplicitLocation(shader))
|
||||
{
|
||||
if (m_environment.extCallback("GL_ARB_separate_shader_objects"))
|
||||
requiredExtensions.emplace_back("GL_ARB_separate_shader_objects");
|
||||
}
|
||||
}
|
||||
|
||||
if (!requiredExtensions.empty())
|
||||
{
|
||||
for (const std::string& ext : requiredExtensions)
|
||||
AppendLine("#extension " + ext + " : require");
|
||||
|
||||
AppendLine();
|
||||
}
|
||||
|
||||
if (m_environment.glES)
|
||||
{
|
||||
AppendLine("#if GL_FRAGMENT_PRECISION_HIGH");
|
||||
AppendLine("precision highp float;");
|
||||
AppendLine("#else");
|
||||
AppendLine("precision mediump float;");
|
||||
AppendLine("#endif");
|
||||
AppendLine();
|
||||
}
|
||||
|
||||
// Structures
|
||||
/*if (shader.GetStructCount() > 0)
|
||||
{
|
||||
AppendCommentSection("Structures");
|
||||
for (const auto& s : shader.GetStructs())
|
||||
{
|
||||
Append("struct ");
|
||||
AppendLine(s.name);
|
||||
AppendLine("{");
|
||||
for (const auto& m : s.members)
|
||||
{
|
||||
Append("\t");
|
||||
Append(m.type);
|
||||
Append(" ");
|
||||
Append(m.name);
|
||||
AppendLine(";");
|
||||
}
|
||||
AppendLine("};");
|
||||
AppendLine();
|
||||
}
|
||||
}*/
|
||||
|
||||
// Global variables (uniforms, input and outputs)
|
||||
const char* inKeyword = (glslVersion >= 130) ? "in" : "varying";
|
||||
const char* outKeyword = (glslVersion >= 130) ? "out" : "varying";
|
||||
|
||||
DeclareVariables(shader, shader.GetUniforms(), "uniform", "Uniforms");
|
||||
DeclareVariables(shader, shader.GetInputs(), inKeyword, "Inputs");
|
||||
DeclareVariables(shader, shader.GetOutputs(), outKeyword, "Outputs");
|
||||
|
||||
std::size_t functionCount = shader.GetFunctionCount();
|
||||
if (functionCount > 1)
|
||||
{
|
||||
AppendCommentSection("Prototypes");
|
||||
for (const auto& func : shader.GetFunctions())
|
||||
{
|
||||
if (func.name != "main")
|
||||
{
|
||||
AppendFunctionPrototype(func);
|
||||
AppendLine(";");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& func : shader.GetFunctions())
|
||||
AppendFunction(func);
|
||||
|
||||
return state.stream.str();
|
||||
}
|
||||
|
||||
void GlslWriter::SetEnv(Environment environment)
|
||||
{
|
||||
m_environment = std::move(environment);
|
||||
}
|
||||
|
||||
void GlslWriter::Append(ShaderExpressionType type)
|
||||
{
|
||||
std::visit([&](auto&& arg)
|
||||
{
|
||||
Append(arg);
|
||||
}, type);
|
||||
}
|
||||
|
||||
void GlslWriter::Append(ShaderNodes::BuiltinEntry builtin)
|
||||
{
|
||||
switch (builtin)
|
||||
{
|
||||
case ShaderNodes::BuiltinEntry::VertexPosition:
|
||||
Append("gl_Position");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GlslWriter::Append(ShaderNodes::BasicType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ShaderNodes::BasicType::Boolean: return Append("bool");
|
||||
case ShaderNodes::BasicType::Float1: return Append("float");
|
||||
case ShaderNodes::BasicType::Float2: return Append("vec2");
|
||||
case ShaderNodes::BasicType::Float3: return Append("vec3");
|
||||
case ShaderNodes::BasicType::Float4: return Append("vec4");
|
||||
case ShaderNodes::BasicType::Int1: return Append("int");
|
||||
case ShaderNodes::BasicType::Int2: return Append("ivec2");
|
||||
case ShaderNodes::BasicType::Int3: return Append("ivec3");
|
||||
case ShaderNodes::BasicType::Int4: return Append("ivec4");
|
||||
case ShaderNodes::BasicType::Mat4x4: return Append("mat4");
|
||||
case ShaderNodes::BasicType::Sampler2D: return Append("sampler2D");
|
||||
case ShaderNodes::BasicType::Void: return Append("void");
|
||||
}
|
||||
}
|
||||
|
||||
void GlslWriter::Append(ShaderNodes::MemoryLayout layout)
|
||||
{
|
||||
switch (layout)
|
||||
{
|
||||
case ShaderNodes::MemoryLayout::Std140:
|
||||
Append("std140");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GlslWriter::AppendCommentSection(const std::string& section)
|
||||
{
|
||||
NazaraAssert(m_currentState, "This function should only be called while processing an AST");
|
||||
|
||||
String stars((section.size() < 33) ? (36 - section.size()) / 2 : 3, '*');
|
||||
m_currentState->stream << "/*" << stars << ' ' << section << ' ' << stars << "*/";
|
||||
AppendLine();
|
||||
}
|
||||
|
||||
void GlslWriter::AppendFunction(const ShaderAst::Function& func)
|
||||
{
|
||||
NazaraAssert(!m_context.currentFunction, "A function is already being processed");
|
||||
NazaraAssert(m_currentState, "This function should only be called while processing an AST");
|
||||
|
||||
AppendFunctionPrototype(func);
|
||||
|
||||
m_context.currentFunction = &func;
|
||||
CallOnExit onExit([this] ()
|
||||
{
|
||||
m_context.currentFunction = nullptr;
|
||||
});
|
||||
|
||||
EnterScope();
|
||||
{
|
||||
AstAdapter adapter;
|
||||
adapter.flipYPosition = m_environment.flipYPosition;
|
||||
|
||||
Visit(adapter.Clone(func.statement));
|
||||
}
|
||||
LeaveScope();
|
||||
}
|
||||
|
||||
void GlslWriter::AppendFunctionPrototype(const ShaderAst::Function& func)
|
||||
{
|
||||
Append(func.returnType);
|
||||
|
||||
Append(" ");
|
||||
Append(func.name);
|
||||
|
||||
Append("(");
|
||||
for (std::size_t i = 0; i < func.parameters.size(); ++i)
|
||||
{
|
||||
if (i != 0)
|
||||
Append(", ");
|
||||
|
||||
Append(func.parameters[i].type);
|
||||
Append(" ");
|
||||
Append(func.parameters[i].name);
|
||||
}
|
||||
Append(")\n");
|
||||
}
|
||||
|
||||
void GlslWriter::AppendLine(const std::string& txt)
|
||||
{
|
||||
NazaraAssert(m_currentState, "This function should only be called while processing an AST");
|
||||
|
||||
m_currentState->stream << txt << '\n' << std::string(m_currentState->indentLevel, '\t');
|
||||
}
|
||||
|
||||
void GlslWriter::EnterScope()
|
||||
{
|
||||
NazaraAssert(m_currentState, "This function should only be called while processing an AST");
|
||||
|
||||
m_currentState->indentLevel++;
|
||||
AppendLine("{");
|
||||
}
|
||||
|
||||
void GlslWriter::LeaveScope()
|
||||
{
|
||||
NazaraAssert(m_currentState, "This function should only be called while processing an AST");
|
||||
|
||||
m_currentState->indentLevel--;
|
||||
AppendLine();
|
||||
AppendLine("}");
|
||||
}
|
||||
|
||||
void GlslWriter::Visit(ShaderNodes::ExpressionPtr& expr, bool encloseIfRequired)
|
||||
{
|
||||
bool enclose = encloseIfRequired && (expr->GetExpressionCategory() != ShaderNodes::ExpressionCategory::LValue);
|
||||
|
||||
if (enclose)
|
||||
Append("(");
|
||||
|
||||
ShaderAstVisitor::Visit(expr);
|
||||
|
||||
if (enclose)
|
||||
Append(")");
|
||||
}
|
||||
|
||||
void GlslWriter::Visit(ShaderNodes::AccessMember& node)
|
||||
{
|
||||
Visit(node.structExpr, true);
|
||||
|
||||
const ShaderExpressionType& exprType = node.structExpr->GetExpressionType();
|
||||
assert(std::holds_alternative<std::string>(exprType));
|
||||
|
||||
const std::string& structName = std::get<std::string>(exprType);
|
||||
|
||||
const auto& structs = m_context.shader->GetStructs();
|
||||
auto it = std::find_if(structs.begin(), structs.end(), [&](const auto& s) { return s.name == structName; });
|
||||
assert(it != structs.end());
|
||||
|
||||
const ShaderAst::Struct& s = *it;
|
||||
assert(node.memberIndex < s.members.size());
|
||||
|
||||
const auto& member = s.members[node.memberIndex];
|
||||
Append(".");
|
||||
Append(member.name);
|
||||
}
|
||||
|
||||
void GlslWriter::Visit(ShaderNodes::AssignOp& node)
|
||||
{
|
||||
Visit(node.left);
|
||||
|
||||
switch (node.op)
|
||||
{
|
||||
case ShaderNodes::AssignType::Simple:
|
||||
Append(" = ");
|
||||
break;
|
||||
}
|
||||
|
||||
Visit(node.right);
|
||||
}
|
||||
|
||||
void GlslWriter::Visit(ShaderNodes::Branch& node)
|
||||
{
|
||||
bool first = true;
|
||||
for (const auto& statement : node.condStatements)
|
||||
{
|
||||
if (!first)
|
||||
Append("else ");
|
||||
|
||||
Append("if (");
|
||||
Visit(statement.condition);
|
||||
AppendLine(")");
|
||||
|
||||
EnterScope();
|
||||
Visit(statement.statement);
|
||||
LeaveScope();
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (node.elseStatement)
|
||||
{
|
||||
AppendLine("else");
|
||||
|
||||
EnterScope();
|
||||
Visit(node.elseStatement);
|
||||
LeaveScope();
|
||||
}
|
||||
}
|
||||
|
||||
void GlslWriter::Visit(ShaderNodes::BinaryOp& node)
|
||||
{
|
||||
Visit(node.left, true);
|
||||
|
||||
switch (node.op)
|
||||
{
|
||||
case ShaderNodes::BinaryType::Add:
|
||||
Append(" + ");
|
||||
break;
|
||||
case ShaderNodes::BinaryType::Substract:
|
||||
Append(" - ");
|
||||
break;
|
||||
case ShaderNodes::BinaryType::Multiply:
|
||||
Append(" * ");
|
||||
break;
|
||||
case ShaderNodes::BinaryType::Divide:
|
||||
Append(" / ");
|
||||
break;
|
||||
case ShaderNodes::BinaryType::Equality:
|
||||
Append(" == ");
|
||||
break;
|
||||
}
|
||||
|
||||
Visit(node.right, true);
|
||||
}
|
||||
|
||||
void GlslWriter::Visit(ShaderNodes::BuiltinVariable& var)
|
||||
{
|
||||
Append(var.entry);
|
||||
}
|
||||
|
||||
void GlslWriter::Visit(ShaderNodes::Cast& node)
|
||||
{
|
||||
Append(node.exprType);
|
||||
Append("(");
|
||||
|
||||
for (std::size_t i = 0; node.expressions[i]; ++i)
|
||||
{
|
||||
if (i != 0)
|
||||
m_currentState->stream << ", ";
|
||||
|
||||
const auto& exprPtr = node.expressions[i];
|
||||
NazaraAssert(exprPtr, "Invalid expression");
|
||||
|
||||
Visit(exprPtr);
|
||||
}
|
||||
|
||||
Append(")");
|
||||
}
|
||||
|
||||
void GlslWriter::Visit(ShaderNodes::Constant& node)
|
||||
{
|
||||
std::visit([&](auto&& arg)
|
||||
{
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, Vector2i32> || std::is_same_v<T, Vector3i32> || std::is_same_v<T, Vector4i32>)
|
||||
Append("i"); //< for ivec
|
||||
|
||||
if constexpr (std::is_same_v<T, bool>)
|
||||
Append((arg) ? "true" : "false");
|
||||
else if constexpr (std::is_same_v<T, float> || std::is_same_v<T, Int32>)
|
||||
Append(std::to_string(arg));
|
||||
else if constexpr (std::is_same_v<T, Vector2f> || std::is_same_v<T, Vector2i32>)
|
||||
Append("vec2(" + std::to_string(arg.x) + ", " + std::to_string(arg.y) + ")");
|
||||
else if constexpr (std::is_same_v<T, Vector3f> || std::is_same_v<T, Vector3i32>)
|
||||
Append("vec3(" + std::to_string(arg.x) + ", " + std::to_string(arg.y) + ", " + std::to_string(arg.z) + ")");
|
||||
else if constexpr (std::is_same_v<T, Vector4f> || std::is_same_v<T, Vector4i32>)
|
||||
Append("vec4(" + std::to_string(arg.x) + ", " + std::to_string(arg.y) + ", " + std::to_string(arg.z) + ", " + std::to_string(arg.w) + ")");
|
||||
else
|
||||
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
|
||||
}, node.value);
|
||||
}
|
||||
|
||||
void GlslWriter::Visit(ShaderNodes::DeclareVariable& node)
|
||||
{
|
||||
assert(node.variable->GetType() == ShaderNodes::VariableType::LocalVariable);
|
||||
|
||||
const auto& localVar = static_cast<const ShaderNodes::LocalVariable&>(*node.variable);
|
||||
|
||||
Append(localVar.type);
|
||||
Append(" ");
|
||||
Append(localVar.name);
|
||||
if (node.expression)
|
||||
{
|
||||
Append(" = ");
|
||||
Visit(node.expression);
|
||||
}
|
||||
|
||||
AppendLine(";");
|
||||
}
|
||||
|
||||
void GlslWriter::Visit(ShaderNodes::ExpressionStatement& node)
|
||||
{
|
||||
Visit(node.expression);
|
||||
Append(";");
|
||||
}
|
||||
|
||||
void GlslWriter::Visit(ShaderNodes::Identifier& node)
|
||||
{
|
||||
Visit(node.var);
|
||||
}
|
||||
|
||||
void GlslWriter::Visit(ShaderNodes::InputVariable& var)
|
||||
{
|
||||
Append(var.name);
|
||||
}
|
||||
|
||||
void GlslWriter::Visit(ShaderNodes::IntrinsicCall& node)
|
||||
{
|
||||
switch (node.intrinsic)
|
||||
{
|
||||
case ShaderNodes::IntrinsicType::CrossProduct:
|
||||
Append("cross");
|
||||
break;
|
||||
|
||||
case ShaderNodes::IntrinsicType::DotProduct:
|
||||
Append("dot");
|
||||
break;
|
||||
}
|
||||
|
||||
Append("(");
|
||||
for (std::size_t i = 0; i < node.parameters.size(); ++i)
|
||||
{
|
||||
if (i != 0)
|
||||
Append(", ");
|
||||
|
||||
Visit(node.parameters[i]);
|
||||
}
|
||||
Append(")");
|
||||
}
|
||||
|
||||
void GlslWriter::Visit(ShaderNodes::LocalVariable& var)
|
||||
{
|
||||
Append(var.name);
|
||||
}
|
||||
|
||||
void GlslWriter::Visit(ShaderNodes::ParameterVariable& var)
|
||||
{
|
||||
Append(var.name);
|
||||
}
|
||||
|
||||
void GlslWriter::Visit(ShaderNodes::OutputVariable& var)
|
||||
{
|
||||
Append(var.name);
|
||||
}
|
||||
|
||||
void GlslWriter::Visit(ShaderNodes::Sample2D& node)
|
||||
{
|
||||
Append("texture(");
|
||||
Visit(node.sampler);
|
||||
Append(", ");
|
||||
Visit(node.coordinates);
|
||||
Append(")");
|
||||
}
|
||||
|
||||
void GlslWriter::Visit(ShaderNodes::StatementBlock& node)
|
||||
{
|
||||
bool first = true;
|
||||
for (const ShaderNodes::StatementPtr& statement : node.statements)
|
||||
{
|
||||
if (!first)
|
||||
AppendLine();
|
||||
|
||||
Visit(statement);
|
||||
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
void GlslWriter::Visit(ShaderNodes::SwizzleOp& node)
|
||||
{
|
||||
Visit(node.expression);
|
||||
Append(".");
|
||||
|
||||
for (std::size_t i = 0; i < node.componentCount; ++i)
|
||||
{
|
||||
switch (node.components[i])
|
||||
{
|
||||
case ShaderNodes::SwizzleComponent::First:
|
||||
Append("x");
|
||||
break;
|
||||
|
||||
case ShaderNodes::SwizzleComponent::Second:
|
||||
Append("y");
|
||||
break;
|
||||
|
||||
case ShaderNodes::SwizzleComponent::Third:
|
||||
Append("z");
|
||||
break;
|
||||
|
||||
case ShaderNodes::SwizzleComponent::Fourth:
|
||||
Append("w");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GlslWriter::Visit(ShaderNodes::UniformVariable& var)
|
||||
{
|
||||
Append(var.name);
|
||||
}
|
||||
|
||||
bool GlslWriter::HasExplicitBinding(const ShaderAst& shader)
|
||||
{
|
||||
for (const auto& uniform : shader.GetUniforms())
|
||||
{
|
||||
if (uniform.bindingIndex.has_value())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GlslWriter::HasExplicitLocation(const ShaderAst& shader)
|
||||
{
|
||||
for (const auto& input : shader.GetInputs())
|
||||
{
|
||||
if (input.locationIndex.has_value())
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto& output : shader.GetOutputs())
|
||||
{
|
||||
if (output.locationIndex.has_value())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
// Copyright (C) 2020 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Renderer module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Renderer/ShaderAst.hpp>
|
||||
#include <Nazara/Renderer/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
void ShaderAst::AddFunction(std::string name, ShaderNodes::StatementPtr statement, std::vector<FunctionParameter> parameters, ShaderNodes::BasicType returnType)
|
||||
{
|
||||
auto& functionEntry = m_functions.emplace_back();
|
||||
functionEntry.name = std::move(name);
|
||||
functionEntry.parameters = std::move(parameters);
|
||||
functionEntry.returnType = returnType;
|
||||
functionEntry.statement = std::move(statement);
|
||||
}
|
||||
|
||||
void ShaderAst::AddInput(std::string name, ShaderExpressionType type, std::optional<std::size_t> locationIndex)
|
||||
{
|
||||
auto& inputEntry = m_inputs.emplace_back();
|
||||
inputEntry.name = std::move(name);
|
||||
inputEntry.locationIndex = std::move(locationIndex);
|
||||
inputEntry.type = std::move(type);
|
||||
}
|
||||
|
||||
void ShaderAst::AddOutput(std::string name, ShaderExpressionType type, std::optional<std::size_t> locationIndex)
|
||||
{
|
||||
auto& outputEntry = m_outputs.emplace_back();
|
||||
outputEntry.name = std::move(name);
|
||||
outputEntry.locationIndex = std::move(locationIndex);
|
||||
outputEntry.type = std::move(type);
|
||||
}
|
||||
|
||||
void ShaderAst::AddStruct(std::string name, std::vector<StructMember> members)
|
||||
{
|
||||
auto& structEntry = m_structs.emplace_back();
|
||||
structEntry.name = std::move(name);
|
||||
structEntry.members = std::move(members);
|
||||
}
|
||||
|
||||
void ShaderAst::AddUniform(std::string name, ShaderExpressionType type, std::optional<std::size_t> bindingIndex, std::optional<ShaderNodes::MemoryLayout> memoryLayout)
|
||||
{
|
||||
auto& uniformEntry = m_uniforms.emplace_back();
|
||||
uniformEntry.bindingIndex = std::move(bindingIndex);
|
||||
uniformEntry.memoryLayout = std::move(memoryLayout);
|
||||
uniformEntry.name = std::move(name);
|
||||
uniformEntry.type = std::move(type);
|
||||
}
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Renderer module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Renderer/ShaderAstCloner.hpp>
|
||||
#include <stdexcept>
|
||||
#include <Nazara/Renderer/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
ShaderNodes::StatementPtr ShaderAstCloner::Clone(const ShaderNodes::StatementPtr& statement)
|
||||
{
|
||||
ShaderAstVisitor::Visit(statement);
|
||||
|
||||
if (!m_expressionStack.empty() || !m_variableStack.empty() || m_statementStack.size() != 1)
|
||||
throw std::runtime_error("An error occurred during clone");
|
||||
|
||||
return PopStatement();
|
||||
}
|
||||
|
||||
ShaderNodes::ExpressionPtr ShaderAstCloner::CloneExpression(const ShaderNodes::ExpressionPtr& expr)
|
||||
{
|
||||
if (!expr)
|
||||
return nullptr;
|
||||
|
||||
ShaderAstVisitor::Visit(expr);
|
||||
return PopExpression();
|
||||
}
|
||||
|
||||
ShaderNodes::StatementPtr ShaderAstCloner::CloneStatement(const ShaderNodes::StatementPtr& statement)
|
||||
{
|
||||
if (!statement)
|
||||
return nullptr;
|
||||
|
||||
ShaderAstVisitor::Visit(statement);
|
||||
return PopStatement();
|
||||
}
|
||||
|
||||
ShaderNodes::VariablePtr ShaderAstCloner::CloneVariable(const ShaderNodes::VariablePtr& variable)
|
||||
{
|
||||
if (!variable)
|
||||
return nullptr;
|
||||
|
||||
ShaderVarVisitor::Visit(variable);
|
||||
return PopVariable();
|
||||
}
|
||||
|
||||
void ShaderAstCloner::Visit(ShaderNodes::AccessMember& node)
|
||||
{
|
||||
PushExpression(ShaderNodes::AccessMember::Build(CloneExpression(node.structExpr), node.memberIndex, node.exprType));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::Visit(ShaderNodes::AssignOp& node)
|
||||
{
|
||||
PushExpression(ShaderNodes::AssignOp::Build(node.op, CloneExpression(node.left), CloneExpression(node.right)));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::Visit(ShaderNodes::BinaryOp& node)
|
||||
{
|
||||
PushExpression(ShaderNodes::BinaryOp::Build(node.op, CloneExpression(node.left), CloneExpression(node.right)));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::Visit(ShaderNodes::Branch& node)
|
||||
{
|
||||
std::vector<ShaderNodes::Branch::ConditionalStatement> condStatements;
|
||||
condStatements.reserve(node.condStatements.size());
|
||||
|
||||
for (auto& cond : node.condStatements)
|
||||
{
|
||||
auto& condStatement = condStatements.emplace_back();
|
||||
condStatement.condition = CloneExpression(cond.condition);
|
||||
condStatement.statement = CloneStatement(cond.statement);
|
||||
}
|
||||
|
||||
PushStatement(ShaderNodes::Branch::Build(std::move(condStatements), CloneStatement(node.elseStatement)));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::Visit(ShaderNodes::Cast& node)
|
||||
{
|
||||
std::size_t expressionCount = 0;
|
||||
std::array<ShaderNodes::ExpressionPtr, 4> expressions;
|
||||
for (auto& expr : node.expressions)
|
||||
{
|
||||
if (!expr)
|
||||
break;
|
||||
|
||||
expressions[expressionCount] = CloneExpression(expr);
|
||||
expressionCount++;
|
||||
}
|
||||
|
||||
PushExpression(ShaderNodes::Cast::Build(node.exprType, expressions.data(), expressionCount));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::Visit(ShaderNodes::Constant& node)
|
||||
{
|
||||
PushExpression(ShaderNodes::Constant::Build(node.value));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::Visit(ShaderNodes::DeclareVariable& node)
|
||||
{
|
||||
PushStatement(ShaderNodes::DeclareVariable::Build(CloneVariable(node.variable), CloneExpression(node.expression)));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::Visit(ShaderNodes::ExpressionStatement& node)
|
||||
{
|
||||
PushStatement(ShaderNodes::ExpressionStatement::Build(CloneExpression(node.expression)));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::Visit(ShaderNodes::Identifier& node)
|
||||
{
|
||||
PushExpression(ShaderNodes::Identifier::Build(CloneVariable(node.var)));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::Visit(ShaderNodes::IntrinsicCall& node)
|
||||
{
|
||||
std::vector<ShaderNodes::ExpressionPtr> parameters;
|
||||
parameters.reserve(node.parameters.size());
|
||||
|
||||
for (auto& parameter : node.parameters)
|
||||
parameters.push_back(CloneExpression(parameter));
|
||||
|
||||
PushExpression(ShaderNodes::IntrinsicCall::Build(node.intrinsic, std::move(parameters)));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::Visit(ShaderNodes::Sample2D& node)
|
||||
{
|
||||
PushExpression(ShaderNodes::Sample2D::Build(CloneExpression(node.sampler), CloneExpression(node.coordinates)));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::Visit(ShaderNodes::StatementBlock& node)
|
||||
{
|
||||
std::vector<ShaderNodes::StatementPtr> statements;
|
||||
statements.reserve(node.statements.size());
|
||||
|
||||
for (auto& statement : node.statements)
|
||||
statements.push_back(CloneStatement(statement));
|
||||
|
||||
PushStatement(ShaderNodes::StatementBlock::Build(std::move(statements)));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::Visit(ShaderNodes::SwizzleOp& node)
|
||||
{
|
||||
PushExpression(ShaderNodes::SwizzleOp::Build(PopExpression(), node.components.data(), node.componentCount));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::Visit(ShaderNodes::BuiltinVariable& var)
|
||||
{
|
||||
PushVariable(ShaderNodes::BuiltinVariable::Build(var.entry, var.type));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::Visit(ShaderNodes::InputVariable& var)
|
||||
{
|
||||
PushVariable(ShaderNodes::InputVariable::Build(var.name, var.type));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::Visit(ShaderNodes::LocalVariable& var)
|
||||
{
|
||||
PushVariable(ShaderNodes::LocalVariable::Build(var.name, var.type));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::Visit(ShaderNodes::OutputVariable& var)
|
||||
{
|
||||
PushVariable(ShaderNodes::OutputVariable::Build(var.name, var.type));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::Visit(ShaderNodes::ParameterVariable& var)
|
||||
{
|
||||
PushVariable(ShaderNodes::ParameterVariable::Build(var.name, var.type));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::Visit(ShaderNodes::UniformVariable& var)
|
||||
{
|
||||
PushVariable(ShaderNodes::UniformVariable::Build(var.name, var.type));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::PushExpression(ShaderNodes::ExpressionPtr expression)
|
||||
{
|
||||
m_expressionStack.emplace_back(std::move(expression));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::PushStatement(ShaderNodes::StatementPtr statement)
|
||||
{
|
||||
m_statementStack.emplace_back(std::move(statement));
|
||||
}
|
||||
|
||||
void ShaderAstCloner::PushVariable(ShaderNodes::VariablePtr variable)
|
||||
{
|
||||
m_variableStack.emplace_back(std::move(variable));
|
||||
}
|
||||
|
||||
ShaderNodes::ExpressionPtr ShaderAstCloner::PopExpression()
|
||||
{
|
||||
assert(!m_expressionStack.empty());
|
||||
|
||||
ShaderNodes::ExpressionPtr expr = std::move(m_expressionStack.back());
|
||||
m_expressionStack.pop_back();
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
ShaderNodes::StatementPtr ShaderAstCloner::PopStatement()
|
||||
{
|
||||
assert(!m_statementStack.empty());
|
||||
|
||||
ShaderNodes::StatementPtr expr = std::move(m_statementStack.back());
|
||||
m_statementStack.pop_back();
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
ShaderNodes::VariablePtr ShaderAstCloner::PopVariable()
|
||||
{
|
||||
assert(!m_variableStack.empty());
|
||||
|
||||
ShaderNodes::VariablePtr var = std::move(m_variableStack.back());
|
||||
m_variableStack.pop_back();
|
||||
|
||||
return var;
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Renderer module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Renderer/ShaderAstRecursiveVisitor.hpp>
|
||||
#include <Nazara/Renderer/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
void ShaderAstRecursiveVisitor::Visit(ShaderNodes::AccessMember& node)
|
||||
{
|
||||
Visit(node.structExpr);
|
||||
}
|
||||
|
||||
void ShaderAstRecursiveVisitor::Visit(ShaderNodes::AssignOp& node)
|
||||
{
|
||||
Visit(node.left);
|
||||
Visit(node.right);
|
||||
}
|
||||
|
||||
void ShaderAstRecursiveVisitor::Visit(ShaderNodes::BinaryOp& node)
|
||||
{
|
||||
Visit(node.left);
|
||||
Visit(node.right);
|
||||
}
|
||||
|
||||
void ShaderAstRecursiveVisitor::Visit(ShaderNodes::Branch& node)
|
||||
{
|
||||
for (auto& cond : node.condStatements)
|
||||
{
|
||||
Visit(cond.condition);
|
||||
Visit(cond.statement);
|
||||
}
|
||||
|
||||
if (node.elseStatement)
|
||||
Visit(node.elseStatement);
|
||||
}
|
||||
|
||||
void ShaderAstRecursiveVisitor::Visit(ShaderNodes::Cast& node)
|
||||
{
|
||||
for (auto& expr : node.expressions)
|
||||
{
|
||||
if (!expr)
|
||||
break;
|
||||
|
||||
Visit(expr);
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderAstRecursiveVisitor::Visit(ShaderNodes::Constant& /*node*/)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
void ShaderAstRecursiveVisitor::Visit(ShaderNodes::DeclareVariable& node)
|
||||
{
|
||||
if (node.expression)
|
||||
Visit(node.expression);
|
||||
}
|
||||
|
||||
void ShaderAstRecursiveVisitor::Visit(ShaderNodes::ExpressionStatement& node)
|
||||
{
|
||||
Visit(node.expression);
|
||||
}
|
||||
|
||||
void ShaderAstRecursiveVisitor::Visit(ShaderNodes::Identifier& /*node*/)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
void ShaderAstRecursiveVisitor::Visit(ShaderNodes::IntrinsicCall& node)
|
||||
{
|
||||
for (auto& param : node.parameters)
|
||||
Visit(param);
|
||||
}
|
||||
|
||||
void ShaderAstRecursiveVisitor::Visit(ShaderNodes::Sample2D& node)
|
||||
{
|
||||
Visit(node.sampler);
|
||||
Visit(node.coordinates);
|
||||
}
|
||||
|
||||
void ShaderAstRecursiveVisitor::Visit(ShaderNodes::StatementBlock& node)
|
||||
{
|
||||
for (auto& statement : node.statements)
|
||||
Visit(statement);
|
||||
}
|
||||
|
||||
void ShaderAstRecursiveVisitor::Visit(ShaderNodes::SwizzleOp& node)
|
||||
{
|
||||
Visit(node.expression);
|
||||
}
|
||||
}
|
||||
@@ -1,767 +0,0 @@
|
||||
// Copyright (C) 2020 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Renderer module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Renderer/ShaderAstSerializer.hpp>
|
||||
#include <Nazara/Renderer/ShaderVarVisitor.hpp>
|
||||
#include <Nazara/Renderer/ShaderAstVisitor.hpp>
|
||||
#include <Nazara/Renderer/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr UInt32 s_magicNumber = 0x4E534852;
|
||||
constexpr UInt32 s_currentVersion = 1;
|
||||
|
||||
class ShaderSerializerVisitor : public ShaderAstVisitor, public ShaderVarVisitor
|
||||
{
|
||||
public:
|
||||
ShaderSerializerVisitor(ShaderAstSerializerBase& serializer) :
|
||||
m_serializer(serializer)
|
||||
{
|
||||
}
|
||||
|
||||
void Visit(ShaderNodes::AccessMember& node) override
|
||||
{
|
||||
Serialize(node);
|
||||
}
|
||||
|
||||
void Visit(ShaderNodes::AssignOp& node) override
|
||||
{
|
||||
Serialize(node);
|
||||
}
|
||||
|
||||
void Visit(ShaderNodes::BinaryOp& node) override
|
||||
{
|
||||
Serialize(node);
|
||||
}
|
||||
|
||||
void Visit(ShaderNodes::Branch& node) override
|
||||
{
|
||||
Serialize(node);
|
||||
}
|
||||
|
||||
void Visit(ShaderNodes::Cast& node) override
|
||||
{
|
||||
Serialize(node);
|
||||
}
|
||||
|
||||
void Visit(ShaderNodes::Constant& node) override
|
||||
{
|
||||
Serialize(node);
|
||||
}
|
||||
|
||||
void Visit(ShaderNodes::DeclareVariable& node) override
|
||||
{
|
||||
Serialize(node);
|
||||
}
|
||||
|
||||
void Visit(ShaderNodes::ExpressionStatement& node) override
|
||||
{
|
||||
Serialize(node);
|
||||
}
|
||||
|
||||
void Visit(ShaderNodes::Identifier& node) override
|
||||
{
|
||||
Serialize(node);
|
||||
}
|
||||
|
||||
void Visit(ShaderNodes::IntrinsicCall& node) override
|
||||
{
|
||||
Serialize(node);
|
||||
}
|
||||
|
||||
void Visit(ShaderNodes::Sample2D& node) override
|
||||
{
|
||||
Serialize(node);
|
||||
}
|
||||
|
||||
void Visit(ShaderNodes::StatementBlock& node) override
|
||||
{
|
||||
Serialize(node);
|
||||
}
|
||||
|
||||
void Visit(ShaderNodes::SwizzleOp& node) override
|
||||
{
|
||||
Serialize(node);
|
||||
}
|
||||
|
||||
|
||||
void Visit(ShaderNodes::BuiltinVariable& var) override
|
||||
{
|
||||
Serialize(var);
|
||||
}
|
||||
|
||||
void Visit(ShaderNodes::InputVariable& var) override
|
||||
{
|
||||
Serialize(var);
|
||||
}
|
||||
|
||||
void Visit(ShaderNodes::LocalVariable& var) override
|
||||
{
|
||||
Serialize(var);
|
||||
}
|
||||
|
||||
void Visit(ShaderNodes::OutputVariable& var) override
|
||||
{
|
||||
Serialize(var);
|
||||
}
|
||||
|
||||
void Visit(ShaderNodes::ParameterVariable& var) override
|
||||
{
|
||||
Serialize(var);
|
||||
}
|
||||
|
||||
void Visit(ShaderNodes::UniformVariable& var) override
|
||||
{
|
||||
Serialize(var);
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
void Serialize(const T& node)
|
||||
{
|
||||
// I know const_cast is evil but I don't have a better solution here (it's not used to write)
|
||||
m_serializer.Serialize(const_cast<T&>(node));
|
||||
}
|
||||
|
||||
ShaderAstSerializerBase& m_serializer;
|
||||
};
|
||||
}
|
||||
|
||||
void ShaderAstSerializerBase::Serialize(ShaderNodes::AccessMember& node)
|
||||
{
|
||||
Value(node.memberIndex);
|
||||
Node(node.structExpr);
|
||||
Type(node.exprType);
|
||||
}
|
||||
|
||||
void ShaderAstSerializerBase::Serialize(ShaderNodes::AssignOp& node)
|
||||
{
|
||||
Enum(node.op);
|
||||
Node(node.left);
|
||||
Node(node.right);
|
||||
}
|
||||
|
||||
void ShaderAstSerializerBase::Serialize(ShaderNodes::BinaryOp& node)
|
||||
{
|
||||
Enum(node.op);
|
||||
Node(node.left);
|
||||
Node(node.right);
|
||||
}
|
||||
|
||||
void ShaderAstSerializerBase::Serialize(ShaderNodes::Branch& node)
|
||||
{
|
||||
Container(node.condStatements);
|
||||
for (auto& condStatement : node.condStatements)
|
||||
{
|
||||
Node(condStatement.condition);
|
||||
Node(condStatement.statement);
|
||||
}
|
||||
|
||||
Node(node.elseStatement);
|
||||
}
|
||||
|
||||
void ShaderAstSerializerBase::Serialize(ShaderNodes::BuiltinVariable& node)
|
||||
{
|
||||
Enum(node.entry);
|
||||
Type(node.type);
|
||||
}
|
||||
|
||||
void ShaderAstSerializerBase::Serialize(ShaderNodes::Cast& node)
|
||||
{
|
||||
Enum(node.exprType);
|
||||
for (auto& expr : node.expressions)
|
||||
Node(expr);
|
||||
}
|
||||
|
||||
void ShaderAstSerializerBase::Serialize(ShaderNodes::Constant& node)
|
||||
{
|
||||
UInt32 typeIndex;
|
||||
if (IsWriting())
|
||||
typeIndex = UInt32(node.value.index());
|
||||
|
||||
Value(typeIndex);
|
||||
|
||||
// Waiting for template lambda in C++20
|
||||
auto SerializeValue = [&](auto dummyType)
|
||||
{
|
||||
using T = std::decay_t<decltype(dummyType)>;
|
||||
|
||||
auto& value = (IsWriting()) ? std::get<T>(node.value) : node.value.emplace<T>();
|
||||
Value(value);
|
||||
};
|
||||
|
||||
static_assert(std::variant_size_v<decltype(node.value)> == 9);
|
||||
switch (typeIndex)
|
||||
{
|
||||
case 0: SerializeValue(bool()); break;
|
||||
case 1: SerializeValue(float()); break;
|
||||
case 2: SerializeValue(Int32()); break;
|
||||
case 3: SerializeValue(Vector2f()); break;
|
||||
case 4: SerializeValue(Vector3f()); break;
|
||||
case 5: SerializeValue(Vector4f()); break;
|
||||
case 6: SerializeValue(Vector2i32()); break;
|
||||
case 7: SerializeValue(Vector3i32()); break;
|
||||
case 8: SerializeValue(Vector4i32()); break;
|
||||
default: throw std::runtime_error("unexpected data type");
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderAstSerializerBase::Serialize(ShaderNodes::DeclareVariable& node)
|
||||
{
|
||||
Variable(node.variable);
|
||||
Node(node.expression);
|
||||
}
|
||||
|
||||
void ShaderAstSerializerBase::Serialize(ShaderNodes::ExpressionStatement& node)
|
||||
{
|
||||
Node(node.expression);
|
||||
}
|
||||
|
||||
void ShaderAstSerializerBase::Serialize(ShaderNodes::Identifier& node)
|
||||
{
|
||||
Variable(node.var);
|
||||
}
|
||||
|
||||
void ShaderAstSerializerBase::Serialize(ShaderNodes::IntrinsicCall& node)
|
||||
{
|
||||
Enum(node.intrinsic);
|
||||
Container(node.parameters);
|
||||
for (auto& param : node.parameters)
|
||||
Node(param);
|
||||
}
|
||||
|
||||
void ShaderAstSerializerBase::Serialize(ShaderNodes::NamedVariable& node)
|
||||
{
|
||||
Value(node.name);
|
||||
Type(node.type);
|
||||
}
|
||||
|
||||
void ShaderAstSerializerBase::Serialize(ShaderNodes::Sample2D& node)
|
||||
{
|
||||
Node(node.sampler);
|
||||
Node(node.coordinates);
|
||||
}
|
||||
|
||||
void ShaderAstSerializerBase::Serialize(ShaderNodes::StatementBlock& node)
|
||||
{
|
||||
Container(node.statements);
|
||||
for (auto& statement : node.statements)
|
||||
Node(statement);
|
||||
}
|
||||
|
||||
void ShaderAstSerializerBase::Serialize(ShaderNodes::SwizzleOp& node)
|
||||
{
|
||||
Value(node.componentCount);
|
||||
Node(node.expression);
|
||||
|
||||
for (std::size_t i = 0; i < node.componentCount; ++i)
|
||||
Enum(node.components[i]);
|
||||
}
|
||||
|
||||
|
||||
void ShaderAstSerializer::Serialize(const ShaderAst& shader)
|
||||
{
|
||||
m_stream << s_magicNumber << s_currentVersion;
|
||||
|
||||
m_stream << UInt32(shader.GetStage());
|
||||
|
||||
auto SerializeType = [&](const ShaderExpressionType& type)
|
||||
{
|
||||
std::visit([&](auto&& arg)
|
||||
{
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, ShaderNodes::BasicType>)
|
||||
{
|
||||
m_stream << UInt8(0);
|
||||
m_stream << UInt32(arg);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, std::string>)
|
||||
{
|
||||
m_stream << UInt8(1);
|
||||
m_stream << arg;
|
||||
}
|
||||
else
|
||||
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
|
||||
}, type);
|
||||
};
|
||||
|
||||
auto SerializeInputOutput = [&](auto& inout)
|
||||
{
|
||||
m_stream << UInt32(inout.size());
|
||||
for (const auto& data : inout)
|
||||
{
|
||||
m_stream << data.name;
|
||||
SerializeType(data.type);
|
||||
|
||||
m_stream << data.locationIndex.has_value();
|
||||
if (data.locationIndex)
|
||||
m_stream << UInt32(data.locationIndex.value());
|
||||
}
|
||||
};
|
||||
|
||||
m_stream << UInt32(shader.GetStructCount());
|
||||
for (const auto& s : shader.GetStructs())
|
||||
{
|
||||
m_stream << s.name;
|
||||
m_stream << UInt32(s.members.size());
|
||||
for (const auto& member : s.members)
|
||||
{
|
||||
m_stream << member.name;
|
||||
SerializeType(member.type);
|
||||
}
|
||||
}
|
||||
|
||||
SerializeInputOutput(shader.GetInputs());
|
||||
SerializeInputOutput(shader.GetOutputs());
|
||||
|
||||
m_stream << UInt32(shader.GetUniformCount());
|
||||
for (const auto& uniform : shader.GetUniforms())
|
||||
{
|
||||
m_stream << uniform.name;
|
||||
SerializeType(uniform.type);
|
||||
|
||||
m_stream << uniform.bindingIndex.has_value();
|
||||
if (uniform.bindingIndex)
|
||||
m_stream << UInt32(uniform.bindingIndex.value());
|
||||
|
||||
m_stream << uniform.memoryLayout.has_value();
|
||||
if (uniform.memoryLayout)
|
||||
m_stream << UInt32(uniform.memoryLayout.value());
|
||||
}
|
||||
|
||||
m_stream << UInt32(shader.GetFunctionCount());
|
||||
for (const auto& func : shader.GetFunctions())
|
||||
{
|
||||
m_stream << func.name << UInt32(func.returnType);
|
||||
|
||||
m_stream << UInt32(func.parameters.size());
|
||||
for (const auto& param : func.parameters)
|
||||
{
|
||||
m_stream << param.name;
|
||||
SerializeType(param.type);
|
||||
}
|
||||
|
||||
Node(func.statement);
|
||||
}
|
||||
|
||||
m_stream.FlushBits();
|
||||
}
|
||||
|
||||
bool ShaderAstSerializer::IsWriting() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShaderAstSerializer::Node(ShaderNodes::NodePtr& node)
|
||||
{
|
||||
ShaderNodes::NodeType nodeType = (node) ? node->GetType() : ShaderNodes::NodeType::None;
|
||||
m_stream << static_cast<Int32>(nodeType);
|
||||
|
||||
if (node)
|
||||
{
|
||||
ShaderSerializerVisitor visitor(*this);
|
||||
node->Visit(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderAstSerializer::Type(ShaderExpressionType& type)
|
||||
{
|
||||
std::visit([&](auto&& arg)
|
||||
{
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, ShaderNodes::BasicType>)
|
||||
{
|
||||
m_stream << UInt8(0);
|
||||
m_stream << UInt32(arg);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, std::string>)
|
||||
{
|
||||
m_stream << UInt8(1);
|
||||
m_stream << arg;
|
||||
}
|
||||
else
|
||||
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
|
||||
}, type);
|
||||
}
|
||||
|
||||
void ShaderAstSerializer::Node(const ShaderNodes::NodePtr& node)
|
||||
{
|
||||
Node(const_cast<ShaderNodes::NodePtr&>(node)); //< Yes const_cast is ugly but it won't be used for writing
|
||||
}
|
||||
|
||||
void ShaderAstSerializer::Value(bool& val)
|
||||
{
|
||||
m_stream << val;
|
||||
}
|
||||
|
||||
void ShaderAstSerializer::Value(float& val)
|
||||
{
|
||||
m_stream << val;
|
||||
}
|
||||
|
||||
void ShaderAstSerializer::Value(std::string& val)
|
||||
{
|
||||
m_stream << val;
|
||||
}
|
||||
|
||||
void ShaderAstSerializer::Value(Int32& val)
|
||||
{
|
||||
m_stream << val;
|
||||
}
|
||||
|
||||
void ShaderAstSerializer::Value(Vector2f& val)
|
||||
{
|
||||
m_stream << val;
|
||||
}
|
||||
|
||||
void ShaderAstSerializer::Value(Vector3f& val)
|
||||
{
|
||||
m_stream << val;
|
||||
}
|
||||
|
||||
void ShaderAstSerializer::Value(Vector4f& val)
|
||||
{
|
||||
m_stream << val;
|
||||
}
|
||||
|
||||
void ShaderAstSerializer::Value(Vector2i32& val)
|
||||
{
|
||||
m_stream << val;
|
||||
}
|
||||
|
||||
void ShaderAstSerializer::Value(Vector3i32& val)
|
||||
{
|
||||
m_stream << val;
|
||||
}
|
||||
|
||||
void ShaderAstSerializer::Value(Vector4i32& val)
|
||||
{
|
||||
m_stream << val;
|
||||
}
|
||||
|
||||
void ShaderAstSerializer::Value(UInt8& val)
|
||||
{
|
||||
m_stream << val;
|
||||
}
|
||||
|
||||
void ShaderAstSerializer::Value(UInt16& val)
|
||||
{
|
||||
m_stream << val;
|
||||
}
|
||||
|
||||
void ShaderAstSerializer::Value(UInt32& val)
|
||||
{
|
||||
m_stream << val;
|
||||
}
|
||||
|
||||
void ShaderAstSerializer::Variable(ShaderNodes::VariablePtr& var)
|
||||
{
|
||||
ShaderNodes::VariableType nodeType = (var) ? var->GetType() : ShaderNodes::VariableType::None;
|
||||
m_stream << static_cast<Int32>(nodeType);
|
||||
|
||||
if (var)
|
||||
{
|
||||
ShaderSerializerVisitor visitor(*this);
|
||||
var->Visit(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
ShaderAst ShaderAstUnserializer::Unserialize()
|
||||
{
|
||||
UInt32 magicNumber;
|
||||
UInt32 version;
|
||||
m_stream >> magicNumber;
|
||||
if (magicNumber != s_magicNumber)
|
||||
throw std::runtime_error("invalid shader file");
|
||||
|
||||
m_stream >> version;
|
||||
if (version > s_currentVersion)
|
||||
throw std::runtime_error("unsupported version");
|
||||
|
||||
UInt32 shaderStage;
|
||||
m_stream >> shaderStage;
|
||||
|
||||
ShaderAst shader(static_cast<ShaderStageType>(shaderStage));
|
||||
|
||||
UInt32 structCount;
|
||||
m_stream >> structCount;
|
||||
for (UInt32 i = 0; i < structCount; ++i)
|
||||
{
|
||||
std::string structName;
|
||||
std::vector<ShaderAst::StructMember> members;
|
||||
|
||||
Value(structName);
|
||||
Container(members);
|
||||
|
||||
for (auto& member : members)
|
||||
{
|
||||
Value(member.name);
|
||||
Type(member.type);
|
||||
}
|
||||
|
||||
shader.AddStruct(std::move(structName), std::move(members));
|
||||
}
|
||||
|
||||
UInt32 inputCount;
|
||||
m_stream >> inputCount;
|
||||
for (UInt32 i = 0; i < inputCount; ++i)
|
||||
{
|
||||
std::string inputName;
|
||||
ShaderExpressionType inputType;
|
||||
std::optional<std::size_t> location;
|
||||
|
||||
Value(inputName);
|
||||
Type(inputType);
|
||||
OptVal(location);
|
||||
|
||||
shader.AddInput(std::move(inputName), std::move(inputType), location);
|
||||
}
|
||||
|
||||
UInt32 outputCount;
|
||||
m_stream >> outputCount;
|
||||
for (UInt32 i = 0; i < outputCount; ++i)
|
||||
{
|
||||
std::string outputName;
|
||||
ShaderExpressionType outputType;
|
||||
std::optional<std::size_t> location;
|
||||
|
||||
Value(outputName);
|
||||
Type(outputType);
|
||||
OptVal(location);
|
||||
|
||||
shader.AddOutput(std::move(outputName), std::move(outputType), location);
|
||||
}
|
||||
|
||||
UInt32 uniformCount;
|
||||
m_stream >> uniformCount;
|
||||
for (UInt32 i = 0; i < uniformCount; ++i)
|
||||
{
|
||||
std::string name;
|
||||
ShaderExpressionType type;
|
||||
std::optional<std::size_t> binding;
|
||||
std::optional<ShaderNodes::MemoryLayout> memLayout;
|
||||
|
||||
Value(name);
|
||||
Type(type);
|
||||
OptVal(binding);
|
||||
OptEnum(memLayout);
|
||||
|
||||
shader.AddUniform(std::move(name), std::move(type), std::move(binding), std::move(memLayout));
|
||||
}
|
||||
|
||||
UInt32 funcCount;
|
||||
m_stream >> funcCount;
|
||||
for (UInt32 i = 0; i < funcCount; ++i)
|
||||
{
|
||||
std::string name;
|
||||
ShaderNodes::BasicType retType;
|
||||
std::vector<ShaderAst::FunctionParameter> parameters;
|
||||
|
||||
Value(name);
|
||||
Enum(retType);
|
||||
Container(parameters);
|
||||
for (auto& param : parameters)
|
||||
{
|
||||
Value(param.name);
|
||||
Type(param.type);
|
||||
}
|
||||
|
||||
ShaderNodes::NodePtr node;
|
||||
Node(node);
|
||||
if (!node || !node->IsStatement())
|
||||
throw std::runtime_error("functions can only have statements");
|
||||
|
||||
ShaderNodes::StatementPtr statement = std::static_pointer_cast<ShaderNodes::Statement>(node);
|
||||
|
||||
shader.AddFunction(std::move(name), std::move(statement), std::move(parameters), retType);
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
bool ShaderAstUnserializer::IsWriting() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShaderAstUnserializer::Node(ShaderNodes::NodePtr& node)
|
||||
{
|
||||
Int32 nodeTypeInt;
|
||||
m_stream >> nodeTypeInt;
|
||||
|
||||
ShaderNodes::NodeType nodeType = static_cast<ShaderNodes::NodeType>(nodeTypeInt);
|
||||
|
||||
#define HandleType(Type) case ShaderNodes::NodeType:: Type : node = std::make_shared<ShaderNodes:: Type>(); break
|
||||
switch (nodeType)
|
||||
{
|
||||
case ShaderNodes::NodeType::None: break;
|
||||
|
||||
HandleType(AccessMember);
|
||||
HandleType(AssignOp);
|
||||
HandleType(BinaryOp);
|
||||
HandleType(Branch);
|
||||
HandleType(Cast);
|
||||
HandleType(Constant);
|
||||
HandleType(ConditionalStatement);
|
||||
HandleType(DeclareVariable);
|
||||
HandleType(ExpressionStatement);
|
||||
HandleType(Identifier);
|
||||
HandleType(IntrinsicCall);
|
||||
HandleType(Sample2D);
|
||||
HandleType(SwizzleOp);
|
||||
HandleType(StatementBlock);
|
||||
}
|
||||
#undef HandleType
|
||||
|
||||
if (node)
|
||||
{
|
||||
ShaderSerializerVisitor visitor(*this);
|
||||
node->Visit(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderAstUnserializer::Type(ShaderExpressionType& type)
|
||||
{
|
||||
UInt8 typeIndex;
|
||||
Value(typeIndex);
|
||||
|
||||
switch (typeIndex)
|
||||
{
|
||||
case 0: //< Primitive
|
||||
{
|
||||
ShaderNodes::BasicType exprType;
|
||||
Enum(exprType);
|
||||
|
||||
type = exprType;
|
||||
break;
|
||||
}
|
||||
|
||||
case 1: //< Struct (name)
|
||||
{
|
||||
std::string structName;
|
||||
Value(structName);
|
||||
|
||||
type = std::move(structName);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderAstUnserializer::Value(bool& val)
|
||||
{
|
||||
m_stream >> val;
|
||||
}
|
||||
|
||||
void ShaderAstUnserializer::Value(float& val)
|
||||
{
|
||||
m_stream >> val;
|
||||
}
|
||||
|
||||
void ShaderAstUnserializer::Value(std::string& val)
|
||||
{
|
||||
m_stream >> val;
|
||||
}
|
||||
|
||||
void ShaderAstUnserializer::Value(Int32& val)
|
||||
{
|
||||
m_stream >> val;
|
||||
}
|
||||
|
||||
void ShaderAstUnserializer::Value(Vector2f& val)
|
||||
{
|
||||
m_stream >> val;
|
||||
}
|
||||
|
||||
void ShaderAstUnserializer::Value(Vector3f& val)
|
||||
{
|
||||
m_stream >> val;
|
||||
}
|
||||
|
||||
void ShaderAstUnserializer::Value(Vector4f& val)
|
||||
{
|
||||
m_stream >> val;
|
||||
}
|
||||
|
||||
void ShaderAstUnserializer::Value(Vector2i32& val)
|
||||
{
|
||||
m_stream >> val;
|
||||
}
|
||||
|
||||
void ShaderAstUnserializer::Value(Vector3i32& val)
|
||||
{
|
||||
m_stream >> val;
|
||||
}
|
||||
|
||||
void ShaderAstUnserializer::Value(Vector4i32& val)
|
||||
{
|
||||
m_stream >> val;
|
||||
}
|
||||
|
||||
void ShaderAstUnserializer::Value(UInt8& val)
|
||||
{
|
||||
m_stream >> val;
|
||||
}
|
||||
|
||||
void ShaderAstUnserializer::Value(UInt16& val)
|
||||
{
|
||||
m_stream >> val;
|
||||
}
|
||||
|
||||
void ShaderAstUnserializer::Value(UInt32& val)
|
||||
{
|
||||
m_stream >> val;
|
||||
}
|
||||
|
||||
void ShaderAstUnserializer::Variable(ShaderNodes::VariablePtr& var)
|
||||
{
|
||||
Int32 nodeTypeInt;
|
||||
m_stream >> nodeTypeInt;
|
||||
|
||||
ShaderNodes::VariableType nodeType = static_cast<ShaderNodes:: VariableType>(nodeTypeInt);
|
||||
|
||||
#define HandleType(Type) case ShaderNodes::VariableType:: Type : var = std::make_shared<ShaderNodes::Type>(); break
|
||||
switch (nodeType)
|
||||
{
|
||||
case ShaderNodes::VariableType::None: break;
|
||||
|
||||
HandleType(BuiltinVariable);
|
||||
HandleType(InputVariable);
|
||||
HandleType(LocalVariable);
|
||||
HandleType(ParameterVariable);
|
||||
HandleType(OutputVariable);
|
||||
HandleType(UniformVariable);
|
||||
}
|
||||
#undef HandleType
|
||||
|
||||
if (var)
|
||||
{
|
||||
ShaderSerializerVisitor visitor(*this);
|
||||
var->Visit(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ByteArray SerializeShader(const ShaderAst& shader)
|
||||
{
|
||||
ByteArray byteArray;
|
||||
ByteStream stream(&byteArray, OpenModeFlags(OpenMode_WriteOnly));
|
||||
|
||||
ShaderAstSerializer serializer(stream);
|
||||
serializer.Serialize(shader);
|
||||
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
ShaderAst UnserializeShader(ByteStream& stream)
|
||||
{
|
||||
ShaderAstUnserializer unserializer(stream);
|
||||
return unserializer.Unserialize();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,452 +0,0 @@
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Renderer module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Renderer/ShaderAstValidator.hpp>
|
||||
#include <Nazara/Core/CallOnExit.hpp>
|
||||
#include <Nazara/Renderer/ShaderAst.hpp>
|
||||
#include <Nazara/Renderer/ShaderVariables.hpp>
|
||||
#include <vector>
|
||||
#include <Nazara/Renderer/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
struct AstError
|
||||
{
|
||||
std::string errMsg;
|
||||
};
|
||||
|
||||
struct ShaderAstValidator::Context
|
||||
{
|
||||
struct Local
|
||||
{
|
||||
std::string name;
|
||||
ShaderExpressionType type;
|
||||
};
|
||||
|
||||
const ShaderAst::Function* currentFunction;
|
||||
std::vector<Local> declaredLocals;
|
||||
std::vector<std::size_t> blockLocalIndex;
|
||||
};
|
||||
|
||||
bool ShaderAstValidator::Validate(std::string* error)
|
||||
{
|
||||
try
|
||||
{
|
||||
for (std::size_t i = 0; i < m_shader.GetFunctionCount(); ++i)
|
||||
{
|
||||
const auto& func = m_shader.GetFunction(i);
|
||||
|
||||
Context currentContext;
|
||||
currentContext.currentFunction = &func;
|
||||
|
||||
m_context = ¤tContext;
|
||||
CallOnExit resetContext([&] { m_context = nullptr; });
|
||||
|
||||
func.statement->Visit(*this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (const AstError& e)
|
||||
{
|
||||
if (error)
|
||||
*error = e.errMsg;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const ShaderNodes::ExpressionPtr& ShaderAstValidator::MandatoryExpr(const ShaderNodes::ExpressionPtr& node)
|
||||
{
|
||||
MandatoryNode(node);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
const ShaderNodes::NodePtr& ShaderAstValidator::MandatoryNode(const ShaderNodes::NodePtr& node)
|
||||
{
|
||||
if (!node)
|
||||
throw AstError{ "Invalid node" };
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void ShaderAstValidator::TypeMustMatch(const ShaderNodes::ExpressionPtr& left, const ShaderNodes::ExpressionPtr& right)
|
||||
{
|
||||
return TypeMustMatch(left->GetExpressionType(), right->GetExpressionType());
|
||||
}
|
||||
|
||||
void ShaderAstValidator::TypeMustMatch(const ShaderExpressionType& left, const ShaderExpressionType& right)
|
||||
{
|
||||
if (left != right)
|
||||
throw AstError{ "Left expression type must match right expression type" };
|
||||
}
|
||||
|
||||
void ShaderAstValidator::Visit(ShaderNodes::AccessMember& node)
|
||||
{
|
||||
const ShaderExpressionType& exprType = MandatoryExpr(node.structExpr)->GetExpressionType();
|
||||
if (!std::holds_alternative<std::string>(exprType))
|
||||
throw AstError{ "expression is not a structure" };
|
||||
|
||||
const std::string& structName = std::get<std::string>(exprType);
|
||||
|
||||
const auto& structs = m_shader.GetStructs();
|
||||
auto it = std::find_if(structs.begin(), structs.end(), [&](const auto& s) { return s.name == structName; });
|
||||
if (it == structs.end())
|
||||
throw AstError{ "invalid structure" };
|
||||
|
||||
const ShaderAst::Struct& s = *it;
|
||||
if (node.memberIndex >= s.members.size())
|
||||
throw AstError{ "member index out of bounds" };
|
||||
|
||||
const auto& member = s.members[node.memberIndex];
|
||||
if (member.type != node.exprType)
|
||||
throw AstError{ "member type does not match node type" };
|
||||
}
|
||||
|
||||
void ShaderAstValidator::Visit(ShaderNodes::AssignOp& node)
|
||||
{
|
||||
MandatoryNode(node.left);
|
||||
MandatoryNode(node.right);
|
||||
TypeMustMatch(node.left, node.right);
|
||||
|
||||
if (node.left->GetExpressionCategory() != ShaderNodes::ExpressionCategory::LValue)
|
||||
throw AstError { "Assignation is only possible with a l-value" };
|
||||
|
||||
ShaderAstRecursiveVisitor::Visit(node);
|
||||
}
|
||||
|
||||
void ShaderAstValidator::Visit(ShaderNodes::BinaryOp& node)
|
||||
{
|
||||
MandatoryNode(node.left);
|
||||
MandatoryNode(node.right);
|
||||
|
||||
const ShaderExpressionType& leftExprType = MandatoryExpr(node.left)->GetExpressionType();
|
||||
if (!std::holds_alternative<ShaderNodes::BasicType>(leftExprType))
|
||||
throw AstError{ "left expression type does not support binary operation" };
|
||||
|
||||
const ShaderExpressionType& rightExprType = MandatoryExpr(node.right)->GetExpressionType();
|
||||
if (!std::holds_alternative<ShaderNodes::BasicType>(rightExprType))
|
||||
throw AstError{ "right expression type does not support binary operation" };
|
||||
|
||||
ShaderNodes::BasicType leftType = std::get<ShaderNodes::BasicType>(leftExprType);
|
||||
ShaderNodes::BasicType rightType = std::get<ShaderNodes::BasicType>(rightExprType);
|
||||
|
||||
switch (node.op)
|
||||
{
|
||||
case ShaderNodes::BinaryType::Add:
|
||||
case ShaderNodes::BinaryType::Equality:
|
||||
case ShaderNodes::BinaryType::Substract:
|
||||
TypeMustMatch(node.left, node.right);
|
||||
break;
|
||||
|
||||
case ShaderNodes::BinaryType::Multiply:
|
||||
case ShaderNodes::BinaryType::Divide:
|
||||
{
|
||||
switch (leftType)
|
||||
{
|
||||
case ShaderNodes::BasicType::Float1:
|
||||
case ShaderNodes::BasicType::Int1:
|
||||
{
|
||||
if (ShaderNodes::Node::GetComponentType(rightType) != leftType)
|
||||
throw AstError{ "Left expression type is not compatible with right expression type" };
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ShaderNodes::BasicType::Float2:
|
||||
case ShaderNodes::BasicType::Float3:
|
||||
case ShaderNodes::BasicType::Float4:
|
||||
case ShaderNodes::BasicType::Int2:
|
||||
case ShaderNodes::BasicType::Int3:
|
||||
case ShaderNodes::BasicType::Int4:
|
||||
{
|
||||
if (leftType != rightType && rightType != ShaderNodes::Node::GetComponentType(leftType))
|
||||
throw AstError{ "Left expression type is not compatible with right expression type" };
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ShaderNodes::BasicType::Mat4x4:
|
||||
{
|
||||
switch (rightType)
|
||||
{
|
||||
case ShaderNodes::BasicType::Float1:
|
||||
case ShaderNodes::BasicType::Float4:
|
||||
case ShaderNodes::BasicType::Mat4x4:
|
||||
break;
|
||||
|
||||
default:
|
||||
TypeMustMatch(node.left, node.right);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
TypeMustMatch(node.left, node.right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShaderAstRecursiveVisitor::Visit(node);
|
||||
}
|
||||
|
||||
void ShaderAstValidator::Visit(ShaderNodes::Branch& node)
|
||||
{
|
||||
for (const auto& condStatement : node.condStatements)
|
||||
{
|
||||
MandatoryNode(condStatement.condition);
|
||||
MandatoryNode(condStatement.statement);
|
||||
}
|
||||
|
||||
ShaderAstRecursiveVisitor::Visit(node);
|
||||
}
|
||||
|
||||
void ShaderAstValidator::Visit(ShaderNodes::Cast& node)
|
||||
{
|
||||
unsigned int componentCount = 0;
|
||||
unsigned int requiredComponents = node.GetComponentCount(node.exprType);
|
||||
for (const auto& exprPtr : node.expressions)
|
||||
{
|
||||
if (!exprPtr)
|
||||
break;
|
||||
|
||||
const ShaderExpressionType& exprType = exprPtr->GetExpressionType();
|
||||
if (!std::holds_alternative<ShaderNodes::BasicType>(exprType))
|
||||
throw AstError{ "incompatible type" };
|
||||
|
||||
componentCount += node.GetComponentCount(std::get<ShaderNodes::BasicType>(exprType));
|
||||
}
|
||||
|
||||
if (componentCount != requiredComponents)
|
||||
throw AstError{ "Component count doesn't match required component count" };
|
||||
|
||||
ShaderAstRecursiveVisitor::Visit(node);
|
||||
}
|
||||
|
||||
void ShaderAstValidator::Visit(ShaderNodes::Constant& /*node*/)
|
||||
{
|
||||
}
|
||||
|
||||
void ShaderAstValidator::Visit(ShaderNodes::DeclareVariable& node)
|
||||
{
|
||||
assert(m_context);
|
||||
|
||||
if (node.variable->GetType() != ShaderNodes::VariableType::LocalVariable)
|
||||
throw AstError{ "Only local variables can be declared in a statement" };
|
||||
|
||||
const auto& localVar = static_cast<const ShaderNodes::LocalVariable&>(*node.variable);
|
||||
|
||||
auto& local = m_context->declaredLocals.emplace_back();
|
||||
local.name = localVar.name;
|
||||
local.type = localVar.type;
|
||||
|
||||
ShaderAstRecursiveVisitor::Visit(node);
|
||||
}
|
||||
|
||||
void ShaderAstValidator::Visit(ShaderNodes::ExpressionStatement& node)
|
||||
{
|
||||
MandatoryNode(node.expression);
|
||||
|
||||
ShaderAstRecursiveVisitor::Visit(node);
|
||||
}
|
||||
|
||||
void ShaderAstValidator::Visit(ShaderNodes::Identifier& node)
|
||||
{
|
||||
assert(m_context);
|
||||
|
||||
if (!node.var)
|
||||
throw AstError{ "Invalid variable" };
|
||||
|
||||
Visit(node.var);
|
||||
}
|
||||
|
||||
void ShaderAstValidator::Visit(ShaderNodes::IntrinsicCall& node)
|
||||
{
|
||||
switch (node.intrinsic)
|
||||
{
|
||||
case ShaderNodes::IntrinsicType::CrossProduct:
|
||||
case ShaderNodes::IntrinsicType::DotProduct:
|
||||
{
|
||||
if (node.parameters.size() != 2)
|
||||
throw AstError { "Expected 2 parameters" };
|
||||
|
||||
for (auto& param : node.parameters)
|
||||
MandatoryNode(param);
|
||||
|
||||
ShaderExpressionType type = node.parameters.front()->GetExpressionType();
|
||||
for (std::size_t i = 1; i < node.parameters.size(); ++i)
|
||||
{
|
||||
if (type != node.parameters[i]->GetExpressionType())
|
||||
throw AstError{ "All type must match" };
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (node.intrinsic)
|
||||
{
|
||||
case ShaderNodes::IntrinsicType::CrossProduct:
|
||||
{
|
||||
if (node.parameters[0]->GetExpressionType() != ShaderExpressionType{ ShaderNodes::BasicType::Float3 })
|
||||
throw AstError{ "CrossProduct only works with Float3 expressions" };
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ShaderNodes::IntrinsicType::DotProduct:
|
||||
break;
|
||||
}
|
||||
|
||||
ShaderAstRecursiveVisitor::Visit(node);
|
||||
}
|
||||
|
||||
void ShaderAstValidator::Visit(ShaderNodes::Sample2D& node)
|
||||
{
|
||||
if (MandatoryExpr(node.sampler)->GetExpressionType() != ShaderExpressionType{ ShaderNodes::BasicType::Sampler2D })
|
||||
throw AstError{ "Sampler must be a Sampler2D" };
|
||||
|
||||
if (MandatoryExpr(node.coordinates)->GetExpressionType() != ShaderExpressionType{ ShaderNodes::BasicType::Float2 })
|
||||
throw AstError{ "Coordinates must be a Float2" };
|
||||
|
||||
ShaderAstRecursiveVisitor::Visit(node);
|
||||
}
|
||||
|
||||
void ShaderAstValidator::Visit(ShaderNodes::StatementBlock& node)
|
||||
{
|
||||
assert(m_context);
|
||||
|
||||
m_context->blockLocalIndex.push_back(m_context->declaredLocals.size());
|
||||
|
||||
for (const auto& statement : node.statements)
|
||||
MandatoryNode(statement);
|
||||
|
||||
assert(m_context->declaredLocals.size() >= m_context->blockLocalIndex.back());
|
||||
m_context->declaredLocals.resize(m_context->blockLocalIndex.back());
|
||||
m_context->blockLocalIndex.pop_back();
|
||||
|
||||
ShaderAstRecursiveVisitor::Visit(node);
|
||||
}
|
||||
|
||||
void ShaderAstValidator::Visit(ShaderNodes::SwizzleOp& node)
|
||||
{
|
||||
if (node.componentCount > 4)
|
||||
throw AstError{ "Cannot swizzle more than four elements" };
|
||||
|
||||
const ShaderExpressionType& exprType = MandatoryExpr(node.expression)->GetExpressionType();
|
||||
if (!std::holds_alternative<ShaderNodes::BasicType>(exprType))
|
||||
throw AstError{ "Cannot swizzle this type" };
|
||||
|
||||
switch (std::get<ShaderNodes::BasicType>(exprType))
|
||||
{
|
||||
case ShaderNodes::BasicType::Float1:
|
||||
case ShaderNodes::BasicType::Float2:
|
||||
case ShaderNodes::BasicType::Float3:
|
||||
case ShaderNodes::BasicType::Float4:
|
||||
case ShaderNodes::BasicType::Int1:
|
||||
case ShaderNodes::BasicType::Int2:
|
||||
case ShaderNodes::BasicType::Int3:
|
||||
case ShaderNodes::BasicType::Int4:
|
||||
break;
|
||||
|
||||
default:
|
||||
throw AstError{ "Cannot swizzle this type" };
|
||||
}
|
||||
|
||||
ShaderAstRecursiveVisitor::Visit(node);
|
||||
}
|
||||
|
||||
void ShaderAstValidator::Visit(ShaderNodes::BuiltinVariable& var)
|
||||
{
|
||||
switch (var.entry)
|
||||
{
|
||||
case ShaderNodes::BuiltinEntry::VertexPosition:
|
||||
if (!std::holds_alternative<ShaderNodes::BasicType>(var.type) ||
|
||||
std::get<ShaderNodes::BasicType>(var.type) != ShaderNodes::BasicType::Float4)
|
||||
throw AstError{ "Builtin is not of the expected type" };
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderAstValidator::Visit(ShaderNodes::InputVariable& var)
|
||||
{
|
||||
for (std::size_t i = 0; i < m_shader.GetInputCount(); ++i)
|
||||
{
|
||||
const auto& input = m_shader.GetInput(i);
|
||||
if (input.name == var.name)
|
||||
{
|
||||
TypeMustMatch(input.type, var.type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw AstError{ "Input not found" };
|
||||
}
|
||||
|
||||
void ShaderAstValidator::Visit(ShaderNodes::LocalVariable& var)
|
||||
{
|
||||
const auto& vars = m_context->declaredLocals;
|
||||
|
||||
auto it = std::find_if(vars.begin(), vars.end(), [&](const auto& v) { return v.name == var.name; });
|
||||
if (it == vars.end())
|
||||
throw AstError{ "Local variable not found in this block" };
|
||||
|
||||
TypeMustMatch(it->type, var.type);
|
||||
}
|
||||
|
||||
void ShaderAstValidator::Visit(ShaderNodes::OutputVariable& var)
|
||||
{
|
||||
for (std::size_t i = 0; i < m_shader.GetOutputCount(); ++i)
|
||||
{
|
||||
const auto& input = m_shader.GetOutput(i);
|
||||
if (input.name == var.name)
|
||||
{
|
||||
TypeMustMatch(input.type, var.type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw AstError{ "Output not found" };
|
||||
}
|
||||
|
||||
void ShaderAstValidator::Visit(ShaderNodes::ParameterVariable& var)
|
||||
{
|
||||
assert(m_context->currentFunction);
|
||||
|
||||
const auto& parameters = m_context->currentFunction->parameters;
|
||||
|
||||
auto it = std::find_if(parameters.begin(), parameters.end(), [&](const auto& parameter) { return parameter.name == var.name; });
|
||||
if (it == parameters.end())
|
||||
throw AstError{ "Parameter not found in function" };
|
||||
|
||||
TypeMustMatch(it->type, var.type);
|
||||
}
|
||||
|
||||
void ShaderAstValidator::Visit(ShaderNodes::UniformVariable& var)
|
||||
{
|
||||
for (std::size_t i = 0; i < m_shader.GetUniformCount(); ++i)
|
||||
{
|
||||
const auto& uniform = m_shader.GetUniform(i);
|
||||
if (uniform.name == var.name)
|
||||
{
|
||||
TypeMustMatch(uniform.type, var.type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw AstError{ "Uniform not found" };
|
||||
}
|
||||
|
||||
bool ValidateShader(const ShaderAst& shader, std::string* error)
|
||||
{
|
||||
ShaderAstValidator validator(shader);
|
||||
return validator.Validate(error);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Renderer module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Renderer/ShaderAstVisitor.hpp>
|
||||
#include <Nazara/Renderer/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
ShaderAstVisitor::~ShaderAstVisitor() = default;
|
||||
|
||||
void ShaderAstVisitor::EnableCondition(const std::string& name, bool cond)
|
||||
{
|
||||
if (cond)
|
||||
m_conditions.insert(name);
|
||||
else
|
||||
m_conditions.erase(name);
|
||||
}
|
||||
|
||||
bool ShaderAstVisitor::IsConditionEnabled(const std::string& name) const
|
||||
{
|
||||
return m_conditions.count(name) != 0;
|
||||
}
|
||||
|
||||
void ShaderAstVisitor::Visit(const ShaderNodes::NodePtr& node)
|
||||
{
|
||||
node->Visit(*this);
|
||||
}
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
// Copyright (C) 2020 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Renderer module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Renderer/ShaderNodes.hpp>
|
||||
#include <Nazara/Core/Algorithm.hpp>
|
||||
#include <Nazara/Renderer/ShaderAstSerializer.hpp>
|
||||
#include <Nazara/Renderer/ShaderAstVisitor.hpp>
|
||||
#include <Nazara/Renderer/ShaderWriter.hpp>
|
||||
#include <Nazara/Renderer/Debug.hpp>
|
||||
|
||||
namespace Nz::ShaderNodes
|
||||
{
|
||||
Node::~Node() = default;
|
||||
|
||||
ExpressionCategory Expression::GetExpressionCategory() const
|
||||
{
|
||||
return ExpressionCategory::RValue;
|
||||
}
|
||||
|
||||
void ExpressionStatement::Visit(ShaderAstVisitor& visitor)
|
||||
{
|
||||
visitor.Visit(*this);
|
||||
}
|
||||
|
||||
|
||||
void ConditionalStatement::Visit(ShaderAstVisitor& visitor)
|
||||
{
|
||||
if (visitor.IsConditionEnabled(conditionName))
|
||||
statement->Visit(visitor);
|
||||
}
|
||||
|
||||
|
||||
void StatementBlock::Visit(ShaderAstVisitor& visitor)
|
||||
{
|
||||
visitor.Visit(*this);
|
||||
}
|
||||
|
||||
|
||||
void DeclareVariable::Visit(ShaderAstVisitor& visitor)
|
||||
{
|
||||
visitor.Visit(*this);
|
||||
}
|
||||
|
||||
|
||||
ExpressionCategory Identifier::GetExpressionCategory() const
|
||||
{
|
||||
return ExpressionCategory::LValue;
|
||||
}
|
||||
|
||||
ShaderExpressionType Identifier::GetExpressionType() const
|
||||
{
|
||||
assert(var);
|
||||
return var->type;
|
||||
}
|
||||
|
||||
void Identifier::Visit(ShaderAstVisitor& visitor)
|
||||
{
|
||||
visitor.Visit(*this);
|
||||
}
|
||||
|
||||
ExpressionCategory AccessMember::GetExpressionCategory() const
|
||||
{
|
||||
return ExpressionCategory::LValue;
|
||||
}
|
||||
|
||||
ShaderExpressionType AccessMember::GetExpressionType() const
|
||||
{
|
||||
return exprType;
|
||||
}
|
||||
|
||||
void AccessMember::Visit(ShaderAstVisitor& visitor)
|
||||
{
|
||||
visitor.Visit(*this);
|
||||
}
|
||||
|
||||
ShaderExpressionType AssignOp::GetExpressionType() const
|
||||
{
|
||||
return left->GetExpressionType();
|
||||
}
|
||||
|
||||
void AssignOp::Visit(ShaderAstVisitor& visitor)
|
||||
{
|
||||
visitor.Visit(*this);
|
||||
}
|
||||
|
||||
|
||||
ShaderExpressionType BinaryOp::GetExpressionType() const
|
||||
{
|
||||
std::optional<ShaderExpressionType> exprType;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case BinaryType::Add:
|
||||
case BinaryType::Substract:
|
||||
exprType = left->GetExpressionType();
|
||||
break;
|
||||
|
||||
case BinaryType::Divide:
|
||||
case BinaryType::Multiply:
|
||||
{
|
||||
const ShaderExpressionType& leftExprType = left->GetExpressionType();
|
||||
assert(std::holds_alternative<BasicType>(leftExprType));
|
||||
|
||||
const ShaderExpressionType& rightExprType = right->GetExpressionType();
|
||||
assert(std::holds_alternative<BasicType>(rightExprType));
|
||||
|
||||
switch (std::get<BasicType>(leftExprType))
|
||||
{
|
||||
case BasicType::Boolean:
|
||||
case BasicType::Float2:
|
||||
case BasicType::Float3:
|
||||
case BasicType::Float4:
|
||||
case BasicType::Int2:
|
||||
case BasicType::Int3:
|
||||
case BasicType::Int4:
|
||||
exprType = leftExprType;
|
||||
break;
|
||||
|
||||
case BasicType::Float1:
|
||||
case BasicType::Int1:
|
||||
case BasicType::Mat4x4:
|
||||
exprType = rightExprType;
|
||||
break;
|
||||
|
||||
case BasicType::Sampler2D:
|
||||
case BasicType::Void:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case BinaryType::Equality:
|
||||
exprType = BasicType::Boolean;
|
||||
break;
|
||||
}
|
||||
|
||||
NazaraAssert(exprType.has_value(), "Unhandled builtin");
|
||||
|
||||
return *exprType;
|
||||
}
|
||||
|
||||
void BinaryOp::Visit(ShaderAstVisitor& visitor)
|
||||
{
|
||||
visitor.Visit(*this);
|
||||
}
|
||||
|
||||
|
||||
void Branch::Visit(ShaderAstVisitor& visitor)
|
||||
{
|
||||
visitor.Visit(*this);
|
||||
}
|
||||
|
||||
|
||||
ShaderExpressionType Constant::GetExpressionType() const
|
||||
{
|
||||
return std::visit([&](auto&& arg)
|
||||
{
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, bool>)
|
||||
return ShaderNodes::BasicType::Boolean;
|
||||
else if constexpr (std::is_same_v<T, float>)
|
||||
return ShaderNodes::BasicType::Float1;
|
||||
else if constexpr (std::is_same_v<T, Int32>)
|
||||
return ShaderNodes::BasicType::Int1;
|
||||
else if constexpr (std::is_same_v<T, Vector2f>)
|
||||
return ShaderNodes::BasicType::Float2;
|
||||
else if constexpr (std::is_same_v<T, Vector3f>)
|
||||
return ShaderNodes::BasicType::Float3;
|
||||
else if constexpr (std::is_same_v<T, Vector4f>)
|
||||
return ShaderNodes::BasicType::Float4;
|
||||
else if constexpr (std::is_same_v<T, Vector2i32>)
|
||||
return ShaderNodes::BasicType::Int2;
|
||||
else if constexpr (std::is_same_v<T, Vector3i32>)
|
||||
return ShaderNodes::BasicType::Int3;
|
||||
else if constexpr (std::is_same_v<T, Vector4i32>)
|
||||
return ShaderNodes::BasicType::Int4;
|
||||
else
|
||||
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
|
||||
}, value);
|
||||
}
|
||||
|
||||
void Constant::Visit(ShaderAstVisitor& visitor)
|
||||
{
|
||||
visitor.Visit(*this);
|
||||
}
|
||||
|
||||
ShaderExpressionType Cast::GetExpressionType() const
|
||||
{
|
||||
return exprType;
|
||||
}
|
||||
|
||||
void Cast::Visit(ShaderAstVisitor& visitor)
|
||||
{
|
||||
visitor.Visit(*this);
|
||||
}
|
||||
|
||||
|
||||
ExpressionCategory SwizzleOp::GetExpressionCategory() const
|
||||
{
|
||||
return ExpressionCategory::LValue;
|
||||
}
|
||||
|
||||
ShaderExpressionType SwizzleOp::GetExpressionType() const
|
||||
{
|
||||
const ShaderExpressionType& exprType = expression->GetExpressionType();
|
||||
assert(std::holds_alternative<BasicType>(exprType));
|
||||
|
||||
return static_cast<BasicType>(UnderlyingCast(GetComponentType(std::get<BasicType>(exprType))) + componentCount - 1);
|
||||
}
|
||||
|
||||
void SwizzleOp::Visit(ShaderAstVisitor& visitor)
|
||||
{
|
||||
visitor.Visit(*this);
|
||||
}
|
||||
|
||||
|
||||
ShaderExpressionType Sample2D::GetExpressionType() const
|
||||
{
|
||||
return BasicType::Float4;
|
||||
}
|
||||
|
||||
void Sample2D::Visit(ShaderAstVisitor& visitor)
|
||||
{
|
||||
visitor.Visit(*this);
|
||||
}
|
||||
|
||||
|
||||
ShaderExpressionType IntrinsicCall::GetExpressionType() const
|
||||
{
|
||||
switch (intrinsic)
|
||||
{
|
||||
case IntrinsicType::CrossProduct:
|
||||
return parameters.front()->GetExpressionType();
|
||||
|
||||
case IntrinsicType::DotProduct:
|
||||
return BasicType::Float1;
|
||||
}
|
||||
|
||||
NazaraAssert(false, "Unhandled builtin");
|
||||
return BasicType::Void;
|
||||
}
|
||||
|
||||
void IntrinsicCall::Visit(ShaderAstVisitor& visitor)
|
||||
{
|
||||
visitor.Visit(*this);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Renderer module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Renderer/ShaderVarVisitor.hpp>
|
||||
#include <Nazara/Renderer/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
ShaderVarVisitor::~ShaderVarVisitor() = default;
|
||||
|
||||
void ShaderVarVisitor::Visit(const ShaderNodes::VariablePtr& node)
|
||||
{
|
||||
node->Visit(*this);
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
// Copyright (C) 2020 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Renderer module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Renderer/ShaderVariables.hpp>
|
||||
#include <Nazara/Renderer/ShaderVarVisitor.hpp>
|
||||
#include <Nazara/Renderer/Debug.hpp>
|
||||
|
||||
namespace Nz::ShaderNodes
|
||||
{
|
||||
ShaderNodes::Variable::~Variable() = default;
|
||||
|
||||
VariableType BuiltinVariable::GetType() const
|
||||
{
|
||||
return VariableType::BuiltinVariable;
|
||||
}
|
||||
|
||||
void BuiltinVariable::Visit(ShaderVarVisitor& visitor)
|
||||
{
|
||||
visitor.Visit(*this);
|
||||
}
|
||||
|
||||
|
||||
VariableType InputVariable::GetType() const
|
||||
{
|
||||
return VariableType::InputVariable;
|
||||
}
|
||||
|
||||
void InputVariable::Visit(ShaderVarVisitor& visitor)
|
||||
{
|
||||
visitor.Visit(*this);
|
||||
}
|
||||
|
||||
|
||||
VariableType LocalVariable::GetType() const
|
||||
{
|
||||
return VariableType::LocalVariable;
|
||||
}
|
||||
|
||||
void LocalVariable::Visit(ShaderVarVisitor& visitor)
|
||||
{
|
||||
visitor.Visit(*this);
|
||||
}
|
||||
|
||||
|
||||
VariableType OutputVariable::GetType() const
|
||||
{
|
||||
return VariableType::OutputVariable;
|
||||
}
|
||||
|
||||
void OutputVariable::Visit(ShaderVarVisitor& visitor)
|
||||
{
|
||||
visitor.Visit(*this);
|
||||
}
|
||||
|
||||
|
||||
VariableType ParameterVariable::GetType() const
|
||||
{
|
||||
return VariableType::ParameterVariable;
|
||||
}
|
||||
|
||||
void ParameterVariable::Visit(ShaderVarVisitor& visitor)
|
||||
{
|
||||
visitor.Visit(*this);
|
||||
}
|
||||
|
||||
|
||||
VariableType UniformVariable::GetType() const
|
||||
{
|
||||
return VariableType::UniformVariable;
|
||||
}
|
||||
|
||||
void UniformVariable::Visit(ShaderVarVisitor& visitor)
|
||||
{
|
||||
visitor.Visit(*this);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright (C) 2015 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Renderer module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Renderer/ShaderWriter.hpp>
|
||||
#include <Nazara/Renderer/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
ShaderWriter::~ShaderWriter() = default;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user