This commit is contained in:
Lynix
2020-08-02 20:42:51 +02:00
parent 10860ed562
commit 50bd150345
8 changed files with 3626 additions and 113 deletions

View File

@@ -5,19 +5,119 @@
#include <Nazara/Renderer/SpirvWriter.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Renderer/ShaderValidator.hpp>
#include <tsl/ordered_map.h>
#include <SpirV/spirv.h>
#include <SpirV/GLSL.std.450.h>
#include <cassert>
#include <stdexcept>
#include <type_traits>
#include <vector>
#include <Nazara/Renderer/Debug.hpp>
namespace Nz
{
namespace
{
class PreVisitor : public ShaderRecursiveVisitor, public ShaderVarVisitor
{
public:
using BuiltinContainer = std::unordered_set<std::shared_ptr<const ShaderNodes::BuiltinVariable>>;
using ExtInstList = std::unordered_set<std::string>;
using LocalContainer = std::unordered_set<std::shared_ptr<const ShaderNodes::LocalVariable>>;
using ParameterContainer = std::unordered_set< std::shared_ptr<const ShaderNodes::ParameterVariable>>;
using ShaderRecursiveVisitor::Visit;
using ShaderVarVisitor::Visit;
void Visit(const ShaderNodes::DeclareVariable& node) override
{
Visit(node.variable);
}
void Visit(const ShaderNodes::Identifier& node) override
{
Visit(node.var);
}
void Visit(const ShaderNodes::IntrinsicCall& node) override
{
ShaderRecursiveVisitor::Visit(node);
switch (node.intrinsic)
{
// Require GLSL.std.450
case ShaderNodes::IntrinsicType::CrossProduct:
extInsts.emplace("GLSL.std.450");
break;
// Part of SPIR-V core
case ShaderNodes::IntrinsicType::DotProduct:
break;
}
}
void Visit(const ShaderNodes::BuiltinVariable& var) override
{
builtinVars.insert(std::static_pointer_cast<const ShaderNodes::BuiltinVariable>(var.shared_from_this()));
}
void Visit(const ShaderNodes::InputVariable& var) override
{
/* Handled by ShaderAst */
}
void Visit(const ShaderNodes::LocalVariable& var) override
{
localVars.insert(std::static_pointer_cast<const ShaderNodes::LocalVariable>(var.shared_from_this()));
}
void Visit(const ShaderNodes::OutputVariable& var) override
{
/* Handled by ShaderAst */
}
void Visit(const ShaderNodes::ParameterVariable& var) override
{
paramVars.insert(std::static_pointer_cast<const ShaderNodes::ParameterVariable>(var.shared_from_this()));
}
void Visit(const ShaderNodes::UniformVariable& var) override
{
/* Handled by ShaderAst */
}
BuiltinContainer builtinVars;
ExtInstList extInsts;
LocalContainer localVars;
ParameterContainer paramVars;
};
}
struct SpirvWriter::Opcode
{
SpvOp op;
};
struct SpirvWriter::State
{
std::size_t boundIndex;
std::unordered_map<std::string, UInt32> extensionInstructions;
std::unordered_map<ShaderNodes::BuiltinEntry, UInt32> builtinIds;
tsl::ordered_map<ShaderExpressionType, UInt32> typeIds;
std::vector<UInt32> funcIds;
std::vector<UInt32> funcTypeIds;
std::vector<UInt32> inputIds;
std::vector<UInt32> outputIds;
std::vector<UInt32> uniformIds;
UInt32 nextVarIndex = 1;
// Output
std::vector<UInt32>* output;
std::vector<UInt32> header;
std::vector<UInt32> info;
std::vector<UInt32> instructions;
};
SpirvWriter::SpirvWriter() :
m_currentState(nullptr)
{
@@ -38,63 +138,78 @@ namespace Nz
m_currentState = nullptr;
});
PreVisitor preVisitor;
for (const auto& func : shader.GetFunctions())
preVisitor.Visit(func.statement);
// Register all extended instruction sets
for (const std::string& extInst : preVisitor.extInsts)
m_currentState->extensionInstructions[extInst] = AllocateResultId();
// Register all types
state.output = &state.instructions;
for (const auto& func : shader.GetFunctions())
{
ProcessType(func.returnType);
for (const auto& param : func.parameters)
ProcessType(param.type);
m_currentState->funcTypeIds.push_back(AllocateResultId());
}
for (const auto& input : shader.GetInputs())
ProcessType(input.type);
for (const auto& output : shader.GetOutputs())
ProcessType(output.type);
for (const auto& uniform : shader.GetUniforms())
ProcessType(uniform.type);
for (const auto& local : preVisitor.localVars)
ProcessType(local->type);
// Register result id and debug infos for global variables/functions
state.output = &state.info;
for (const auto& input : shader.GetInputs())
{
UInt32 resultId = AllocateResultId();
Append(Opcode{ SpvOpName }, resultId, input.name);
m_currentState->inputIds.push_back(resultId);
}
for (const auto& output : shader.GetOutputs())
{
UInt32 resultId = AllocateResultId();
Append(Opcode{ SpvOpName }, resultId, output.name);
m_currentState->outputIds.push_back(resultId);
}
for (const auto& uniform : shader.GetUniforms())
{
UInt32 resultId = AllocateResultId();
Append(Opcode{ SpvOpName }, resultId, uniform.name);
m_currentState->uniformIds.push_back(resultId);
}
for (const auto& func : shader.GetFunctions())
{
UInt32 resultId = AllocateResultId();
Append(Opcode{ SpvOpName }, resultId, func.name);
m_currentState->funcIds.push_back(resultId);
}
state.output = &state.header;
AppendHeader();
std::vector<UInt32> ret = std::move(state.output);
return ret;
}
void SpirvWriter::SetEnv(Environment environment)
{
m_environment = std::move(environment);
}
void SpirvWriter::Append(const std::string_view& str)
{
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);
}
}
void SpirvWriter::Append(const Opcode& opcode, unsigned int wordCount)
{
Append(UInt32(opcode.op) | UInt32(wordCount) << 16);
}
void SpirvWriter::Append(UInt32 codepoint)
{
assert(m_currentState);
m_currentState->output.push_back(codepoint);
}
void SpirvWriter::Append(std::initializer_list<UInt32> codepoints)
{
for (UInt32 cp : codepoints)
Append(cp);
}
void SpirvWriter::AppendHeader()
{
Append(SpvMagicNumber); //< Spir-V magic number
Append(0x00010000); //< Spir-V version number (1.0 for compatibility)
Append(0); //< Generator magic number (TODO: Register generator to Khronos)
Append(1); //< Bound (ID count)
Append(0); //< Instruction schema (required to be 0 for now)
Append(Opcode{ SpvOpCapability }, SpvCapabilityShader);
Append(Opcode{ SpvOpExtInstImport }, 1, "GLSL.std.450");
Append(Opcode{ SpvOpMemoryModel }, SpvAddressingModelLogical, SpvMemoryModelGLSL450);
assert(m_context.shader);
/*assert(m_context.shader);
switch (m_context.shader->GetStage())
{
case ShaderStageType::Fragment:
@@ -104,67 +219,210 @@ namespace Nz
default:
break;
}*/
state.header[state.boundIndex] = state.nextVarIndex;
std::vector<UInt32> ret;
ret.reserve(state.header.size() + state.info.size() + state.instructions.size());
MergeBlocks(ret, state.header);
MergeBlocks(ret, state.info);
MergeBlocks(ret, state.instructions);
return ret;
}
void SpirvWriter::SetEnv(Environment environment)
{
m_environment = std::move(environment);
}
std::size_t Nz::SpirvWriter::Append(UInt32 value)
{
std::size_t offset = GetOutputOffset();
m_currentState->output->push_back(value);
return offset;
}
std::size_t SpirvWriter::Append(const Opcode& opcode, unsigned int wordCount)
{
return Append(UInt32(opcode.op) | UInt32(wordCount) << 16);
}
UInt32 Nz::SpirvWriter::AllocateResultId()
{
return m_currentState->nextVarIndex++;
}
void SpirvWriter::AppendHeader()
{
Append(SpvMagicNumber); //< Spir-V magic number
UInt32 version = (m_environment.spvMajorVersion << 16) | m_environment.spvMinorVersion << 8;
Append(version); //< Spir-V version number (1.0 for compatibility)
Append(0); //< Generator identifier (TODO: Register generator to Khronos)
m_currentState->boundIndex = Append(0); //< Bound (ID count), will be filled later
Append(0); //< Instruction schema (required to be 0 for now)
Append(Opcode{ SpvOpCapability }, SpvCapabilityShader);
for (const auto& [extInst, resultId] : m_currentState->extensionInstructions)
Append(Opcode{ SpvOpExtInstImport }, resultId, extInst);
Append(Opcode{ SpvOpMemoryModel }, SpvAddressingModelLogical, SpvMemoryModelGLSL450);
}
void SpirvWriter::AppendTypes()
{
for (const auto& [type, typeId] : m_currentState->typeIds.values_container())
{
UInt32 resultId = typeId;
// Register sub-types, if any
std::visit([&](auto&& arg)
{
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, ShaderNodes::BasicType>)
{
// In SPIR-V, vec3 (for example) depends on float
UInt32 depResultId;
if (ShaderNodes::Node::GetComponentCount(arg) != 1)
depResultId = ProcessType(ShaderNodes::Node::GetComponentType(arg));
switch (arg)
{
case ShaderNodes::BasicType::Boolean:
Append(Opcode{ SpvOpTypeBool }, resultId);
break;
case ShaderNodes::BasicType::Float1:
Append(Opcode{ SpvOpTypeFloat }, resultId);
break;
case ShaderNodes::BasicType::Float2:
case ShaderNodes::BasicType::Float3:
case ShaderNodes::BasicType::Float4:
case ShaderNodes::BasicType::Mat4x4:
case ShaderNodes::BasicType::Sampler2D:
case ShaderNodes::BasicType::Void:
Append(Opcode{ SpvOpTypeVoid }, resultId);
break;
}
}
else if constexpr (std::is_same_v<T, std::string>)
{
// Register struct members type
const auto& structs = m_context.shader->GetStructs();
auto it = std::find_if(structs.begin(), structs.end(), [&](const auto& s) { return s.name == arg; });
if (it == structs.end())
throw std::runtime_error("struct " + arg + " has not been defined");
const ShaderAst::Struct& s = *it;
for (const auto& member : s.members)
ProcessType(member.type);
}
else
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
}, type);
}
}
std::size_t SpirvWriter::GetOutputOffset() const
{
assert(m_currentState);
return m_currentState->output->size();
}
UInt32 SpirvWriter::ProcessType(ShaderExpressionType type)
{
auto it = m_currentState->typeIds.find(type);
if (it == m_currentState->typeIds.end())
{
// Register sub-types, if any
std::visit([&](auto&& arg)
{
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, ShaderNodes::BasicType>)
{
// In SPIR-V, vec3 (for example) depends on float
if (ShaderNodes::Node::GetComponentCount(arg) != 1)
ProcessType(ShaderNodes::Node::GetComponentType(arg));
}
else if constexpr (std::is_same_v<T, std::string>)
{
// Register struct members type
const auto& structs = m_context.shader->GetStructs();
auto it = std::find_if(structs.begin(), structs.end(), [&](const auto& s) { return s.name == arg; });
if (it == structs.end())
throw std::runtime_error("struct " + arg + " has not been defined");
const ShaderAst::Struct& s = *it;
for (const auto& member : s.members)
ProcessType(member.type);
}
else
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
}, type);
it = m_currentState->typeIds.emplace(std::move(type), AllocateResultId()).first;
}
return it->second;
}
void SpirvWriter::Visit(const ShaderNodes::ExpressionPtr& expr, bool encloseIfRequired)
{
}
void SpirvWriter::Visit(const ShaderNodes::AccessMember& node)
void SpirvWriter::Visit(const ShaderNodes::AccessMember& /*node*/)
{
}
void SpirvWriter::Visit(const ShaderNodes::AssignOp& node)
void SpirvWriter::Visit(const ShaderNodes::AssignOp& /*node*/)
{
}
void SpirvWriter::Visit(const ShaderNodes::Branch& node)
void SpirvWriter::Visit(const ShaderNodes::Branch& /*node*/)
{
}
void SpirvWriter::Visit(const ShaderNodes::BinaryOp& node)
void SpirvWriter::Visit(const ShaderNodes::BinaryOp& /*node*/)
{
}
void SpirvWriter::Visit(const ShaderNodes::BuiltinVariable& var)
void SpirvWriter::Visit(const ShaderNodes::Cast& /*node*/)
{
}
void SpirvWriter::Visit(const ShaderNodes::Cast& node)
void SpirvWriter::Visit(const ShaderNodes::Constant& /*node*/)
{
}
void SpirvWriter::Visit(const ShaderNodes::Constant& node)
void SpirvWriter::Visit(const ShaderNodes::DeclareVariable& /*node*/)
{
}
void SpirvWriter::Visit(const ShaderNodes::DeclareVariable& node)
void SpirvWriter::Visit(const ShaderNodes::ExpressionStatement& /*node*/)
{
}
void SpirvWriter::Visit(const ShaderNodes::ExpressionStatement& node)
void SpirvWriter::Visit(const ShaderNodes::Identifier& /*node*/)
{
}
void SpirvWriter::Visit(const ShaderNodes::Identifier& node)
void SpirvWriter::Visit(const ShaderNodes::IntrinsicCall& /*node*/)
{
}
void SpirvWriter::Visit(const ShaderNodes::InputVariable& var)
void SpirvWriter::Visit(const ShaderNodes::Sample2D& /*node*/)
{
}
void SpirvWriter::Visit(const ShaderNodes::IntrinsicCall& node)
void SpirvWriter::Visit(const ShaderNodes::StatementBlock& /*node*/)
{
}
void SpirvWriter::Visit(const ShaderNodes::LocalVariable& var)
void SpirvWriter::Visit(const ShaderNodes::SwizzleOp& /*node*/)
{
}
void SpirvWriter::Visit(const ShaderNodes::ParameterVariable& var)
{
}
void SpirvWriter::Visit(const ShaderNodes::OutputVariable& var)
{
}
void SpirvWriter::Visit(const ShaderNodes::Sample2D& node)
{
}
void SpirvWriter::Visit(const ShaderNodes::StatementBlock& node)
{
}
void SpirvWriter::Visit(const ShaderNodes::SwizzleOp& node)
{
}
void SpirvWriter::Visit(const ShaderNodes::UniformVariable& var)
void SpirvWriter::MergeBlocks(std::vector<UInt32>& output, const std::vector<UInt32>& from)
{
std::size_t prevSize = output.size();
output.resize(prevSize + from.size());
std::copy(from.begin(), from.end(), output.begin() + prevSize);
}
}

View File

@@ -57,7 +57,7 @@ namespace Nz
String appName = "Another application made with Nazara Engine";
String engineName = "Nazara Engine - Vulkan Renderer";
UInt32 appVersion = VK_MAKE_VERSION(1, 0, 0);
constexpr UInt32 appVersion = VK_MAKE_VERSION(1, 0, 0);
UInt32 engineVersion = VK_MAKE_VERSION(1, 0, 0);
parameters.GetStringParameter("VkAppInfo_OverrideApplicationName", &appName);