Big SpirVWriter refactor
This commit is contained in:
parent
66a14721cb
commit
93de44d293
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef NAZARA_SHADERVISITOR_HPP
|
#ifndef NAZARA_SHADERASTVISITOR_HPP
|
||||||
#define NAZARA_SHADERVISITOR_HPP
|
#define NAZARA_SHADERASTVISITOR_HPP
|
||||||
|
|
||||||
#include <Nazara/Prerequisites.hpp>
|
#include <Nazara/Prerequisites.hpp>
|
||||||
#include <Nazara/Shader/Config.hpp>
|
#include <Nazara/Shader/Config.hpp>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NAZARA_SHADERASTVISITOREXCEPT_HPP
|
||||||
|
#define NAZARA_SHADERASTVISITOREXCEPT_HPP
|
||||||
|
|
||||||
|
#include <Nazara/Prerequisites.hpp>
|
||||||
|
#include <Nazara/Shader/Config.hpp>
|
||||||
|
#include <Nazara/Shader/ShaderAstVisitor.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
class NAZARA_SHADER_API ShaderAstVisitorExcept : public ShaderAstVisitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using ShaderAstVisitor::Visit;
|
||||||
|
void Visit(ShaderNodes::AccessMember& node) override;
|
||||||
|
void Visit(ShaderNodes::AssignOp& node) override;
|
||||||
|
void Visit(ShaderNodes::BinaryOp& node) override;
|
||||||
|
void Visit(ShaderNodes::Branch& node) override;
|
||||||
|
void Visit(ShaderNodes::Cast& node) override;
|
||||||
|
void Visit(ShaderNodes::Constant& node) override;
|
||||||
|
void Visit(ShaderNodes::DeclareVariable& node) override;
|
||||||
|
void Visit(ShaderNodes::ExpressionStatement& node) override;
|
||||||
|
void Visit(ShaderNodes::Identifier& node) override;
|
||||||
|
void Visit(ShaderNodes::IntrinsicCall& node) override;
|
||||||
|
void Visit(ShaderNodes::Sample2D& node) override;
|
||||||
|
void Visit(ShaderNodes::StatementBlock& node) override;
|
||||||
|
void Visit(ShaderNodes::SwizzleOp& node) override;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright (C) 2015 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NAZARA_SHADERVARVISITOREXCEPT_HPP
|
||||||
|
#define NAZARA_SHADERVARVISITOREXCEPT_HPP
|
||||||
|
|
||||||
|
#include <Nazara/Prerequisites.hpp>
|
||||||
|
#include <Nazara/Shader/ShaderVarVisitor.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
class NAZARA_SHADER_API ShaderVarVisitorExcept : public ShaderVarVisitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using ShaderVarVisitor::Visit;
|
||||||
|
void Visit(ShaderNodes::BuiltinVariable& var) override;
|
||||||
|
void Visit(ShaderNodes::InputVariable& var) override;
|
||||||
|
void Visit(ShaderNodes::LocalVariable& var) override;
|
||||||
|
void Visit(ShaderNodes::OutputVariable& var) override;
|
||||||
|
void Visit(ShaderNodes::ParameterVariable& var) override;
|
||||||
|
void Visit(ShaderNodes::UniformVariable& var) override;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NAZARA_SPIRVEXPRESSIONLOAD_HPP
|
||||||
|
#define NAZARA_SPIRVEXPRESSIONLOAD_HPP
|
||||||
|
|
||||||
|
#include <Nazara/Prerequisites.hpp>
|
||||||
|
#include <Nazara/Shader/Config.hpp>
|
||||||
|
#include <Nazara/Shader/ShaderAstVisitorExcept.hpp>
|
||||||
|
#include <Nazara/Shader/ShaderVarVisitorExcept.hpp>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
class SpirvWriter;
|
||||||
|
|
||||||
|
class NAZARA_SHADER_API SpirvExpressionLoad : public ShaderAstVisitorExcept, public ShaderVarVisitorExcept
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline SpirvExpressionLoad(SpirvWriter& writer);
|
||||||
|
SpirvExpressionLoad(const SpirvExpressionLoad&) = delete;
|
||||||
|
SpirvExpressionLoad(SpirvExpressionLoad&&) = delete;
|
||||||
|
~SpirvExpressionLoad() = default;
|
||||||
|
|
||||||
|
UInt32 EvaluateExpression(const ShaderNodes::ExpressionPtr& expr);
|
||||||
|
|
||||||
|
using ShaderAstVisitorExcept::Visit;
|
||||||
|
void Visit(ShaderNodes::AccessMember& node) override;
|
||||||
|
void Visit(ShaderNodes::AssignOp& node) override;
|
||||||
|
void Visit(ShaderNodes::BinaryOp& node) override;
|
||||||
|
void Visit(ShaderNodes::Cast& node) override;
|
||||||
|
void Visit(ShaderNodes::Constant& node) override;
|
||||||
|
void Visit(ShaderNodes::DeclareVariable& node) override;
|
||||||
|
void Visit(ShaderNodes::ExpressionStatement& node) override;
|
||||||
|
void Visit(ShaderNodes::Identifier& node) override;
|
||||||
|
void Visit(ShaderNodes::IntrinsicCall& node) override;
|
||||||
|
void Visit(ShaderNodes::Sample2D& node) override;
|
||||||
|
void Visit(ShaderNodes::SwizzleOp& node) override;
|
||||||
|
|
||||||
|
using ShaderVarVisitorExcept::Visit;
|
||||||
|
void Visit(ShaderNodes::BuiltinVariable& var) override;
|
||||||
|
void Visit(ShaderNodes::InputVariable& var) override;
|
||||||
|
void Visit(ShaderNodes::LocalVariable& var) override;
|
||||||
|
void Visit(ShaderNodes::ParameterVariable& var) override;
|
||||||
|
void Visit(ShaderNodes::UniformVariable& var) override;
|
||||||
|
|
||||||
|
SpirvExpressionLoad& operator=(const SpirvExpressionLoad&) = delete;
|
||||||
|
SpirvExpressionLoad& operator=(SpirvExpressionLoad&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void PushResultId(UInt32 value);
|
||||||
|
UInt32 PopResultId();
|
||||||
|
|
||||||
|
std::vector<UInt32> m_resultIds;
|
||||||
|
SpirvWriter& m_writer;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <Nazara/Shader/SpirvExpressionLoad.inl>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/Shader/SpirvExpressionLoad.hpp>
|
||||||
|
#include <Nazara/Shader/Debug.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
inline SpirvExpressionLoad::SpirvExpressionLoad(SpirvWriter& writer) :
|
||||||
|
m_writer(writer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <Nazara/Shader/DebugOff.hpp>
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NAZARA_SPIRVEXPRESSIONLOADACCESSMEMBER_HPP
|
||||||
|
#define NAZARA_SPIRVEXPRESSIONLOADACCESSMEMBER_HPP
|
||||||
|
|
||||||
|
#include <Nazara/Prerequisites.hpp>
|
||||||
|
#include <Nazara/Shader/Config.hpp>
|
||||||
|
#include <Nazara/Shader/ShaderAstVisitorExcept.hpp>
|
||||||
|
#include <Nazara/Shader/ShaderVarVisitorExcept.hpp>
|
||||||
|
#include <Nazara/Shader/SpirvData.hpp>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
class SpirvWriter;
|
||||||
|
|
||||||
|
class NAZARA_SHADER_API SpirvExpressionLoadAccessMember : public ShaderAstVisitorExcept, public ShaderVarVisitorExcept
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline SpirvExpressionLoadAccessMember(SpirvWriter& writer);
|
||||||
|
SpirvExpressionLoadAccessMember(const SpirvExpressionLoadAccessMember&) = delete;
|
||||||
|
SpirvExpressionLoadAccessMember(SpirvExpressionLoadAccessMember&&) = delete;
|
||||||
|
~SpirvExpressionLoadAccessMember() = default;
|
||||||
|
|
||||||
|
UInt32 EvaluateExpression(ShaderNodes::AccessMember& expr);
|
||||||
|
|
||||||
|
using ShaderAstVisitor::Visit;
|
||||||
|
void Visit(ShaderNodes::AccessMember& node) override;
|
||||||
|
void Visit(ShaderNodes::Identifier& node) override;
|
||||||
|
|
||||||
|
using ShaderVarVisitor::Visit;
|
||||||
|
void Visit(ShaderNodes::InputVariable& var) override;
|
||||||
|
void Visit(ShaderNodes::UniformVariable& var) override;
|
||||||
|
|
||||||
|
SpirvExpressionLoadAccessMember& operator=(const SpirvExpressionLoadAccessMember&) = delete;
|
||||||
|
SpirvExpressionLoadAccessMember& operator=(SpirvExpressionLoadAccessMember&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Pointer
|
||||||
|
{
|
||||||
|
SpirvStorageClass storage;
|
||||||
|
UInt32 resultId;
|
||||||
|
UInt32 pointedTypeId;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Value
|
||||||
|
{
|
||||||
|
UInt32 resultId;
|
||||||
|
};
|
||||||
|
|
||||||
|
SpirvWriter& m_writer;
|
||||||
|
std::variant<std::monostate, Pointer, Value> m_value;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <Nazara/Shader/SpirvExpressionLoadAccessMember.inl>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/Shader/SpirvExpressionLoadAccessMember.hpp>
|
||||||
|
#include <Nazara/Shader/Debug.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
inline SpirvExpressionLoadAccessMember::SpirvExpressionLoadAccessMember(SpirvWriter& writer) :
|
||||||
|
m_writer(writer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <Nazara/Shader/DebugOff.hpp>
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NAZARA_SPIRVEXPRESSIONSTORE_HPP
|
||||||
|
#define NAZARA_SPIRVEXPRESSIONSTORE_HPP
|
||||||
|
|
||||||
|
#include <Nazara/Prerequisites.hpp>
|
||||||
|
#include <Nazara/Shader/Config.hpp>
|
||||||
|
#include <Nazara/Shader/ShaderAstVisitorExcept.hpp>
|
||||||
|
#include <Nazara/Shader/ShaderVarVisitorExcept.hpp>
|
||||||
|
#include <Nazara/Shader/SpirvData.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
class SpirvSection;
|
||||||
|
class SpirvWriter;
|
||||||
|
|
||||||
|
class NAZARA_SHADER_API SpirvExpressionStore : public ShaderAstVisitorExcept, public ShaderVarVisitorExcept
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline SpirvExpressionStore(SpirvWriter& writer);
|
||||||
|
SpirvExpressionStore(const SpirvExpressionStore&) = delete;
|
||||||
|
SpirvExpressionStore(SpirvExpressionStore&&) = delete;
|
||||||
|
~SpirvExpressionStore() = default;
|
||||||
|
|
||||||
|
void Store(const ShaderNodes::ExpressionPtr& node, UInt32 resultId);
|
||||||
|
|
||||||
|
using ShaderAstVisitorExcept::Visit;
|
||||||
|
void Visit(ShaderNodes::AccessMember& node) override;
|
||||||
|
void Visit(ShaderNodes::Identifier& node) override;
|
||||||
|
void Visit(ShaderNodes::SwizzleOp& node) override;
|
||||||
|
|
||||||
|
using ShaderVarVisitorExcept::Visit;
|
||||||
|
void Visit(ShaderNodes::BuiltinVariable& var) override;
|
||||||
|
void Visit(ShaderNodes::LocalVariable& var) override;
|
||||||
|
void Visit(ShaderNodes::OutputVariable& var) override;
|
||||||
|
|
||||||
|
SpirvExpressionStore& operator=(const SpirvExpressionStore&) = delete;
|
||||||
|
SpirvExpressionStore& operator=(SpirvExpressionStore&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct LocalVar
|
||||||
|
{
|
||||||
|
std::string varName;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Pointer
|
||||||
|
{
|
||||||
|
SpirvStorageClass storage;
|
||||||
|
UInt32 resultId;
|
||||||
|
};
|
||||||
|
|
||||||
|
SpirvWriter& m_writer;
|
||||||
|
std::variant<std::monostate, LocalVar, Pointer> m_value;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <Nazara/Shader/SpirvExpressionStore.inl>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/Shader/SpirvExpressionStore.hpp>
|
||||||
|
#include <Nazara/Shader/Debug.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
inline SpirvExpressionStore::SpirvExpressionStore(SpirvWriter& writer) :
|
||||||
|
m_writer(writer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <Nazara/Shader/DebugOff.hpp>
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NAZARA_SPIRVPRINTER_HPP
|
||||||
|
#define NAZARA_SPIRVPRINTER_HPP
|
||||||
|
|
||||||
|
#include <Nazara/Prerequisites.hpp>
|
||||||
|
#include <Nazara/Shader/Config.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
class NAZARA_SHADER_API SpirvPrinter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline SpirvPrinter();
|
||||||
|
SpirvPrinter(const SpirvPrinter&) = default;
|
||||||
|
SpirvPrinter(SpirvPrinter&&) = default;
|
||||||
|
~SpirvPrinter() = default;
|
||||||
|
|
||||||
|
std::string Print(const UInt32* codepoints, std::size_t count);
|
||||||
|
|
||||||
|
SpirvPrinter& operator=(const SpirvPrinter&) = default;
|
||||||
|
SpirvPrinter& operator=(SpirvPrinter&&) = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void AppendInstruction();
|
||||||
|
std::string ReadString();
|
||||||
|
UInt32 ReadWord();
|
||||||
|
|
||||||
|
struct State;
|
||||||
|
|
||||||
|
State* m_currentState;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <Nazara/Shader/SpirvPrinter.inl>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/Shader/SpirvPrinter.hpp>
|
||||||
|
#include <Nazara/Shader/Debug.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
inline SpirvPrinter::SpirvPrinter() :
|
||||||
|
m_currentState(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <Nazara/Shader/DebugOff.hpp>
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NAZARA_SPIRVSTATEMENTVISITOR_HPP
|
||||||
|
#define NAZARA_SPIRVSTATEMENTVISITOR_HPP
|
||||||
|
|
||||||
|
#include <Nazara/Prerequisites.hpp>
|
||||||
|
#include <Nazara/Shader/Config.hpp>
|
||||||
|
#include <Nazara/Shader/ShaderAstVisitorExcept.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
class SpirvWriter;
|
||||||
|
|
||||||
|
class NAZARA_SHADER_API SpirvStatementVisitor : public ShaderAstVisitorExcept
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline SpirvStatementVisitor(SpirvWriter& writer);
|
||||||
|
SpirvStatementVisitor(const SpirvStatementVisitor&) = delete;
|
||||||
|
SpirvStatementVisitor(SpirvStatementVisitor&&) = delete;
|
||||||
|
~SpirvStatementVisitor() = default;
|
||||||
|
|
||||||
|
using ShaderAstVisitor::Visit;
|
||||||
|
void Visit(ShaderNodes::AssignOp& node) override;
|
||||||
|
void Visit(ShaderNodes::Branch& node) override;
|
||||||
|
void Visit(ShaderNodes::DeclareVariable& node) override;
|
||||||
|
void Visit(ShaderNodes::ExpressionStatement& node) override;
|
||||||
|
void Visit(ShaderNodes::StatementBlock& node) override;
|
||||||
|
|
||||||
|
SpirvStatementVisitor& operator=(const SpirvStatementVisitor&) = delete;
|
||||||
|
SpirvStatementVisitor& operator=(SpirvStatementVisitor&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SpirvWriter& m_writer;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <Nazara/Shader/SpirvStatementVisitor.inl>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/Shader/SpirvStatementVisitor.hpp>
|
||||||
|
#include <Nazara/Shader/Debug.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
inline SpirvStatementVisitor::SpirvStatementVisitor(SpirvWriter& writer) :
|
||||||
|
m_writer(writer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <Nazara/Shader/DebugOff.hpp>
|
||||||
|
|
@ -23,8 +23,13 @@ namespace Nz
|
||||||
{
|
{
|
||||||
class SpirvSection;
|
class SpirvSection;
|
||||||
|
|
||||||
class NAZARA_SHADER_API SpirvWriter : public ShaderAstVisitor, public ShaderVarVisitor
|
class NAZARA_SHADER_API SpirvWriter
|
||||||
{
|
{
|
||||||
|
friend class SpirvExpressionLoad;
|
||||||
|
friend class SpirvExpressionLoadAccessMember;
|
||||||
|
friend class SpirvExpressionStore;
|
||||||
|
friend class SpirvStatementVisitor;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct Environment;
|
struct Environment;
|
||||||
|
|
||||||
|
|
@ -45,49 +50,37 @@ namespace Nz
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ExtVar;
|
struct ExtVar;
|
||||||
|
struct OnlyCache {};
|
||||||
|
|
||||||
UInt32 AllocateResultId();
|
UInt32 AllocateResultId();
|
||||||
|
|
||||||
void AppendHeader();
|
void AppendHeader();
|
||||||
|
|
||||||
UInt32 EvaluateExpression(const ShaderNodes::ExpressionPtr& expr);
|
|
||||||
|
|
||||||
UInt32 GetConstantId(const ShaderConstantValue& value) const;
|
UInt32 GetConstantId(const ShaderConstantValue& value) const;
|
||||||
UInt32 GetFunctionTypeId(ShaderExpressionType retType, const std::vector<ShaderAst::FunctionParameter>& parameters);
|
UInt32 GetFunctionTypeId(ShaderExpressionType retType, const std::vector<ShaderAst::FunctionParameter>& parameters);
|
||||||
|
const ExtVar& GetBuiltinVariable(ShaderNodes::BuiltinEntry builtin) const;
|
||||||
|
const ExtVar& GetInputVariable(const std::string& name) const;
|
||||||
|
const ExtVar& GetOutputVariable(const std::string& name) const;
|
||||||
|
const ExtVar& GetUniformVariable(const std::string& name) const;
|
||||||
|
SpirvSection& GetInstructions();
|
||||||
UInt32 GetPointerTypeId(const ShaderExpressionType& type, SpirvStorageClass storageClass) const;
|
UInt32 GetPointerTypeId(const ShaderExpressionType& type, SpirvStorageClass storageClass) const;
|
||||||
UInt32 GetTypeId(const ShaderExpressionType& type) const;
|
UInt32 GetTypeId(const ShaderExpressionType& type) const;
|
||||||
|
|
||||||
void PushResultId(UInt32 value);
|
UInt32 ReadInputVariable(const std::string& name);
|
||||||
UInt32 PopResultId();
|
std::optional<UInt32> ReadInputVariable(const std::string& name, OnlyCache);
|
||||||
|
UInt32 ReadLocalVariable(const std::string& name);
|
||||||
|
std::optional<UInt32> ReadLocalVariable(const std::string& name, OnlyCache);
|
||||||
|
UInt32 ReadUniformVariable(const std::string& name);
|
||||||
|
std::optional<UInt32> ReadUniformVariable(const std::string& name, OnlyCache);
|
||||||
UInt32 ReadVariable(ExtVar& var);
|
UInt32 ReadVariable(ExtVar& var);
|
||||||
|
std::optional<UInt32> ReadVariable(const ExtVar& var, OnlyCache);
|
||||||
|
|
||||||
UInt32 RegisterConstant(const ShaderConstantValue& value);
|
UInt32 RegisterConstant(const ShaderConstantValue& value);
|
||||||
UInt32 RegisterFunctionType(ShaderExpressionType retType, const std::vector<ShaderAst::FunctionParameter>& parameters);
|
UInt32 RegisterFunctionType(ShaderExpressionType retType, const std::vector<ShaderAst::FunctionParameter>& parameters);
|
||||||
UInt32 RegisterPointerType(ShaderExpressionType type, SpirvStorageClass storageClass);
|
UInt32 RegisterPointerType(ShaderExpressionType type, SpirvStorageClass storageClass);
|
||||||
UInt32 RegisterType(ShaderExpressionType type);
|
UInt32 RegisterType(ShaderExpressionType type);
|
||||||
|
|
||||||
using ShaderAstVisitor::Visit;
|
void WriteLocalVariable(std::string name, UInt32 resultId);
|
||||||
void Visit(ShaderNodes::AccessMember& node) override;
|
|
||||||
void Visit(ShaderNodes::AssignOp& node) override;
|
|
||||||
void Visit(ShaderNodes::Branch& node) override;
|
|
||||||
void Visit(ShaderNodes::BinaryOp& node) override;
|
|
||||||
void Visit(ShaderNodes::Cast& node) override;
|
|
||||||
void Visit(ShaderNodes::Constant& node) override;
|
|
||||||
void Visit(ShaderNodes::DeclareVariable& node) override;
|
|
||||||
void Visit(ShaderNodes::ExpressionStatement& node) override;
|
|
||||||
void Visit(ShaderNodes::Identifier& node) override;
|
|
||||||
void Visit(ShaderNodes::IntrinsicCall& node) override;
|
|
||||||
void Visit(ShaderNodes::Sample2D& node) override;
|
|
||||||
void Visit(ShaderNodes::StatementBlock& node) override;
|
|
||||||
void Visit(ShaderNodes::SwizzleOp& node) override;
|
|
||||||
|
|
||||||
using ShaderVarVisitor::Visit;
|
|
||||||
void Visit(ShaderNodes::BuiltinVariable& var) override;
|
|
||||||
void Visit(ShaderNodes::InputVariable& var) override;
|
|
||||||
void Visit(ShaderNodes::LocalVariable& var) override;
|
|
||||||
void Visit(ShaderNodes::OutputVariable& var) override;
|
|
||||||
void Visit(ShaderNodes::ParameterVariable& var) override;
|
|
||||||
void Visit(ShaderNodes::UniformVariable& var) override;
|
|
||||||
|
|
||||||
static void MergeBlocks(std::vector<UInt32>& output, const SpirvSection& from);
|
static void MergeBlocks(std::vector<UInt32>& output, const SpirvSection& from);
|
||||||
|
|
||||||
|
|
@ -97,6 +90,14 @@ namespace Nz
|
||||||
const ShaderAst::Function* currentFunction = nullptr;
|
const ShaderAst::Function* currentFunction = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ExtVar
|
||||||
|
{
|
||||||
|
UInt32 pointerTypeId;
|
||||||
|
UInt32 typeId;
|
||||||
|
UInt32 varId;
|
||||||
|
std::optional<UInt32> valueId;
|
||||||
|
};
|
||||||
|
|
||||||
struct State;
|
struct State;
|
||||||
|
|
||||||
Context m_context;
|
Context m_context;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/Shader/ShaderAstVisitorExcept.hpp>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <Nazara/Shader/Debug.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
void ShaderAstVisitorExcept::Visit(ShaderNodes::AccessMember& node)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unhandled AccessMember node");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderAstVisitorExcept::Visit(ShaderNodes::AssignOp& node)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unhandled AssignOp node");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderAstVisitorExcept::Visit(ShaderNodes::BinaryOp& node)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unhandled AccessMember node");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderAstVisitorExcept::Visit(ShaderNodes::Branch& node)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unhandled Branch node");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderAstVisitorExcept::Visit(ShaderNodes::Cast& node)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unhandled Cast node");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderAstVisitorExcept::Visit(ShaderNodes::Constant& node)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unhandled Constant node");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderAstVisitorExcept::Visit(ShaderNodes::DeclareVariable& node)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unhandled DeclareVariable node");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderAstVisitorExcept::Visit(ShaderNodes::ExpressionStatement& node)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unhandled ExpressionStatement node");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderAstVisitorExcept::Visit(ShaderNodes::Identifier& node)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unhandled Identifier node");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderAstVisitorExcept::Visit(ShaderNodes::IntrinsicCall& node)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unhandled IntrinsicCall node");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderAstVisitorExcept::Visit(ShaderNodes::Sample2D& node)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unhandled Sample2D node");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderAstVisitorExcept::Visit(ShaderNodes::StatementBlock& node)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unhandled StatementBlock node");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderAstVisitorExcept::Visit(ShaderNodes::SwizzleOp& node)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unhandled SwizzleOp node");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/Shader/ShaderVarVisitorExcept.hpp>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <Nazara/Shader/Debug.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
void ShaderVarVisitorExcept::Visit(ShaderNodes::BuiltinVariable& var)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unhandled BuiltinVariable");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderVarVisitorExcept::Visit(ShaderNodes::InputVariable& var)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unhandled InputVariable");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderVarVisitorExcept::Visit(ShaderNodes::LocalVariable& var)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unhandled LocalVariable");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderVarVisitorExcept::Visit(ShaderNodes::OutputVariable& var)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unhandled OutputVariable");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderVarVisitorExcept::Visit(ShaderNodes::ParameterVariable& var)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unhandled ParameterVariable");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderVarVisitorExcept::Visit(ShaderNodes::UniformVariable& var)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unhandled UniformVariable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,448 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/Shader/SpirvExpressionLoad.hpp>
|
||||||
|
#include <Nazara/Core/StackVector.hpp>
|
||||||
|
#include <Nazara/Shader/SpirvSection.hpp>
|
||||||
|
#include <Nazara/Shader/SpirvExpressionLoadAccessMember.hpp>
|
||||||
|
#include <Nazara/Shader/SpirvExpressionStore.hpp>
|
||||||
|
#include <Nazara/Shader/SpirvWriter.hpp>
|
||||||
|
#include <Nazara/Shader/Debug.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
void SpirvExpressionLoad::Visit(ShaderNodes::AccessMember& node)
|
||||||
|
{
|
||||||
|
SpirvExpressionLoadAccessMember accessMemberVisitor(m_writer);
|
||||||
|
PushResultId(accessMemberVisitor.EvaluateExpression(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionLoad::Visit(ShaderNodes::AssignOp& node)
|
||||||
|
{
|
||||||
|
SpirvExpressionLoad loadVisitor(m_writer);
|
||||||
|
SpirvExpressionStore storeVisitor(m_writer);
|
||||||
|
storeVisitor.Store(node.left, EvaluateExpression(node.right));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionLoad::Visit(ShaderNodes::BinaryOp& node)
|
||||||
|
{
|
||||||
|
ShaderExpressionType resultExprType = node.GetExpressionType();
|
||||||
|
assert(std::holds_alternative<ShaderNodes::BasicType>(resultExprType));
|
||||||
|
|
||||||
|
const ShaderExpressionType& leftExprType = node.left->GetExpressionType();
|
||||||
|
assert(std::holds_alternative<ShaderNodes::BasicType>(leftExprType));
|
||||||
|
|
||||||
|
const ShaderExpressionType& rightExprType = node.right->GetExpressionType();
|
||||||
|
assert(std::holds_alternative<ShaderNodes::BasicType>(rightExprType));
|
||||||
|
|
||||||
|
ShaderNodes::BasicType resultType = std::get<ShaderNodes::BasicType>(resultExprType);
|
||||||
|
ShaderNodes::BasicType leftType = std::get<ShaderNodes::BasicType>(leftExprType);
|
||||||
|
ShaderNodes::BasicType rightType = std::get<ShaderNodes::BasicType>(rightExprType);
|
||||||
|
|
||||||
|
|
||||||
|
UInt32 leftOperand = EvaluateExpression(node.left);
|
||||||
|
UInt32 rightOperand = EvaluateExpression(node.right);
|
||||||
|
UInt32 resultId = m_writer.AllocateResultId();
|
||||||
|
|
||||||
|
bool swapOperands = false;
|
||||||
|
|
||||||
|
SpirvOp op = [&]
|
||||||
|
{
|
||||||
|
switch (node.op)
|
||||||
|
{
|
||||||
|
case ShaderNodes::BinaryType::Add:
|
||||||
|
{
|
||||||
|
switch (leftType)
|
||||||
|
{
|
||||||
|
case ShaderNodes::BasicType::Float1:
|
||||||
|
case ShaderNodes::BasicType::Float2:
|
||||||
|
case ShaderNodes::BasicType::Float3:
|
||||||
|
case ShaderNodes::BasicType::Float4:
|
||||||
|
case ShaderNodes::BasicType::Mat4x4:
|
||||||
|
return SpirvOp::OpFAdd;
|
||||||
|
|
||||||
|
case ShaderNodes::BasicType::Int1:
|
||||||
|
case ShaderNodes::BasicType::Int2:
|
||||||
|
case ShaderNodes::BasicType::Int3:
|
||||||
|
case ShaderNodes::BasicType::Int4:
|
||||||
|
case ShaderNodes::BasicType::UInt1:
|
||||||
|
case ShaderNodes::BasicType::UInt2:
|
||||||
|
case ShaderNodes::BasicType::UInt3:
|
||||||
|
case ShaderNodes::BasicType::UInt4:
|
||||||
|
return SpirvOp::OpIAdd;
|
||||||
|
|
||||||
|
case ShaderNodes::BasicType::Boolean:
|
||||||
|
case ShaderNodes::BasicType::Sampler2D:
|
||||||
|
case ShaderNodes::BasicType::Void:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case ShaderNodes::BinaryType::Substract:
|
||||||
|
{
|
||||||
|
switch (leftType)
|
||||||
|
{
|
||||||
|
case ShaderNodes::BasicType::Float1:
|
||||||
|
case ShaderNodes::BasicType::Float2:
|
||||||
|
case ShaderNodes::BasicType::Float3:
|
||||||
|
case ShaderNodes::BasicType::Float4:
|
||||||
|
case ShaderNodes::BasicType::Mat4x4:
|
||||||
|
return SpirvOp::OpFSub;
|
||||||
|
|
||||||
|
case ShaderNodes::BasicType::Int1:
|
||||||
|
case ShaderNodes::BasicType::Int2:
|
||||||
|
case ShaderNodes::BasicType::Int3:
|
||||||
|
case ShaderNodes::BasicType::Int4:
|
||||||
|
case ShaderNodes::BasicType::UInt1:
|
||||||
|
case ShaderNodes::BasicType::UInt2:
|
||||||
|
case ShaderNodes::BasicType::UInt3:
|
||||||
|
case ShaderNodes::BasicType::UInt4:
|
||||||
|
return SpirvOp::OpISub;
|
||||||
|
|
||||||
|
case ShaderNodes::BasicType::Boolean:
|
||||||
|
case ShaderNodes::BasicType::Sampler2D:
|
||||||
|
case ShaderNodes::BasicType::Void:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case ShaderNodes::BinaryType::Divide:
|
||||||
|
{
|
||||||
|
switch (leftType)
|
||||||
|
{
|
||||||
|
case ShaderNodes::BasicType::Float1:
|
||||||
|
case ShaderNodes::BasicType::Float2:
|
||||||
|
case ShaderNodes::BasicType::Float3:
|
||||||
|
case ShaderNodes::BasicType::Float4:
|
||||||
|
case ShaderNodes::BasicType::Mat4x4:
|
||||||
|
return SpirvOp::OpFDiv;
|
||||||
|
|
||||||
|
case ShaderNodes::BasicType::Int1:
|
||||||
|
case ShaderNodes::BasicType::Int2:
|
||||||
|
case ShaderNodes::BasicType::Int3:
|
||||||
|
case ShaderNodes::BasicType::Int4:
|
||||||
|
return SpirvOp::OpSDiv;
|
||||||
|
|
||||||
|
case ShaderNodes::BasicType::UInt1:
|
||||||
|
case ShaderNodes::BasicType::UInt2:
|
||||||
|
case ShaderNodes::BasicType::UInt3:
|
||||||
|
case ShaderNodes::BasicType::UInt4:
|
||||||
|
return SpirvOp::OpUDiv;
|
||||||
|
|
||||||
|
case ShaderNodes::BasicType::Boolean:
|
||||||
|
case ShaderNodes::BasicType::Sampler2D:
|
||||||
|
case ShaderNodes::BasicType::Void:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case ShaderNodes::BinaryType::Equality:
|
||||||
|
{
|
||||||
|
switch (leftType)
|
||||||
|
{
|
||||||
|
case ShaderNodes::BasicType::Boolean:
|
||||||
|
return SpirvOp::OpLogicalEqual;
|
||||||
|
|
||||||
|
case ShaderNodes::BasicType::Float1:
|
||||||
|
case ShaderNodes::BasicType::Float2:
|
||||||
|
case ShaderNodes::BasicType::Float3:
|
||||||
|
case ShaderNodes::BasicType::Float4:
|
||||||
|
case ShaderNodes::BasicType::Mat4x4:
|
||||||
|
return SpirvOp::OpFOrdEqual;
|
||||||
|
|
||||||
|
case ShaderNodes::BasicType::Int1:
|
||||||
|
case ShaderNodes::BasicType::Int2:
|
||||||
|
case ShaderNodes::BasicType::Int3:
|
||||||
|
case ShaderNodes::BasicType::Int4:
|
||||||
|
case ShaderNodes::BasicType::UInt1:
|
||||||
|
case ShaderNodes::BasicType::UInt2:
|
||||||
|
case ShaderNodes::BasicType::UInt3:
|
||||||
|
case ShaderNodes::BasicType::UInt4:
|
||||||
|
return SpirvOp::OpIEqual;
|
||||||
|
|
||||||
|
case ShaderNodes::BasicType::Sampler2D:
|
||||||
|
case ShaderNodes::BasicType::Void:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case ShaderNodes::BinaryType::Multiply:
|
||||||
|
{
|
||||||
|
switch (leftType)
|
||||||
|
{
|
||||||
|
case ShaderNodes::BasicType::Float1:
|
||||||
|
{
|
||||||
|
switch (rightType)
|
||||||
|
{
|
||||||
|
case ShaderNodes::BasicType::Float1:
|
||||||
|
return SpirvOp::OpFMul;
|
||||||
|
|
||||||
|
case ShaderNodes::BasicType::Float2:
|
||||||
|
case ShaderNodes::BasicType::Float3:
|
||||||
|
case ShaderNodes::BasicType::Float4:
|
||||||
|
swapOperands = true;
|
||||||
|
return SpirvOp::OpVectorTimesScalar;
|
||||||
|
|
||||||
|
case ShaderNodes::BasicType::Mat4x4:
|
||||||
|
swapOperands = true;
|
||||||
|
return SpirvOp::OpMatrixTimesScalar;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ShaderNodes::BasicType::Float2:
|
||||||
|
case ShaderNodes::BasicType::Float3:
|
||||||
|
case ShaderNodes::BasicType::Float4:
|
||||||
|
{
|
||||||
|
switch (rightType)
|
||||||
|
{
|
||||||
|
case ShaderNodes::BasicType::Float1:
|
||||||
|
return SpirvOp::OpVectorTimesScalar;
|
||||||
|
|
||||||
|
case ShaderNodes::BasicType::Float2:
|
||||||
|
case ShaderNodes::BasicType::Float3:
|
||||||
|
case ShaderNodes::BasicType::Float4:
|
||||||
|
return SpirvOp::OpFMul;
|
||||||
|
|
||||||
|
case ShaderNodes::BasicType::Mat4x4:
|
||||||
|
return SpirvOp::OpVectorTimesMatrix;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ShaderNodes::BasicType::Int1:
|
||||||
|
case ShaderNodes::BasicType::Int2:
|
||||||
|
case ShaderNodes::BasicType::Int3:
|
||||||
|
case ShaderNodes::BasicType::Int4:
|
||||||
|
case ShaderNodes::BasicType::UInt1:
|
||||||
|
case ShaderNodes::BasicType::UInt2:
|
||||||
|
case ShaderNodes::BasicType::UInt3:
|
||||||
|
case ShaderNodes::BasicType::UInt4:
|
||||||
|
return SpirvOp::OpIMul;
|
||||||
|
|
||||||
|
case ShaderNodes::BasicType::Mat4x4:
|
||||||
|
{
|
||||||
|
switch (rightType)
|
||||||
|
{
|
||||||
|
case ShaderNodes::BasicType::Float1: return SpirvOp::OpMatrixTimesScalar;
|
||||||
|
case ShaderNodes::BasicType::Float4: return SpirvOp::OpMatrixTimesVector;
|
||||||
|
case ShaderNodes::BasicType::Mat4x4: return SpirvOp::OpMatrixTimesMatrix;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(false);
|
||||||
|
throw std::runtime_error("unexpected binary operation");
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (swapOperands)
|
||||||
|
std::swap(leftOperand, rightOperand);
|
||||||
|
|
||||||
|
m_writer.GetInstructions().Append(op, m_writer.GetTypeId(resultType), resultId, leftOperand, rightOperand);
|
||||||
|
PushResultId(resultId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionLoad::Visit(ShaderNodes::Cast& node)
|
||||||
|
{
|
||||||
|
const ShaderExpressionType& targetExprType = node.exprType;
|
||||||
|
assert(std::holds_alternative<ShaderNodes::BasicType>(targetExprType));
|
||||||
|
|
||||||
|
ShaderNodes::BasicType targetType = std::get<ShaderNodes::BasicType>(targetExprType);
|
||||||
|
|
||||||
|
StackVector<UInt32> exprResults = NazaraStackVector(UInt32, node.expressions.size());
|
||||||
|
|
||||||
|
for (const auto& exprPtr : node.expressions)
|
||||||
|
{
|
||||||
|
if (!exprPtr)
|
||||||
|
break;
|
||||||
|
|
||||||
|
exprResults.push_back(EvaluateExpression(exprPtr));
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 resultId = m_writer.AllocateResultId();
|
||||||
|
|
||||||
|
m_writer.GetInstructions().AppendVariadic(SpirvOp::OpCompositeConstruct, [&](const auto& appender)
|
||||||
|
{
|
||||||
|
appender(m_writer.GetTypeId(targetType));
|
||||||
|
appender(resultId);
|
||||||
|
|
||||||
|
for (UInt32 exprResultId : exprResults)
|
||||||
|
appender(exprResultId);
|
||||||
|
});
|
||||||
|
|
||||||
|
PushResultId(resultId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionLoad::Visit(ShaderNodes::Constant& node)
|
||||||
|
{
|
||||||
|
std::visit([&] (const auto& value)
|
||||||
|
{
|
||||||
|
PushResultId(m_writer.GetConstantId(value));
|
||||||
|
}, node.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionLoad::Visit(ShaderNodes::DeclareVariable& node)
|
||||||
|
{
|
||||||
|
if (node.expression)
|
||||||
|
{
|
||||||
|
assert(node.variable->GetType() == ShaderNodes::VariableType::LocalVariable);
|
||||||
|
|
||||||
|
const auto& localVar = static_cast<const ShaderNodes::LocalVariable&>(*node.variable);
|
||||||
|
m_writer.WriteLocalVariable(localVar.name, EvaluateExpression(node.expression));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionLoad::Visit(ShaderNodes::ExpressionStatement& node)
|
||||||
|
{
|
||||||
|
Visit(node.expression);
|
||||||
|
PopResultId();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionLoad::Visit(ShaderNodes::Identifier& node)
|
||||||
|
{
|
||||||
|
Visit(node.var);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionLoad::Visit(ShaderNodes::IntrinsicCall& node)
|
||||||
|
{
|
||||||
|
switch (node.intrinsic)
|
||||||
|
{
|
||||||
|
case ShaderNodes::IntrinsicType::DotProduct:
|
||||||
|
{
|
||||||
|
const ShaderExpressionType& vecExprType = node.parameters[0]->GetExpressionType();
|
||||||
|
assert(std::holds_alternative<ShaderNodes::BasicType>(vecExprType));
|
||||||
|
|
||||||
|
ShaderNodes::BasicType vecType = std::get<ShaderNodes::BasicType>(vecExprType);
|
||||||
|
|
||||||
|
UInt32 typeId = m_writer.GetTypeId(node.GetComponentType(vecType));
|
||||||
|
|
||||||
|
UInt32 vec1 = EvaluateExpression(node.parameters[0]);
|
||||||
|
UInt32 vec2 = EvaluateExpression(node.parameters[1]);
|
||||||
|
|
||||||
|
UInt32 resultId = m_writer.AllocateResultId();
|
||||||
|
|
||||||
|
m_writer.GetInstructions().Append(SpirvOp::OpDot, typeId, resultId, vec1, vec2);
|
||||||
|
PushResultId(resultId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ShaderNodes::IntrinsicType::CrossProduct:
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("not yet implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionLoad::Visit(ShaderNodes::Sample2D& node)
|
||||||
|
{
|
||||||
|
UInt32 typeId = m_writer.GetTypeId(ShaderNodes::BasicType::Float4);
|
||||||
|
|
||||||
|
UInt32 samplerId = EvaluateExpression(node.sampler);
|
||||||
|
UInt32 coordinatesId = EvaluateExpression(node.coordinates);
|
||||||
|
UInt32 resultId = m_writer.AllocateResultId();
|
||||||
|
|
||||||
|
m_writer.GetInstructions().Append(SpirvOp::OpImageSampleImplicitLod, typeId, resultId, samplerId, coordinatesId);
|
||||||
|
PushResultId(resultId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionLoad::Visit(ShaderNodes::SwizzleOp& node)
|
||||||
|
{
|
||||||
|
const ShaderExpressionType& targetExprType = node.GetExpressionType();
|
||||||
|
assert(std::holds_alternative<ShaderNodes::BasicType>(targetExprType));
|
||||||
|
|
||||||
|
ShaderNodes::BasicType targetType = std::get<ShaderNodes::BasicType>(targetExprType);
|
||||||
|
|
||||||
|
UInt32 exprResultId = EvaluateExpression(node.expression);
|
||||||
|
UInt32 resultId = m_writer.AllocateResultId();
|
||||||
|
|
||||||
|
if (node.componentCount > 1)
|
||||||
|
{
|
||||||
|
// Swizzling is implemented via SpirvOp::OpVectorShuffle using the same vector twice as operands
|
||||||
|
m_writer.GetInstructions().AppendVariadic(SpirvOp::OpVectorShuffle, [&](const auto& appender)
|
||||||
|
{
|
||||||
|
appender(m_writer.GetTypeId(targetType));
|
||||||
|
appender(resultId);
|
||||||
|
appender(exprResultId);
|
||||||
|
appender(exprResultId);
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < node.componentCount; ++i)
|
||||||
|
appender(UInt32(node.components[0]) - UInt32(node.components[i]));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Extract a single component from the vector
|
||||||
|
assert(node.componentCount == 1);
|
||||||
|
|
||||||
|
m_writer.GetInstructions().Append(SpirvOp::OpCompositeExtract, m_writer.GetTypeId(targetType), resultId, exprResultId, UInt32(node.components[0]) - UInt32(ShaderNodes::SwizzleComponent::First) );
|
||||||
|
}
|
||||||
|
|
||||||
|
PushResultId(resultId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionLoad::Visit(ShaderNodes::BuiltinVariable& /*var*/)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionLoad::Visit(ShaderNodes::InputVariable& var)
|
||||||
|
{
|
||||||
|
PushResultId(m_writer.ReadInputVariable(var.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionLoad::Visit(ShaderNodes::LocalVariable& var)
|
||||||
|
{
|
||||||
|
PushResultId(m_writer.ReadLocalVariable(var.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionLoad::Visit(ShaderNodes::ParameterVariable& /*var*/)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionLoad::Visit(ShaderNodes::UniformVariable& var)
|
||||||
|
{
|
||||||
|
PushResultId(m_writer.ReadUniformVariable(var.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 SpirvExpressionLoad::EvaluateExpression(const ShaderNodes::ExpressionPtr& expr)
|
||||||
|
{
|
||||||
|
Visit(expr);
|
||||||
|
return PopResultId();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionLoad::PushResultId(UInt32 value)
|
||||||
|
{
|
||||||
|
m_resultIds.push_back(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 SpirvExpressionLoad::PopResultId()
|
||||||
|
{
|
||||||
|
if (m_resultIds.empty())
|
||||||
|
throw std::runtime_error("invalid operation");
|
||||||
|
|
||||||
|
UInt32 resultId = m_resultIds.back();
|
||||||
|
m_resultIds.pop_back();
|
||||||
|
|
||||||
|
return resultId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/Shader/SpirvExpressionLoadAccessMember.hpp>
|
||||||
|
#include <Nazara/Core/StackVector.hpp>
|
||||||
|
#include <Nazara/Shader/SpirvSection.hpp>
|
||||||
|
#include <Nazara/Shader/SpirvWriter.hpp>
|
||||||
|
#include <Nazara/Shader/Debug.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
|
||||||
|
template<class... Ts> overloaded(Ts...)->overloaded<Ts...>;
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 SpirvExpressionLoadAccessMember::EvaluateExpression(ShaderNodes::AccessMember& expr)
|
||||||
|
{
|
||||||
|
Visit(expr);
|
||||||
|
|
||||||
|
return std::visit(overloaded
|
||||||
|
{
|
||||||
|
[&](const Pointer& pointer) -> UInt32
|
||||||
|
{
|
||||||
|
UInt32 resultId = m_writer.AllocateResultId();
|
||||||
|
|
||||||
|
m_writer.GetInstructions().Append(SpirvOp::OpLoad, pointer.pointedTypeId, resultId, pointer.resultId);
|
||||||
|
|
||||||
|
return resultId;
|
||||||
|
},
|
||||||
|
[&](const Value& value) -> UInt32
|
||||||
|
{
|
||||||
|
return value.resultId;
|
||||||
|
},
|
||||||
|
[this](std::monostate) -> UInt32
|
||||||
|
{
|
||||||
|
throw std::runtime_error("an internal error occurred");
|
||||||
|
}
|
||||||
|
}, m_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionLoadAccessMember::Visit(ShaderNodes::AccessMember& node)
|
||||||
|
{
|
||||||
|
Visit(node.structExpr);
|
||||||
|
|
||||||
|
std::visit(overloaded
|
||||||
|
{
|
||||||
|
[&](const Pointer& pointer)
|
||||||
|
{
|
||||||
|
UInt32 resultId = m_writer.AllocateResultId();
|
||||||
|
UInt32 pointerType = m_writer.RegisterPointerType(node.exprType, pointer.storage); //< FIXME
|
||||||
|
UInt32 typeId = m_writer.GetTypeId(node.exprType);
|
||||||
|
|
||||||
|
m_writer.GetInstructions().AppendVariadic(SpirvOp::OpAccessChain, [&](const auto& appender)
|
||||||
|
{
|
||||||
|
appender(pointerType);
|
||||||
|
appender(resultId);
|
||||||
|
appender(pointer.resultId);
|
||||||
|
|
||||||
|
for (std::size_t index : node.memberIndices)
|
||||||
|
appender(m_writer.GetConstantId(Int32(index)));
|
||||||
|
});
|
||||||
|
|
||||||
|
m_value = Pointer { pointer.storage, resultId, typeId };
|
||||||
|
},
|
||||||
|
[&](const Value& value)
|
||||||
|
{
|
||||||
|
UInt32 resultId = m_writer.AllocateResultId();
|
||||||
|
UInt32 typeId = m_writer.GetTypeId(node.exprType);
|
||||||
|
|
||||||
|
m_writer.GetInstructions().AppendVariadic(SpirvOp::OpCompositeExtract, [&](const auto& appender)
|
||||||
|
{
|
||||||
|
appender(typeId);
|
||||||
|
appender(resultId);
|
||||||
|
appender(value.resultId);
|
||||||
|
|
||||||
|
for (std::size_t index : node.memberIndices)
|
||||||
|
appender(m_writer.GetConstantId(Int32(index)));
|
||||||
|
});
|
||||||
|
|
||||||
|
m_value = Value { resultId };
|
||||||
|
},
|
||||||
|
[this](std::monostate)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("an internal error occurred");
|
||||||
|
}
|
||||||
|
}, m_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionLoadAccessMember::Visit(ShaderNodes::Identifier& node)
|
||||||
|
{
|
||||||
|
Visit(node.var);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionLoadAccessMember::Visit(ShaderNodes::InputVariable& var)
|
||||||
|
{
|
||||||
|
auto inputVar = m_writer.GetInputVariable(var.name);
|
||||||
|
|
||||||
|
if (auto resultIdOpt = m_writer.ReadVariable(inputVar, SpirvWriter::OnlyCache{}))
|
||||||
|
m_value = Value{ *resultIdOpt };
|
||||||
|
else
|
||||||
|
m_value = Pointer{ SpirvStorageClass::Input, inputVar.varId, inputVar.typeId };
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionLoadAccessMember::Visit(ShaderNodes::UniformVariable& var)
|
||||||
|
{
|
||||||
|
auto uniformVar = m_writer.GetUniformVariable(var.name);
|
||||||
|
|
||||||
|
if (auto resultIdOpt = m_writer.ReadVariable(uniformVar, SpirvWriter::OnlyCache{}))
|
||||||
|
m_value = Value{ *resultIdOpt };
|
||||||
|
else
|
||||||
|
m_value = Pointer{ SpirvStorageClass::Uniform, uniformVar.varId, uniformVar.typeId };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/Shader/SpirvExpressionStore.hpp>
|
||||||
|
#include <Nazara/Shader/SpirvSection.hpp>
|
||||||
|
#include <Nazara/Shader/SpirvWriter.hpp>
|
||||||
|
#include <Nazara/Shader/Debug.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
|
||||||
|
template<class... Ts> overloaded(Ts...)->overloaded<Ts...>;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionStore::Store(const ShaderNodes::ExpressionPtr& node, UInt32 resultId)
|
||||||
|
{
|
||||||
|
Visit(node);
|
||||||
|
|
||||||
|
std::visit(overloaded
|
||||||
|
{
|
||||||
|
[&](const Pointer& pointer)
|
||||||
|
{
|
||||||
|
m_writer.GetInstructions().Append(SpirvOp::OpStore, pointer.resultId, resultId);
|
||||||
|
},
|
||||||
|
[&](const LocalVar& value)
|
||||||
|
{
|
||||||
|
m_writer.WriteLocalVariable(value.varName, resultId);
|
||||||
|
},
|
||||||
|
[this](std::monostate)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("an internal error occurred");
|
||||||
|
}
|
||||||
|
}, m_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionStore::Visit(ShaderNodes::AccessMember& node)
|
||||||
|
{
|
||||||
|
Visit(node.structExpr);
|
||||||
|
|
||||||
|
std::visit(overloaded
|
||||||
|
{
|
||||||
|
[&](const Pointer& pointer) -> UInt32
|
||||||
|
{
|
||||||
|
UInt32 resultId = m_writer.AllocateResultId();
|
||||||
|
UInt32 pointerType = m_writer.RegisterPointerType(node.exprType, pointer.storage); //< FIXME
|
||||||
|
UInt32 typeId = m_writer.GetTypeId(node.exprType);
|
||||||
|
|
||||||
|
m_writer.GetInstructions().AppendVariadic(SpirvOp::OpAccessChain, [&](const auto& appender)
|
||||||
|
{
|
||||||
|
appender(pointerType);
|
||||||
|
appender(resultId);
|
||||||
|
appender(pointer.resultId);
|
||||||
|
|
||||||
|
for (std::size_t index : node.memberIndices)
|
||||||
|
appender(m_writer.GetConstantId(Int32(index)));
|
||||||
|
});
|
||||||
|
|
||||||
|
m_value = Pointer{ pointer.storage, resultId };
|
||||||
|
|
||||||
|
return resultId;
|
||||||
|
},
|
||||||
|
[&](const LocalVar& value) -> UInt32
|
||||||
|
{
|
||||||
|
throw std::runtime_error("not yet implemented");
|
||||||
|
},
|
||||||
|
[this](std::monostate) -> UInt32
|
||||||
|
{
|
||||||
|
throw std::runtime_error("an internal error occurred");
|
||||||
|
}
|
||||||
|
}, m_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionStore::Visit(ShaderNodes::Identifier& node)
|
||||||
|
{
|
||||||
|
Visit(node.var);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionStore::Visit(ShaderNodes::SwizzleOp& node)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionStore::Visit(ShaderNodes::BuiltinVariable& var)
|
||||||
|
{
|
||||||
|
const auto& outputVar = m_writer.GetBuiltinVariable(var.entry);
|
||||||
|
|
||||||
|
m_value = Pointer{ SpirvStorageClass::Output, outputVar.varId };
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionStore::Visit(ShaderNodes::LocalVariable& var)
|
||||||
|
{
|
||||||
|
m_value = LocalVar{ var.name };
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvExpressionStore::Visit(ShaderNodes::OutputVariable& var)
|
||||||
|
{
|
||||||
|
const auto& outputVar = m_writer.GetOutputVariable(var.name);
|
||||||
|
|
||||||
|
m_value = Pointer{ SpirvStorageClass::Output, outputVar.varId };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,231 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/Shader/SpirvPrinter.hpp>
|
||||||
|
#include <Nazara/Core/CallOnExit.hpp>
|
||||||
|
#include <Nazara/Shader/SpirvData.hpp>
|
||||||
|
#include <SpirV/spirv.h>
|
||||||
|
#include <cassert>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <Nazara/Shader/Debug.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
struct SpirvPrinter::State
|
||||||
|
{
|
||||||
|
const UInt32* codepoints;
|
||||||
|
std::size_t index = 0;
|
||||||
|
std::size_t count;
|
||||||
|
std::ostringstream stream;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string SpirvPrinter::Print(const UInt32* codepoints, std::size_t count)
|
||||||
|
{
|
||||||
|
State state;
|
||||||
|
state.codepoints = codepoints;
|
||||||
|
state.count = count;
|
||||||
|
|
||||||
|
m_currentState = &state;
|
||||||
|
CallOnExit resetOnExit([&] { m_currentState = nullptr; });
|
||||||
|
|
||||||
|
UInt32 magicNumber = ReadWord();
|
||||||
|
if (magicNumber != SpvMagicNumber)
|
||||||
|
throw std::runtime_error("invalid Spir-V: magic number didn't match");
|
||||||
|
|
||||||
|
m_currentState->stream << "Spir-V module\n";
|
||||||
|
|
||||||
|
UInt32 versionNumber = ReadWord();
|
||||||
|
if (versionNumber > SpvVersion)
|
||||||
|
throw std::runtime_error("Spir-V is more recent than printer, dismissing");
|
||||||
|
|
||||||
|
UInt8 majorVersion = ((versionNumber) >> 16) & 0xFF;
|
||||||
|
UInt8 minorVersion = ((versionNumber) >> 8) & 0xFF;
|
||||||
|
|
||||||
|
m_currentState->stream << "Version " + std::to_string(+majorVersion) << "." << std::to_string(+minorVersion) << "\n";
|
||||||
|
|
||||||
|
UInt32 generatorId = ReadWord();
|
||||||
|
|
||||||
|
m_currentState->stream << "Generator: " << std::to_string(generatorId) << "\n";
|
||||||
|
|
||||||
|
UInt32 bound = ReadWord();
|
||||||
|
m_currentState->stream << "Bound: " << std::to_string(bound) << "\n";
|
||||||
|
|
||||||
|
UInt32 schema = ReadWord();
|
||||||
|
m_currentState->stream << "Schema: " << std::to_string(schema) << "\n";
|
||||||
|
|
||||||
|
while (m_currentState->index < m_currentState->count)
|
||||||
|
AppendInstruction();
|
||||||
|
|
||||||
|
return m_currentState->stream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvPrinter::AppendInstruction()
|
||||||
|
{
|
||||||
|
std::size_t startIndex = m_currentState->index;
|
||||||
|
|
||||||
|
UInt32 firstWord = ReadWord();
|
||||||
|
|
||||||
|
UInt16 wordCount = static_cast<UInt16>((firstWord >> 16) & 0xFFFF);
|
||||||
|
UInt16 opcode = static_cast<UInt16>(firstWord & 0xFFFF);
|
||||||
|
|
||||||
|
const SpirvInstruction* inst = GetInstructionData(opcode);
|
||||||
|
if (!inst)
|
||||||
|
throw std::runtime_error("invalid instruction");
|
||||||
|
|
||||||
|
m_currentState->stream << inst->name;
|
||||||
|
|
||||||
|
std::size_t currentOperand = 0;
|
||||||
|
std::size_t instructionEnd = startIndex + wordCount;
|
||||||
|
while (m_currentState->index < instructionEnd)
|
||||||
|
{
|
||||||
|
const SpirvInstruction::Operand* operand = &inst->operands[currentOperand];
|
||||||
|
|
||||||
|
m_currentState->stream << " " << operand->name << "(";
|
||||||
|
|
||||||
|
switch (operand->kind)
|
||||||
|
{
|
||||||
|
case SpirvOperandKind::ImageOperands:
|
||||||
|
case SpirvOperandKind::FPFastMathMode:
|
||||||
|
case SpirvOperandKind::SelectionControl:
|
||||||
|
case SpirvOperandKind::LoopControl:
|
||||||
|
case SpirvOperandKind::FunctionControl:
|
||||||
|
case SpirvOperandKind::MemorySemantics:
|
||||||
|
case SpirvOperandKind::MemoryAccess:
|
||||||
|
case SpirvOperandKind::KernelProfilingInfo:
|
||||||
|
case SpirvOperandKind::RayFlags:
|
||||||
|
case SpirvOperandKind::SourceLanguage:
|
||||||
|
case SpirvOperandKind::ExecutionModel:
|
||||||
|
case SpirvOperandKind::AddressingModel:
|
||||||
|
case SpirvOperandKind::MemoryModel:
|
||||||
|
case SpirvOperandKind::ExecutionMode:
|
||||||
|
case SpirvOperandKind::StorageClass:
|
||||||
|
case SpirvOperandKind::Dim:
|
||||||
|
case SpirvOperandKind::SamplerAddressingMode:
|
||||||
|
case SpirvOperandKind::SamplerFilterMode:
|
||||||
|
case SpirvOperandKind::ImageFormat:
|
||||||
|
case SpirvOperandKind::ImageChannelOrder:
|
||||||
|
case SpirvOperandKind::ImageChannelDataType:
|
||||||
|
case SpirvOperandKind::FPRoundingMode:
|
||||||
|
case SpirvOperandKind::LinkageType:
|
||||||
|
case SpirvOperandKind::AccessQualifier:
|
||||||
|
case SpirvOperandKind::FunctionParameterAttribute:
|
||||||
|
case SpirvOperandKind::Decoration:
|
||||||
|
case SpirvOperandKind::BuiltIn:
|
||||||
|
case SpirvOperandKind::Scope:
|
||||||
|
case SpirvOperandKind::GroupOperation:
|
||||||
|
case SpirvOperandKind::KernelEnqueueFlags:
|
||||||
|
case SpirvOperandKind::Capability:
|
||||||
|
case SpirvOperandKind::RayQueryIntersection:
|
||||||
|
case SpirvOperandKind::RayQueryCommittedIntersectionType:
|
||||||
|
case SpirvOperandKind::RayQueryCandidateIntersectionType:
|
||||||
|
case SpirvOperandKind::IdResultType:
|
||||||
|
case SpirvOperandKind::IdResult:
|
||||||
|
case SpirvOperandKind::IdMemorySemantics:
|
||||||
|
case SpirvOperandKind::IdScope:
|
||||||
|
case SpirvOperandKind::IdRef:
|
||||||
|
case SpirvOperandKind::LiteralInteger:
|
||||||
|
case SpirvOperandKind::LiteralExtInstInteger:
|
||||||
|
case SpirvOperandKind::LiteralSpecConstantOpInteger:
|
||||||
|
case SpirvOperandKind::LiteralContextDependentNumber: //< FIXME
|
||||||
|
{
|
||||||
|
UInt32 value = ReadWord();
|
||||||
|
m_currentState->stream << value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SpirvOperandKind::LiteralString:
|
||||||
|
{
|
||||||
|
std::string str = ReadString();
|
||||||
|
m_currentState->stream << "\"" << str << "\"";
|
||||||
|
|
||||||
|
/*
|
||||||
|
std::size_t offset = GetOutputOffset();
|
||||||
|
|
||||||
|
std::size_t size4 = CountWord(str);
|
||||||
|
for (std::size_t i = 0; i < size4; ++i)
|
||||||
|
{
|
||||||
|
UInt32 codepoint = 0;
|
||||||
|
for (std::size_t j = 0; j < 4; ++j)
|
||||||
|
{
|
||||||
|
std::size_t pos = i * 4 + j;
|
||||||
|
if (pos < str.size())
|
||||||
|
codepoint |= UInt32(str[pos]) << (j * 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
Append(codepoint);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
case SpirvOperandKind::PairLiteralIntegerIdRef:
|
||||||
|
{
|
||||||
|
ReadWord();
|
||||||
|
ReadWord();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SpirvOperandKind::PairIdRefLiteralInteger:
|
||||||
|
{
|
||||||
|
ReadWord();
|
||||||
|
ReadWord();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SpirvOperandKind::PairIdRefIdRef:
|
||||||
|
{
|
||||||
|
ReadWord();
|
||||||
|
ReadWord();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*case SpirvOperandKind::LiteralContextDependentNumber:
|
||||||
|
{
|
||||||
|
throw std::runtime_error("not yet implemented");
|
||||||
|
}*/
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
m_currentState->stream << ")";
|
||||||
|
|
||||||
|
if (currentOperand < inst->minOperandCount - 1)
|
||||||
|
currentOperand++;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_currentState->stream << "\n";
|
||||||
|
|
||||||
|
assert(m_currentState->index == startIndex + wordCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SpirvPrinter::ReadString()
|
||||||
|
{
|
||||||
|
std::string str;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
UInt32 value = ReadWord();
|
||||||
|
for (std::size_t j = 0; j < 4; ++j)
|
||||||
|
{
|
||||||
|
char c = static_cast<char>((value >> (j * 8)) & 0xFF);
|
||||||
|
if (c == '\0')
|
||||||
|
return str;
|
||||||
|
|
||||||
|
str.push_back(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 SpirvPrinter::ReadWord()
|
||||||
|
{
|
||||||
|
if (m_currentState->index >= m_currentState->count)
|
||||||
|
throw std::runtime_error("unexpected end of stream");
|
||||||
|
|
||||||
|
return m_currentState->codepoints[m_currentState->index++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright (C) 2020 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - Shader generator"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/Shader/SpirvStatementVisitor.hpp>
|
||||||
|
#include <Nazara/Shader/SpirvExpressionLoad.hpp>
|
||||||
|
#include <Nazara/Shader/SpirvExpressionStore.hpp>
|
||||||
|
#include <Nazara/Shader/SpirvWriter.hpp>
|
||||||
|
#include <Nazara/Shader/Debug.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
void SpirvStatementVisitor::Visit(ShaderNodes::AssignOp& node)
|
||||||
|
{
|
||||||
|
SpirvExpressionLoad loadVisitor(m_writer);
|
||||||
|
SpirvExpressionStore storeVisitor(m_writer);
|
||||||
|
storeVisitor.Store(node.left, loadVisitor.EvaluateExpression(node.right));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvStatementVisitor::Visit(ShaderNodes::Branch& node)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvStatementVisitor::Visit(ShaderNodes::DeclareVariable& node)
|
||||||
|
{
|
||||||
|
if (node.expression)
|
||||||
|
{
|
||||||
|
assert(node.variable->GetType() == ShaderNodes::VariableType::LocalVariable);
|
||||||
|
|
||||||
|
const auto& localVar = static_cast<const ShaderNodes::LocalVariable&>(*node.variable);
|
||||||
|
|
||||||
|
SpirvExpressionLoad loadVisitor(m_writer);
|
||||||
|
m_writer.WriteLocalVariable(localVar.name, loadVisitor.EvaluateExpression(node.expression));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvStatementVisitor::Visit(ShaderNodes::ExpressionStatement& node)
|
||||||
|
{
|
||||||
|
SpirvExpressionLoad loadVisitor(m_writer);
|
||||||
|
loadVisitor.Visit(node.expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpirvStatementVisitor::Visit(ShaderNodes::StatementBlock& node)
|
||||||
|
{
|
||||||
|
for (auto& statement : node.statements)
|
||||||
|
Visit(statement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include <Nazara/Shader/SpirvConstantCache.hpp>
|
#include <Nazara/Shader/SpirvConstantCache.hpp>
|
||||||
#include <Nazara/Shader/SpirvData.hpp>
|
#include <Nazara/Shader/SpirvData.hpp>
|
||||||
#include <Nazara/Shader/SpirvSection.hpp>
|
#include <Nazara/Shader/SpirvSection.hpp>
|
||||||
|
#include <Nazara/Shader/SpirvStatementVisitor.hpp>
|
||||||
#include <tsl/ordered_map.h>
|
#include <tsl/ordered_map.h>
|
||||||
#include <tsl/ordered_set.h>
|
#include <tsl/ordered_set.h>
|
||||||
#include <SpirV/spirv.h>
|
#include <SpirV/spirv.h>
|
||||||
|
|
@ -154,14 +155,6 @@ namespace Nz
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SpirvWriter::ExtVar
|
|
||||||
{
|
|
||||||
UInt32 pointerTypeId;
|
|
||||||
UInt32 typeId;
|
|
||||||
UInt32 varId;
|
|
||||||
std::optional<UInt32> valueId;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SpirvWriter::State
|
struct SpirvWriter::State
|
||||||
{
|
{
|
||||||
State() :
|
State() :
|
||||||
|
|
@ -387,7 +380,8 @@ namespace Nz
|
||||||
state.instructions.Append(SpirvOp::OpFunctionParameter, GetTypeId(param.type), paramResultId);
|
state.instructions.Append(SpirvOp::OpFunctionParameter, GetTypeId(param.type), paramResultId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Visit(functionStatements[funcIndex]);
|
SpirvStatementVisitor visitor(*this);
|
||||||
|
visitor.Visit(functionStatements[funcIndex]);
|
||||||
|
|
||||||
if (func.returnType == ShaderNodes::BasicType::Void)
|
if (func.returnType == ShaderNodes::BasicType::Void)
|
||||||
state.instructions.Append(SpirvOp::OpReturn);
|
state.instructions.Append(SpirvOp::OpReturn);
|
||||||
|
|
@ -480,12 +474,6 @@ namespace Nz
|
||||||
m_currentState->header.Append(SpirvOp::OpMemoryModel, SpvAddressingModelLogical, SpvMemoryModelGLSL450);
|
m_currentState->header.Append(SpirvOp::OpMemoryModel, SpvAddressingModelLogical, SpvMemoryModelGLSL450);
|
||||||
}
|
}
|
||||||
|
|
||||||
UInt32 SpirvWriter::EvaluateExpression(const ShaderNodes::ExpressionPtr& expr)
|
|
||||||
{
|
|
||||||
Visit(expr);
|
|
||||||
return PopResultId();
|
|
||||||
}
|
|
||||||
|
|
||||||
UInt32 SpirvWriter::GetConstantId(const ShaderConstantValue& value) const
|
UInt32 SpirvWriter::GetConstantId(const ShaderConstantValue& value) const
|
||||||
{
|
{
|
||||||
return m_currentState->constantTypeCache.GetId(*SpirvConstantCache::BuildConstant(value));
|
return m_currentState->constantTypeCache.GetId(*SpirvConstantCache::BuildConstant(value));
|
||||||
|
|
@ -507,6 +495,43 @@ namespace Nz
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto SpirvWriter::GetBuiltinVariable(ShaderNodes::BuiltinEntry builtin) const -> const ExtVar&
|
||||||
|
{
|
||||||
|
auto it = m_currentState->builtinIds.find(builtin);
|
||||||
|
assert(it != m_currentState->builtinIds.end());
|
||||||
|
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto SpirvWriter::GetInputVariable(const std::string& name) const -> const ExtVar&
|
||||||
|
{
|
||||||
|
auto it = m_currentState->inputIds.find(name);
|
||||||
|
assert(it != m_currentState->inputIds.end());
|
||||||
|
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto SpirvWriter::GetOutputVariable(const std::string& name) const -> const ExtVar&
|
||||||
|
{
|
||||||
|
auto it = m_currentState->outputIds.find(name);
|
||||||
|
assert(it != m_currentState->outputIds.end());
|
||||||
|
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto SpirvWriter::GetUniformVariable(const std::string& name) const -> const ExtVar&
|
||||||
|
{
|
||||||
|
auto it = m_currentState->uniformIds.find(name);
|
||||||
|
assert(it != m_currentState->uniformIds.end());
|
||||||
|
|
||||||
|
return it.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
SpirvSection& SpirvWriter::GetInstructions()
|
||||||
|
{
|
||||||
|
return m_currentState->instructions;
|
||||||
|
}
|
||||||
|
|
||||||
UInt32 SpirvWriter::GetPointerTypeId(const ShaderExpressionType& type, SpirvStorageClass storageClass) const
|
UInt32 SpirvWriter::GetPointerTypeId(const ShaderExpressionType& type, SpirvStorageClass storageClass) const
|
||||||
{
|
{
|
||||||
return m_currentState->constantTypeCache.GetId(*SpirvConstantCache::BuildPointerType(*m_context.shader, type, storageClass));
|
return m_currentState->constantTypeCache.GetId(*SpirvConstantCache::BuildPointerType(*m_context.shader, type, storageClass));
|
||||||
|
|
@ -517,20 +542,53 @@ namespace Nz
|
||||||
return m_currentState->constantTypeCache.GetId(*SpirvConstantCache::BuildType(*m_context.shader, type));
|
return m_currentState->constantTypeCache.GetId(*SpirvConstantCache::BuildType(*m_context.shader, type));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpirvWriter::PushResultId(UInt32 value)
|
UInt32 SpirvWriter::ReadInputVariable(const std::string& name)
|
||||||
{
|
{
|
||||||
m_currentState->resultIds.push_back(value);
|
auto it = m_currentState->inputIds.find(name);
|
||||||
|
assert(it != m_currentState->inputIds.end());
|
||||||
|
|
||||||
|
return ReadVariable(it.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
UInt32 SpirvWriter::PopResultId()
|
std::optional<UInt32> SpirvWriter::ReadInputVariable(const std::string& name, OnlyCache)
|
||||||
{
|
{
|
||||||
if (m_currentState->resultIds.empty())
|
auto it = m_currentState->inputIds.find(name);
|
||||||
throw std::runtime_error("invalid operation");
|
assert(it != m_currentState->inputIds.end());
|
||||||
|
|
||||||
UInt32 resultId = m_currentState->resultIds.back();
|
return ReadVariable(it.value(), OnlyCache{});
|
||||||
m_currentState->resultIds.pop_back();
|
}
|
||||||
|
|
||||||
return resultId;
|
UInt32 SpirvWriter::ReadLocalVariable(const std::string& name)
|
||||||
|
{
|
||||||
|
auto it = m_currentState->varToResult.find(name);
|
||||||
|
assert(it != m_currentState->varToResult.end());
|
||||||
|
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<UInt32> SpirvWriter::ReadLocalVariable(const std::string& name, OnlyCache)
|
||||||
|
{
|
||||||
|
auto it = m_currentState->varToResult.find(name);
|
||||||
|
if (it == m_currentState->varToResult.end())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 SpirvWriter::ReadUniformVariable(const std::string& name)
|
||||||
|
{
|
||||||
|
auto it = m_currentState->uniformIds.find(name);
|
||||||
|
assert(it != m_currentState->uniformIds.end());
|
||||||
|
|
||||||
|
return ReadVariable(it.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<UInt32> SpirvWriter::ReadUniformVariable(const std::string& name, OnlyCache)
|
||||||
|
{
|
||||||
|
auto it = m_currentState->uniformIds.find(name);
|
||||||
|
assert(it != m_currentState->uniformIds.end());
|
||||||
|
|
||||||
|
return ReadVariable(it.value(), OnlyCache{});
|
||||||
}
|
}
|
||||||
|
|
||||||
UInt32 SpirvWriter::ReadVariable(ExtVar& var)
|
UInt32 SpirvWriter::ReadVariable(ExtVar& var)
|
||||||
|
|
@ -546,6 +604,14 @@ namespace Nz
|
||||||
return var.valueId.value();
|
return var.valueId.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<UInt32> SpirvWriter::ReadVariable(const ExtVar& var, OnlyCache)
|
||||||
|
{
|
||||||
|
if (!var.valueId.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return var.valueId.value();
|
||||||
|
}
|
||||||
|
|
||||||
UInt32 SpirvWriter::RegisterConstant(const ShaderConstantValue& value)
|
UInt32 SpirvWriter::RegisterConstant(const ShaderConstantValue& value)
|
||||||
{
|
{
|
||||||
return m_currentState->constantTypeCache.Register(*SpirvConstantCache::BuildConstant(value));
|
return m_currentState->constantTypeCache.Register(*SpirvConstantCache::BuildConstant(value));
|
||||||
|
|
@ -578,572 +644,10 @@ namespace Nz
|
||||||
return m_currentState->constantTypeCache.Register(*SpirvConstantCache::BuildType(*m_context.shader, type));
|
return m_currentState->constantTypeCache.Register(*SpirvConstantCache::BuildType(*m_context.shader, type));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpirvWriter::Visit(ShaderNodes::AccessMember& node)
|
void SpirvWriter::WriteLocalVariable(std::string name, UInt32 resultId)
|
||||||
{
|
{
|
||||||
UInt32 pointerId;
|
assert(m_currentState);
|
||||||
SpirvStorageClass storage;
|
m_currentState->varToResult.insert_or_assign(std::move(name), resultId);
|
||||||
|
|
||||||
switch (node.structExpr->GetType())
|
|
||||||
{
|
|
||||||
case ShaderNodes::NodeType::Identifier:
|
|
||||||
{
|
|
||||||
auto& identifier = static_cast<ShaderNodes::Identifier&>(*node.structExpr);
|
|
||||||
switch (identifier.var->GetType())
|
|
||||||
{
|
|
||||||
case ShaderNodes::VariableType::BuiltinVariable:
|
|
||||||
{
|
|
||||||
auto& builtinvar = static_cast<ShaderNodes::BuiltinVariable&>(*identifier.var);
|
|
||||||
auto it = m_currentState->builtinIds.find(builtinvar.entry);
|
|
||||||
assert(it != m_currentState->builtinIds.end());
|
|
||||||
|
|
||||||
pointerId = it->second.varId;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ShaderNodes::VariableType::InputVariable:
|
|
||||||
{
|
|
||||||
auto& inputVar = static_cast<ShaderNodes::InputVariable&>(*identifier.var);
|
|
||||||
auto it = m_currentState->inputIds.find(inputVar.name);
|
|
||||||
assert(it != m_currentState->inputIds.end());
|
|
||||||
|
|
||||||
storage = SpirvStorageClass::Input;
|
|
||||||
|
|
||||||
pointerId = it->second.varId;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ShaderNodes::VariableType::OutputVariable:
|
|
||||||
{
|
|
||||||
auto& outputVar = static_cast<ShaderNodes::OutputVariable&>(*identifier.var);
|
|
||||||
auto it = m_currentState->outputIds.find(outputVar.name);
|
|
||||||
assert(it != m_currentState->outputIds.end());
|
|
||||||
|
|
||||||
storage = SpirvStorageClass::Output;
|
|
||||||
|
|
||||||
pointerId = it->second.varId;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ShaderNodes::VariableType::UniformVariable:
|
|
||||||
{
|
|
||||||
auto& uniformVar = static_cast<ShaderNodes::UniformVariable&>(*identifier.var);
|
|
||||||
auto it = m_currentState->uniformIds.find(uniformVar.name);
|
|
||||||
assert(it != m_currentState->uniformIds.end());
|
|
||||||
|
|
||||||
storage = SpirvStorageClass::Uniform;
|
|
||||||
|
|
||||||
pointerId = it->second.varId;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ShaderNodes::VariableType::LocalVariable:
|
|
||||||
case ShaderNodes::VariableType::ParameterVariable:
|
|
||||||
default:
|
|
||||||
throw std::runtime_error("not yet implemented");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ShaderNodes::NodeType::SwizzleOp: //< TODO
|
|
||||||
default:
|
|
||||||
throw std::runtime_error("not yet implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
UInt32 memberPointerId = AllocateResultId();
|
|
||||||
UInt32 pointerType = RegisterPointerType(node.exprType, storage); //< FIXME
|
|
||||||
UInt32 typeId = GetTypeId(node.exprType);
|
|
||||||
|
|
||||||
m_currentState->instructions.AppendVariadic(SpirvOp::OpAccessChain, [&](const auto& appender)
|
|
||||||
{
|
|
||||||
appender(pointerType);
|
|
||||||
appender(memberPointerId);
|
|
||||||
appender(pointerId);
|
|
||||||
|
|
||||||
for (std::size_t index : node.memberIndices)
|
|
||||||
appender(GetConstantId(Int32(index)));
|
|
||||||
});
|
|
||||||
|
|
||||||
UInt32 resultId = AllocateResultId();
|
|
||||||
|
|
||||||
m_currentState->instructions.Append(SpirvOp::OpLoad, typeId, resultId, memberPointerId);
|
|
||||||
|
|
||||||
PushResultId(resultId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpirvWriter::Visit(ShaderNodes::AssignOp& node)
|
|
||||||
{
|
|
||||||
UInt32 result = EvaluateExpression(node.right);
|
|
||||||
|
|
||||||
switch (node.left->GetType())
|
|
||||||
{
|
|
||||||
case ShaderNodes::NodeType::Identifier:
|
|
||||||
{
|
|
||||||
auto& identifier = static_cast<ShaderNodes::Identifier&>(*node.left);
|
|
||||||
switch (identifier.var->GetType())
|
|
||||||
{
|
|
||||||
case ShaderNodes::VariableType::BuiltinVariable:
|
|
||||||
{
|
|
||||||
auto& builtinvar = static_cast<ShaderNodes::BuiltinVariable&>(*identifier.var);
|
|
||||||
auto it = m_currentState->builtinIds.find(builtinvar.entry);
|
|
||||||
assert(it != m_currentState->builtinIds.end());
|
|
||||||
|
|
||||||
m_currentState->instructions.Append(SpirvOp::OpStore, it->second.varId, result);
|
|
||||||
PushResultId(result);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ShaderNodes::VariableType::OutputVariable:
|
|
||||||
{
|
|
||||||
auto& outputVar = static_cast<ShaderNodes::OutputVariable&>(*identifier.var);
|
|
||||||
auto it = m_currentState->outputIds.find(outputVar.name);
|
|
||||||
assert(it != m_currentState->outputIds.end());
|
|
||||||
|
|
||||||
m_currentState->instructions.Append(SpirvOp::OpStore, it->second.varId, result);
|
|
||||||
PushResultId(result);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ShaderNodes::VariableType::InputVariable:
|
|
||||||
case ShaderNodes::VariableType::LocalVariable:
|
|
||||||
case ShaderNodes::VariableType::ParameterVariable:
|
|
||||||
case ShaderNodes::VariableType::UniformVariable:
|
|
||||||
default:
|
|
||||||
throw std::runtime_error("not yet implemented");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ShaderNodes::NodeType::SwizzleOp: //< TODO
|
|
||||||
default:
|
|
||||||
throw std::runtime_error("not yet implemented");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpirvWriter::Visit(ShaderNodes::Branch& node)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("not yet implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpirvWriter::Visit(ShaderNodes::BinaryOp& node)
|
|
||||||
{
|
|
||||||
ShaderExpressionType resultExprType = node.GetExpressionType();
|
|
||||||
assert(std::holds_alternative<ShaderNodes::BasicType>(resultExprType));
|
|
||||||
|
|
||||||
const ShaderExpressionType& leftExprType = node.left->GetExpressionType();
|
|
||||||
assert(std::holds_alternative<ShaderNodes::BasicType>(leftExprType));
|
|
||||||
|
|
||||||
const ShaderExpressionType& rightExprType = node.right->GetExpressionType();
|
|
||||||
assert(std::holds_alternative<ShaderNodes::BasicType>(rightExprType));
|
|
||||||
|
|
||||||
ShaderNodes::BasicType resultType = std::get<ShaderNodes::BasicType>(resultExprType);
|
|
||||||
ShaderNodes::BasicType leftType = std::get<ShaderNodes::BasicType>(leftExprType);
|
|
||||||
ShaderNodes::BasicType rightType = std::get<ShaderNodes::BasicType>(rightExprType);
|
|
||||||
|
|
||||||
|
|
||||||
UInt32 leftOperand = EvaluateExpression(node.left);
|
|
||||||
UInt32 rightOperand = EvaluateExpression(node.right);
|
|
||||||
UInt32 resultId = AllocateResultId();
|
|
||||||
|
|
||||||
bool swapOperands = false;
|
|
||||||
|
|
||||||
SpirvOp op = [&]
|
|
||||||
{
|
|
||||||
switch (node.op)
|
|
||||||
{
|
|
||||||
case ShaderNodes::BinaryType::Add:
|
|
||||||
{
|
|
||||||
switch (leftType)
|
|
||||||
{
|
|
||||||
case ShaderNodes::BasicType::Float1:
|
|
||||||
case ShaderNodes::BasicType::Float2:
|
|
||||||
case ShaderNodes::BasicType::Float3:
|
|
||||||
case ShaderNodes::BasicType::Float4:
|
|
||||||
case ShaderNodes::BasicType::Mat4x4:
|
|
||||||
return SpirvOp::OpFAdd;
|
|
||||||
|
|
||||||
case ShaderNodes::BasicType::Int1:
|
|
||||||
case ShaderNodes::BasicType::Int2:
|
|
||||||
case ShaderNodes::BasicType::Int3:
|
|
||||||
case ShaderNodes::BasicType::Int4:
|
|
||||||
case ShaderNodes::BasicType::UInt1:
|
|
||||||
case ShaderNodes::BasicType::UInt2:
|
|
||||||
case ShaderNodes::BasicType::UInt3:
|
|
||||||
case ShaderNodes::BasicType::UInt4:
|
|
||||||
return SpirvOp::OpIAdd;
|
|
||||||
|
|
||||||
case ShaderNodes::BasicType::Boolean:
|
|
||||||
case ShaderNodes::BasicType::Sampler2D:
|
|
||||||
case ShaderNodes::BasicType::Void:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case ShaderNodes::BinaryType::Substract:
|
|
||||||
{
|
|
||||||
switch (leftType)
|
|
||||||
{
|
|
||||||
case ShaderNodes::BasicType::Float1:
|
|
||||||
case ShaderNodes::BasicType::Float2:
|
|
||||||
case ShaderNodes::BasicType::Float3:
|
|
||||||
case ShaderNodes::BasicType::Float4:
|
|
||||||
case ShaderNodes::BasicType::Mat4x4:
|
|
||||||
return SpirvOp::OpFSub;
|
|
||||||
|
|
||||||
case ShaderNodes::BasicType::Int1:
|
|
||||||
case ShaderNodes::BasicType::Int2:
|
|
||||||
case ShaderNodes::BasicType::Int3:
|
|
||||||
case ShaderNodes::BasicType::Int4:
|
|
||||||
case ShaderNodes::BasicType::UInt1:
|
|
||||||
case ShaderNodes::BasicType::UInt2:
|
|
||||||
case ShaderNodes::BasicType::UInt3:
|
|
||||||
case ShaderNodes::BasicType::UInt4:
|
|
||||||
return SpirvOp::OpISub;
|
|
||||||
|
|
||||||
case ShaderNodes::BasicType::Boolean:
|
|
||||||
case ShaderNodes::BasicType::Sampler2D:
|
|
||||||
case ShaderNodes::BasicType::Void:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case ShaderNodes::BinaryType::Divide:
|
|
||||||
{
|
|
||||||
switch (leftType)
|
|
||||||
{
|
|
||||||
case ShaderNodes::BasicType::Float1:
|
|
||||||
case ShaderNodes::BasicType::Float2:
|
|
||||||
case ShaderNodes::BasicType::Float3:
|
|
||||||
case ShaderNodes::BasicType::Float4:
|
|
||||||
case ShaderNodes::BasicType::Mat4x4:
|
|
||||||
return SpirvOp::OpFDiv;
|
|
||||||
|
|
||||||
case ShaderNodes::BasicType::Int1:
|
|
||||||
case ShaderNodes::BasicType::Int2:
|
|
||||||
case ShaderNodes::BasicType::Int3:
|
|
||||||
case ShaderNodes::BasicType::Int4:
|
|
||||||
return SpirvOp::OpSDiv;
|
|
||||||
|
|
||||||
case ShaderNodes::BasicType::UInt1:
|
|
||||||
case ShaderNodes::BasicType::UInt2:
|
|
||||||
case ShaderNodes::BasicType::UInt3:
|
|
||||||
case ShaderNodes::BasicType::UInt4:
|
|
||||||
return SpirvOp::OpUDiv;
|
|
||||||
|
|
||||||
case ShaderNodes::BasicType::Boolean:
|
|
||||||
case ShaderNodes::BasicType::Sampler2D:
|
|
||||||
case ShaderNodes::BasicType::Void:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case ShaderNodes::BinaryType::Equality:
|
|
||||||
{
|
|
||||||
switch (leftType)
|
|
||||||
{
|
|
||||||
case ShaderNodes::BasicType::Boolean:
|
|
||||||
return SpirvOp::OpLogicalEqual;
|
|
||||||
|
|
||||||
case ShaderNodes::BasicType::Float1:
|
|
||||||
case ShaderNodes::BasicType::Float2:
|
|
||||||
case ShaderNodes::BasicType::Float3:
|
|
||||||
case ShaderNodes::BasicType::Float4:
|
|
||||||
case ShaderNodes::BasicType::Mat4x4:
|
|
||||||
return SpirvOp::OpFOrdEqual;
|
|
||||||
|
|
||||||
case ShaderNodes::BasicType::Int1:
|
|
||||||
case ShaderNodes::BasicType::Int2:
|
|
||||||
case ShaderNodes::BasicType::Int3:
|
|
||||||
case ShaderNodes::BasicType::Int4:
|
|
||||||
case ShaderNodes::BasicType::UInt1:
|
|
||||||
case ShaderNodes::BasicType::UInt2:
|
|
||||||
case ShaderNodes::BasicType::UInt3:
|
|
||||||
case ShaderNodes::BasicType::UInt4:
|
|
||||||
return SpirvOp::OpIEqual;
|
|
||||||
|
|
||||||
case ShaderNodes::BasicType::Sampler2D:
|
|
||||||
case ShaderNodes::BasicType::Void:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case ShaderNodes::BinaryType::Multiply:
|
|
||||||
{
|
|
||||||
switch (leftType)
|
|
||||||
{
|
|
||||||
case ShaderNodes::BasicType::Float1:
|
|
||||||
{
|
|
||||||
switch (rightType)
|
|
||||||
{
|
|
||||||
case ShaderNodes::BasicType::Float1:
|
|
||||||
return SpirvOp::OpFMul;
|
|
||||||
|
|
||||||
case ShaderNodes::BasicType::Float2:
|
|
||||||
case ShaderNodes::BasicType::Float3:
|
|
||||||
case ShaderNodes::BasicType::Float4:
|
|
||||||
swapOperands = true;
|
|
||||||
return SpirvOp::OpVectorTimesScalar;
|
|
||||||
|
|
||||||
case ShaderNodes::BasicType::Mat4x4:
|
|
||||||
swapOperands = true;
|
|
||||||
return SpirvOp::OpMatrixTimesScalar;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ShaderNodes::BasicType::Float2:
|
|
||||||
case ShaderNodes::BasicType::Float3:
|
|
||||||
case ShaderNodes::BasicType::Float4:
|
|
||||||
{
|
|
||||||
switch (rightType)
|
|
||||||
{
|
|
||||||
case ShaderNodes::BasicType::Float1:
|
|
||||||
return SpirvOp::OpVectorTimesScalar;
|
|
||||||
|
|
||||||
case ShaderNodes::BasicType::Float2:
|
|
||||||
case ShaderNodes::BasicType::Float3:
|
|
||||||
case ShaderNodes::BasicType::Float4:
|
|
||||||
return SpirvOp::OpFMul;
|
|
||||||
|
|
||||||
case ShaderNodes::BasicType::Mat4x4:
|
|
||||||
return SpirvOp::OpVectorTimesMatrix;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ShaderNodes::BasicType::Int1:
|
|
||||||
case ShaderNodes::BasicType::Int2:
|
|
||||||
case ShaderNodes::BasicType::Int3:
|
|
||||||
case ShaderNodes::BasicType::Int4:
|
|
||||||
case ShaderNodes::BasicType::UInt1:
|
|
||||||
case ShaderNodes::BasicType::UInt2:
|
|
||||||
case ShaderNodes::BasicType::UInt3:
|
|
||||||
case ShaderNodes::BasicType::UInt4:
|
|
||||||
return SpirvOp::OpIMul;
|
|
||||||
|
|
||||||
case ShaderNodes::BasicType::Mat4x4:
|
|
||||||
{
|
|
||||||
switch (rightType)
|
|
||||||
{
|
|
||||||
case ShaderNodes::BasicType::Float1: return SpirvOp::OpMatrixTimesScalar;
|
|
||||||
case ShaderNodes::BasicType::Float4: return SpirvOp::OpMatrixTimesVector;
|
|
||||||
case ShaderNodes::BasicType::Mat4x4: return SpirvOp::OpMatrixTimesMatrix;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(false);
|
|
||||||
throw std::runtime_error("unexpected binary operation");
|
|
||||||
}();
|
|
||||||
|
|
||||||
if (swapOperands)
|
|
||||||
std::swap(leftOperand, rightOperand);
|
|
||||||
|
|
||||||
m_currentState->instructions.Append(op, GetTypeId(resultType), resultId, leftOperand, rightOperand);
|
|
||||||
PushResultId(resultId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpirvWriter::Visit(ShaderNodes::Cast& node)
|
|
||||||
{
|
|
||||||
const ShaderExpressionType& targetExprType = node.exprType;
|
|
||||||
assert(std::holds_alternative<ShaderNodes::BasicType>(targetExprType));
|
|
||||||
|
|
||||||
ShaderNodes::BasicType targetType = std::get<ShaderNodes::BasicType>(targetExprType);
|
|
||||||
|
|
||||||
StackVector<UInt32> exprResults = NazaraStackVector(UInt32, node.expressions.size());
|
|
||||||
|
|
||||||
for (const auto& exprPtr : node.expressions)
|
|
||||||
{
|
|
||||||
if (!exprPtr)
|
|
||||||
break;
|
|
||||||
|
|
||||||
exprResults.push_back(EvaluateExpression(exprPtr));
|
|
||||||
}
|
|
||||||
|
|
||||||
UInt32 resultId = AllocateResultId();
|
|
||||||
|
|
||||||
m_currentState->instructions.AppendVariadic(SpirvOp::OpCompositeConstruct, [&](const auto& appender)
|
|
||||||
{
|
|
||||||
appender(GetTypeId(targetType));
|
|
||||||
appender(resultId);
|
|
||||||
|
|
||||||
for (UInt32 exprResultId : exprResults)
|
|
||||||
appender(exprResultId);
|
|
||||||
});
|
|
||||||
|
|
||||||
PushResultId(resultId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpirvWriter::Visit(ShaderNodes::Constant& node)
|
|
||||||
{
|
|
||||||
std::visit([&] (const auto& value)
|
|
||||||
{
|
|
||||||
PushResultId(GetConstantId(value));
|
|
||||||
}, node.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpirvWriter::Visit(ShaderNodes::DeclareVariable& node)
|
|
||||||
{
|
|
||||||
if (node.expression)
|
|
||||||
{
|
|
||||||
assert(node.variable->GetType() == ShaderNodes::VariableType::LocalVariable);
|
|
||||||
|
|
||||||
const auto& localVar = static_cast<const ShaderNodes::LocalVariable&>(*node.variable);
|
|
||||||
m_currentState->varToResult[localVar.name] = EvaluateExpression(node.expression);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpirvWriter::Visit(ShaderNodes::ExpressionStatement& node)
|
|
||||||
{
|
|
||||||
Visit(node.expression);
|
|
||||||
PopResultId();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpirvWriter::Visit(ShaderNodes::Identifier& node)
|
|
||||||
{
|
|
||||||
Visit(node.var);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpirvWriter::Visit(ShaderNodes::IntrinsicCall& node)
|
|
||||||
{
|
|
||||||
switch (node.intrinsic)
|
|
||||||
{
|
|
||||||
case ShaderNodes::IntrinsicType::DotProduct:
|
|
||||||
{
|
|
||||||
const ShaderExpressionType& vecExprType = node.parameters[0]->GetExpressionType();
|
|
||||||
assert(std::holds_alternative<ShaderNodes::BasicType>(vecExprType));
|
|
||||||
|
|
||||||
ShaderNodes::BasicType vecType = std::get<ShaderNodes::BasicType>(vecExprType);
|
|
||||||
|
|
||||||
UInt32 typeId = GetTypeId(node.GetComponentType(vecType));
|
|
||||||
|
|
||||||
UInt32 vec1 = EvaluateExpression(node.parameters[0]);
|
|
||||||
UInt32 vec2 = EvaluateExpression(node.parameters[1]);
|
|
||||||
|
|
||||||
UInt32 resultId = AllocateResultId();
|
|
||||||
|
|
||||||
m_currentState->instructions.Append(SpirvOp::OpDot, typeId, resultId, vec1, vec2);
|
|
||||||
PushResultId(resultId);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ShaderNodes::IntrinsicType::CrossProduct:
|
|
||||||
default:
|
|
||||||
throw std::runtime_error("not yet implemented");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpirvWriter::Visit(ShaderNodes::Sample2D& node)
|
|
||||||
{
|
|
||||||
UInt32 typeId = GetTypeId(ShaderNodes::BasicType::Float4);
|
|
||||||
|
|
||||||
UInt32 samplerId = EvaluateExpression(node.sampler);
|
|
||||||
UInt32 coordinatesId = EvaluateExpression(node.coordinates);
|
|
||||||
UInt32 resultId = AllocateResultId();
|
|
||||||
|
|
||||||
m_currentState->instructions.Append(SpirvOp::OpImageSampleImplicitLod, typeId, resultId, samplerId, coordinatesId);
|
|
||||||
PushResultId(resultId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpirvWriter::Visit(ShaderNodes::StatementBlock& node)
|
|
||||||
{
|
|
||||||
for (auto& statement : node.statements)
|
|
||||||
Visit(statement);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpirvWriter::Visit(ShaderNodes::SwizzleOp& node)
|
|
||||||
{
|
|
||||||
const ShaderExpressionType& targetExprType = node.GetExpressionType();
|
|
||||||
assert(std::holds_alternative<ShaderNodes::BasicType>(targetExprType));
|
|
||||||
|
|
||||||
ShaderNodes::BasicType targetType = std::get<ShaderNodes::BasicType>(targetExprType);
|
|
||||||
|
|
||||||
UInt32 exprResultId = EvaluateExpression(node.expression);
|
|
||||||
UInt32 resultId = AllocateResultId();
|
|
||||||
|
|
||||||
if (node.componentCount > 1)
|
|
||||||
{
|
|
||||||
// Swizzling is implemented via SpirvOp::OpVectorShuffle using the same vector twice as operands
|
|
||||||
m_currentState->instructions.AppendVariadic(SpirvOp::OpVectorShuffle, [&](const auto& appender)
|
|
||||||
{
|
|
||||||
appender(GetTypeId(targetType));
|
|
||||||
appender(resultId);
|
|
||||||
appender(exprResultId);
|
|
||||||
appender(exprResultId);
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < node.componentCount; ++i)
|
|
||||||
appender(UInt32(node.components[0]) - UInt32(node.components[i]));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Extract a single component from the vector
|
|
||||||
assert(node.componentCount == 1);
|
|
||||||
|
|
||||||
m_currentState->instructions.Append(SpirvOp::OpCompositeExtract, GetTypeId(targetType), resultId, exprResultId, UInt32(node.components[0]) - UInt32(ShaderNodes::SwizzleComponent::First) );
|
|
||||||
}
|
|
||||||
|
|
||||||
PushResultId(resultId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpirvWriter::Visit(ShaderNodes::BuiltinVariable& var)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("not implemented yet");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpirvWriter::Visit(ShaderNodes::InputVariable& var)
|
|
||||||
{
|
|
||||||
auto it = m_currentState->inputIds.find(var.name);
|
|
||||||
assert(it != m_currentState->inputIds.end());
|
|
||||||
|
|
||||||
PushResultId(ReadVariable(it.value()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpirvWriter::Visit(ShaderNodes::LocalVariable& var)
|
|
||||||
{
|
|
||||||
auto it = m_currentState->varToResult.find(var.name);
|
|
||||||
assert(it != m_currentState->varToResult.end());
|
|
||||||
|
|
||||||
PushResultId(it->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpirvWriter::Visit(ShaderNodes::OutputVariable& var)
|
|
||||||
{
|
|
||||||
auto it = m_currentState->outputIds.find(var.name);
|
|
||||||
assert(it != m_currentState->outputIds.end());
|
|
||||||
|
|
||||||
PushResultId(ReadVariable(it.value()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpirvWriter::Visit(ShaderNodes::ParameterVariable& var)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("not implemented yet");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpirvWriter::Visit(ShaderNodes::UniformVariable& var)
|
|
||||||
{
|
|
||||||
auto it = m_currentState->uniformIds.find(var.name);
|
|
||||||
assert(it != m_currentState->uniformIds.end());
|
|
||||||
|
|
||||||
PushResultId(ReadVariable(it.value()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpirvWriter::MergeBlocks(std::vector<UInt32>& output, const SpirvSection& from)
|
void SpirvWriter::MergeBlocks(std::vector<UInt32>& output, const SpirvSection& from)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue